Commit | Line | Data |
---|---|---|
916703c0 TT |
1 | /* Everything about catch/throw catchpoints, for GDB. |
2 | ||
3 | Copyright (C) 1986-2013 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" | |
15a73f56 TT |
32 | #include "exceptions.h" |
33 | #include "linespec.h" | |
fc4746a2 | 34 | #include "probe.h" |
916703c0 TT |
35 | |
36 | /* Enums for exception-handling support. */ | |
37 | enum exception_event_kind | |
38 | { | |
39 | EX_EVENT_THROW, | |
40 | EX_EVENT_RETHROW, | |
41 | EX_EVENT_CATCH | |
42 | }; | |
43 | ||
fc4746a2 TT |
44 | /* Each spot where we may place an exception-related catchpoint has |
45 | two names: the SDT probe point and the function name. This | |
46 | structure holds both. */ | |
47 | ||
48 | struct exception_names | |
49 | { | |
50 | /* The name of the probe point to try, in the form accepted by | |
51 | 'parse_probes'. */ | |
52 | ||
53 | const char *probe; | |
54 | ||
55 | /* The name of the corresponding function. */ | |
56 | ||
57 | const char *function; | |
58 | }; | |
59 | ||
60 | /* Names of the probe points and functions on which to break. This is | |
61 | indexed by exception_event_kind. */ | |
62 | static const struct exception_names exception_functions[] = | |
15a73f56 | 63 | { |
fc4746a2 TT |
64 | { "-probe-stap libstdcxx:throw", "__cxa_throw" }, |
65 | { "-probe-stap libstdcxx:rethrow", "__cxa_rethrow" }, | |
66 | { "-probe-stap libstdcxx:catch", "__cxa_begin_catch" } | |
15a73f56 TT |
67 | }; |
68 | ||
69 | static struct breakpoint_ops gnu_v3_exception_catchpoint_ops; | |
70 | ||
71 | /* The type of an exception catchpoint. */ | |
72 | ||
73 | struct exception_catchpoint | |
74 | { | |
75 | /* The base class. */ | |
76 | ||
77 | struct breakpoint base; | |
78 | ||
79 | /* The kind of exception catchpoint. */ | |
80 | ||
81 | enum exception_event_kind kind; | |
82 | }; | |
83 | ||
916703c0 TT |
84 | /* A helper function that returns a value indicating the kind of the |
85 | exception catchpoint B. */ | |
86 | ||
87 | static enum exception_event_kind | |
88 | classify_exception_breakpoint (struct breakpoint *b) | |
89 | { | |
15a73f56 TT |
90 | struct exception_catchpoint *cp = (struct exception_catchpoint *) b; |
91 | ||
92 | return cp->kind; | |
93 | } | |
94 | ||
95 | /* Implement the 're_set' method. */ | |
96 | ||
97 | static void | |
98 | re_set_exception_catchpoint (struct breakpoint *self) | |
99 | { | |
100 | struct symtabs_and_lines sals = {0}; | |
101 | struct symtabs_and_lines sals_end = {0}; | |
102 | volatile struct gdb_exception e; | |
103 | struct cleanup *cleanup; | |
104 | enum exception_event_kind kind = classify_exception_breakpoint (self); | |
fc4746a2 | 105 | int pass; |
15a73f56 | 106 | |
fc4746a2 | 107 | for (pass = 0; sals.sals == NULL && pass < 2; ++pass) |
15a73f56 | 108 | { |
fc4746a2 TT |
109 | TRY_CATCH (e, RETURN_MASK_ERROR) |
110 | { | |
111 | char *spec; | |
112 | ||
113 | if (pass == 0) | |
114 | { | |
115 | spec = ASTRDUP (exception_functions[kind].probe); | |
116 | sals = parse_probes (&spec, NULL); | |
117 | } | |
118 | else | |
119 | { | |
120 | spec = ASTRDUP (exception_functions[kind].function); | |
121 | self->ops->decode_linespec (self, &spec, &sals); | |
122 | } | |
123 | } | |
124 | /* NOT_FOUND_ERROR just means the breakpoint will be pending, so | |
125 | let it through. */ | |
126 | if (e.reason < 0 && e.error != NOT_FOUND_ERROR) | |
127 | throw_exception (e); | |
15a73f56 | 128 | } |
15a73f56 TT |
129 | |
130 | cleanup = make_cleanup (xfree, sals.sals); | |
131 | update_breakpoint_locations (self, sals, sals_end); | |
132 | do_cleanups (cleanup); | |
916703c0 TT |
133 | } |
134 | ||
135 | static enum print_stop_action | |
136 | print_it_exception_catchpoint (bpstat bs) | |
137 | { | |
138 | struct ui_out *uiout = current_uiout; | |
139 | struct breakpoint *b = bs->breakpoint_at; | |
140 | int bp_temp; | |
141 | enum exception_event_kind kind = classify_exception_breakpoint (b); | |
142 | ||
143 | annotate_catchpoint (b->number); | |
144 | ||
145 | bp_temp = b->disposition == disp_del; | |
146 | ui_out_text (uiout, | |
147 | bp_temp ? "Temporary catchpoint " | |
148 | : "Catchpoint "); | |
149 | if (!ui_out_is_mi_like_p (uiout)) | |
150 | ui_out_field_int (uiout, "bkptno", b->number); | |
151 | ui_out_text (uiout, | |
152 | (kind == EX_EVENT_THROW ? " (exception thrown), " | |
153 | : (kind == EX_EVENT_CATCH ? " (exception caught), " | |
154 | : " (exception rethrown), "))); | |
155 | if (ui_out_is_mi_like_p (uiout)) | |
156 | { | |
157 | ui_out_field_string (uiout, "reason", | |
158 | async_reason_lookup (EXEC_ASYNC_BREAKPOINT_HIT)); | |
159 | ui_out_field_string (uiout, "disp", bpdisp_text (b->disposition)); | |
160 | ui_out_field_int (uiout, "bkptno", b->number); | |
161 | } | |
162 | return PRINT_SRC_AND_LOC; | |
163 | } | |
164 | ||
165 | static void | |
166 | print_one_exception_catchpoint (struct breakpoint *b, | |
167 | struct bp_location **last_loc) | |
168 | { | |
169 | struct value_print_options opts; | |
170 | struct ui_out *uiout = current_uiout; | |
171 | enum exception_event_kind kind = classify_exception_breakpoint (b); | |
172 | ||
173 | get_user_print_options (&opts); | |
174 | if (opts.addressprint) | |
175 | { | |
176 | annotate_field (4); | |
177 | if (b->loc == NULL || b->loc->shlib_disabled) | |
178 | ui_out_field_string (uiout, "addr", "<PENDING>"); | |
179 | else | |
180 | ui_out_field_core_addr (uiout, "addr", | |
181 | b->loc->gdbarch, b->loc->address); | |
182 | } | |
183 | annotate_field (5); | |
184 | if (b->loc) | |
185 | *last_loc = b->loc; | |
186 | ||
187 | switch (kind) | |
188 | { | |
189 | case EX_EVENT_THROW: | |
190 | ui_out_field_string (uiout, "what", "exception throw"); | |
191 | if (ui_out_is_mi_like_p (uiout)) | |
192 | ui_out_field_string (uiout, "catch-type", "throw"); | |
193 | break; | |
194 | ||
195 | case EX_EVENT_RETHROW: | |
196 | ui_out_field_string (uiout, "what", "exception rethrow"); | |
197 | if (ui_out_is_mi_like_p (uiout)) | |
198 | ui_out_field_string (uiout, "catch-type", "rethrow"); | |
199 | break; | |
200 | ||
201 | case EX_EVENT_CATCH: | |
202 | ui_out_field_string (uiout, "what", "exception catch"); | |
203 | if (ui_out_is_mi_like_p (uiout)) | |
204 | ui_out_field_string (uiout, "catch-type", "catch"); | |
205 | break; | |
206 | } | |
207 | } | |
208 | ||
209 | static void | |
210 | print_mention_exception_catchpoint (struct breakpoint *b) | |
211 | { | |
212 | struct ui_out *uiout = current_uiout; | |
213 | int bp_temp; | |
214 | enum exception_event_kind kind = classify_exception_breakpoint (b); | |
215 | ||
216 | bp_temp = b->disposition == disp_del; | |
217 | ui_out_text (uiout, bp_temp ? _("Temporary catchpoint ") | |
218 | : _("Catchpoint ")); | |
219 | ui_out_field_int (uiout, "bkptno", b->number); | |
220 | ui_out_text (uiout, (kind == EX_EVENT_THROW ? _(" (throw)") | |
221 | : (kind == EX_EVENT_CATCH ? _(" (catch)") | |
222 | : _(" (rethrow)")))); | |
223 | } | |
224 | ||
225 | /* Implement the "print_recreate" breakpoint_ops method for throw and | |
226 | catch catchpoints. */ | |
227 | ||
228 | static void | |
229 | print_recreate_exception_catchpoint (struct breakpoint *b, | |
230 | struct ui_file *fp) | |
231 | { | |
232 | int bp_temp; | |
233 | enum exception_event_kind kind = classify_exception_breakpoint (b); | |
234 | ||
235 | bp_temp = b->disposition == disp_del; | |
236 | fprintf_unfiltered (fp, bp_temp ? "tcatch " : "catch "); | |
237 | switch (kind) | |
238 | { | |
239 | case EX_EVENT_THROW: | |
240 | fprintf_unfiltered (fp, "throw"); | |
241 | break; | |
242 | case EX_EVENT_CATCH: | |
243 | fprintf_unfiltered (fp, "catch"); | |
244 | break; | |
245 | case EX_EVENT_RETHROW: | |
246 | fprintf_unfiltered (fp, "rethrow"); | |
247 | break; | |
248 | } | |
249 | print_recreate_thread (b, fp); | |
250 | } | |
251 | ||
15a73f56 | 252 | static void |
916703c0 TT |
253 | handle_gnu_v3_exceptions (int tempflag, char *cond_string, |
254 | enum exception_event_kind ex_event, int from_tty) | |
255 | { | |
15a73f56 | 256 | struct exception_catchpoint *cp; |
916703c0 | 257 | |
15a73f56 TT |
258 | cp = XCNEW (struct exception_catchpoint); |
259 | init_catchpoint (&cp->base, get_current_arch (), tempflag, cond_string, | |
260 | &gnu_v3_exception_catchpoint_ops); | |
261 | /* We need to reset 'type' in order for code in breakpoint.c to do | |
262 | the right thing. */ | |
263 | cp->base.type = bp_breakpoint; | |
264 | cp->kind = ex_event; | |
265 | ||
266 | re_set_exception_catchpoint (&cp->base); | |
267 | ||
268 | install_breakpoint (0, &cp->base, 1); | |
916703c0 TT |
269 | } |
270 | ||
271 | /* Deal with "catch catch", "catch throw", and "catch rethrow" | |
272 | commands. */ | |
273 | ||
274 | static void | |
275 | catch_exception_command_1 (enum exception_event_kind ex_event, char *arg, | |
276 | int tempflag, int from_tty) | |
277 | { | |
278 | char *cond_string = NULL; | |
279 | ||
280 | if (!arg) | |
281 | arg = ""; | |
282 | arg = skip_spaces (arg); | |
283 | ||
284 | cond_string = ep_parse_optional_if_clause (&arg); | |
285 | ||
286 | if ((*arg != '\0') && !isspace (*arg)) | |
287 | error (_("Junk at end of arguments.")); | |
288 | ||
289 | if (ex_event != EX_EVENT_THROW | |
290 | && ex_event != EX_EVENT_CATCH | |
291 | && ex_event != EX_EVENT_RETHROW) | |
292 | error (_("Unsupported or unknown exception event; cannot catch it")); | |
293 | ||
15a73f56 | 294 | handle_gnu_v3_exceptions (tempflag, cond_string, ex_event, from_tty); |
916703c0 TT |
295 | } |
296 | ||
297 | /* Implementation of "catch catch" command. */ | |
298 | ||
299 | static void | |
300 | catch_catch_command (char *arg, int from_tty, struct cmd_list_element *command) | |
301 | { | |
302 | int tempflag = get_cmd_context (command) == CATCH_TEMPORARY; | |
303 | ||
304 | catch_exception_command_1 (EX_EVENT_CATCH, arg, tempflag, from_tty); | |
305 | } | |
306 | ||
307 | /* Implementation of "catch throw" command. */ | |
308 | ||
309 | static void | |
310 | catch_throw_command (char *arg, int from_tty, struct cmd_list_element *command) | |
311 | { | |
312 | int tempflag = get_cmd_context (command) == CATCH_TEMPORARY; | |
313 | ||
314 | catch_exception_command_1 (EX_EVENT_THROW, arg, tempflag, from_tty); | |
315 | } | |
316 | ||
317 | /* Implementation of "catch rethrow" command. */ | |
318 | ||
319 | static void | |
320 | catch_rethrow_command (char *arg, int from_tty, | |
321 | struct cmd_list_element *command) | |
322 | { | |
323 | int tempflag = get_cmd_context (command) == CATCH_TEMPORARY; | |
324 | ||
325 | catch_exception_command_1 (EX_EVENT_RETHROW, arg, tempflag, from_tty); | |
326 | } | |
327 | ||
328 | \f | |
329 | ||
330 | static void | |
331 | initialize_throw_catchpoint_ops (void) | |
332 | { | |
333 | struct breakpoint_ops *ops; | |
334 | ||
335 | initialize_breakpoint_ops (); | |
336 | ||
337 | /* GNU v3 exception catchpoints. */ | |
338 | ops = &gnu_v3_exception_catchpoint_ops; | |
339 | *ops = bkpt_breakpoint_ops; | |
15a73f56 | 340 | ops->re_set = re_set_exception_catchpoint; |
916703c0 TT |
341 | ops->print_it = print_it_exception_catchpoint; |
342 | ops->print_one = print_one_exception_catchpoint; | |
343 | ops->print_mention = print_mention_exception_catchpoint; | |
344 | ops->print_recreate = print_recreate_exception_catchpoint; | |
345 | } | |
346 | ||
347 | initialize_file_ftype _initialize_break_catch_throw; | |
348 | ||
349 | void | |
350 | _initialize_break_catch_throw (void) | |
351 | { | |
352 | initialize_throw_catchpoint_ops (); | |
353 | ||
354 | /* Add catch and tcatch sub-commands. */ | |
355 | add_catch_command ("catch", _("\ | |
356 | Catch an exception, when caught."), | |
357 | catch_catch_command, | |
358 | NULL, | |
359 | CATCH_PERMANENT, | |
360 | CATCH_TEMPORARY); | |
361 | add_catch_command ("throw", _("\ | |
362 | Catch an exception, when thrown."), | |
363 | catch_throw_command, | |
364 | NULL, | |
365 | CATCH_PERMANENT, | |
366 | CATCH_TEMPORARY); | |
367 | add_catch_command ("rethrow", _("\ | |
368 | Catch an exception, when rethrown."), | |
369 | catch_rethrow_command, | |
370 | NULL, | |
371 | CATCH_PERMANENT, | |
372 | CATCH_TEMPORARY); | |
373 | } |