From 72f1fe8a88c4dc9219c4263eff2bbcf61173e607 Mon Sep 17 00:00:00 2001 From: Tom Tromey Date: Mon, 15 Apr 2013 18:09:02 +0000 Subject: [PATCH] PR c++/15176: * NEWS: Update. * break-catch-throw.c (compute_exception): New function. (exception_funcs): New global. (_initialize_break_catch_throw): Create $_exception. * cp-abi.c (cplus_type_from_type_info): New function. * cp-abi.h (cplus_type_from_type_info): Declare. (struct cp_abi_ops) : New field. * gnu-v3-abi.c (gnuv3_get_typename_from_type_info) (gnuv3_get_type_from_type_info): New functions. (init_gnuv3_ops): Set get_type_from_type_info ABI field. gdb/doc * gdb.texinfo (Set Catchpoints): Document $_exception. (Convenience Vars): Mention $_exception. gdb/testsuite * gdb.base/default.exp: Update for $_exception. * gdb.cp/exceptprint.cc: New file. * gdb.cp/exceptprint.exp: New file. * lib/gdb.exp (skip_libstdcxx_probe_tests): New proc. --- gdb/ChangeLog | 14 ++++++ gdb/NEWS | 3 ++ gdb/break-catch-throw.c | 59 ++++++++++++++++++++++ gdb/cp-abi.c | 10 ++++ gdb/cp-abi.h | 7 +++ gdb/doc/ChangeLog | 5 ++ gdb/doc/gdb.texinfo | 18 +++++++ gdb/gnu-v3-abi.c | 64 ++++++++++++++++++++++++ gdb/testsuite/ChangeLog | 7 +++ gdb/testsuite/gdb.base/default.exp | 1 + gdb/testsuite/gdb.cp/exceptprint.cc | 65 +++++++++++++++++++++++++ gdb/testsuite/gdb.cp/exceptprint.exp | 73 ++++++++++++++++++++++++++++ gdb/testsuite/lib/gdb.exp | 18 +++++++ 13 files changed, 344 insertions(+) create mode 100644 gdb/testsuite/gdb.cp/exceptprint.cc create mode 100644 gdb/testsuite/gdb.cp/exceptprint.exp diff --git a/gdb/ChangeLog b/gdb/ChangeLog index f0a39dea51..72540f3e98 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,17 @@ +2013-04-15 Tom Tromey + + PR c++/15176: + * NEWS: Update. + * break-catch-throw.c (compute_exception): New function. + (exception_funcs): New global. + (_initialize_break_catch_throw): Create $_exception. + * cp-abi.c (cplus_type_from_type_info): New function. + * cp-abi.h (cplus_type_from_type_info): Declare. + (struct cp_abi_ops) : New field. + * gnu-v3-abi.c (gnuv3_get_typename_from_type_info) + (gnuv3_get_type_from_type_info): New functions. + (init_gnuv3_ops): Set get_type_from_type_info ABI field. + 2013-04-15 Tom Tromey * break-catch-throw.c (struct exception_names): New. diff --git a/gdb/NEWS b/gdb/NEWS index c78a4fb70f..aa2d5717e7 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -105,6 +105,9 @@ Tilera TILE-Gx GNU/Linux tilegx*-*-linux * 'info proc' now works on some core files. +* The new convenience variable $_exception holds the exception being + thrown or caught at an exception-related catchpoint. + * 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 8b9b837f9f..ed236ef20b 100644 --- a/gdb/break-catch-throw.c +++ b/gdb/break-catch-throw.c @@ -32,6 +32,8 @@ #include "exceptions.h" #include "linespec.h" #include "probe.h" +#include "objfiles.h" +#include "cp-abi.h" /* Enums for exception-handling support. */ enum exception_event_kind @@ -327,6 +329,61 @@ catch_rethrow_command (char *arg, int 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 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")); + + /* 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) { @@ -370,4 +427,6 @@ Catch an exception, when rethrown."), NULL, CATCH_PERMANENT, CATCH_TEMPORARY); + + create_internalvar_type_lazy ("_exception", &exception_funcs, NULL); } diff --git a/gdb/cp-abi.c b/gdb/cp-abi.c index 7ac2a204c6..a15e35902a 100644 --- a/gdb/cp-abi.c +++ b/gdb/cp-abi.c @@ -199,6 +199,16 @@ cplus_typeid_type (struct gdbarch *gdbarch) return (*current_cp_abi.get_typeid_type) (gdbarch); } +/* See cp-abi.h. */ + +struct type * +cplus_type_from_type_info (struct value *value) +{ + if (current_cp_abi.get_type_from_type_info == NULL) + error (_("GDB cannot find the type from a std::type_info on this target")); + return (*current_cp_abi.get_type_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 d68e2ec8be..0b954debec 100644 --- a/gdb/cp-abi.h +++ b/gdb/cp-abi.h @@ -188,6 +188,12 @@ extern struct value *cplus_typeid (struct value *value); extern struct type *cplus_typeid_type (struct gdbarch *gdbarch); +/* Given a value which holds a pointer to a std::type_info, return the + type which that type_info represents. Throw an exception if the + type cannot be found. */ + +extern struct type *cplus_type_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. */ @@ -231,6 +237,7 @@ struct cp_abi_ops void (*print_vtable) (struct value *); 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); 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 08a61b6d2a..e1904aa6c8 100644 --- a/gdb/doc/ChangeLog +++ b/gdb/doc/ChangeLog @@ -1,3 +1,8 @@ +2013-04-15 Tom Tromey + + * gdb.texinfo (Set Catchpoints): Document $_exception. + (Convenience Vars): Mention $_exception. + 2013-04-15 Tom Tromey * gdb.texinfo (Set Catchpoints): Reorganize exception diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 1634113d4b..cba3a2f116 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -4080,6 +4080,11 @@ Stop when @var{event} occurs. @var{event} can be any of the following: @cindex stop on C@t{++} exceptions The throwing, re-throwing, or catching of a C@t{++} exception. +@vindex $_exception@r{, convenience variable} +The convenience variable @code{$_exception} is available at an +exception-related catchpoint, on some systems. This holds the +exception being thrown. + There are currently some limitations to C@t{++} exception handling in @value{GDBN}: @@ -4089,6 +4094,15 @@ The support for these commands is system-dependent. Currently, only 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. + +@item +The @code{$_exception} convenience variable is only valid at the +instruction at which an exception-related catchpoint is set. + @item When an exception-related catchpoint is hit, @value{GDBN} stops at a location in the system library which implements runtime exception @@ -9510,6 +9524,10 @@ to match the format in which the data was printed. The variable @code{$_exitcode} is automatically set to the exit code when the program being debugged terminates. +@item $_exception +The variable @code{$_exception} is set to the exception object being +thrown at an exception-related catchpoint. @xref{Set Catchpoints}. + @item $_probe_argc @itemx $_probe_arg0@dots{}$_probe_arg11 Arguments to a static probe. @xref{Static Probe Points}. diff --git a/gdb/gnu-v3-abi.c b/gdb/gnu-v3-abi.c index 7649a48ea7..fa12879b78 100644 --- a/gdb/gnu-v3-abi.c +++ b/gdb/gnu-v3-abi.c @@ -1138,6 +1138,69 @@ gnuv3_get_typeid (struct value *value) return result; } +/* Get the type name given a type_info object. */ + +static char * +gnuv3_get_typename_from_type_info (struct value *type_info_ptr) +{ + struct gdbarch *gdbarch = get_type_arch (value_type (type_info_ptr)); + struct bound_minimal_symbol typeinfo_sym; + CORE_ADDR addr; + const char *symname; + const char *class_name; + const char *atsign; + + addr = value_as_address (type_info_ptr); + typeinfo_sym = lookup_minimal_symbol_by_pc (addr); + if (typeinfo_sym.minsym == NULL) + error (_("could not find minimal symbol for typeinfo address %s"), + paddress (gdbarch, addr)); + +#define TYPEINFO_PREFIX "typeinfo for " +#define TYPEINFO_PREFIX_LEN (sizeof (TYPEINFO_PREFIX) - 1) + symname = SYMBOL_DEMANGLED_NAME (typeinfo_sym.minsym); + if (symname == NULL || strncmp (symname, TYPEINFO_PREFIX, + TYPEINFO_PREFIX_LEN)) + error (_("typeinfo symbol '%s' has unexpected name"), + SYMBOL_LINKAGE_NAME (typeinfo_sym.minsym)); + class_name = symname + TYPEINFO_PREFIX_LEN; + + /* Strip off @plt and version suffixes. */ + atsign = strchr (class_name, '@'); + if (atsign != NULL) + return savestring (class_name, atsign - class_name); + return xstrdup (class_name); +} + +/* Implement the 'get_type_from_type_info' method. */ + +static struct type * +gnuv3_get_type_from_type_info (struct value *type_info_ptr) +{ + char *typename; + struct cleanup *cleanup; + struct value *type_val; + struct expression *expr; + struct type *result; + + typename = gnuv3_get_typename_from_type_info (type_info_ptr); + cleanup = make_cleanup (xfree, typename); + + /* We have to parse the type name, since in general there is not a + symbol for a type. This is somewhat bogus since there may be a + mis-parse. Another approach might be to re-use the demangler's + internal form to reconstruct the type somehow. */ + + expr = parse_expression (typename); + make_cleanup (xfree, expr); + + type_val = evaluate_type (expr); + result = value_type (type_val); + + do_cleanups (cleanup); + return result; +} + /* 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. */ @@ -1292,6 +1355,7 @@ init_gnuv3_ops (void) gnu_v3_abi_ops.print_vtable = gnuv3_print_vtable; 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.skip_trampoline = gnuv3_skip_trampoline; gnu_v3_abi_ops.pass_by_reference = gnuv3_pass_by_reference; } diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 7da3ff0e23..855ce19e9f 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,10 @@ +2013-04-15 Tom Tromey + + * gdb.base/default.exp: Update for $_exception. + * gdb.cp/exceptprint.cc: New file. + * gdb.cp/exceptprint.exp: New file. + * lib/gdb.exp (skip_libstdcxx_probe_tests): New proc. + 2013-04-15 Tom Tromey * gdb.cp/typeid.cc: New file. diff --git a/gdb/testsuite/gdb.base/default.exp b/gdb/testsuite/gdb.base/default.exp index aaedadc1db..6920090a16 100644 --- a/gdb/testsuite/gdb.base/default.exp +++ b/gdb/testsuite/gdb.base/default.exp @@ -604,6 +604,7 @@ set show_conv_list \ {$_sdata = void} \ {$_siginfo = void} \ {$_thread = 0} \ + {$_exception = } \ {$_probe_argc = } \ {$_probe_arg0 = } \ {$_probe_arg1 = } \ diff --git a/gdb/testsuite/gdb.cp/exceptprint.cc b/gdb/testsuite/gdb.cp/exceptprint.cc new file mode 100644 index 0000000000..994b5010d6 --- /dev/null +++ b/gdb/testsuite/gdb.cp/exceptprint.cc @@ -0,0 +1,65 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2013 Free Software Foundation, Inc. + + 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 . */ + +template +void +throwit (T val) +{ + throw val; +} + +template +void +rethrowit (T val) +{ + try + { + try + { + throwit (val); + } + catch (...) + { + throw; + } + } + catch (...) + { + // Ignore. + } +} + +struct maude +{ + int mv; + + maude (int x) : mv (x) { } +}; + +int +main (int argc, char **argv) +{ + maude mm (77); + maude &mmm (mm); + + rethrowit ("hi bob"); + rethrowit (23); + rethrowit (mm); + rethrowit (mmm); + + return 0; +} diff --git a/gdb/testsuite/gdb.cp/exceptprint.exp b/gdb/testsuite/gdb.cp/exceptprint.exp new file mode 100644 index 0000000000..6e03fd9ab5 --- /dev/null +++ b/gdb/testsuite/gdb.cp/exceptprint.exp @@ -0,0 +1,73 @@ +# Copyright 2013 Free Software Foundation, Inc. + +# 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 . + +standard_testfile .cc + +if {[skip_cplus_tests]} { + return -1 +} + +if {[prepare_for_testing $testfile.exp $testfile $srcfile {debug c++}]} { + return -1 +} + +if {![runto_main]} { + return -1 +} + +if {![skip_libstdcxx_probe_tests]} { + untested "could not find libstdc++ stap probe" + return -1 +} + +proc do_continue_to_catchpoint {name} { + global gdb_prompt + + gdb_test_multiple "continue" $name { + -re "Continuing.*Catchpoint \[0-9\].*\r\n$gdb_prompt $" { + pass $name + } + } +} + +proc do_exceptprint_tests {prefix output} { + with_test_prefix $prefix { + do_continue_to_catchpoint "continue to throw" + gdb_test "print \$_exception" " = $output" \ + "print exception value at throw" + + do_continue_to_catchpoint "continue to catch" + gdb_test "print \$_exception" " = $output" \ + "print exception value at catch" + + do_continue_to_catchpoint "continue to rethrow" + gdb_test "print \$_exception" " = $output" \ + "print exception value at rethrow" + + do_continue_to_catchpoint "continue to final catch" + } +} + +gdb_test "catch catch" "Catchpoint \[0-9\]+ \\(catch\\)" \ + "catch catch" +gdb_test "catch throw" "Catchpoint \[0-9\]+ \\(throw\\)" \ + "catch throw" +gdb_test "catch rethrow" "Catchpoint \[0-9\]+ \\(rethrow\\)" \ + "catch rethrow" + +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}" diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp index d05257d0f3..3273bf4f8c 100644 --- a/gdb/testsuite/lib/gdb.exp +++ b/gdb/testsuite/lib/gdb.exp @@ -2310,6 +2310,24 @@ proc skip_unwinder_tests {} { return $ok } +# Return 0 if we should skip tests that require the libstdc++ stap +# probes. This must be invoked while gdb is running, after shared +# libraries have been loaded. + +proc skip_libstdcxx_probe_tests {} { + global gdb_prompt + + set ok 0 + gdb_test_multiple "info probe" "check for stap probe in libstdc++" { + -re ".*libstdcxx.*catch.*\r\n$gdb_prompt $" { + set ok 1 + } + -re "\r\n$gdb_prompt $" { + } + } + return $ok +} + set compiler_info "unknown" set gcc_compiled 0 set hp_cc_compiler 0 -- 2.34.1