1 /* GDB/Scheme exception support. 2 3 Copyright (C) 2014-2024 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 <signal.h> 32 #include "guile-internal.h" 33 34 /* The <gdb:exception> smob. 35 This is used to record and handle Scheme exceptions. 36 One important invariant is that <gdb:exception> smobs are never a valid 37 result of a function, other than to signify an exception occurred. */ 38 39 struct exception_smob 40 { 41 /* This always appears first. */ 42 gdb_smob base; 43 44 /* The key and args parameters to "throw". */ 45 SCM key; 46 SCM args; 47 }; 48 49 static const char exception_smob_name[] = "gdb:exception"; 50 51 /* The tag Guile knows the exception smob by. */ 52 static scm_t_bits exception_smob_tag; 53 54 /* A generic error in struct gdb_exception. 55 I.e., not RETURN_QUIT and not MEMORY_ERROR. */ 56 static SCM error_symbol; 57 58 /* An error occurred accessing inferior memory. 59 This is not a Scheme programming error. */ 60 static SCM memory_error_symbol; 61 62 /* User interrupt, e.g., RETURN_QUIT in struct gdb_exception. */ 63 static SCM signal_symbol; 64 65 /* A user error, e.g., bad arg to gdb command. */ 66 static SCM user_error_symbol; 67 68 /* Printing the stack is done by first capturing the stack and recording it in 69 a <gdb:exception> object with this key and with the ARGS field set to 70 (cons real-key (cons stack real-args)). 71 See gdbscm_make_exception_with_stack. */ 72 static SCM with_stack_error_symbol; 73 74 /* The key to use for an invalid object exception. An invalid object is one 75 where the underlying object has been removed from GDB. */ 76 SCM gdbscm_invalid_object_error_symbol; 77 78 /* Values for "guile print-stack" as symbols. */ 79 static SCM none_symbol; 80 static SCM message_symbol; 81 static SCM full_symbol; 82 83 static const char percent_print_exception_message_name[] = 84 "%print-exception-message"; 85 86 /* Variable containing %print-exception-message. 87 It is not defined until late in initialization, after our init routine 88 has run. Cope by looking it up lazily. */ 89 static SCM percent_print_exception_message_var = SCM_BOOL_F; 90 91 static const char percent_print_exception_with_stack_name[] = 92 "%print-exception-with-stack"; 93 94 /* Variable containing %print-exception-with-stack. 95 It is not defined until late in initialization, after our init routine 96 has run. Cope by looking it up lazily. */ 97 static SCM percent_print_exception_with_stack_var = SCM_BOOL_F; 98 99 /* Counter to keep track of the number of times we create a <gdb:exception> 100 object, for performance monitoring purposes. */ 101 static unsigned long gdbscm_exception_count = 0; 102 103 /* Administrivia for exception smobs. */ 105 106 /* The smob "print" function for <gdb:exception>. */ 107 108 static int 109 exscm_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 127 SCM 128 gdbscm_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 146 int 147 gdbscm_is_exception (SCM scm) 148 { 149 return SCM_SMOB_PREDICATE (exception_smob_tag, scm); 150 } 151 152 /* (exception? scm) -> boolean */ 153 154 static SCM 155 gdbscm_exception_p (SCM scm) 156 { 157 return scm_from_bool (gdbscm_is_exception (scm)); 158 } 159 160 /* (exception-key <gdb:exception>) -> key */ 161 162 SCM 163 gdbscm_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 176 SCM 177 gdbscm_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. 190 gdbscm_print_exception_with_stack knows how to unwrap it. */ 191 192 SCM 193 gdbscm_make_exception_with_stack (SCM key, SCM args, SCM stack) 194 { 195 return gdbscm_make_exception (with_stack_error_symbol, 196 scm_cons (key, scm_cons (stack, args))); 197 } 198 199 /* Version of scm_error_scm that creates a gdb:exception object that can later 200 be passed to gdbscm_throw. 201 KEY is a symbol denoting the kind of error. 202 SUBR is either #f or a string marking the function in which the error 203 occurred. 204 MESSAGE is either #f or the error message string. It may contain ~a and ~s 205 modifiers, provided by ARGS. 206 ARGS is a list of args to MESSAGE. 207 DATA is an arbitrary object, its value depends on KEY. The value to pass 208 here is a bit underspecified by Guile. */ 209 210 SCM 211 gdbscm_make_error_scm (SCM key, SCM subr, SCM message, SCM args, SCM data) 212 { 213 return gdbscm_make_exception (key, scm_list_4 (subr, message, args, data)); 214 } 215 216 /* Version of scm_error that creates a gdb:exception object that can later 217 be passed to gdbscm_throw. 218 See gdbscm_make_error_scm for a description of the arguments. */ 219 220 SCM 221 gdbscm_make_error (SCM key, const char *subr, const char *message, 222 SCM args, SCM data) 223 { 224 return gdbscm_make_error_scm 225 (key, 226 subr == NULL ? SCM_BOOL_F : scm_from_latin1_string (subr), 227 message == NULL ? SCM_BOOL_F : scm_from_latin1_string (message), 228 args, data); 229 } 230 231 /* Version of SCM_ASSERT_TYPE/scm_wrong_type_arg_msg that creates a 232 gdb:exception object that can later be passed to gdbscm_throw. */ 233 234 SCM 235 gdbscm_make_type_error (const char *subr, int arg_pos, SCM bad_value, 236 const char *expected_type) 237 { 238 gdb::unique_xmalloc_ptr<char> msg; 239 SCM result; 240 241 if (arg_pos > 0) 242 { 243 if (expected_type != NULL) 244 { 245 msg = xstrprintf (_("Wrong type argument in position %d" 246 " (expecting %s): ~S"), 247 arg_pos, expected_type); 248 } 249 else 250 { 251 msg = xstrprintf (_("Wrong type argument in position %d: ~S"), 252 arg_pos); 253 } 254 } 255 else 256 { 257 if (expected_type != NULL) 258 { 259 msg = xstrprintf (_("Wrong type argument (expecting %s): ~S"), 260 expected_type); 261 } 262 else 263 msg = xstrprintf (_("Wrong type argument: ~S")); 264 } 265 266 result = gdbscm_make_error (scm_arg_type_key, subr, msg.get (), 267 scm_list_1 (bad_value), scm_list_1 (bad_value)); 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 278 static SCM 279 gdbscm_make_arg_error (SCM key, const char *subr, int arg_pos, SCM bad_value, 280 const char *error_prefix, const char *error) 281 { 282 gdb::unique_xmalloc_ptr<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.get (), scm_list_1 (bad_value), 304 scm_list_1 (bad_value)); 305 return result; 306 } 307 308 /* Make an invalid-object error <gdb:exception> object. 309 OBJECT is the name of the kind of object that is invalid. */ 310 311 SCM 312 gdbscm_make_invalid_object_error (const char *subr, int arg_pos, SCM bad_value, 313 const char *object) 314 { 315 return gdbscm_make_arg_error (gdbscm_invalid_object_error_symbol, 316 subr, arg_pos, bad_value, 317 _("Invalid object:"), object); 318 } 319 320 /* Throw an invalid-object error. 321 OBJECT is the name of the kind of object that is invalid. */ 322 323 void 324 gdbscm_invalid_object_error (const char *subr, int arg_pos, SCM bad_value, 325 const char *object) 326 { 327 SCM exception 328 = gdbscm_make_invalid_object_error (subr, arg_pos, bad_value, object); 329 330 gdbscm_throw (exception); 331 } 332 333 /* Make an out-of-range error <gdb:exception> object. */ 334 335 SCM 336 gdbscm_make_out_of_range_error (const char *subr, int arg_pos, SCM bad_value, 337 const char *error) 338 { 339 return gdbscm_make_arg_error (scm_out_of_range_key, 340 subr, arg_pos, bad_value, 341 _("Out of range:"), error); 342 } 343 344 /* Throw an out-of-range error. 345 This is the standard Guile out-of-range exception. */ 346 347 void 348 gdbscm_out_of_range_error (const char *subr, int arg_pos, SCM bad_value, 349 const char *error) 350 { 351 SCM exception 352 = gdbscm_make_out_of_range_error (subr, arg_pos, bad_value, error); 353 354 gdbscm_throw (exception); 355 } 356 357 /* Make a misc-error <gdb:exception> object. */ 358 359 SCM 360 gdbscm_make_misc_error (const char *subr, int arg_pos, SCM bad_value, 361 const char *error) 362 { 363 return gdbscm_make_arg_error (scm_misc_error_key, 364 subr, arg_pos, bad_value, NULL, error); 365 } 366 367 /* Throw a misc-error error. */ 368 369 void 370 gdbscm_misc_error (const char *subr, int arg_pos, SCM bad_value, 371 const char *error) 372 { 373 SCM exception = gdbscm_make_misc_error (subr, arg_pos, bad_value, error); 374 375 gdbscm_throw (exception); 376 } 377 378 /* Return a <gdb:exception> object for gdb:memory-error. */ 379 380 SCM 381 gdbscm_make_memory_error (const char *subr, const char *msg, SCM args) 382 { 383 return gdbscm_make_error (memory_error_symbol, subr, msg, args, 384 SCM_EOL); 385 } 386 387 /* Throw a gdb:memory-error exception. */ 388 389 void 390 gdbscm_memory_error (const char *subr, const char *msg, SCM args) 391 { 392 SCM exception = gdbscm_make_memory_error (subr, msg, args); 393 394 gdbscm_throw (exception); 395 } 396 397 /* Return non-zero if KEY is gdb:memory-error. 398 Note: This is an excp_matcher_func function. */ 399 400 int 401 gdbscm_memory_error_p (SCM key) 402 { 403 return scm_is_eq (key, memory_error_symbol); 404 } 405 406 /* Return non-zero if KEY is gdb:user-error. 407 Note: This is an excp_matcher_func function. */ 408 409 int 410 gdbscm_user_error_p (SCM key) 411 { 412 return scm_is_eq (key, user_error_symbol); 413 } 414 415 /* Wrapper around scm_throw to throw a gdb:exception. 416 This function does not return. 417 This function cannot be called from inside TRY_CATCH. */ 418 419 void 420 gdbscm_throw (SCM exception) 421 { 422 scm_throw (gdbscm_exception_key (exception), 423 gdbscm_exception_args (exception)); 424 gdb_assert_not_reached ("scm_throw returned"); 425 } 426 427 /* Convert a GDB exception to a <gdb:exception> object. */ 428 429 SCM 430 gdbscm_scm_from_gdb_exception (const gdbscm_gdb_exception &exception) 431 { 432 SCM key; 433 434 if (exception.reason == RETURN_QUIT) 435 { 436 /* Handle this specially to be consistent with top-repl.scm. */ 437 return gdbscm_make_error (signal_symbol, NULL, _("User interrupt"), 438 SCM_EOL, scm_list_1 (scm_from_int (SIGINT))); 439 } 440 441 if (exception.error == MEMORY_ERROR) 442 key = memory_error_symbol; 443 else 444 key = error_symbol; 445 446 return gdbscm_make_error (key, NULL, "~A", 447 scm_list_1 (gdbscm_scm_from_c_string 448 (exception.message)), 449 SCM_BOOL_F); 450 } 451 452 /* Convert a GDB exception to the appropriate Scheme exception and throw it. 453 This function does not return. */ 454 455 void 456 gdbscm_throw_gdb_exception (gdbscm_gdb_exception exception) 457 { 458 SCM scm_exception = gdbscm_scm_from_gdb_exception (exception); 459 xfree (exception.message); 460 gdbscm_throw (scm_exception); 461 } 462 463 /* Print the error message portion of an exception. 464 If PORT is #f, use the standard error port. 465 KEY cannot be gdb:with-stack. 466 467 Basically this function is just a wrapper around calling 468 %print-exception-message. */ 469 470 static void 471 gdbscm_print_exception_message (SCM port, SCM frame, SCM key, SCM args) 472 { 473 SCM printer, status; 474 475 if (gdbscm_is_false (port)) 476 port = scm_current_error_port (); 477 478 gdb_assert (!scm_is_eq (key, with_stack_error_symbol)); 479 480 /* This does not use scm_print_exception because we tweak the output a bit. 481 Compare Guile's print-exception with our %print-exception-message for 482 details. */ 483 if (gdbscm_is_false (percent_print_exception_message_var)) 484 { 485 percent_print_exception_message_var 486 = scm_c_private_variable (gdbscm_init_module_name, 487 percent_print_exception_message_name); 488 /* If we can't find %print-exception-message, there's a problem on the 489 Scheme side. Don't kill GDB, just flag an error and leave it at 490 that. */ 491 if (gdbscm_is_false (percent_print_exception_message_var)) 492 { 493 gdbscm_printf (port, _("Error in Scheme exception printing," 494 " can't find %s.\n"), 495 percent_print_exception_message_name); 496 return; 497 } 498 } 499 printer = scm_variable_ref (percent_print_exception_message_var); 500 501 status = gdbscm_safe_call_4 (printer, port, frame, key, args, NULL); 502 503 /* If that failed still tell the user something. 504 But don't use the exception printing machinery! */ 505 if (gdbscm_is_exception (status)) 506 { 507 gdbscm_printf (port, _("Error in Scheme exception printing:\n")); 508 scm_display (status, port); 509 scm_newline (port); 510 } 511 } 512 513 /* Print the description of exception KEY, ARGS to PORT, according to the 514 setting of "set guile print-stack". 515 If PORT is #f, use the standard error port. 516 If STACK is #f, never print the stack, regardless of whether printing it 517 is enabled. If STACK is #t, then print it if it is contained in ARGS 518 (i.e., KEY is gdb:with-stack). Otherwise STACK is the result of calling 519 scm_make_stack (which will be ignored in favor of the stack in ARGS if 520 KEY is gdb:with-stack). 521 KEY, ARGS are the standard arguments to scm_throw, et.al. 522 523 Basically this function is just a wrapper around calling 524 %print-exception-with-stack. */ 525 526 void 527 gdbscm_print_exception_with_stack (SCM port, SCM stack, SCM key, SCM args) 528 { 529 SCM printer, status; 530 531 if (gdbscm_is_false (port)) 532 port = scm_current_error_port (); 533 534 if (gdbscm_is_false (percent_print_exception_with_stack_var)) 535 { 536 percent_print_exception_with_stack_var 537 = scm_c_private_variable (gdbscm_init_module_name, 538 percent_print_exception_with_stack_name); 539 /* If we can't find %print-exception-with-stack, there's a problem on the 540 Scheme side. Don't kill GDB, just flag an error and leave it at 541 that. */ 542 if (gdbscm_is_false (percent_print_exception_with_stack_var)) 543 { 544 gdbscm_printf (port, _("Error in Scheme exception printing," 545 " can't find %s.\n"), 546 percent_print_exception_with_stack_name); 547 return; 548 } 549 } 550 printer = scm_variable_ref (percent_print_exception_with_stack_var); 551 552 status = gdbscm_safe_call_4 (printer, port, stack, key, args, NULL); 553 554 /* If that failed still tell the user something. 555 But don't use the exception printing machinery! */ 556 if (gdbscm_is_exception (status)) 557 { 558 gdbscm_printf (port, _("Error in Scheme exception printing:\n")); 559 scm_display (status, port); 560 scm_newline (port); 561 } 562 } 563 564 /* Print EXCEPTION, a <gdb:exception> object, to PORT. 565 If PORT is #f, use the standard error port. */ 566 567 void 568 gdbscm_print_gdb_exception (SCM port, SCM exception) 569 { 570 gdb_assert (gdbscm_is_exception (exception)); 571 572 gdbscm_print_exception_with_stack (port, SCM_BOOL_T, 573 gdbscm_exception_key (exception), 574 gdbscm_exception_args (exception)); 575 } 576 577 /* Return a string description of <gdb:exception> EXCEPTION. 578 If EXCEPTION is a gdb:with-stack exception, unwrap it, a backtrace 579 is never returned as part of the result. */ 580 581 gdb::unique_xmalloc_ptr<char> 582 gdbscm_exception_message_to_string (SCM exception) 583 { 584 SCM port = scm_open_output_string (); 585 SCM key, args; 586 587 gdb_assert (gdbscm_is_exception (exception)); 588 589 key = gdbscm_exception_key (exception); 590 args = gdbscm_exception_args (exception); 591 592 if (scm_is_eq (key, with_stack_error_symbol) 593 /* Don't crash on a badly generated gdb:with-stack exception. */ 594 && scm_is_pair (args) 595 && scm_is_pair (scm_cdr (args))) 596 { 597 key = scm_car (args); 598 args = scm_cddr (args); 599 } 600 601 gdbscm_print_exception_message (port, SCM_BOOL_F, key, args); 602 gdb::unique_xmalloc_ptr<char> result 603 = gdbscm_scm_to_c_string (scm_get_output_string (port)); 604 scm_close_port (port); 605 return result; 606 } 607 608 /* Return the value of the "guile print-stack" option as one of: 609 'none, 'message, 'full. */ 610 611 static SCM 612 gdbscm_percent_exception_print_style (void) 613 { 614 if (gdbscm_print_excp == gdbscm_print_excp_none) 615 return none_symbol; 616 if (gdbscm_print_excp == gdbscm_print_excp_message) 617 return message_symbol; 618 if (gdbscm_print_excp == gdbscm_print_excp_full) 619 return full_symbol; 620 gdb_assert_not_reached ("bad value for \"guile print-stack\""); 621 } 622 623 /* Return the current <gdb:exception> counter. 624 This is for debugging purposes. */ 625 626 static SCM 627 gdbscm_percent_exception_count (void) 628 { 629 return scm_from_ulong (gdbscm_exception_count); 630 } 631 632 /* Initialize the Scheme exception support. */ 634 635 static const scheme_function exception_functions[] = 636 { 637 { "make-exception", 2, 0, 0, as_a_scm_t_subr (gdbscm_make_exception), 638 "\ 639 Create 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 "\ 646 Return #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 "\ 650 Return the exception's key." }, 651 652 { "exception-args", 1, 0, 0, as_a_scm_t_subr (gdbscm_exception_args), 653 "\ 654 Return the exception's arg list." }, 655 656 END_FUNCTIONS 657 }; 658 659 static 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 "\ 664 Return 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 "\ 669 Return a count of the number of <gdb:exception> objects created.\n\ 670 This is for debugging purposes." }, 671 672 END_FUNCTIONS 673 }; 674 675 void 676 gdbscm_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