1/* GDB/Scheme exception support. 2 3 Copyright (C) 2014-2020 Free Software Foundation, Inc. 4 5 This file is part of GDB. 6 7 This program is free software; you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 3 of the License, or 10 (at your option) any later version. 11 12 This program is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with this program. If not, see <http://www.gnu.org/licenses/>. */ 19 20/* See README file in this directory for implementation notes, coding 21 conventions, et.al. */ 22 23/* Notes: 24 25 IWBN to support SRFI 34/35. At the moment we follow Guile's own 26 exception mechanism. 27 28 The non-static functions in this file have prefix gdbscm_ and 29 not exscm_ on purpose. */ 30 31#include "defs.h" 32#include <signal.h> 33#include "guile-internal.h" 34 35/* The <gdb:exception> smob. 36 This is used to record and handle Scheme exceptions. 37 One important invariant is that <gdb:exception> smobs are never a valid 38 result of a function, other than to signify an exception occurred. */ 39 40typedef struct 41{ 42 /* This always appears first. */ 43 gdb_smob base; 44 45 /* The key and args parameters to "throw". */ 46 SCM key; 47 SCM args; 48} exception_smob; 49 50static const char exception_smob_name[] = "gdb:exception"; 51 52/* The tag Guile knows the exception smob by. */ 53static scm_t_bits exception_smob_tag; 54 55/* A generic error in struct gdb_exception. 56 I.e., not RETURN_QUIT and not MEMORY_ERROR. */ 57static SCM error_symbol; 58 59/* An error occurred accessing inferior memory. 60 This is not a Scheme programming error. */ 61static SCM memory_error_symbol; 62 63/* User interrupt, e.g., RETURN_QUIT in struct gdb_exception. */ 64static SCM signal_symbol; 65 66/* A user error, e.g., bad arg to gdb command. */ 67static SCM user_error_symbol; 68 69/* Printing the stack is done by first capturing the stack and recording it in 70 a <gdb:exception> object with this key and with the ARGS field set to 71 (cons real-key (cons stack real-args)). 72 See gdbscm_make_exception_with_stack. */ 73static SCM with_stack_error_symbol; 74 75/* The key to use for an invalid object exception. An invalid object is one 76 where the underlying object has been removed from GDB. */ 77SCM gdbscm_invalid_object_error_symbol; 78 79/* Values for "guile print-stack" as symbols. */ 80static SCM none_symbol; 81static SCM message_symbol; 82static SCM full_symbol; 83 84static const char percent_print_exception_message_name[] = 85 "%print-exception-message"; 86 87/* Variable containing %print-exception-message. 88 It is not defined until late in initialization, after our init routine 89 has run. Cope by looking it up lazily. */ 90static SCM percent_print_exception_message_var = SCM_BOOL_F; 91 92static const char percent_print_exception_with_stack_name[] = 93 "%print-exception-with-stack"; 94 95/* Variable containing %print-exception-with-stack. 96 It is not defined until late in initialization, after our init routine 97 has run. Cope by looking it up lazily. */ 98static SCM percent_print_exception_with_stack_var = SCM_BOOL_F; 99 100/* Counter to keep track of the number of times we create a <gdb:exception> 101 object, for performance monitoring purposes. */ 102static unsigned long gdbscm_exception_count = 0; 103 104/* Administrivia for exception smobs. */ 105 106/* The smob "print" function for <gdb:exception>. */ 107 108static int 109exscm_print_exception_smob (SCM self, SCM port, scm_print_state *pstate) 110{ 111 exception_smob *e_smob = (exception_smob *) SCM_SMOB_DATA (self); 112 113 gdbscm_printf (port, "#<%s ", exception_smob_name); 114 scm_write (e_smob->key, port); 115 scm_puts (" ", port); 116 scm_write (e_smob->args, port); 117 scm_puts (">", port); 118 119 scm_remember_upto_here_1 (self); 120 121 /* Non-zero means success. */ 122 return 1; 123} 124 125/* (make-exception key args) -> <gdb:exception> */ 126 127SCM 128gdbscm_make_exception (SCM key, SCM args) 129{ 130 exception_smob *e_smob = (exception_smob *) 131 scm_gc_malloc (sizeof (exception_smob), exception_smob_name); 132 SCM smob; 133 134 e_smob->key = key; 135 e_smob->args = args; 136 smob = scm_new_smob (exception_smob_tag, (scm_t_bits) e_smob); 137 gdbscm_init_gsmob (&e_smob->base); 138 139 ++gdbscm_exception_count; 140 141 return smob; 142} 143 144/* Return non-zero if SCM is a <gdb:exception> object. */ 145 146int 147gdbscm_is_exception (SCM scm) 148{ 149 return SCM_SMOB_PREDICATE (exception_smob_tag, scm); 150} 151 152/* (exception? scm) -> boolean */ 153 154static SCM 155gdbscm_exception_p (SCM scm) 156{ 157 return scm_from_bool (gdbscm_is_exception (scm)); 158} 159 160/* (exception-key <gdb:exception>) -> key */ 161 162SCM 163gdbscm_exception_key (SCM self) 164{ 165 exception_smob *e_smob; 166 167 SCM_ASSERT_TYPE (gdbscm_is_exception (self), self, SCM_ARG1, FUNC_NAME, 168 "gdb:exception"); 169 170 e_smob = (exception_smob *) SCM_SMOB_DATA (self); 171 return e_smob->key; 172} 173 174/* (exception-args <gdb:exception>) -> arg-list */ 175 176SCM 177gdbscm_exception_args (SCM self) 178{ 179 exception_smob *e_smob; 180 181 SCM_ASSERT_TYPE (gdbscm_is_exception (self), self, SCM_ARG1, FUNC_NAME, 182 "gdb:exception"); 183 184 e_smob = (exception_smob *) SCM_SMOB_DATA (self); 185 return e_smob->args; 186} 187 188/* Wrap an exception in a <gdb:exception> object that includes STACK. 189 gdbscm_print_exception_with_stack knows how to unwrap it. */ 190 191SCM 192gdbscm_make_exception_with_stack (SCM key, SCM args, SCM stack) 193{ 194 return gdbscm_make_exception (with_stack_error_symbol, 195 scm_cons (key, scm_cons (stack, args))); 196} 197 198/* Version of scm_error_scm that creates a gdb:exception object that can later 199 be passed to gdbscm_throw. 200 KEY is a symbol denoting the kind of error. 201 SUBR is either #f or a string marking the function in which the error 202 occurred. 203 MESSAGE is either #f or the error message string. It may contain ~a and ~s 204 modifiers, provided by ARGS. 205 ARGS is a list of args to MESSAGE. 206 DATA is an arbitrary object, its value depends on KEY. The value to pass 207 here is a bit underspecified by Guile. */ 208 209SCM 210gdbscm_make_error_scm (SCM key, SCM subr, SCM message, SCM args, SCM data) 211{ 212 return gdbscm_make_exception (key, scm_list_4 (subr, message, args, data)); 213} 214 215/* Version of scm_error that creates a gdb:exception object that can later 216 be passed to gdbscm_throw. 217 See gdbscm_make_error_scm for a description of the arguments. */ 218 219SCM 220gdbscm_make_error (SCM key, const char *subr, const char *message, 221 SCM args, SCM data) 222{ 223 return gdbscm_make_error_scm 224 (key, 225 subr == NULL ? SCM_BOOL_F : scm_from_latin1_string (subr), 226 message == NULL ? SCM_BOOL_F : scm_from_latin1_string (message), 227 args, data); 228} 229 230/* Version of SCM_ASSERT_TYPE/scm_wrong_type_arg_msg that creates a 231 gdb:exception object that can later be passed to gdbscm_throw. */ 232 233SCM 234gdbscm_make_type_error (const char *subr, int arg_pos, SCM bad_value, 235 const char *expected_type) 236{ 237 char *msg; 238 SCM result; 239 240 if (arg_pos > 0) 241 { 242 if (expected_type != NULL) 243 { 244 msg = xstrprintf (_("Wrong type argument in position %d" 245 " (expecting %s): ~S"), 246 arg_pos, expected_type); 247 } 248 else 249 { 250 msg = xstrprintf (_("Wrong type argument in position %d: ~S"), 251 arg_pos); 252 } 253 } 254 else 255 { 256 if (expected_type != NULL) 257 { 258 msg = xstrprintf (_("Wrong type argument (expecting %s): ~S"), 259 expected_type); 260 } 261 else 262 msg = xstrprintf (_("Wrong type argument: ~S")); 263 } 264 265 result = gdbscm_make_error (scm_arg_type_key, subr, msg, 266 scm_list_1 (bad_value), scm_list_1 (bad_value)); 267 xfree (msg); 268 return result; 269} 270 271/* A variant of gdbscm_make_type_error for non-type argument errors. 272 ERROR_PREFIX and ERROR are combined to build the error message. 273 Care needs to be taken so that the i18n composed form is still 274 reasonable, but no one is going to translate these anyway so we don't 275 worry too much. 276 ERROR_PREFIX may be NULL, ERROR may not be NULL. */ 277 278static SCM 279gdbscm_make_arg_error (SCM key, const char *subr, int arg_pos, SCM bad_value, 280 const char *error_prefix, const char *error) 281{ 282 char *msg; 283 SCM result; 284 285 if (error_prefix != NULL) 286 { 287 if (arg_pos > 0) 288 { 289 msg = xstrprintf (_("%s %s in position %d: ~S"), 290 error_prefix, error, arg_pos); 291 } 292 else 293 msg = xstrprintf (_("%s %s: ~S"), error_prefix, error); 294 } 295 else 296 { 297 if (arg_pos > 0) 298 msg = xstrprintf (_("%s in position %d: ~S"), error, arg_pos); 299 else 300 msg = xstrprintf (_("%s: ~S"), error); 301 } 302 303 result = gdbscm_make_error (key, subr, msg, 304 scm_list_1 (bad_value), scm_list_1 (bad_value)); 305 xfree (msg); 306 return result; 307} 308 309/* Make an invalid-object error <gdb:exception> object. 310 OBJECT is the name of the kind of object that is invalid. */ 311 312SCM 313gdbscm_make_invalid_object_error (const char *subr, int arg_pos, SCM bad_value, 314 const char *object) 315{ 316 return gdbscm_make_arg_error (gdbscm_invalid_object_error_symbol, 317 subr, arg_pos, bad_value, 318 _("Invalid object:"), object); 319} 320 321/* Throw an invalid-object error. 322 OBJECT is the name of the kind of object that is invalid. */ 323 324void 325gdbscm_invalid_object_error (const char *subr, int arg_pos, SCM bad_value, 326 const char *object) 327{ 328 SCM exception 329 = gdbscm_make_invalid_object_error (subr, arg_pos, bad_value, object); 330 331 gdbscm_throw (exception); 332} 333 334/* Make an out-of-range error <gdb:exception> object. */ 335 336SCM 337gdbscm_make_out_of_range_error (const char *subr, int arg_pos, SCM bad_value, 338 const char *error) 339{ 340 return gdbscm_make_arg_error (scm_out_of_range_key, 341 subr, arg_pos, bad_value, 342 _("Out of range:"), error); 343} 344 345/* Throw an out-of-range error. 346 This is the standard Guile out-of-range exception. */ 347 348void 349gdbscm_out_of_range_error (const char *subr, int arg_pos, SCM bad_value, 350 const char *error) 351{ 352 SCM exception 353 = gdbscm_make_out_of_range_error (subr, arg_pos, bad_value, error); 354 355 gdbscm_throw (exception); 356} 357 358/* Make a misc-error <gdb:exception> object. */ 359 360SCM 361gdbscm_make_misc_error (const char *subr, int arg_pos, SCM bad_value, 362 const char *error) 363{ 364 return gdbscm_make_arg_error (scm_misc_error_key, 365 subr, arg_pos, bad_value, NULL, error); 366} 367 368/* Throw a misc-error error. */ 369 370void 371gdbscm_misc_error (const char *subr, int arg_pos, SCM bad_value, 372 const char *error) 373{ 374 SCM exception = gdbscm_make_misc_error (subr, arg_pos, bad_value, error); 375 376 gdbscm_throw (exception); 377} 378 379/* Return a <gdb:exception> object for gdb:memory-error. */ 380 381SCM 382gdbscm_make_memory_error (const char *subr, const char *msg, SCM args) 383{ 384 return gdbscm_make_error (memory_error_symbol, subr, msg, args, 385 SCM_EOL); 386} 387 388/* Throw a gdb:memory-error exception. */ 389 390void 391gdbscm_memory_error (const char *subr, const char *msg, SCM args) 392{ 393 SCM exception = gdbscm_make_memory_error (subr, msg, args); 394 395 gdbscm_throw (exception); 396} 397 398/* Return non-zero if KEY is gdb:memory-error. 399 Note: This is an excp_matcher_func function. */ 400 401int 402gdbscm_memory_error_p (SCM key) 403{ 404 return scm_is_eq (key, memory_error_symbol); 405} 406 407/* Return non-zero if KEY is gdb:user-error. 408 Note: This is an excp_matcher_func function. */ 409 410int 411gdbscm_user_error_p (SCM key) 412{ 413 return scm_is_eq (key, user_error_symbol); 414} 415 416/* Wrapper around scm_throw to throw a gdb:exception. 417 This function does not return. 418 This function cannot be called from inside TRY_CATCH. */ 419 420void 421gdbscm_throw (SCM exception) 422{ 423 scm_throw (gdbscm_exception_key (exception), 424 gdbscm_exception_args (exception)); 425 gdb_assert_not_reached ("scm_throw returned"); 426} 427 428/* Convert a GDB exception to a <gdb:exception> object. */ 429 430SCM 431gdbscm_scm_from_gdb_exception (const gdbscm_gdb_exception &exception) 432{ 433 SCM key; 434 435 if (exception.reason == RETURN_QUIT) 436 { 437 /* Handle this specially to be consistent with top-repl.scm. */ 438 return gdbscm_make_error (signal_symbol, NULL, _("User interrupt"), 439 SCM_EOL, scm_list_1 (scm_from_int (SIGINT))); 440 } 441 442 if (exception.error == MEMORY_ERROR) 443 key = memory_error_symbol; 444 else 445 key = error_symbol; 446 447 return gdbscm_make_error (key, NULL, "~A", 448 scm_list_1 (gdbscm_scm_from_c_string 449 (exception.message)), 450 SCM_BOOL_F); 451} 452 453/* Convert a GDB exception to the appropriate Scheme exception and throw it. 454 This function does not return. */ 455 456void 457gdbscm_throw_gdb_exception (gdbscm_gdb_exception exception) 458{ 459 SCM scm_exception = gdbscm_scm_from_gdb_exception (exception); 460 xfree (exception.message); 461 gdbscm_throw (scm_exception); 462} 463 464/* Print the error message portion of an exception. 465 If PORT is #f, use the standard error port. 466 KEY cannot be gdb:with-stack. 467 468 Basically this function is just a wrapper around calling 469 %print-exception-message. */ 470 471static void 472gdbscm_print_exception_message (SCM port, SCM frame, SCM key, SCM args) 473{ 474 SCM printer, status; 475 476 if (gdbscm_is_false (port)) 477 port = scm_current_error_port (); 478 479 gdb_assert (!scm_is_eq (key, with_stack_error_symbol)); 480 481 /* This does not use scm_print_exception because we tweak the output a bit. 482 Compare Guile's print-exception with our %print-exception-message for 483 details. */ 484 if (gdbscm_is_false (percent_print_exception_message_var)) 485 { 486 percent_print_exception_message_var 487 = scm_c_private_variable (gdbscm_init_module_name, 488 percent_print_exception_message_name); 489 /* If we can't find %print-exception-message, there's a problem on the 490 Scheme side. Don't kill GDB, just flag an error and leave it at 491 that. */ 492 if (gdbscm_is_false (percent_print_exception_message_var)) 493 { 494 gdbscm_printf (port, _("Error in Scheme exception printing," 495 " can't find %s.\n"), 496 percent_print_exception_message_name); 497 return; 498 } 499 } 500 printer = scm_variable_ref (percent_print_exception_message_var); 501 502 status = gdbscm_safe_call_4 (printer, port, frame, key, args, NULL); 503 504 /* If that failed still tell the user something. 505 But don't use the exception printing machinery! */ 506 if (gdbscm_is_exception (status)) 507 { 508 gdbscm_printf (port, _("Error in Scheme exception printing:\n")); 509 scm_display (status, port); 510 scm_newline (port); 511 } 512} 513 514/* Print the description of exception KEY, ARGS to PORT, according to the 515 setting of "set guile print-stack". 516 If PORT is #f, use the standard error port. 517 If STACK is #f, never print the stack, regardless of whether printing it 518 is enabled. If STACK is #t, then print it if it is contained in ARGS 519 (i.e., KEY is gdb:with-stack). Otherwise STACK is the result of calling 520 scm_make_stack (which will be ignored in favor of the stack in ARGS if 521 KEY is gdb:with-stack). 522 KEY, ARGS are the standard arguments to scm_throw, et.al. 523 524 Basically this function is just a wrapper around calling 525 %print-exception-with-stack. */ 526 527void 528gdbscm_print_exception_with_stack (SCM port, SCM stack, SCM key, SCM args) 529{ 530 SCM printer, status; 531 532 if (gdbscm_is_false (port)) 533 port = scm_current_error_port (); 534 535 if (gdbscm_is_false (percent_print_exception_with_stack_var)) 536 { 537 percent_print_exception_with_stack_var 538 = scm_c_private_variable (gdbscm_init_module_name, 539 percent_print_exception_with_stack_name); 540 /* If we can't find %print-exception-with-stack, there's a problem on the 541 Scheme side. Don't kill GDB, just flag an error and leave it at 542 that. */ 543 if (gdbscm_is_false (percent_print_exception_with_stack_var)) 544 { 545 gdbscm_printf (port, _("Error in Scheme exception printing," 546 " can't find %s.\n"), 547 percent_print_exception_with_stack_name); 548 return; 549 } 550 } 551 printer = scm_variable_ref (percent_print_exception_with_stack_var); 552 553 status = gdbscm_safe_call_4 (printer, port, stack, key, args, NULL); 554 555 /* If that failed still tell the user something. 556 But don't use the exception printing machinery! */ 557 if (gdbscm_is_exception (status)) 558 { 559 gdbscm_printf (port, _("Error in Scheme exception printing:\n")); 560 scm_display (status, port); 561 scm_newline (port); 562 } 563} 564 565/* Print EXCEPTION, a <gdb:exception> object, to PORT. 566 If PORT is #f, use the standard error port. */ 567 568void 569gdbscm_print_gdb_exception (SCM port, SCM exception) 570{ 571 gdb_assert (gdbscm_is_exception (exception)); 572 573 gdbscm_print_exception_with_stack (port, SCM_BOOL_T, 574 gdbscm_exception_key (exception), 575 gdbscm_exception_args (exception)); 576} 577 578/* Return a string description of <gdb:exception> EXCEPTION. 579 If EXCEPTION is a gdb:with-stack exception, unwrap it, a backtrace 580 is never returned as part of the result. */ 581 582gdb::unique_xmalloc_ptr<char> 583gdbscm_exception_message_to_string (SCM exception) 584{ 585 SCM port = scm_open_output_string (); 586 SCM key, args; 587 588 gdb_assert (gdbscm_is_exception (exception)); 589 590 key = gdbscm_exception_key (exception); 591 args = gdbscm_exception_args (exception); 592 593 if (scm_is_eq (key, with_stack_error_symbol) 594 /* Don't crash on a badly generated gdb:with-stack exception. */ 595 && scm_is_pair (args) 596 && scm_is_pair (scm_cdr (args))) 597 { 598 key = scm_car (args); 599 args = scm_cddr (args); 600 } 601 602 gdbscm_print_exception_message (port, SCM_BOOL_F, key, args); 603 gdb::unique_xmalloc_ptr<char> result 604 = gdbscm_scm_to_c_string (scm_get_output_string (port)); 605 scm_close_port (port); 606 return result; 607} 608 609/* Return the value of the "guile print-stack" option as one of: 610 'none, 'message, 'full. */ 611 612static SCM 613gdbscm_percent_exception_print_style (void) 614{ 615 if (gdbscm_print_excp == gdbscm_print_excp_none) 616 return none_symbol; 617 if (gdbscm_print_excp == gdbscm_print_excp_message) 618 return message_symbol; 619 if (gdbscm_print_excp == gdbscm_print_excp_full) 620 return full_symbol; 621 gdb_assert_not_reached ("bad value for \"guile print-stack\""); 622} 623 624/* Return the current <gdb:exception> counter. 625 This is for debugging purposes. */ 626 627static SCM 628gdbscm_percent_exception_count (void) 629{ 630 return scm_from_ulong (gdbscm_exception_count); 631} 632 633/* Initialize the Scheme exception support. */ 634 635static const scheme_function exception_functions[] = 636{ 637 { "make-exception", 2, 0, 0, as_a_scm_t_subr (gdbscm_make_exception), 638 "\ 639Create a <gdb:exception> object.\n\ 640\n\ 641 Arguments: key args\n\ 642 These are the standard key,args arguments of \"throw\"." }, 643 644 { "exception?", 1, 0, 0, as_a_scm_t_subr (gdbscm_exception_p), 645 "\ 646Return #t if the object is a <gdb:exception> object." }, 647 648 { "exception-key", 1, 0, 0, as_a_scm_t_subr (gdbscm_exception_key), 649 "\ 650Return the exception's key." }, 651 652 { "exception-args", 1, 0, 0, as_a_scm_t_subr (gdbscm_exception_args), 653 "\ 654Return the exception's arg list." }, 655 656 END_FUNCTIONS 657}; 658 659static const scheme_function private_exception_functions[] = 660{ 661 { "%exception-print-style", 0, 0, 0, 662 as_a_scm_t_subr (gdbscm_percent_exception_print_style), 663 "\ 664Return the value of the \"guile print-stack\" option." }, 665 666 { "%exception-count", 0, 0, 0, 667 as_a_scm_t_subr (gdbscm_percent_exception_count), 668 "\ 669Return a count of the number of <gdb:exception> objects created.\n\ 670This is for debugging purposes." }, 671 672 END_FUNCTIONS 673}; 674 675void 676gdbscm_initialize_exceptions (void) 677{ 678 exception_smob_tag = gdbscm_make_smob_type (exception_smob_name, 679 sizeof (exception_smob)); 680 scm_set_smob_print (exception_smob_tag, exscm_print_exception_smob); 681 682 gdbscm_define_functions (exception_functions, 1); 683 gdbscm_define_functions (private_exception_functions, 0); 684 685 error_symbol = scm_from_latin1_symbol ("gdb:error"); 686 687 memory_error_symbol = scm_from_latin1_symbol ("gdb:memory-error"); 688 689 user_error_symbol = scm_from_latin1_symbol ("gdb:user-error"); 690 691 gdbscm_invalid_object_error_symbol 692 = scm_from_latin1_symbol ("gdb:invalid-object-error"); 693 694 with_stack_error_symbol = scm_from_latin1_symbol ("gdb:with-stack"); 695 696 /* The text of this symbol is taken from Guile's top-repl.scm. */ 697 signal_symbol = scm_from_latin1_symbol ("signal"); 698 699 none_symbol = scm_from_latin1_symbol ("none"); 700 message_symbol = scm_from_latin1_symbol ("message"); 701 full_symbol = scm_from_latin1_symbol ("full"); 702} 703