PR c++/15176:
authorTom Tromey <tromey@redhat.com>
Mon, 15 Apr 2013 18:09:02 +0000 (18:09 +0000)
committerTom Tromey <tromey@redhat.com>
Mon, 15 Apr 2013 18:09:02 +0000 (18:09 +0000)
* 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) <get_type_from_type_info>: 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.

13 files changed:
gdb/ChangeLog
gdb/NEWS
gdb/break-catch-throw.c
gdb/cp-abi.c
gdb/cp-abi.h
gdb/doc/ChangeLog
gdb/doc/gdb.texinfo
gdb/gnu-v3-abi.c
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.base/default.exp
gdb/testsuite/gdb.cp/exceptprint.cc [new file with mode: 0644]
gdb/testsuite/gdb.cp/exceptprint.exp [new file with mode: 0644]
gdb/testsuite/lib/gdb.exp

index f0a39dea5176b28ec51a4a30d0a351008ff1c1ab..72540f3e98aee4bca0abb80e080b7535228991f1 100644 (file)
@@ -1,3 +1,17 @@
+2013-04-15  Tom Tromey  <tromey@redhat.com>
+
+       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) <get_type_from_type_info>: 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  <tromey@redhat.com>
 
        * break-catch-throw.c (struct exception_names): New.
index c78a4fb70f52c22bb729b9602faff5b2ecedef08..aa2d5717e7b82588cc89911a3eabd80065246cb0 100644 (file)
--- 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.
index 8b9b837f9fdc44c7db61efd09b938f1dc445a4d7..ed236ef20bfda3dcd4bb5b8cd12b5d3b520bb945 100644 (file)
@@ -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,
 
 \f
 
+/* 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
+};
+
+\f
+
 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);
 }
index 7ac2a204c6300ae895efcf4b931e28aba63913c6..a15e35902a0d60c11688657d8de970fa66b7395b 100644 (file)
@@ -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)
 {
index d68e2ec8be3106dac24d89b0977b0a4cb4d74afb..0b954debec739dfe41094f5e100a83b8963ce6e5 100644 (file)
@@ -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);
 };
index 08a61b6d2a2518fae395c55fd702bda7ee6b531f..e1904aa6c893ec492f0256304b083dc3d7ea7291 100644 (file)
@@ -1,3 +1,8 @@
+2013-04-15  Tom Tromey  <tromey@redhat.com>
+
+       * gdb.texinfo (Set Catchpoints): Document $_exception.
+       (Convenience Vars): Mention $_exception.
+
 2013-04-15  Tom Tromey  <tromey@redhat.com>
 
        * gdb.texinfo (Set Catchpoints): Reorganize exception
index 1634113d4b26c70aa0d9089411e8945a5de26964..cba3a2f116ff87895fb5ce0261e583213076b294 100644 (file)
@@ -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}.
index 7649a48ea72af5776910222e6ebfb1f26bd8f47b..fa12879b7866d17c21046fc2edf578eedbedcc20 100644 (file)
@@ -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;
 }
index 7da3ff0e23e22eff7bd068a4ddb95a8b157a32e8..855ce19e9f4406474c9dc0c273a6d8a35579da03 100644 (file)
@@ -1,3 +1,10 @@
+2013-04-15  Tom Tromey  <tromey@redhat.com>
+
+       * 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  <tromey@redhat.com>
 
        * gdb.cp/typeid.cc: New file.
index aaedadc1db910730fb51f381477272aea608ff28..6920090a16d9a497cd9bdfdad0c8adde791c0974 100644 (file)
@@ -604,6 +604,7 @@ set show_conv_list \
        {$_sdata = void} \
        {$_siginfo = void} \
        {$_thread = 0} \
+       {$_exception = <error: No frame selected>} \
        {$_probe_argc = <error: No frame selected>} \
        {$_probe_arg0 = <error: No frame selected>} \
        {$_probe_arg1 = <error: No frame selected>} \
diff --git a/gdb/testsuite/gdb.cp/exceptprint.cc b/gdb/testsuite/gdb.cp/exceptprint.cc
new file mode 100644 (file)
index 0000000..994b501
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.  */
+
+template<typename T>
+void
+throwit (T val)
+{
+  throw val;
+}
+
+template<typename T>
+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 (file)
index 0000000..6e03fd9
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+
+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}"
index d05257d0f3cbfc29d7104f8b7951d816dfec624e..3273bf4f8c2d0c288b6f5052961dbd074448d3b4 100644 (file)
@@ -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