break-catch-throw.c revision 1.7
1/* Everything about catch/throw catchpoints, for GDB. 2 3 Copyright (C) 1986-2017 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#include "defs.h" 21#include "arch-utils.h" 22#include <ctype.h> 23#include "breakpoint.h" 24#include "gdbcmd.h" 25#include "inferior.h" 26#include "annotate.h" 27#include "valprint.h" 28#include "cli/cli-utils.h" 29#include "completer.h" 30#include "gdb_obstack.h" 31#include "mi/mi-common.h" 32#include "linespec.h" 33#include "probe.h" 34#include "objfiles.h" 35#include "cp-abi.h" 36#include "gdb_regex.h" 37#include "cp-support.h" 38#include "location.h" 39 40/* Enums for exception-handling support. */ 41enum exception_event_kind 42{ 43 EX_EVENT_THROW, 44 EX_EVENT_RETHROW, 45 EX_EVENT_CATCH 46}; 47 48/* Each spot where we may place an exception-related catchpoint has 49 two names: the SDT probe point and the function name. This 50 structure holds both. */ 51 52struct exception_names 53{ 54 /* The name of the probe point to try, in the form accepted by 55 'parse_probes'. */ 56 57 const char *probe; 58 59 /* The name of the corresponding function. */ 60 61 const char *function; 62}; 63 64/* Names of the probe points and functions on which to break. This is 65 indexed by exception_event_kind. */ 66static const struct exception_names exception_functions[] = 67{ 68 { "-probe-stap libstdcxx:throw", "__cxa_throw" }, 69 { "-probe-stap libstdcxx:rethrow", "__cxa_rethrow" }, 70 { "-probe-stap libstdcxx:catch", "__cxa_begin_catch" } 71}; 72 73static struct breakpoint_ops gnu_v3_exception_catchpoint_ops; 74 75/* The type of an exception catchpoint. */ 76 77struct exception_catchpoint 78{ 79 /* The base class. */ 80 81 struct breakpoint base; 82 83 /* The kind of exception catchpoint. */ 84 85 enum exception_event_kind kind; 86 87 /* If non-NULL, an xmalloc'd string holding the source form of the 88 regular expression to match against. */ 89 90 char *exception_rx; 91 92 /* If non-NULL, an xmalloc'd, compiled regular expression which is 93 used to determine which exceptions to stop on. */ 94 95 regex_t *pattern; 96}; 97 98 99 100/* A helper function that fetches exception probe arguments. This 101 fills in *ARG0 (if non-NULL) and *ARG1 (which must be non-NULL). 102 It will throw an exception on any kind of failure. */ 103 104static void 105fetch_probe_arguments (struct value **arg0, struct value **arg1) 106{ 107 struct frame_info *frame = get_selected_frame (_("No frame selected")); 108 CORE_ADDR pc = get_frame_pc (frame); 109 struct bound_probe pc_probe; 110 unsigned n_args; 111 112 pc_probe = find_probe_by_pc (pc); 113 if (pc_probe.probe == NULL 114 || strcmp (pc_probe.probe->provider, "libstdcxx") != 0 115 || (strcmp (pc_probe.probe->name, "catch") != 0 116 && strcmp (pc_probe.probe->name, "throw") != 0 117 && strcmp (pc_probe.probe->name, "rethrow") != 0)) 118 error (_("not stopped at a C++ exception catchpoint")); 119 120 n_args = get_probe_argument_count (pc_probe.probe, frame); 121 if (n_args < 2) 122 error (_("C++ exception catchpoint has too few arguments")); 123 124 if (arg0 != NULL) 125 *arg0 = evaluate_probe_argument (pc_probe.probe, 0, frame); 126 *arg1 = evaluate_probe_argument (pc_probe.probe, 1, frame); 127 128 if ((arg0 != NULL && *arg0 == NULL) || *arg1 == NULL) 129 error (_("error computing probe argument at c++ exception catchpoint")); 130} 131 132 133 134/* A helper function that returns a value indicating the kind of the 135 exception catchpoint B. */ 136 137static enum exception_event_kind 138classify_exception_breakpoint (struct breakpoint *b) 139{ 140 struct exception_catchpoint *cp = (struct exception_catchpoint *) b; 141 142 return cp->kind; 143} 144 145/* Implement the 'dtor' method. */ 146 147static void 148dtor_exception_catchpoint (struct breakpoint *self) 149{ 150 struct exception_catchpoint *cp = (struct exception_catchpoint *) self; 151 152 xfree (cp->exception_rx); 153 if (cp->pattern != NULL) 154 regfree (cp->pattern); 155 bkpt_breakpoint_ops.dtor (self); 156} 157 158/* Implement the 'check_status' method. */ 159 160static void 161check_status_exception_catchpoint (struct bpstats *bs) 162{ 163 struct exception_catchpoint *self 164 = (struct exception_catchpoint *) bs->breakpoint_at; 165 std::string type_name; 166 167 bkpt_breakpoint_ops.check_status (bs); 168 if (bs->stop == 0) 169 return; 170 171 if (self->pattern == NULL) 172 return; 173 174 TRY 175 { 176 struct value *typeinfo_arg; 177 std::string canon; 178 179 fetch_probe_arguments (NULL, &typeinfo_arg); 180 type_name = cplus_typename_from_type_info (typeinfo_arg); 181 182 canon = cp_canonicalize_string (type_name.c_str ()); 183 if (!canon.empty ()) 184 std::swap (type_name, canon); 185 } 186 CATCH (e, RETURN_MASK_ERROR) 187 { 188 exception_print (gdb_stderr, e); 189 } 190 END_CATCH 191 192 if (!type_name.empty ()) 193 { 194 if (regexec (self->pattern, type_name.c_str (), 0, NULL, 0) != 0) 195 bs->stop = 0; 196 } 197} 198 199/* Implement the 're_set' method. */ 200 201static void 202re_set_exception_catchpoint (struct breakpoint *self) 203{ 204 struct symtabs_and_lines sals = {0}; 205 struct symtabs_and_lines sals_end = {0}; 206 struct cleanup *cleanup; 207 enum exception_event_kind kind = classify_exception_breakpoint (self); 208 struct program_space *filter_pspace = current_program_space; 209 210 /* We first try to use the probe interface. */ 211 TRY 212 { 213 event_location_up location 214 = new_probe_location (exception_functions[kind].probe); 215 sals = parse_probes (location.get (), filter_pspace, NULL); 216 } 217 CATCH (e, RETURN_MASK_ERROR) 218 { 219 /* Using the probe interface failed. Let's fallback to the normal 220 catchpoint mode. */ 221 TRY 222 { 223 struct explicit_location explicit_loc; 224 225 initialize_explicit_location (&explicit_loc); 226 explicit_loc.function_name 227 = ASTRDUP (exception_functions[kind].function); 228 event_location_up location = new_explicit_location (&explicit_loc); 229 self->ops->decode_location (self, location.get (), filter_pspace, 230 &sals); 231 } 232 CATCH (ex, RETURN_MASK_ERROR) 233 { 234 /* NOT_FOUND_ERROR just means the breakpoint will be 235 pending, so let it through. */ 236 if (ex.error != NOT_FOUND_ERROR) 237 throw_exception (ex); 238 } 239 END_CATCH 240 } 241 END_CATCH 242 243 cleanup = make_cleanup (xfree, sals.sals); 244 update_breakpoint_locations (self, filter_pspace, sals, sals_end); 245 do_cleanups (cleanup); 246} 247 248static enum print_stop_action 249print_it_exception_catchpoint (bpstat bs) 250{ 251 struct ui_out *uiout = current_uiout; 252 struct breakpoint *b = bs->breakpoint_at; 253 int bp_temp; 254 enum exception_event_kind kind = classify_exception_breakpoint (b); 255 256 annotate_catchpoint (b->number); 257 maybe_print_thread_hit_breakpoint (uiout); 258 259 bp_temp = b->disposition == disp_del; 260 uiout->text (bp_temp ? "Temporary catchpoint " 261 : "Catchpoint "); 262 if (!uiout->is_mi_like_p ()) 263 uiout->field_int ("bkptno", b->number); 264 uiout->text ((kind == EX_EVENT_THROW ? " (exception thrown), " 265 : (kind == EX_EVENT_CATCH ? " (exception caught), " 266 : " (exception rethrown), "))); 267 if (uiout->is_mi_like_p ()) 268 { 269 uiout->field_string ("reason", 270 async_reason_lookup (EXEC_ASYNC_BREAKPOINT_HIT)); 271 uiout->field_string ("disp", bpdisp_text (b->disposition)); 272 uiout->field_int ("bkptno", b->number); 273 } 274 return PRINT_SRC_AND_LOC; 275} 276 277static void 278print_one_exception_catchpoint (struct breakpoint *b, 279 struct bp_location **last_loc) 280{ 281 struct value_print_options opts; 282 struct ui_out *uiout = current_uiout; 283 enum exception_event_kind kind = classify_exception_breakpoint (b); 284 285 get_user_print_options (&opts); 286 if (opts.addressprint) 287 { 288 annotate_field (4); 289 if (b->loc == NULL || b->loc->shlib_disabled) 290 uiout->field_string ("addr", "<PENDING>"); 291 else 292 uiout->field_core_addr ("addr", 293 b->loc->gdbarch, b->loc->address); 294 } 295 annotate_field (5); 296 if (b->loc) 297 *last_loc = b->loc; 298 299 switch (kind) 300 { 301 case EX_EVENT_THROW: 302 uiout->field_string ("what", "exception throw"); 303 if (uiout->is_mi_like_p ()) 304 uiout->field_string ("catch-type", "throw"); 305 break; 306 307 case EX_EVENT_RETHROW: 308 uiout->field_string ("what", "exception rethrow"); 309 if (uiout->is_mi_like_p ()) 310 uiout->field_string ("catch-type", "rethrow"); 311 break; 312 313 case EX_EVENT_CATCH: 314 uiout->field_string ("what", "exception catch"); 315 if (uiout->is_mi_like_p ()) 316 uiout->field_string ("catch-type", "catch"); 317 break; 318 } 319} 320 321/* Implement the 'print_one_detail' method. */ 322 323static void 324print_one_detail_exception_catchpoint (const struct breakpoint *b, 325 struct ui_out *uiout) 326{ 327 const struct exception_catchpoint *cp 328 = (const struct exception_catchpoint *) b; 329 330 if (cp->exception_rx != NULL) 331 { 332 uiout->text (_("\tmatching: ")); 333 uiout->field_string ("regexp", cp->exception_rx); 334 uiout->text ("\n"); 335 } 336} 337 338static void 339print_mention_exception_catchpoint (struct breakpoint *b) 340{ 341 struct ui_out *uiout = current_uiout; 342 int bp_temp; 343 enum exception_event_kind kind = classify_exception_breakpoint (b); 344 345 bp_temp = b->disposition == disp_del; 346 uiout->text (bp_temp ? _("Temporary catchpoint ") 347 : _("Catchpoint ")); 348 uiout->field_int ("bkptno", b->number); 349 uiout->text ((kind == EX_EVENT_THROW ? _(" (throw)") 350 : (kind == EX_EVENT_CATCH ? _(" (catch)") 351 : _(" (rethrow)")))); 352} 353 354/* Implement the "print_recreate" breakpoint_ops method for throw and 355 catch catchpoints. */ 356 357static void 358print_recreate_exception_catchpoint (struct breakpoint *b, 359 struct ui_file *fp) 360{ 361 int bp_temp; 362 enum exception_event_kind kind = classify_exception_breakpoint (b); 363 364 bp_temp = b->disposition == disp_del; 365 fprintf_unfiltered (fp, bp_temp ? "tcatch " : "catch "); 366 switch (kind) 367 { 368 case EX_EVENT_THROW: 369 fprintf_unfiltered (fp, "throw"); 370 break; 371 case EX_EVENT_CATCH: 372 fprintf_unfiltered (fp, "catch"); 373 break; 374 case EX_EVENT_RETHROW: 375 fprintf_unfiltered (fp, "rethrow"); 376 break; 377 } 378 print_recreate_thread (b, fp); 379} 380 381static void 382handle_gnu_v3_exceptions (int tempflag, char *except_rx, 383 const char *cond_string, 384 enum exception_event_kind ex_event, int from_tty) 385{ 386 regex_t *pattern = NULL; 387 388 if (except_rx != NULL) 389 { 390 pattern = XNEW (regex_t); 391 make_cleanup (xfree, pattern); 392 393 compile_rx_or_error (pattern, except_rx, 394 _("invalid type-matching regexp")); 395 } 396 397 std::unique_ptr<exception_catchpoint> cp (new exception_catchpoint ()); 398 399 init_catchpoint (&cp->base, get_current_arch (), tempflag, cond_string, 400 &gnu_v3_exception_catchpoint_ops); 401 /* We need to reset 'type' in order for code in breakpoint.c to do 402 the right thing. */ 403 cp->base.type = bp_breakpoint; 404 cp->kind = ex_event; 405 cp->exception_rx = except_rx; 406 cp->pattern = pattern; 407 408 re_set_exception_catchpoint (&cp->base); 409 410 install_breakpoint (0, &cp->base, 1); 411 cp.release (); 412} 413 414/* Look for an "if" token in *STRING. The "if" token must be preceded 415 by whitespace. 416 417 If there is any non-whitespace text between *STRING and the "if" 418 token, then it is returned in a newly-xmalloc'd string. Otherwise, 419 this returns NULL. 420 421 STRING is updated to point to the "if" token, if it exists, or to 422 the end of the string. */ 423 424static char * 425extract_exception_regexp (const char **string) 426{ 427 const char *start; 428 const char *last, *last_space; 429 430 start = skip_spaces_const (*string); 431 432 last = start; 433 last_space = start; 434 while (*last != '\0') 435 { 436 const char *if_token = last; 437 438 /* Check for the "if". */ 439 if (check_for_argument (&if_token, "if", 2)) 440 break; 441 442 /* No "if" token here. Skip to the next word start. */ 443 last_space = skip_to_space (last); 444 last = skip_spaces_const (last_space); 445 } 446 447 *string = last; 448 if (last_space > start) 449 return savestring (start, last_space - start); 450 return NULL; 451} 452 453/* Deal with "catch catch", "catch throw", and "catch rethrow" 454 commands. */ 455 456static void 457catch_exception_command_1 (enum exception_event_kind ex_event, 458 char *arg_entry, 459 int tempflag, int from_tty) 460{ 461 char *except_rx; 462 const char *cond_string = NULL; 463 struct cleanup *cleanup; 464 const char *arg = arg_entry; 465 466 if (!arg) 467 arg = ""; 468 arg = skip_spaces_const (arg); 469 470 except_rx = extract_exception_regexp (&arg); 471 cleanup = make_cleanup (xfree, except_rx); 472 473 cond_string = ep_parse_optional_if_clause (&arg); 474 475 if ((*arg != '\0') && !isspace (*arg)) 476 error (_("Junk at end of arguments.")); 477 478 if (ex_event != EX_EVENT_THROW 479 && ex_event != EX_EVENT_CATCH 480 && ex_event != EX_EVENT_RETHROW) 481 error (_("Unsupported or unknown exception event; cannot catch it")); 482 483 handle_gnu_v3_exceptions (tempflag, except_rx, cond_string, 484 ex_event, from_tty); 485 486 discard_cleanups (cleanup); 487} 488 489/* Implementation of "catch catch" command. */ 490 491static void 492catch_catch_command (char *arg, int from_tty, struct cmd_list_element *command) 493{ 494 int tempflag = get_cmd_context (command) == CATCH_TEMPORARY; 495 496 catch_exception_command_1 (EX_EVENT_CATCH, arg, tempflag, from_tty); 497} 498 499/* Implementation of "catch throw" command. */ 500 501static void 502catch_throw_command (char *arg, int from_tty, struct cmd_list_element *command) 503{ 504 int tempflag = get_cmd_context (command) == CATCH_TEMPORARY; 505 506 catch_exception_command_1 (EX_EVENT_THROW, arg, tempflag, from_tty); 507} 508 509/* Implementation of "catch rethrow" command. */ 510 511static void 512catch_rethrow_command (char *arg, int from_tty, 513 struct cmd_list_element *command) 514{ 515 int tempflag = get_cmd_context (command) == CATCH_TEMPORARY; 516 517 catch_exception_command_1 (EX_EVENT_RETHROW, arg, tempflag, from_tty); 518} 519 520 521 522/* Implement the 'make_value' method for the $_exception 523 internalvar. */ 524 525static struct value * 526compute_exception (struct gdbarch *argc, struct internalvar *var, void *ignore) 527{ 528 struct value *arg0, *arg1; 529 struct type *obj_type; 530 531 fetch_probe_arguments (&arg0, &arg1); 532 533 /* ARG0 is a pointer to the exception object. ARG1 is a pointer to 534 the std::type_info for the exception. Now we find the type from 535 the type_info and cast the result. */ 536 obj_type = cplus_type_from_type_info (arg1); 537 return value_ind (value_cast (make_pointer_type (obj_type, NULL), arg0)); 538} 539 540/* Implementation of the '$_exception' variable. */ 541 542static const struct internalvar_funcs exception_funcs = 543{ 544 compute_exception, 545 NULL, 546 NULL 547}; 548 549 550 551static void 552initialize_throw_catchpoint_ops (void) 553{ 554 struct breakpoint_ops *ops; 555 556 initialize_breakpoint_ops (); 557 558 /* GNU v3 exception catchpoints. */ 559 ops = &gnu_v3_exception_catchpoint_ops; 560 *ops = bkpt_breakpoint_ops; 561 ops->dtor = dtor_exception_catchpoint; 562 ops->re_set = re_set_exception_catchpoint; 563 ops->print_it = print_it_exception_catchpoint; 564 ops->print_one = print_one_exception_catchpoint; 565 ops->print_mention = print_mention_exception_catchpoint; 566 ops->print_recreate = print_recreate_exception_catchpoint; 567 ops->print_one_detail = print_one_detail_exception_catchpoint; 568 ops->check_status = check_status_exception_catchpoint; 569} 570 571initialize_file_ftype _initialize_break_catch_throw; 572 573void 574_initialize_break_catch_throw (void) 575{ 576 initialize_throw_catchpoint_ops (); 577 578 /* Add catch and tcatch sub-commands. */ 579 add_catch_command ("catch", _("\ 580Catch an exception, when caught."), 581 catch_catch_command, 582 NULL, 583 CATCH_PERMANENT, 584 CATCH_TEMPORARY); 585 add_catch_command ("throw", _("\ 586Catch an exception, when thrown."), 587 catch_throw_command, 588 NULL, 589 CATCH_PERMANENT, 590 CATCH_TEMPORARY); 591 add_catch_command ("rethrow", _("\ 592Catch an exception, when rethrown."), 593 catch_rethrow_command, 594 NULL, 595 CATCH_PERMANENT, 596 CATCH_TEMPORARY); 597 598 create_internalvar_type_lazy ("_exception", &exception_funcs, NULL); 599} 600