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