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