/* Everything about catch/throw catchpoints, for GDB.
- Copyright (C) 1986-2013 Free Software Foundation, Inc.
+ Copyright (C) 1986-2019 Free Software Foundation, Inc.
This file is part of GDB.
#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<compiled_regex> pattern;
};
+/* See breakpoint.h. */
+
+bool
+is_exception_catchpoint (breakpoint *bp)
+{
+ return bp->ops == &gnu_v3_exception_catchpoint_ops;
+}
+
+\f
+
+/* 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"));
+}
+
+\f
+
/* 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;
+
+ try
+ {
+ struct value *typeinfo_arg;
+ std::string canon;
+
+ fetch_probe_arguments (NULL, &typeinfo_arg);
+ type_name = cplus_typename_from_type_info (typeinfo_arg);
+
+ canon = cp_canonicalize_string (type_name.c_str ());
+ if (!canon.empty ())
+ std::swap (type_name, canon);
+ }
+ catch (const gdb_exception_error &e)
+ {
+ exception_print (gdb_stderr, e);
+ }
+
+ if (!type_name.empty ())
+ {
+ if (self->pattern->exec (type_name.c_str (), 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
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;
}
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", "<PENDING>");
- 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)
{
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
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<compiled_regex> 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<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);
}
-/* 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))
&& 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);
}
\f
+/* 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
+};
+
+\f
+
static void
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)
{
NULL,
CATCH_PERMANENT,
CATCH_TEMPORARY);
+
+ create_internalvar_type_lazy ("_exception", &exception_funcs, NULL);
}