GDB (API)
|
00001 /* Everything about catch/throw catchpoints, for GDB. 00002 00003 Copyright (C) 1986-2013 Free Software Foundation, Inc. 00004 00005 This file is part of GDB. 00006 00007 This program is free software; you can redistribute it and/or modify 00008 it under the terms of the GNU General Public License as published by 00009 the Free Software Foundation; either version 3 of the License, or 00010 (at your option) any later version. 00011 00012 This program is distributed in the hope that it will be useful, 00013 but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00015 GNU General Public License for more details. 00016 00017 You should have received a copy of the GNU General Public License 00018 along with this program. If not, see <http://www.gnu.org/licenses/>. */ 00019 00020 #include "defs.h" 00021 #include "arch-utils.h" 00022 #include <ctype.h> 00023 #include "breakpoint.h" 00024 #include "gdbcmd.h" 00025 #include "inferior.h" 00026 #include "annotate.h" 00027 #include "valprint.h" 00028 #include "cli/cli-utils.h" 00029 #include "completer.h" 00030 #include "gdb_obstack.h" 00031 #include "mi/mi-common.h" 00032 #include "exceptions.h" 00033 #include "linespec.h" 00034 #include "probe.h" 00035 #include "objfiles.h" 00036 #include "cp-abi.h" 00037 #include "gdb_regex.h" 00038 #include "cp-support.h" 00039 00040 /* Enums for exception-handling support. */ 00041 enum exception_event_kind 00042 { 00043 EX_EVENT_THROW, 00044 EX_EVENT_RETHROW, 00045 EX_EVENT_CATCH 00046 }; 00047 00048 /* Each spot where we may place an exception-related catchpoint has 00049 two names: the SDT probe point and the function name. This 00050 structure holds both. */ 00051 00052 struct exception_names 00053 { 00054 /* The name of the probe point to try, in the form accepted by 00055 'parse_probes'. */ 00056 00057 const char *probe; 00058 00059 /* The name of the corresponding function. */ 00060 00061 const char *function; 00062 }; 00063 00064 /* Names of the probe points and functions on which to break. This is 00065 indexed by exception_event_kind. */ 00066 static const struct exception_names exception_functions[] = 00067 { 00068 { "-probe-stap libstdcxx:throw", "__cxa_throw" }, 00069 { "-probe-stap libstdcxx:rethrow", "__cxa_rethrow" }, 00070 { "-probe-stap libstdcxx:catch", "__cxa_begin_catch" } 00071 }; 00072 00073 static struct breakpoint_ops gnu_v3_exception_catchpoint_ops; 00074 00075 /* The type of an exception catchpoint. */ 00076 00077 struct exception_catchpoint 00078 { 00079 /* The base class. */ 00080 00081 struct breakpoint base; 00082 00083 /* The kind of exception catchpoint. */ 00084 00085 enum exception_event_kind kind; 00086 00087 /* If non-NULL, an xmalloc'd string holding the source form of the 00088 regular expression to match against. */ 00089 00090 char *exception_rx; 00091 00092 /* If non-NULL, an xmalloc'd, compiled regular expression which is 00093 used to determine which exceptions to stop on. */ 00094 00095 regex_t *pattern; 00096 }; 00097 00098 00099 00100 /* A helper function that fetches exception probe arguments. This 00101 fills in *ARG0 (if non-NULL) and *ARG1 (which must be non-NULL). 00102 It will throw an exception on any kind of failure. */ 00103 00104 static void 00105 fetch_probe_arguments (struct value **arg0, struct value **arg1) 00106 { 00107 struct frame_info *frame = get_selected_frame (_("No frame selected")); 00108 CORE_ADDR pc = get_frame_pc (frame); 00109 struct probe *pc_probe; 00110 const struct sym_probe_fns *pc_probe_fns; 00111 unsigned n_args; 00112 00113 pc_probe = find_probe_by_pc (pc); 00114 if (pc_probe == NULL 00115 || strcmp (pc_probe->provider, "libstdcxx") != 0 00116 || (strcmp (pc_probe->name, "catch") != 0 00117 && strcmp (pc_probe->name, "throw") != 0 00118 && strcmp (pc_probe->name, "rethrow") != 0)) 00119 error (_("not stopped at a C++ exception catchpoint")); 00120 00121 gdb_assert (pc_probe->objfile != NULL); 00122 gdb_assert (pc_probe->objfile->sf != NULL); 00123 gdb_assert (pc_probe->objfile->sf->sym_probe_fns != NULL); 00124 00125 pc_probe_fns = pc_probe->objfile->sf->sym_probe_fns; 00126 n_args = pc_probe_fns->sym_get_probe_argument_count (pc_probe); 00127 if (n_args < 2) 00128 error (_("C++ exception catchpoint has too few arguments")); 00129 00130 if (arg0 != NULL) 00131 *arg0 = pc_probe_fns->sym_evaluate_probe_argument (pc_probe, 0); 00132 *arg1 = pc_probe_fns->sym_evaluate_probe_argument (pc_probe, 1); 00133 00134 if ((arg0 != NULL && *arg0 == NULL) || *arg1 == NULL) 00135 error (_("error computing probe argument at c++ exception catchpoint")); 00136 } 00137 00138 00139 00140 /* A helper function that returns a value indicating the kind of the 00141 exception catchpoint B. */ 00142 00143 static enum exception_event_kind 00144 classify_exception_breakpoint (struct breakpoint *b) 00145 { 00146 struct exception_catchpoint *cp = (struct exception_catchpoint *) b; 00147 00148 return cp->kind; 00149 } 00150 00151 /* Implement the 'dtor' method. */ 00152 00153 static void 00154 dtor_exception_catchpoint (struct breakpoint *self) 00155 { 00156 struct exception_catchpoint *cp = (struct exception_catchpoint *) self; 00157 00158 xfree (cp->exception_rx); 00159 if (cp->pattern != NULL) 00160 regfree (cp->pattern); 00161 bkpt_breakpoint_ops.dtor (self); 00162 } 00163 00164 /* Implement the 'check_status' method. */ 00165 00166 static void 00167 check_status_exception_catchpoint (struct bpstats *bs) 00168 { 00169 struct exception_catchpoint *self 00170 = (struct exception_catchpoint *) bs->breakpoint_at; 00171 char *typename = NULL; 00172 volatile struct gdb_exception e; 00173 00174 bkpt_breakpoint_ops.check_status (bs); 00175 if (bs->stop == 0) 00176 return; 00177 00178 if (self->pattern == NULL) 00179 return; 00180 00181 TRY_CATCH (e, RETURN_MASK_ERROR) 00182 { 00183 struct value *typeinfo_arg; 00184 char *canon; 00185 00186 fetch_probe_arguments (NULL, &typeinfo_arg); 00187 typename = cplus_typename_from_type_info (typeinfo_arg); 00188 00189 canon = cp_canonicalize_string (typename); 00190 if (canon != NULL) 00191 { 00192 xfree (typename); 00193 typename = canon; 00194 } 00195 } 00196 00197 if (e.reason < 0) 00198 exception_print (gdb_stderr, e); 00199 else if (regexec (self->pattern, typename, 0, NULL, 0) != 0) 00200 bs->stop = 0; 00201 00202 xfree (typename); 00203 } 00204 00205 /* Implement the 're_set' method. */ 00206 00207 static void 00208 re_set_exception_catchpoint (struct breakpoint *self) 00209 { 00210 struct symtabs_and_lines sals = {0}; 00211 struct symtabs_and_lines sals_end = {0}; 00212 volatile struct gdb_exception e; 00213 struct cleanup *cleanup; 00214 enum exception_event_kind kind = classify_exception_breakpoint (self); 00215 int pass; 00216 00217 for (pass = 0; sals.sals == NULL && pass < 2; ++pass) 00218 { 00219 TRY_CATCH (e, RETURN_MASK_ERROR) 00220 { 00221 char *spec; 00222 00223 if (pass == 0) 00224 { 00225 spec = ASTRDUP (exception_functions[kind].probe); 00226 sals = parse_probes (&spec, NULL); 00227 } 00228 else 00229 { 00230 spec = ASTRDUP (exception_functions[kind].function); 00231 self->ops->decode_linespec (self, &spec, &sals); 00232 } 00233 } 00234 /* NOT_FOUND_ERROR just means the breakpoint will be pending, so 00235 let it through. */ 00236 if (e.reason < 0 && e.error != NOT_FOUND_ERROR) 00237 throw_exception (e); 00238 } 00239 00240 cleanup = make_cleanup (xfree, sals.sals); 00241 update_breakpoint_locations (self, sals, sals_end); 00242 do_cleanups (cleanup); 00243 } 00244 00245 static enum print_stop_action 00246 print_it_exception_catchpoint (bpstat bs) 00247 { 00248 struct ui_out *uiout = current_uiout; 00249 struct breakpoint *b = bs->breakpoint_at; 00250 int bp_temp; 00251 enum exception_event_kind kind = classify_exception_breakpoint (b); 00252 00253 annotate_catchpoint (b->number); 00254 00255 bp_temp = b->disposition == disp_del; 00256 ui_out_text (uiout, 00257 bp_temp ? "Temporary catchpoint " 00258 : "Catchpoint "); 00259 if (!ui_out_is_mi_like_p (uiout)) 00260 ui_out_field_int (uiout, "bkptno", b->number); 00261 ui_out_text (uiout, 00262 (kind == EX_EVENT_THROW ? " (exception thrown), " 00263 : (kind == EX_EVENT_CATCH ? " (exception caught), " 00264 : " (exception rethrown), "))); 00265 if (ui_out_is_mi_like_p (uiout)) 00266 { 00267 ui_out_field_string (uiout, "reason", 00268 async_reason_lookup (EXEC_ASYNC_BREAKPOINT_HIT)); 00269 ui_out_field_string (uiout, "disp", bpdisp_text (b->disposition)); 00270 ui_out_field_int (uiout, "bkptno", b->number); 00271 } 00272 return PRINT_SRC_AND_LOC; 00273 } 00274 00275 static void 00276 print_one_exception_catchpoint (struct breakpoint *b, 00277 struct bp_location **last_loc) 00278 { 00279 struct value_print_options opts; 00280 struct ui_out *uiout = current_uiout; 00281 enum exception_event_kind kind = classify_exception_breakpoint (b); 00282 00283 get_user_print_options (&opts); 00284 if (opts.addressprint) 00285 { 00286 annotate_field (4); 00287 if (b->loc == NULL || b->loc->shlib_disabled) 00288 ui_out_field_string (uiout, "addr", "<PENDING>"); 00289 else 00290 ui_out_field_core_addr (uiout, "addr", 00291 b->loc->gdbarch, b->loc->address); 00292 } 00293 annotate_field (5); 00294 if (b->loc) 00295 *last_loc = b->loc; 00296 00297 switch (kind) 00298 { 00299 case EX_EVENT_THROW: 00300 ui_out_field_string (uiout, "what", "exception throw"); 00301 if (ui_out_is_mi_like_p (uiout)) 00302 ui_out_field_string (uiout, "catch-type", "throw"); 00303 break; 00304 00305 case EX_EVENT_RETHROW: 00306 ui_out_field_string (uiout, "what", "exception rethrow"); 00307 if (ui_out_is_mi_like_p (uiout)) 00308 ui_out_field_string (uiout, "catch-type", "rethrow"); 00309 break; 00310 00311 case EX_EVENT_CATCH: 00312 ui_out_field_string (uiout, "what", "exception catch"); 00313 if (ui_out_is_mi_like_p (uiout)) 00314 ui_out_field_string (uiout, "catch-type", "catch"); 00315 break; 00316 } 00317 } 00318 00319 /* Implement the 'print_one_detail' method. */ 00320 00321 static void 00322 print_one_detail_exception_catchpoint (const struct breakpoint *b, 00323 struct ui_out *uiout) 00324 { 00325 const struct exception_catchpoint *cp 00326 = (const struct exception_catchpoint *) b; 00327 00328 if (cp->exception_rx != NULL) 00329 { 00330 ui_out_text (uiout, _("\tmatching: ")); 00331 ui_out_field_string (uiout, "regexp", cp->exception_rx); 00332 ui_out_text (uiout, "\n"); 00333 } 00334 } 00335 00336 static void 00337 print_mention_exception_catchpoint (struct breakpoint *b) 00338 { 00339 struct ui_out *uiout = current_uiout; 00340 int bp_temp; 00341 enum exception_event_kind kind = classify_exception_breakpoint (b); 00342 00343 bp_temp = b->disposition == disp_del; 00344 ui_out_text (uiout, bp_temp ? _("Temporary catchpoint ") 00345 : _("Catchpoint ")); 00346 ui_out_field_int (uiout, "bkptno", b->number); 00347 ui_out_text (uiout, (kind == EX_EVENT_THROW ? _(" (throw)") 00348 : (kind == EX_EVENT_CATCH ? _(" (catch)") 00349 : _(" (rethrow)")))); 00350 } 00351 00352 /* Implement the "print_recreate" breakpoint_ops method for throw and 00353 catch catchpoints. */ 00354 00355 static void 00356 print_recreate_exception_catchpoint (struct breakpoint *b, 00357 struct ui_file *fp) 00358 { 00359 int bp_temp; 00360 enum exception_event_kind kind = classify_exception_breakpoint (b); 00361 00362 bp_temp = b->disposition == disp_del; 00363 fprintf_unfiltered (fp, bp_temp ? "tcatch " : "catch "); 00364 switch (kind) 00365 { 00366 case EX_EVENT_THROW: 00367 fprintf_unfiltered (fp, "throw"); 00368 break; 00369 case EX_EVENT_CATCH: 00370 fprintf_unfiltered (fp, "catch"); 00371 break; 00372 case EX_EVENT_RETHROW: 00373 fprintf_unfiltered (fp, "rethrow"); 00374 break; 00375 } 00376 print_recreate_thread (b, fp); 00377 } 00378 00379 static void 00380 handle_gnu_v3_exceptions (int tempflag, char *except_rx, char *cond_string, 00381 enum exception_event_kind ex_event, int from_tty) 00382 { 00383 struct exception_catchpoint *cp; 00384 struct cleanup *cleanup = make_cleanup (null_cleanup, NULL); 00385 regex_t *pattern = NULL; 00386 00387 if (except_rx != NULL) 00388 { 00389 pattern = XNEW (regex_t); 00390 make_cleanup (xfree, pattern); 00391 00392 compile_rx_or_error (pattern, except_rx, 00393 _("invalid type-matching regexp")); 00394 } 00395 00396 cp = XCNEW (struct exception_catchpoint); 00397 make_cleanup (xfree, cp); 00398 00399 init_catchpoint (&cp->base, get_current_arch (), tempflag, cond_string, 00400 &gnu_v3_exception_catchpoint_ops); 00401 /* We need to reset 'type' in order for code in breakpoint.c to do 00402 the right thing. */ 00403 cp->base.type = bp_breakpoint; 00404 cp->kind = ex_event; 00405 cp->exception_rx = except_rx; 00406 cp->pattern = pattern; 00407 00408 re_set_exception_catchpoint (&cp->base); 00409 00410 install_breakpoint (0, &cp->base, 1); 00411 discard_cleanups (cleanup); 00412 } 00413 00414 /* Look for an "if" token in *STRING. The "if" token must be preceded 00415 by whitespace. 00416 00417 If there is any non-whitespace text between *STRING and the "if" 00418 token, then it is returned in a newly-xmalloc'd string. Otherwise, 00419 this returns NULL. 00420 00421 STRING is updated to point to the "if" token, if it exists, or to 00422 the end of the string. */ 00423 00424 static char * 00425 extract_exception_regexp (char **string) 00426 { 00427 char *start; 00428 char *last, *last_space; 00429 00430 start = skip_spaces (*string); 00431 00432 last = start; 00433 last_space = start; 00434 while (*last != '\0') 00435 { 00436 char *if_token = last; 00437 00438 /* Check for the "if". */ 00439 if (check_for_argument (&if_token, "if", 2)) 00440 break; 00441 00442 /* No "if" token here. Skip to the next word start. */ 00443 last_space = skip_to_space (last); 00444 last = skip_spaces (last_space); 00445 } 00446 00447 *string = last; 00448 if (last_space > start) 00449 return savestring (start, last_space - start); 00450 return NULL; 00451 } 00452 00453 /* Deal with "catch catch", "catch throw", and "catch rethrow" 00454 commands. */ 00455 00456 static void 00457 catch_exception_command_1 (enum exception_event_kind ex_event, char *arg, 00458 int tempflag, int from_tty) 00459 { 00460 char *except_rx; 00461 char *cond_string = NULL; 00462 struct cleanup *cleanup; 00463 00464 if (!arg) 00465 arg = ""; 00466 arg = skip_spaces (arg); 00467 00468 except_rx = extract_exception_regexp (&arg); 00469 cleanup = make_cleanup (xfree, except_rx); 00470 00471 cond_string = ep_parse_optional_if_clause (&arg); 00472 00473 if ((*arg != '\0') && !isspace (*arg)) 00474 error (_("Junk at end of arguments.")); 00475 00476 if (ex_event != EX_EVENT_THROW 00477 && ex_event != EX_EVENT_CATCH 00478 && ex_event != EX_EVENT_RETHROW) 00479 error (_("Unsupported or unknown exception event; cannot catch it")); 00480 00481 handle_gnu_v3_exceptions (tempflag, except_rx, cond_string, 00482 ex_event, from_tty); 00483 00484 discard_cleanups (cleanup); 00485 } 00486 00487 /* Implementation of "catch catch" command. */ 00488 00489 static void 00490 catch_catch_command (char *arg, int from_tty, struct cmd_list_element *command) 00491 { 00492 int tempflag = get_cmd_context (command) == CATCH_TEMPORARY; 00493 00494 catch_exception_command_1 (EX_EVENT_CATCH, arg, tempflag, from_tty); 00495 } 00496 00497 /* Implementation of "catch throw" command. */ 00498 00499 static void 00500 catch_throw_command (char *arg, int from_tty, struct cmd_list_element *command) 00501 { 00502 int tempflag = get_cmd_context (command) == CATCH_TEMPORARY; 00503 00504 catch_exception_command_1 (EX_EVENT_THROW, arg, tempflag, from_tty); 00505 } 00506 00507 /* Implementation of "catch rethrow" command. */ 00508 00509 static void 00510 catch_rethrow_command (char *arg, int from_tty, 00511 struct cmd_list_element *command) 00512 { 00513 int tempflag = get_cmd_context (command) == CATCH_TEMPORARY; 00514 00515 catch_exception_command_1 (EX_EVENT_RETHROW, arg, tempflag, from_tty); 00516 } 00517 00518 00519 00520 /* Implement the 'make_value' method for the $_exception 00521 internalvar. */ 00522 00523 static struct value * 00524 compute_exception (struct gdbarch *argc, struct internalvar *var, void *ignore) 00525 { 00526 struct value *arg0, *arg1; 00527 struct type *obj_type; 00528 00529 fetch_probe_arguments (&arg0, &arg1); 00530 00531 /* ARG0 is a pointer to the exception object. ARG1 is a pointer to 00532 the std::type_info for the exception. Now we find the type from 00533 the type_info and cast the result. */ 00534 obj_type = cplus_type_from_type_info (arg1); 00535 return value_ind (value_cast (make_pointer_type (obj_type, NULL), arg0)); 00536 } 00537 00538 /* Implementation of the '$_exception' variable. */ 00539 00540 static const struct internalvar_funcs exception_funcs = 00541 { 00542 compute_exception, 00543 NULL, 00544 NULL 00545 }; 00546 00547 00548 00549 static void 00550 initialize_throw_catchpoint_ops (void) 00551 { 00552 struct breakpoint_ops *ops; 00553 00554 initialize_breakpoint_ops (); 00555 00556 /* GNU v3 exception catchpoints. */ 00557 ops = &gnu_v3_exception_catchpoint_ops; 00558 *ops = bkpt_breakpoint_ops; 00559 ops->dtor = dtor_exception_catchpoint; 00560 ops->re_set = re_set_exception_catchpoint; 00561 ops->print_it = print_it_exception_catchpoint; 00562 ops->print_one = print_one_exception_catchpoint; 00563 ops->print_mention = print_mention_exception_catchpoint; 00564 ops->print_recreate = print_recreate_exception_catchpoint; 00565 ops->print_one_detail = print_one_detail_exception_catchpoint; 00566 ops->check_status = check_status_exception_catchpoint; 00567 } 00568 00569 initialize_file_ftype _initialize_break_catch_throw; 00570 00571 void 00572 _initialize_break_catch_throw (void) 00573 { 00574 initialize_throw_catchpoint_ops (); 00575 00576 /* Add catch and tcatch sub-commands. */ 00577 add_catch_command ("catch", _("\ 00578 Catch an exception, when caught."), 00579 catch_catch_command, 00580 NULL, 00581 CATCH_PERMANENT, 00582 CATCH_TEMPORARY); 00583 add_catch_command ("throw", _("\ 00584 Catch an exception, when thrown."), 00585 catch_throw_command, 00586 NULL, 00587 CATCH_PERMANENT, 00588 CATCH_TEMPORARY); 00589 add_catch_command ("rethrow", _("\ 00590 Catch an exception, when rethrown."), 00591 catch_rethrow_command, 00592 NULL, 00593 CATCH_PERMANENT, 00594 CATCH_TEMPORARY); 00595 00596 create_internalvar_type_lazy ("_exception", &exception_funcs, NULL); 00597 }