X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=gdb%2Fbreak-catch-throw.c;h=7f4a9f955dfc7edbb61ac5452a0245f1f9f7dab7;hb=60db1b8565060f4bd2287b060ea9724c93289982;hp=b0adbbb3e2981a66c7d3c27eeaba3e7848bbc0ab;hpb=916703c090d3d30421d6b67cfb9f2e9e711d965a;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/break-catch-throw.c b/gdb/break-catch-throw.c index b0adbbb3e2..7f4a9f955d 100644 --- a/gdb/break-catch-throw.c +++ b/gdb/break-catch-throw.c @@ -1,6 +1,6 @@ /* Everything about catch/throw catchpoints, for GDB. - Copyright (C) 1986-2013 Free Software Foundation, Inc. + Copyright (C) 1986-2020 Free Software Foundation, Inc. This file is part of GDB. @@ -29,27 +29,200 @@ #include "completer.h" #include "gdb_obstack.h" #include "mi/mi-common.h" +#include "linespec.h" +#include "probe.h" +#include "objfiles.h" +#include "cp-abi.h" +#include "gdb_regex.h" +#include "cp-support.h" +#include "location.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. */ -/* Enums for exception-handling support. */ -enum exception_event_kind +struct exception_catchpoint : public breakpoint { - EX_EVENT_THROW, - EX_EVENT_RETHROW, - EX_EVENT_CATCH + /* 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 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) { - if (strstr (b->addr_string, "catch") != NULL) - return EX_EVENT_CATCH; - else if (strstr (b->addr_string, "rethrow") != NULL) - return EX_EVENT_RETHROW; - else - return EX_EVENT_THROW; + struct exception_catchpoint *cp = (struct exception_catchpoint *) b; + + return cp->kind; +} + +/* Implement the 'check_status' method. */ + +static void +check_status_exception_catchpoint (struct bpstats *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 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 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 @@ -61,23 +234,20 @@ print_it_exception_catchpoint (bpstat bs) 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; - ui_out_text (uiout, - bp_temp ? "Temporary catchpoint " + uiout->text (bp_temp ? "Temporary catchpoint " : "Catchpoint "); - if (!ui_out_is_mi_like_p (uiout)) - ui_out_field_int (uiout, "bkptno", b->number); - ui_out_text (uiout, - (kind == EX_EVENT_THROW ? " (exception thrown), " + uiout->field_signed ("bkptno", b->number); + uiout->text ((kind == EX_EVENT_THROW ? " (exception thrown), " : (kind == EX_EVENT_CATCH ? " (exception caught), " : " (exception rethrown), "))); - if (ui_out_is_mi_like_p (uiout)) + if (uiout->is_mi_like_p ()) { - ui_out_field_string (uiout, "reason", + uiout->field_string ("reason", async_reason_lookup (EXEC_ASYNC_BREAKPOINT_HIT)); - ui_out_field_string (uiout, "disp", bpdisp_text (b->disposition)); - ui_out_field_int (uiout, "bkptno", b->number); + uiout->field_string ("disp", bpdisp_text (b->disposition)); } return PRINT_SRC_AND_LOC; } @@ -91,41 +261,50 @@ print_one_exception_catchpoint (struct breakpoint *b, enum exception_event_kind kind = classify_exception_breakpoint (b); get_user_print_options (&opts); + if (opts.addressprint) - { - annotate_field (4); - if (b->loc == NULL || b->loc->shlib_disabled) - ui_out_field_string (uiout, "addr", ""); - else - ui_out_field_core_addr (uiout, "addr", - b->loc->gdbarch, b->loc->address); - } + uiout->field_skip ("addr"); annotate_field (5); - if (b->loc) - *last_loc = b->loc; switch (kind) { case EX_EVENT_THROW: - ui_out_field_string (uiout, "what", "exception throw"); - if (ui_out_is_mi_like_p (uiout)) - ui_out_field_string (uiout, "catch-type", "throw"); + uiout->field_string ("what", "exception throw"); + if (uiout->is_mi_like_p ()) + uiout->field_string ("catch-type", "throw"); break; case EX_EVENT_RETHROW: - ui_out_field_string (uiout, "what", "exception rethrow"); - if (ui_out_is_mi_like_p (uiout)) - ui_out_field_string (uiout, "catch-type", "rethrow"); + uiout->field_string ("what", "exception rethrow"); + if (uiout->is_mi_like_p ()) + uiout->field_string ("catch-type", "rethrow"); break; case EX_EVENT_CATCH: - ui_out_field_string (uiout, "what", "exception catch"); - if (ui_out_is_mi_like_p (uiout)) - ui_out_field_string (uiout, "catch-type", "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.c_str ()); + uiout->text ("\n"); + } +} + static void print_mention_exception_catchpoint (struct breakpoint *b) { @@ -134,12 +313,12 @@ print_mention_exception_catchpoint (struct breakpoint *b) enum exception_event_kind kind = classify_exception_breakpoint (b); bp_temp = b->disposition == disp_del; - ui_out_text (uiout, bp_temp ? _("Temporary catchpoint ") - : _("Catchpoint ")); - ui_out_field_int (uiout, "bkptno", b->number); - ui_out_text (uiout, (kind == EX_EVENT_THROW ? _(" (throw)") - : (kind == EX_EVENT_CATCH ? _(" (catch)") - : _(" (rethrow)")))); + 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 @@ -169,51 +348,94 @@ print_recreate_exception_catchpoint (struct breakpoint *b, print_recreate_thread (b, fp); } -static struct breakpoint_ops gnu_v3_exception_catchpoint_ops; +/* Implement the "allocate_location" breakpoint_ops method for throw + and catch catchpoints. */ -static int -handle_gnu_v3_exceptions (int tempflag, char *cond_string, +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) { - char *trigger_func_name; - - if (ex_event == EX_EVENT_CATCH) - trigger_func_name = "__cxa_begin_catch"; - else if (ex_event == EX_EVENT_RETHROW) - trigger_func_name = "__cxa_rethrow"; - else + std::unique_ptr pattern; + + if (!except_rx.empty ()) { - gdb_assert (ex_event == EX_EVENT_THROW); - trigger_func_name = "__cxa_throw"; + pattern.reset (new compiled_regex (except_rx.c_str (), REG_NOSUB, + _("invalid type-matching regexp"))); } - create_breakpoint (get_current_arch (), - trigger_func_name, cond_string, -1, NULL, - 0 /* condition and thread are valid. */, - tempflag, bp_breakpoint, - 0, - AUTO_BOOLEAN_TRUE /* pending */, - &gnu_v3_exception_catchpoint_ops, from_tty, - 1 /* enabled */, - 0 /* internal */, - 0); - - return 1; + std::unique_ptr 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); } -/* Deal with "catch catch", "catch throw", and "catch rethrow" - commands. */ +/* 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; -static void -catch_exception_command_1 (enum exception_event_kind ex_event, char *arg, - int tempflag, int from_tty) + 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) { - char *cond_string = NULL; + 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)) @@ -224,45 +446,74 @@ catch_exception_command_1 (enum exception_event_kind ex_event, char *arg, && ex_event != EX_EVENT_RETHROW) error (_("Unsupported or unknown exception event; cannot catch it")); - if (handle_gnu_v3_exceptions (tempflag, cond_string, ex_event, from_tty)) - return; - - warning (_("Unsupported with this platform/compiler combination.")); + 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 (char *arg, int from_tty, struct cmd_list_element *command) +catch_catch_command (const char *arg, int from_tty, + struct cmd_list_element *command) { - int tempflag = get_cmd_context (command) == CATCH_TEMPORARY; + bool tempflag = get_cmd_context (command) == CATCH_TEMPORARY; - catch_exception_command_1 (EX_EVENT_CATCH, arg, tempflag, from_tty); + catch_exception_event (EX_EVENT_CATCH, arg, tempflag, from_tty); } /* Implementation of "catch throw" command. */ static void -catch_throw_command (char *arg, int from_tty, struct cmd_list_element *command) +catch_throw_command (const char *arg, int from_tty, + struct cmd_list_element *command) { - int tempflag = get_cmd_context (command) == CATCH_TEMPORARY; + bool tempflag = get_cmd_context (command) == CATCH_TEMPORARY; - catch_exception_command_1 (EX_EVENT_THROW, arg, tempflag, from_tty); + catch_exception_event (EX_EVENT_THROW, arg, tempflag, from_tty); } /* Implementation of "catch rethrow" command. */ static void -catch_rethrow_command (char *arg, int from_tty, +catch_rethrow_command (const char *arg, int from_tty, struct cmd_list_element *command) { - int tempflag = get_cmd_context (command) == CATCH_TEMPORARY; + bool tempflag = get_cmd_context (command) == CATCH_TEMPORARY; - catch_exception_command_1 (EX_EVENT_RETHROW, arg, tempflag, from_tty); + 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, + NULL +}; + + + static void initialize_throw_catchpoint_ops (void) { @@ -273,16 +524,19 @@ initialize_throw_catchpoint_ops (void) /* 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; } -initialize_file_ftype _initialize_break_catch_throw; - +void _initialize_break_catch_throw (); void -_initialize_break_catch_throw (void) +_initialize_break_catch_throw () { initialize_throw_catchpoint_ops (); @@ -305,4 +559,6 @@ Catch an exception, when rethrown."), NULL, CATCH_PERMANENT, CATCH_TEMPORARY); + + create_internalvar_type_lazy ("_exception", &exception_funcs, NULL); }