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