123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564 |
- /* Everything about catch/throw catchpoints, for GDB.
- Copyright (C) 1986-2022 Free Software Foundation, Inc.
- This file is part of GDB.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>. */
- #include "defs.h"
- #include "arch-utils.h"
- #include <ctype.h>
- #include "breakpoint.h"
- #include "gdbcmd.h"
- #include "inferior.h"
- #include "annotate.h"
- #include "valprint.h"
- #include "cli/cli-utils.h"
- #include "completer.h"
- #include "gdbsupport/gdb_obstack.h"
- #include "mi/mi-common.h"
- #include "linespec.h"
- #include "probe.h"
- #include "objfiles.h"
- #include "cp-abi.h"
- #include "gdbsupport/gdb_regex.h"
- #include "cp-support.h"
- #include "location.h"
- #include "cli/cli-decode.h"
- /* Each spot where we may place an exception-related catchpoint has
- two names: the SDT probe point and the function name. This
- structure holds both. */
- struct exception_names
- {
- /* The name of the probe point to try, in the form accepted by
- 'parse_probes'. */
- const char *probe;
- /* The name of the corresponding function. */
- const char *function;
- };
- /* Names of the probe points and functions on which to break. This is
- indexed by exception_event_kind. */
- static const struct exception_names exception_functions[] =
- {
- { "-probe-stap libstdcxx:throw", "__cxa_throw" },
- { "-probe-stap libstdcxx:rethrow", "__cxa_rethrow" },
- { "-probe-stap libstdcxx:catch", "__cxa_begin_catch" }
- };
- static struct breakpoint_ops gnu_v3_exception_catchpoint_ops;
- /* The type of an exception catchpoint. */
- struct exception_catchpoint : public breakpoint
- {
- /* The kind of exception catchpoint. */
- enum exception_event_kind kind;
- /* If not empty, a string holding the source form of the regular
- expression to match against. */
- std::string exception_rx;
- /* If non-NULL, a compiled regular expression which is used to
- determine which exceptions to stop on. */
- std::unique_ptr<compiled_regex> pattern;
- };
- /* See breakpoint.h. */
- bool
- is_exception_catchpoint (breakpoint *bp)
- {
- return bp->ops == &gnu_v3_exception_catchpoint_ops;
- }
- /* A helper function that fetches exception probe arguments. This
- fills in *ARG0 (if non-NULL) and *ARG1 (which must be non-NULL).
- It will throw an exception on any kind of failure. */
- static void
- fetch_probe_arguments (struct value **arg0, struct value **arg1)
- {
- struct frame_info *frame = get_selected_frame (_("No frame selected"));
- CORE_ADDR pc = get_frame_pc (frame);
- struct bound_probe pc_probe;
- unsigned n_args;
- pc_probe = find_probe_by_pc (pc);
- if (pc_probe.prob == NULL)
- error (_("did not find exception probe (does libstdcxx have SDT probes?)"));
- if (pc_probe.prob->get_provider () != "libstdcxx"
- || (pc_probe.prob->get_name () != "catch"
- && pc_probe.prob->get_name () != "throw"
- && pc_probe.prob->get_name () != "rethrow"))
- error (_("not stopped at a C++ exception catchpoint"));
- n_args = pc_probe.prob->get_argument_count (get_frame_arch (frame));
- if (n_args < 2)
- error (_("C++ exception catchpoint has too few arguments"));
- if (arg0 != NULL)
- *arg0 = pc_probe.prob->evaluate_argument (0, frame);
- *arg1 = pc_probe.prob->evaluate_argument (1, frame);
- if ((arg0 != NULL && *arg0 == NULL) || *arg1 == NULL)
- error (_("error computing probe argument at c++ exception catchpoint"));
- }
- /* A helper function that returns a value indicating the kind of the
- exception catchpoint B. */
- static enum exception_event_kind
- classify_exception_breakpoint (struct breakpoint *b)
- {
- struct exception_catchpoint *cp = (struct exception_catchpoint *) b;
- return cp->kind;
- }
- /* Implement the 'check_status' method. */
- static void
- check_status_exception_catchpoint (struct bpstat *bs)
- {
- struct exception_catchpoint *self
- = (struct exception_catchpoint *) bs->breakpoint_at;
- std::string type_name;
- bkpt_breakpoint_ops.check_status (bs);
- if (bs->stop == 0)
- return;
- if (self->pattern == NULL)
- return;
- const char *name = nullptr;
- gdb::unique_xmalloc_ptr<char> canon;
- try
- {
- struct value *typeinfo_arg;
- fetch_probe_arguments (NULL, &typeinfo_arg);
- type_name = cplus_typename_from_type_info (typeinfo_arg);
- canon = cp_canonicalize_string (type_name.c_str ());
- name = (canon != nullptr
- ? canon.get ()
- : type_name.c_str ());
- }
- catch (const gdb_exception_error &e)
- {
- exception_print (gdb_stderr, e);
- }
- if (name != nullptr)
- {
- if (self->pattern->exec (name, 0, NULL, 0) != 0)
- bs->stop = 0;
- }
- }
- /* Implement the 're_set' method. */
- static void
- re_set_exception_catchpoint (struct breakpoint *self)
- {
- std::vector<symtab_and_line> sals;
- enum exception_event_kind kind = classify_exception_breakpoint (self);
- struct program_space *filter_pspace = current_program_space;
- /* We first try to use the probe interface. */
- try
- {
- event_location_up location
- = new_probe_location (exception_functions[kind].probe);
- sals = parse_probes (location.get (), filter_pspace, NULL);
- }
- catch (const gdb_exception_error &e)
- {
- /* Using the probe interface failed. Let's fallback to the normal
- catchpoint mode. */
- try
- {
- struct explicit_location explicit_loc;
- initialize_explicit_location (&explicit_loc);
- explicit_loc.function_name
- = ASTRDUP (exception_functions[kind].function);
- event_location_up location = new_explicit_location (&explicit_loc);
- sals = self->ops->decode_location (self, location.get (),
- filter_pspace);
- }
- catch (const gdb_exception_error &ex)
- {
- /* NOT_FOUND_ERROR just means the breakpoint will be
- pending, so let it through. */
- if (ex.error != NOT_FOUND_ERROR)
- throw;
- }
- }
- update_breakpoint_locations (self, filter_pspace, sals, {});
- }
- static enum print_stop_action
- print_it_exception_catchpoint (bpstat *bs)
- {
- struct ui_out *uiout = current_uiout;
- struct breakpoint *b = bs->breakpoint_at;
- int bp_temp;
- enum exception_event_kind kind = classify_exception_breakpoint (b);
- annotate_catchpoint (b->number);
- maybe_print_thread_hit_breakpoint (uiout);
- bp_temp = b->disposition == disp_del;
- uiout->text (bp_temp ? "Temporary catchpoint "
- : "Catchpoint ");
- uiout->field_signed ("bkptno", b->number);
- uiout->text ((kind == EX_EVENT_THROW ? " (exception thrown), "
- : (kind == EX_EVENT_CATCH ? " (exception caught), "
- : " (exception rethrown), ")));
- if (uiout->is_mi_like_p ())
- {
- uiout->field_string ("reason",
- async_reason_lookup (EXEC_ASYNC_BREAKPOINT_HIT));
- uiout->field_string ("disp", bpdisp_text (b->disposition));
- }
- return PRINT_SRC_AND_LOC;
- }
- static void
- print_one_exception_catchpoint (struct breakpoint *b,
- struct bp_location **last_loc)
- {
- struct value_print_options opts;
- struct ui_out *uiout = current_uiout;
- enum exception_event_kind kind = classify_exception_breakpoint (b);
- get_user_print_options (&opts);
- if (opts.addressprint)
- uiout->field_skip ("addr");
- annotate_field (5);
- switch (kind)
- {
- case EX_EVENT_THROW:
- uiout->field_string ("what", "exception throw");
- if (uiout->is_mi_like_p ())
- uiout->field_string ("catch-type", "throw");
- break;
- case EX_EVENT_RETHROW:
- uiout->field_string ("what", "exception rethrow");
- if (uiout->is_mi_like_p ())
- uiout->field_string ("catch-type", "rethrow");
- break;
- case EX_EVENT_CATCH:
- uiout->field_string ("what", "exception catch");
- if (uiout->is_mi_like_p ())
- uiout->field_string ("catch-type", "catch");
- break;
- }
- }
- /* Implement the 'print_one_detail' method. */
- static void
- print_one_detail_exception_catchpoint (const struct breakpoint *b,
- struct ui_out *uiout)
- {
- const struct exception_catchpoint *cp
- = (const struct exception_catchpoint *) b;
- if (!cp->exception_rx.empty ())
- {
- uiout->text (_("\tmatching: "));
- uiout->field_string ("regexp", cp->exception_rx);
- uiout->text ("\n");
- }
- }
- static void
- print_mention_exception_catchpoint (struct breakpoint *b)
- {
- struct ui_out *uiout = current_uiout;
- int bp_temp;
- enum exception_event_kind kind = classify_exception_breakpoint (b);
- bp_temp = b->disposition == disp_del;
- uiout->message ("%s %d %s",
- (bp_temp ? _("Temporary catchpoint ") : _("Catchpoint")),
- b->number,
- (kind == EX_EVENT_THROW
- ? _("(throw)") : (kind == EX_EVENT_CATCH
- ? _("(catch)") : _("(rethrow)"))));
- }
- /* Implement the "print_recreate" breakpoint_ops method for throw and
- catch catchpoints. */
- static void
- print_recreate_exception_catchpoint (struct breakpoint *b,
- struct ui_file *fp)
- {
- int bp_temp;
- enum exception_event_kind kind = classify_exception_breakpoint (b);
- bp_temp = b->disposition == disp_del;
- gdb_printf (fp, bp_temp ? "tcatch " : "catch ");
- switch (kind)
- {
- case EX_EVENT_THROW:
- gdb_printf (fp, "throw");
- break;
- case EX_EVENT_CATCH:
- gdb_printf (fp, "catch");
- break;
- case EX_EVENT_RETHROW:
- gdb_printf (fp, "rethrow");
- break;
- }
- print_recreate_thread (b, fp);
- }
- /* Implement the "allocate_location" breakpoint_ops method for throw
- and catch catchpoints. */
- static bp_location *
- allocate_location_exception_catchpoint (breakpoint *self)
- {
- return new bp_location (self, bp_loc_software_breakpoint);
- }
- static void
- handle_gnu_v3_exceptions (int tempflag, std::string &&except_rx,
- const char *cond_string,
- enum exception_event_kind ex_event, int from_tty)
- {
- std::unique_ptr<compiled_regex> pattern;
- if (!except_rx.empty ())
- {
- pattern.reset (new compiled_regex (except_rx.c_str (), REG_NOSUB,
- _("invalid type-matching regexp")));
- }
- std::unique_ptr<exception_catchpoint> cp (new exception_catchpoint ());
- init_catchpoint (cp.get (), get_current_arch (), tempflag, cond_string,
- &gnu_v3_exception_catchpoint_ops);
- cp->kind = ex_event;
- cp->exception_rx = std::move (except_rx);
- cp->pattern = std::move (pattern);
- re_set_exception_catchpoint (cp.get ());
- install_breakpoint (0, std::move (cp), 1);
- }
- /* Look for an "if" token in *STRING. The "if" token must be preceded
- by whitespace.
-
- If there is any non-whitespace text between *STRING and the "if"
- token, then it is returned in a newly-xmalloc'd string. Otherwise,
- this returns NULL.
-
- STRING is updated to point to the "if" token, if it exists, or to
- the end of the string. */
- static std::string
- extract_exception_regexp (const char **string)
- {
- const char *start;
- const char *last, *last_space;
- start = skip_spaces (*string);
- last = start;
- last_space = start;
- while (*last != '\0')
- {
- const char *if_token = last;
- /* Check for the "if". */
- if (check_for_argument (&if_token, "if", 2))
- break;
- /* No "if" token here. Skip to the next word start. */
- last_space = skip_to_space (last);
- last = skip_spaces (last_space);
- }
- *string = last;
- if (last_space > start)
- return std::string (start, last_space - start);
- return std::string ();
- }
- /* See breakpoint.h. */
- void
- catch_exception_event (enum exception_event_kind ex_event,
- const char *arg, bool tempflag, int from_tty)
- {
- const char *cond_string = NULL;
- if (!arg)
- arg = "";
- arg = skip_spaces (arg);
- std::string except_rx = extract_exception_regexp (&arg);
- cond_string = ep_parse_optional_if_clause (&arg);
- if ((*arg != '\0') && !isspace (*arg))
- error (_("Junk at end of arguments."));
- if (ex_event != EX_EVENT_THROW
- && ex_event != EX_EVENT_CATCH
- && ex_event != EX_EVENT_RETHROW)
- error (_("Unsupported or unknown exception event; cannot catch it"));
- handle_gnu_v3_exceptions (tempflag, std::move (except_rx), cond_string,
- ex_event, from_tty);
- }
- /* Implementation of "catch catch" command. */
- static void
- catch_catch_command (const char *arg, int from_tty,
- struct cmd_list_element *command)
- {
- bool tempflag = command->context () == CATCH_TEMPORARY;
- catch_exception_event (EX_EVENT_CATCH, arg, tempflag, from_tty);
- }
- /* Implementation of "catch throw" command. */
- static void
- catch_throw_command (const char *arg, int from_tty,
- struct cmd_list_element *command)
- {
- bool tempflag = command->context () == CATCH_TEMPORARY;
- catch_exception_event (EX_EVENT_THROW, arg, tempflag, from_tty);
- }
- /* Implementation of "catch rethrow" command. */
- static void
- catch_rethrow_command (const char *arg, int from_tty,
- struct cmd_list_element *command)
- {
- bool tempflag = command->context () == CATCH_TEMPORARY;
- catch_exception_event (EX_EVENT_RETHROW, arg, tempflag, from_tty);
- }
- /* Implement the 'make_value' method for the $_exception
- internalvar. */
- static struct value *
- compute_exception (struct gdbarch *argc, struct internalvar *var, void *ignore)
- {
- struct value *arg0, *arg1;
- struct type *obj_type;
- fetch_probe_arguments (&arg0, &arg1);
- /* ARG0 is a pointer to the exception object. ARG1 is a pointer to
- the std::type_info for the exception. Now we find the type from
- the type_info and cast the result. */
- obj_type = cplus_type_from_type_info (arg1);
- return value_ind (value_cast (make_pointer_type (obj_type, NULL), arg0));
- }
- /* Implementation of the '$_exception' variable. */
- static const struct internalvar_funcs exception_funcs =
- {
- compute_exception,
- NULL,
- };
- static void
- initialize_throw_catchpoint_ops (void)
- {
- struct breakpoint_ops *ops;
- initialize_breakpoint_ops ();
- /* GNU v3 exception catchpoints. */
- ops = &gnu_v3_exception_catchpoint_ops;
- *ops = bkpt_breakpoint_ops;
- ops->re_set = re_set_exception_catchpoint;
- ops->print_it = print_it_exception_catchpoint;
- ops->print_one = print_one_exception_catchpoint;
- ops->print_mention = print_mention_exception_catchpoint;
- ops->print_recreate = print_recreate_exception_catchpoint;
- ops->print_one_detail = print_one_detail_exception_catchpoint;
- ops->check_status = check_status_exception_catchpoint;
- ops->allocate_location = allocate_location_exception_catchpoint;
- }
- void _initialize_break_catch_throw ();
- void
- _initialize_break_catch_throw ()
- {
- initialize_throw_catchpoint_ops ();
- /* Add catch and tcatch sub-commands. */
- add_catch_command ("catch", _("\
- Catch an exception, when caught."),
- catch_catch_command,
- NULL,
- CATCH_PERMANENT,
- CATCH_TEMPORARY);
- add_catch_command ("throw", _("\
- Catch an exception, when thrown."),
- catch_throw_command,
- NULL,
- CATCH_PERMANENT,
- CATCH_TEMPORARY);
- add_catch_command ("rethrow", _("\
- Catch an exception, when rethrown."),
- catch_rethrow_command,
- NULL,
- CATCH_PERMANENT,
- CATCH_TEMPORARY);
- create_internalvar_type_lazy ("_exception", &exception_funcs, NULL);
- }
|