From cc16e6c91580e68369250f5a65c6e01559cdd467 Mon Sep 17 00:00:00 2001 From: Tom Tromey Date: Mon, 15 Apr 2013 18:13:01 +0000 Subject: [PATCH] PR c++/13588: * NEWS: Update. * break-catch-throw.c (struct exception_catchpoint) : New fields. (fetch_probe_arguments, dtor_exception_catchpoint) (check_status_exception_catchpoint) (print_one_detail_exception_catchpoint): New functions. (handle_gnu_v3_exceptions): Add "except_rx" argument. Compile regular expression if needed. (extract_exception_regexp): New function. (catch_exception_command_1): Use extract_exception_regexp. (compute_exception): Use fetch_probe_arguments. (initialize_throw_catchpoint_ops): Set dtor, print_one_detail, and check_status fields. * cp-abi.c (cplus_typename_from_type_info): New function. * cp-abi.h (cplus_typename_from_type_info): Declare. (struct cp_abi_ops) : New field. * gdb_regex.h (compile_rx_or_error): Declare. * gnu-v3-abi.c (gnuv3_get_typename_from_type_info): Update comment. (init_gnuv3_ops): Set get_type_from_type_info field. * probe.c (compile_rx_or_error): Move... * utils.c (compile_rx_or_error): ... here. gdb/doc * gdb.texinfo (Set Catchpoints): Document regexp syntax for exception catchpoints. gdb/testsuite * gdb.cp/exceptprint.exp: Add regexp catchpoint tests. --- gdb/ChangeLog | 26 ++++ gdb/NEWS | 3 + gdb/break-catch-throw.c | 223 +++++++++++++++++++++++---- gdb/cp-abi.c | 11 ++ gdb/cp-abi.h | 8 + gdb/doc/ChangeLog | 5 + gdb/doc/gdb.texinfo | 15 +- gdb/gdb_regex.h | 2 + gdb/gnu-v3-abi.c | 4 +- gdb/probe.c | 24 --- gdb/testsuite/ChangeLog | 4 + gdb/testsuite/gdb.cp/exceptprint.exp | 21 +++ gdb/utils.c | 24 +++ 13 files changed, 310 insertions(+), 60 deletions(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 72540f3e98a..f05d79ef044 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,29 @@ +2013-04-15 Tom Tromey + + PR c++/13588: + * NEWS: Update. + * break-catch-throw.c (struct exception_catchpoint) + : New fields. + (fetch_probe_arguments, dtor_exception_catchpoint) + (check_status_exception_catchpoint) + (print_one_detail_exception_catchpoint): New functions. + (handle_gnu_v3_exceptions): Add "except_rx" argument. + Compile regular expression if needed. + (extract_exception_regexp): New function. + (catch_exception_command_1): Use extract_exception_regexp. + (compute_exception): Use fetch_probe_arguments. + (initialize_throw_catchpoint_ops): Set dtor, print_one_detail, + and check_status fields. + * cp-abi.c (cplus_typename_from_type_info): New function. + * cp-abi.h (cplus_typename_from_type_info): Declare. + (struct cp_abi_ops) : New field. + * gdb_regex.h (compile_rx_or_error): Declare. + * gnu-v3-abi.c (gnuv3_get_typename_from_type_info): Update + comment. + (init_gnuv3_ops): Set get_type_from_type_info field. + * probe.c (compile_rx_or_error): Move... + * utils.c (compile_rx_or_error): ... here. + 2013-04-15 Tom Tromey PR c++/15176: diff --git a/gdb/NEWS b/gdb/NEWS index aa2d5717e7b..f14159d8002 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -108,6 +108,9 @@ Tilera TILE-Gx GNU/Linux tilegx*-*-linux * The new convenience variable $_exception holds the exception being thrown or caught at an exception-related catchpoint. +* The exception-related catchpoints, like "catch throw", now accept a + regular expression which can be used to filter exceptions by type. + * Python scripting ** Vectors can be created with gdb.Type.vector. diff --git a/gdb/break-catch-throw.c b/gdb/break-catch-throw.c index ed236ef20bf..fb247258152 100644 --- a/gdb/break-catch-throw.c +++ b/gdb/break-catch-throw.c @@ -34,6 +34,8 @@ #include "probe.h" #include "objfiles.h" #include "cp-abi.h" +#include "gdb_regex.h" +#include "cp-support.h" /* Enums for exception-handling support. */ enum exception_event_kind @@ -81,8 +83,60 @@ struct exception_catchpoint /* The kind of exception catchpoint. */ enum exception_event_kind kind; + + /* If non-NULL, an xmalloc'd string holding the source form of the + regular expression to match against. */ + + char *exception_rx; + + /* If non-NULL, an xmalloc'd, compiled regular expression which is + used to determine which exceptions to stop on. */ + + regex_t *pattern; }; + + +/* 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 probe *pc_probe; + const struct sym_probe_fns *pc_probe_fns; + unsigned n_args; + + pc_probe = find_probe_by_pc (pc); + if (pc_probe == NULL + || strcmp (pc_probe->provider, "libstdcxx") != 0 + || (strcmp (pc_probe->name, "catch") != 0 + && strcmp (pc_probe->name, "throw") != 0 + && strcmp (pc_probe->name, "rethrow") != 0)) + error (_("not stopped at a C++ exception catchpoint")); + + gdb_assert (pc_probe->objfile != NULL); + gdb_assert (pc_probe->objfile->sf != NULL); + gdb_assert (pc_probe->objfile->sf->sym_probe_fns != NULL); + + pc_probe_fns = pc_probe->objfile->sf->sym_probe_fns; + n_args = pc_probe_fns->sym_get_probe_argument_count (pc_probe); + if (n_args < 2) + error (_("C++ exception catchpoint has too few arguments")); + + if (arg0 != NULL) + *arg0 = pc_probe_fns->sym_evaluate_probe_argument (pc_probe, 0); + *arg1 = pc_probe_fns->sym_evaluate_probe_argument (pc_probe, 1); + + 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. */ @@ -94,6 +148,60 @@ classify_exception_breakpoint (struct breakpoint *b) return cp->kind; } +/* Implement the 'dtor' method. */ + +static void +dtor_exception_catchpoint (struct breakpoint *self) +{ + struct exception_catchpoint *cp = (struct exception_catchpoint *) self; + + xfree (cp->exception_rx); + if (cp->pattern != NULL) + regfree (cp->pattern); + bkpt_breakpoint_ops.dtor (self); +} + +/* Implement the 'check_status' method. */ + +static void +check_status_exception_catchpoint (struct bpstats *bs) +{ + struct exception_catchpoint *self + = (struct exception_catchpoint *) bs->breakpoint_at; + char *typename = NULL; + volatile struct gdb_exception e; + + bkpt_breakpoint_ops.check_status (bs); + if (bs->stop == 0) + return; + + if (self->pattern == NULL) + return; + + TRY_CATCH (e, RETURN_MASK_ERROR) + { + struct value *typeinfo_arg; + char *canon; + + fetch_probe_arguments (NULL, &typeinfo_arg); + typename = cplus_typename_from_type_info (typeinfo_arg); + + canon = cp_canonicalize_string (typename); + if (canon != NULL) + { + xfree (typename); + typename = canon; + } + } + + if (e.reason < 0) + exception_print (gdb_stderr, e); + else if (regexec (self->pattern, typename, 0, NULL, 0) != 0) + bs->stop = 0; + + xfree (typename); +} + /* Implement the 're_set' method. */ static void @@ -208,6 +316,23 @@ print_one_exception_catchpoint (struct breakpoint *b, } } +/* 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 != NULL) + { + ui_out_text (uiout, _("\tmatching: ")); + ui_out_field_string (uiout, "regexp", cp->exception_rx); + ui_out_text (uiout, "\n"); + } +} + static void print_mention_exception_catchpoint (struct breakpoint *b) { @@ -252,22 +377,77 @@ print_recreate_exception_catchpoint (struct breakpoint *b, } static void -handle_gnu_v3_exceptions (int tempflag, char *cond_string, +handle_gnu_v3_exceptions (int tempflag, char *except_rx, char *cond_string, enum exception_event_kind ex_event, int from_tty) { struct exception_catchpoint *cp; + struct cleanup *cleanup = make_cleanup (null_cleanup, NULL); + regex_t *pattern = NULL; + + if (except_rx != NULL) + { + pattern = XNEW (regex_t); + make_cleanup (xfree, pattern); + + compile_rx_or_error (pattern, except_rx, + _("invalid type-matching regexp")); + } cp = XCNEW (struct exception_catchpoint); + make_cleanup (xfree, cp); + init_catchpoint (&cp->base, get_current_arch (), tempflag, cond_string, &gnu_v3_exception_catchpoint_ops); /* We need to reset 'type' in order for code in breakpoint.c to do the right thing. */ cp->base.type = bp_breakpoint; cp->kind = ex_event; + cp->exception_rx = except_rx; + cp->pattern = pattern; re_set_exception_catchpoint (&cp->base); install_breakpoint (0, &cp->base, 1); + discard_cleanups (cleanup); +} + +/* 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 char * +extract_exception_regexp (char **string) +{ + char *start; + char *last, *last_space; + + start = skip_spaces (*string); + + last = start; + last_space = start; + while (*last != '\0') + { + 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 savestring (start, last_space - start); + return NULL; } /* Deal with "catch catch", "catch throw", and "catch rethrow" @@ -277,12 +457,17 @@ static void catch_exception_command_1 (enum exception_event_kind ex_event, char *arg, int tempflag, int from_tty) { + char *except_rx; char *cond_string = NULL; + struct cleanup *cleanup; if (!arg) arg = ""; arg = skip_spaces (arg); + except_rx = extract_exception_regexp (&arg); + cleanup = make_cleanup (xfree, except_rx); + cond_string = ep_parse_optional_if_clause (&arg); if ((*arg != '\0') && !isspace (*arg)) @@ -293,7 +478,10 @@ 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")); - handle_gnu_v3_exceptions (tempflag, cond_string, ex_event, from_tty); + handle_gnu_v3_exceptions (tempflag, except_rx, cond_string, + ex_event, from_tty); + + discard_cleanups (cleanup); } /* Implementation of "catch catch" command. */ @@ -335,36 +523,10 @@ catch_rethrow_command (char *arg, int from_tty, static struct value * compute_exception (struct gdbarch *argc, struct internalvar *var, void *ignore) { - struct frame_info *frame = get_selected_frame (_("No frame selected")); - CORE_ADDR pc = get_frame_pc (frame); - struct probe *pc_probe; - const struct sym_probe_fns *pc_probe_fns; - unsigned n_args; struct value *arg0, *arg1; struct type *obj_type; - pc_probe = find_probe_by_pc (pc); - if (pc_probe == NULL - || strcmp (pc_probe->provider, "libstdcxx") != 0 - || (strcmp (pc_probe->name, "catch") != 0 - && strcmp (pc_probe->name, "throw") != 0 - && strcmp (pc_probe->name, "rethrow") != 0)) - error (_("not stopped at a C++ exception catchpoint")); - - gdb_assert (pc_probe->objfile != NULL); - gdb_assert (pc_probe->objfile->sf != NULL); - gdb_assert (pc_probe->objfile->sf->sym_probe_fns != NULL); - - pc_probe_fns = pc_probe->objfile->sf->sym_probe_fns; - n_args = pc_probe_fns->sym_get_probe_argument_count (pc_probe); - if (n_args < 2) - error (_("C++ exception catchpoint has too few arguments")); - - arg0 = pc_probe_fns->sym_evaluate_probe_argument (pc_probe, 0); - arg1 = pc_probe_fns->sym_evaluate_probe_argument (pc_probe, 1); - - if (arg0 == NULL || arg1 == NULL) - error (_("error computing probe argument at c++ exception catchpoint")); + 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 @@ -394,11 +556,14 @@ initialize_throw_catchpoint_ops (void) /* GNU v3 exception catchpoints. */ ops = &gnu_v3_exception_catchpoint_ops; *ops = bkpt_breakpoint_ops; + ops->dtor = dtor_exception_catchpoint; 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; } initialize_file_ftype _initialize_break_catch_throw; diff --git a/gdb/cp-abi.c b/gdb/cp-abi.c index a15e35902a0..2540eca17aa 100644 --- a/gdb/cp-abi.c +++ b/gdb/cp-abi.c @@ -209,6 +209,17 @@ cplus_type_from_type_info (struct value *value) return (*current_cp_abi.get_type_from_type_info) (value); } +/* See cp-abi.h. */ + +char * +cplus_typename_from_type_info (struct value *value) +{ + if (current_cp_abi.get_typename_from_type_info == NULL) + error (_("GDB cannot find the type name " + "from a std::type_info on this target")); + return (*current_cp_abi.get_typename_from_type_info) (value); +} + int cp_pass_by_reference (struct type *type) { diff --git a/gdb/cp-abi.h b/gdb/cp-abi.h index 0b954debec7..f6d47bad855 100644 --- a/gdb/cp-abi.h +++ b/gdb/cp-abi.h @@ -194,6 +194,13 @@ extern struct type *cplus_typeid_type (struct gdbarch *gdbarch); extern struct type *cplus_type_from_type_info (struct value *value); +/* Given a value which holds a pointer to a std::type_info, return the + name of the type which that type_info represents. Throw an + exception if the type name cannot be found. The result is + xmalloc'd and must be freed by the caller. */ + +extern char *cplus_typename_from_type_info (struct value *value); + /* Determine if we are currently in a C++ thunk. If so, get the address of the routine we are thunking to and continue to there instead. */ @@ -238,6 +245,7 @@ struct cp_abi_ops struct value *(*get_typeid) (struct value *value); struct type *(*get_typeid_type) (struct gdbarch *gdbarch); struct type *(*get_type_from_type_info) (struct value *value); + char *(*get_typename_from_type_info) (struct value *value); CORE_ADDR (*skip_trampoline) (struct frame_info *, CORE_ADDR); int (*pass_by_reference) (struct type *type); }; diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog index e1904aa6c89..e7b77c4f325 100644 --- a/gdb/doc/ChangeLog +++ b/gdb/doc/ChangeLog @@ -1,3 +1,8 @@ +2013-04-15 Tom Tromey + + * gdb.texinfo (Set Catchpoints): Document regexp syntax for + exception catchpoints. + 2013-04-15 Tom Tromey * gdb.texinfo (Set Catchpoints): Document $_exception. diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index cba3a2f116f..337837e5704 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -4074,12 +4074,15 @@ shared library. Use the @code{catch} command to set a catchpoint. Stop when @var{event} occurs. @var{event} can be any of the following: @table @code -@item throw -@itemx rethrow -@itemx catch +@item throw @r{[}@var{regexp}@r{]} +@itemx rethrow @r{[}@var{regexp}@r{]} +@itemx catch @r{[}@var{regexp}@r{]} @cindex stop on C@t{++} exceptions The throwing, re-throwing, or catching of a C@t{++} exception. +If @var{regexp} is given, then only exceptions whose type matches the +regular expression will be caught. + @vindex $_exception@r{, convenience variable} The convenience variable @code{$_exception} is available at an exception-related catchpoint, on some systems. This holds the @@ -4095,9 +4098,9 @@ systems using the @samp{gnu-v3} C@t{++} ABI (@pxref{ABI}) are supported. @item -The @code{$_exception} convenience variable relies on the presence of -some SDT probes in @code{libstdc++}. If these probes are not present, -then this variable cannot be used. +The regular expression feature and the @code{$_exception} convenience +variable rely on the presence of some SDT probes in @code{libstdc++}. +If these probes are not present, then these features cannot be used. @item The @code{$_exception} convenience variable is only valid at the diff --git a/gdb/gdb_regex.h b/gdb/gdb_regex.h index 14475fc8919..cf91f1946c9 100644 --- a/gdb/gdb_regex.h +++ b/gdb/gdb_regex.h @@ -30,5 +30,7 @@ /* From utils.c. */ struct cleanup *make_regfree_cleanup (regex_t *); char *get_regcomp_error (int, regex_t *); +struct cleanup *compile_rx_or_error (regex_t *pattern, const char *rx, + const char *message); #endif /* not GDB_REGEX_H */ diff --git a/gdb/gnu-v3-abi.c b/gdb/gnu-v3-abi.c index fa12879b786..6812c1dcd7b 100644 --- a/gdb/gnu-v3-abi.c +++ b/gdb/gnu-v3-abi.c @@ -1138,7 +1138,7 @@ gnuv3_get_typeid (struct value *value) return result; } -/* Get the type name given a type_info object. */ +/* Implement the 'get_typename_from_type_info' method. */ static char * gnuv3_get_typename_from_type_info (struct value *type_info_ptr) @@ -1356,6 +1356,8 @@ init_gnuv3_ops (void) gnu_v3_abi_ops.get_typeid = gnuv3_get_typeid; gnu_v3_abi_ops.get_typeid_type = gnuv3_get_typeid_type; gnu_v3_abi_ops.get_type_from_type_info = gnuv3_get_type_from_type_info; + gnu_v3_abi_ops.get_typename_from_type_info + = gnuv3_get_typename_from_type_info; gnu_v3_abi_ops.skip_trampoline = gnuv3_skip_trampoline; gnu_v3_abi_ops.pass_by_reference = gnuv3_pass_by_reference; } diff --git a/gdb/probe.c b/gdb/probe.c index dfb6e7eec1f..05bdd1baef7 100644 --- a/gdb/probe.c +++ b/gdb/probe.c @@ -229,30 +229,6 @@ find_probe_by_pc (CORE_ADDR pc) -/* A helper function for collect_probes that compiles a regexp and - throws an exception on error. This installs a cleanup to free the - resulting pattern on success. If RX is NULL, this does nothing. */ - -static void -compile_rx_or_error (regex_t *pattern, const char *rx, const char *message) -{ - int code; - - if (!rx) - return; - - code = regcomp (pattern, rx, REG_NOSUB); - if (code == 0) - make_regfree_cleanup (pattern); - else - { - char *err = get_regcomp_error (code, pattern); - - make_cleanup (xfree, err); - error (("%s: %s"), message, err); - } -} - /* Make a vector of probes matching OBJNAME, PROVIDER, and PROBE_NAME. If POPS is not NULL, only probes of this certain probe_ops will match. Each argument is a regexp, or NULL, which matches anything. */ diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 855ce19e9f4..a35d9ed789f 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,7 @@ +2013-04-15 Tom Tromey + + * gdb.cp/exceptprint.exp: Add regexp catchpoint tests. + 2013-04-15 Tom Tromey * gdb.base/default.exp: Update for $_exception. diff --git a/gdb/testsuite/gdb.cp/exceptprint.exp b/gdb/testsuite/gdb.cp/exceptprint.exp index 6e03fd9ab55..d9f4fd51d02 100644 --- a/gdb/testsuite/gdb.cp/exceptprint.exp +++ b/gdb/testsuite/gdb.cp/exceptprint.exp @@ -71,3 +71,24 @@ do_exceptprint_tests string "$hex \"hi bob\"" do_exceptprint_tests int 23 do_exceptprint_tests struct "{mv = 77}" do_exceptprint_tests "reference to struct" "{mv = 77}" + + +delete_breakpoints + +if {![runto_main]} { + return -1 +} + +gdb_test "catch catch int if \$_exception == 23" \ + "Catchpoint \[0-9\]+ \\(catch\\)" \ + "catch catch" +gdb_test "catch throw int if \$_exception == 23" \ + "Catchpoint \[0-9\]+ \\(throw\\)" \ + "catch throw" +gdb_test "catch rethrow int if \$_exception == 23" \ + "Catchpoint \[0-9\]+ \\(rethrow\\)" \ + "catch rethrow" + +# This tests both the case where the regular expression does not +# match, and the case where it does. +do_exceptprint_tests int 23 diff --git a/gdb/utils.c b/gdb/utils.c index 37deb02c8e9..218faed0bab 100644 --- a/gdb/utils.c +++ b/gdb/utils.c @@ -1124,6 +1124,30 @@ get_regcomp_error (int code, regex_t *rx) return result; } +/* Compile a regexp and throw an exception on error. This returns a + cleanup to free the resulting pattern on success. If RX is NULL, + this does nothing and returns NULL. */ + +struct cleanup * +compile_rx_or_error (regex_t *pattern, const char *rx, const char *message) +{ + int code; + + if (!rx) + return NULL; + + code = regcomp (pattern, rx, REG_NOSUB); + if (code != 0) + { + char *err = get_regcomp_error (code, pattern); + + make_cleanup (xfree, err); + error (("%s: %s"), message, err); + } + + return make_regfree_cleanup (pattern); +} + /* This function supports the query, nquery, and yquery functions. -- 2.30.2