1 /* Everything about catch/throw catchpoints, for GDB. 2 3 Copyright (C) 1986-2023 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 "gdbsupport/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 "gdbsupport/gdb_regex.h" 37 #include "cp-support.h" 38 #include "location.h" 39 #include "cli/cli-decode.h" 40 41 /* Each spot where we may place an exception-related catchpoint has 42 two names: the SDT probe point and the function name. This 43 structure holds both. */ 44 45 struct exception_names 46 { 47 /* The name of the probe point to try, in the form accepted by 48 'parse_probes'. */ 49 50 const char *probe; 51 52 /* The name of the corresponding function. */ 53 54 const char *function; 55 }; 56 57 /* Names of the probe points and functions on which to break. This is 58 indexed by exception_event_kind. */ 59 static const struct exception_names exception_functions[] = 60 { 61 { "-probe-stap libstdcxx:throw", "__cxa_throw" }, 62 { "-probe-stap libstdcxx:rethrow", "__cxa_rethrow" }, 63 { "-probe-stap libstdcxx:catch", "__cxa_begin_catch" } 64 }; 65 66 /* The type of an exception catchpoint. Unlike most catchpoints, this 67 one is implemented with code breakpoints, so it inherits struct 68 code_breakpoint, not struct catchpoint. */ 69 70 struct exception_catchpoint : public code_breakpoint 71 { 72 exception_catchpoint (struct gdbarch *gdbarch, 73 bool temp, const char *cond_string_, 74 enum exception_event_kind kind_, 75 std::string &&except_rx) 76 : code_breakpoint (gdbarch, bp_catchpoint, temp, cond_string_), 77 kind (kind_), 78 exception_rx (std::move (except_rx)), 79 pattern (exception_rx.empty () 80 ? nullptr 81 : new compiled_regex (exception_rx.c_str (), REG_NOSUB, 82 _("invalid type-matching regexp"))) 83 { 84 pspace = current_program_space; 85 re_set (); 86 } 87 88 void re_set () override; 89 enum print_stop_action print_it (const bpstat *bs) const override; 90 bool print_one (bp_location **) const override; 91 void print_mention () const override; 92 void print_recreate (struct ui_file *fp) const override; 93 void print_one_detail (struct ui_out *) const override; 94 void check_status (struct bpstat *bs) override; 95 struct bp_location *allocate_location () override; 96 97 /* FIXME this is temporary - until ordinary breakpoints have been 98 converted. */ 99 int resources_needed (const struct bp_location *) override 100 { 101 return 1; 102 } 103 104 /* The kind of exception catchpoint. */ 105 106 enum exception_event_kind kind; 107 108 /* If not empty, a string holding the source form of the regular 109 expression to match against. */ 110 111 std::string exception_rx; 112 113 /* If non-NULL, a compiled regular expression which is used to 114 determine which exceptions to stop on. */ 115 116 std::unique_ptr<compiled_regex> pattern; 117 }; 118 119 /* See breakpoint.h. */ 120 121 bool 122 is_exception_catchpoint (breakpoint *bp) 123 { 124 return dynamic_cast<exception_catchpoint *> (bp) != nullptr; 125 } 126 127 128 129 /* A helper function that fetches exception probe arguments. This 130 fills in *ARG0 (if non-NULL) and *ARG1 (which must be non-NULL). 131 It will throw an exception on any kind of failure. */ 132 133 static void 134 fetch_probe_arguments (struct value **arg0, struct value **arg1) 135 { 136 frame_info_ptr frame = get_selected_frame (_("No frame selected")); 137 CORE_ADDR pc = get_frame_pc (frame); 138 struct bound_probe pc_probe; 139 unsigned n_args; 140 141 pc_probe = find_probe_by_pc (pc); 142 if (pc_probe.prob == NULL) 143 error (_("did not find exception probe (does libstdcxx have SDT probes?)")); 144 145 if (pc_probe.prob->get_provider () != "libstdcxx" 146 || (pc_probe.prob->get_name () != "catch" 147 && pc_probe.prob->get_name () != "throw" 148 && pc_probe.prob->get_name () != "rethrow")) 149 error (_("not stopped at a C++ exception catchpoint")); 150 151 n_args = pc_probe.prob->get_argument_count (get_frame_arch (frame)); 152 if (n_args < 2) 153 error (_("C++ exception catchpoint has too few arguments")); 154 155 if (arg0 != NULL) 156 *arg0 = pc_probe.prob->evaluate_argument (0, frame); 157 *arg1 = pc_probe.prob->evaluate_argument (1, frame); 158 159 if ((arg0 != NULL && *arg0 == NULL) || *arg1 == NULL) 160 error (_("error computing probe argument at c++ exception catchpoint")); 161 } 162 163 164 165 /* Implement the 'check_status' method. */ 166 167 void 168 exception_catchpoint::check_status (struct bpstat *bs) 169 { 170 struct exception_catchpoint *self 171 = (struct exception_catchpoint *) bs->breakpoint_at; 172 std::string type_name; 173 174 this->breakpoint::check_status (bs); 175 if (bs->stop == 0) 176 return; 177 178 if (self->pattern == NULL) 179 return; 180 181 const char *name = nullptr; 182 gdb::unique_xmalloc_ptr<char> canon; 183 try 184 { 185 struct value *typeinfo_arg; 186 187 fetch_probe_arguments (NULL, &typeinfo_arg); 188 type_name = cplus_typename_from_type_info (typeinfo_arg); 189 190 canon = cp_canonicalize_string (type_name.c_str ()); 191 name = (canon != nullptr 192 ? canon.get () 193 : type_name.c_str ()); 194 } 195 catch (const gdb_exception_error &e) 196 { 197 exception_print (gdb_stderr, e); 198 } 199 200 if (name != nullptr) 201 { 202 if (self->pattern->exec (name, 0, NULL, 0) != 0) 203 bs->stop = 0; 204 } 205 } 206 207 /* Implement the 're_set' method. */ 208 209 void 210 exception_catchpoint::re_set () 211 { 212 std::vector<symtab_and_line> sals; 213 struct program_space *filter_pspace = current_program_space; 214 215 /* We first try to use the probe interface. */ 216 try 217 { 218 location_spec_up locspec 219 = new_probe_location_spec (exception_functions[kind].probe); 220 sals = parse_probes (locspec.get (), filter_pspace, NULL); 221 } 222 catch (const gdb_exception_error &e) 223 { 224 /* Using the probe interface failed. Let's fallback to the normal 225 catchpoint mode. */ 226 try 227 { 228 location_spec_up locspec 229 = (new_explicit_location_spec_function 230 (exception_functions[kind].function)); 231 sals = this->decode_location_spec (locspec.get (), filter_pspace); 232 } 233 catch (const gdb_exception_error &ex) 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; 239 } 240 } 241 242 update_breakpoint_locations (this, filter_pspace, sals, {}); 243 } 244 245 enum print_stop_action 246 exception_catchpoint::print_it (const bpstat *bs) const 247 { 248 struct ui_out *uiout = current_uiout; 249 int bp_temp; 250 251 annotate_catchpoint (number); 252 maybe_print_thread_hit_breakpoint (uiout); 253 254 bp_temp = disposition == disp_del; 255 uiout->text (bp_temp ? "Temporary catchpoint " 256 : "Catchpoint "); 257 print_num_locno (bs, uiout); 258 uiout->text ((kind == EX_EVENT_THROW ? " (exception thrown), " 259 : (kind == EX_EVENT_CATCH ? " (exception caught), " 260 : " (exception rethrown), "))); 261 if (uiout->is_mi_like_p ()) 262 { 263 uiout->field_string ("reason", 264 async_reason_lookup (EXEC_ASYNC_BREAKPOINT_HIT)); 265 uiout->field_string ("disp", bpdisp_text (disposition)); 266 } 267 return PRINT_SRC_AND_LOC; 268 } 269 270 bool 271 exception_catchpoint::print_one (bp_location **last_loc) const 272 { 273 struct value_print_options opts; 274 struct ui_out *uiout = current_uiout; 275 276 get_user_print_options (&opts); 277 278 if (opts.addressprint) 279 uiout->field_skip ("addr"); 280 annotate_field (5); 281 282 switch (kind) 283 { 284 case EX_EVENT_THROW: 285 uiout->field_string ("what", "exception throw"); 286 if (uiout->is_mi_like_p ()) 287 uiout->field_string ("catch-type", "throw"); 288 break; 289 290 case EX_EVENT_RETHROW: 291 uiout->field_string ("what", "exception rethrow"); 292 if (uiout->is_mi_like_p ()) 293 uiout->field_string ("catch-type", "rethrow"); 294 break; 295 296 case EX_EVENT_CATCH: 297 uiout->field_string ("what", "exception catch"); 298 if (uiout->is_mi_like_p ()) 299 uiout->field_string ("catch-type", "catch"); 300 break; 301 } 302 303 return true; 304 } 305 306 /* Implement the 'print_one_detail' method. */ 307 308 void 309 exception_catchpoint::print_one_detail (struct ui_out *uiout) const 310 { 311 if (!exception_rx.empty ()) 312 { 313 uiout->text (_("\tmatching: ")); 314 uiout->field_string ("regexp", exception_rx); 315 uiout->text ("\n"); 316 } 317 } 318 319 void 320 exception_catchpoint::print_mention () const 321 { 322 struct ui_out *uiout = current_uiout; 323 int bp_temp; 324 325 bp_temp = disposition == disp_del; 326 uiout->message ("%s %d %s", 327 (bp_temp ? _("Temporary catchpoint ") : _("Catchpoint")), 328 number, 329 (kind == EX_EVENT_THROW 330 ? _("(throw)") : (kind == EX_EVENT_CATCH 331 ? _("(catch)") : _("(rethrow)")))); 332 } 333 334 /* Implement the "print_recreate" method for throw and catch 335 catchpoints. */ 336 337 void 338 exception_catchpoint::print_recreate (struct ui_file *fp) const 339 { 340 int bp_temp; 341 342 bp_temp = disposition == disp_del; 343 gdb_printf (fp, bp_temp ? "tcatch " : "catch "); 344 switch (kind) 345 { 346 case EX_EVENT_THROW: 347 gdb_printf (fp, "throw"); 348 break; 349 case EX_EVENT_CATCH: 350 gdb_printf (fp, "catch"); 351 break; 352 case EX_EVENT_RETHROW: 353 gdb_printf (fp, "rethrow"); 354 break; 355 } 356 print_recreate_thread (fp); 357 } 358 359 /* Implement the "allocate_location" method for throw and catch 360 catchpoints. */ 361 362 bp_location * 363 exception_catchpoint::allocate_location () 364 { 365 return new bp_location (this, bp_loc_software_breakpoint); 366 } 367 368 static void 369 handle_gnu_v3_exceptions (int tempflag, std::string &&except_rx, 370 const char *cond_string, 371 enum exception_event_kind ex_event, int from_tty) 372 { 373 struct gdbarch *gdbarch = get_current_arch (); 374 375 std::unique_ptr<exception_catchpoint> cp 376 (new exception_catchpoint (gdbarch, tempflag, cond_string, 377 ex_event, std::move (except_rx))); 378 379 install_breakpoint (0, std::move (cp), 1); 380 } 381 382 /* Look for an "if" token in *STRING. The "if" token must be preceded 383 by whitespace. 384 385 If there is any non-whitespace text between *STRING and the "if" 386 token, then it is returned in a newly-xmalloc'd string. Otherwise, 387 this returns NULL. 388 389 STRING is updated to point to the "if" token, if it exists, or to 390 the end of the string. */ 391 392 static std::string 393 extract_exception_regexp (const char **string) 394 { 395 const char *start; 396 const char *last, *last_space; 397 398 start = skip_spaces (*string); 399 400 last = start; 401 last_space = start; 402 while (*last != '\0') 403 { 404 const char *if_token = last; 405 406 /* Check for the "if". */ 407 if (check_for_argument (&if_token, "if", 2)) 408 break; 409 410 /* No "if" token here. Skip to the next word start. */ 411 last_space = skip_to_space (last); 412 last = skip_spaces (last_space); 413 } 414 415 *string = last; 416 if (last_space > start) 417 return std::string (start, last_space - start); 418 return std::string (); 419 } 420 421 /* See breakpoint.h. */ 422 423 void 424 catch_exception_event (enum exception_event_kind ex_event, 425 const char *arg, bool tempflag, int from_tty) 426 { 427 const char *cond_string = NULL; 428 429 if (!arg) 430 arg = ""; 431 arg = skip_spaces (arg); 432 433 std::string except_rx = extract_exception_regexp (&arg); 434 435 cond_string = ep_parse_optional_if_clause (&arg); 436 437 if ((*arg != '\0') && !isspace (*arg)) 438 error (_("Junk at end of arguments.")); 439 440 if (ex_event != EX_EVENT_THROW 441 && ex_event != EX_EVENT_CATCH 442 && ex_event != EX_EVENT_RETHROW) 443 error (_("Unsupported or unknown exception event; cannot catch it")); 444 445 handle_gnu_v3_exceptions (tempflag, std::move (except_rx), cond_string, 446 ex_event, from_tty); 447 } 448 449 /* Implementation of "catch catch" command. */ 450 451 static void 452 catch_catch_command (const char *arg, int from_tty, 453 struct cmd_list_element *command) 454 { 455 bool tempflag = command->context () == CATCH_TEMPORARY; 456 457 catch_exception_event (EX_EVENT_CATCH, arg, tempflag, from_tty); 458 } 459 460 /* Implementation of "catch throw" command. */ 461 462 static void 463 catch_throw_command (const char *arg, int from_tty, 464 struct cmd_list_element *command) 465 { 466 bool tempflag = command->context () == CATCH_TEMPORARY; 467 468 catch_exception_event (EX_EVENT_THROW, arg, tempflag, from_tty); 469 } 470 471 /* Implementation of "catch rethrow" command. */ 472 473 static void 474 catch_rethrow_command (const char *arg, int from_tty, 475 struct cmd_list_element *command) 476 { 477 bool tempflag = command->context () == CATCH_TEMPORARY; 478 479 catch_exception_event (EX_EVENT_RETHROW, arg, tempflag, from_tty); 480 } 481 482 483 484 /* Implement the 'make_value' method for the $_exception 485 internalvar. */ 486 487 static struct value * 488 compute_exception (struct gdbarch *argc, struct internalvar *var, void *ignore) 489 { 490 struct value *arg0, *arg1; 491 struct type *obj_type; 492 493 fetch_probe_arguments (&arg0, &arg1); 494 495 /* ARG0 is a pointer to the exception object. ARG1 is a pointer to 496 the std::type_info for the exception. Now we find the type from 497 the type_info and cast the result. */ 498 obj_type = cplus_type_from_type_info (arg1); 499 return value_ind (value_cast (make_pointer_type (obj_type, NULL), arg0)); 500 } 501 502 /* Implementation of the '$_exception' variable. */ 503 504 static const struct internalvar_funcs exception_funcs = 505 { 506 compute_exception, 507 NULL, 508 }; 509 510 511 512 void _initialize_break_catch_throw (); 513 void 514 _initialize_break_catch_throw () 515 { 516 /* Add catch and tcatch sub-commands. */ 517 add_catch_command ("catch", _("\ 518 Catch an exception, when caught."), 519 catch_catch_command, 520 NULL, 521 CATCH_PERMANENT, 522 CATCH_TEMPORARY); 523 add_catch_command ("throw", _("\ 524 Catch an exception, when thrown."), 525 catch_throw_command, 526 NULL, 527 CATCH_PERMANENT, 528 CATCH_TEMPORARY); 529 add_catch_command ("rethrow", _("\ 530 Catch an exception, when rethrown."), 531 catch_rethrow_command, 532 NULL, 533 CATCH_PERMANENT, 534 CATCH_TEMPORARY); 535 536 create_internalvar_type_lazy ("_exception", &exception_funcs, NULL); 537 } 538