This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[4/5] implement "info vtable"


This one needs a doc review, and of course commentary on the naming and
output.

This adds a new command, "info vtable".  This command can be used to
inspect the virtual tables of an object.

E.g., from the test suite's virtfunc.cc:

    (gdb) info vtable e
    vtable for 'E' @ 0x401478 (subobject @ 0x602580):
    [0]: 0x400d18 <E::f()>
    [1]: 0x400d42 <E::vg()>
    [2]: 0x400d8e <E::vv()>

    vtable for 'D' @ 0x4014a8 (subobject @ 0x602590):
    [0]: 0x400d3c <non-virtual thunk to E::vg()>
    [1]: 0x400dc8 <D::vd()>

    vtable for 'V' @ 0x4014f0 (subobject @ 0x6025b0):
    [0]: 0x400df4 <VB::vvb()>
    [1]: 0x400d84 <virtual thunk to E::vv()>

The implementation is somewhat complicated due to dealing with multiple
inheritance; plus the fact that the only way I could find to determine
the size of a vtable is to find the maximum of the indices of all
virtual functions.

Tom

        * gnu-v3-abi.c (struct value_and_voffset): New.
        (hash_value_and_voffset, eq_value_and_voffset)
        (compare_value_and_voffset, compute_vtable_size)
        (print_one_vtable, gnuv3_print_vtable): New functions.
        (init_gnuv3_ops): Initialize 'print_vtable' field.
        * cp-support.c (info_vtable_command): New function.
        (_initialize_cp_support): Add "info vtable".
        * cp-abi.h (cplus_print_vtable): Declare.
        (struct cp_abi_ops) <print_vtable>: New field.
        * cp-abi.c (cplus_print_vtable): New function.
        * NEWS: Update.
    gdb/testsuite
        * gdb.cp/virtfunc.exp (make_one_vtable_result): New proc.
        (test_info_vtable): Likewise.
        (do_tests): Call test_info_vtable.
    gdb/doc
        * gdb.texinfo (Debugging C Plus Plus): Document "info vtable".


>From 36f61ee5e765c8de26ac897e6e6e2c332ba5eb0f Mon Sep 17 00:00:00 2001
From: Tom Tromey <tromey@redhat.com>
Date: Mon, 27 Feb 2012 13:17:22 -0700
Subject: [PATCH 4/5] 	* gnu-v3-abi.c (struct value_and_voffset): New. 
 (hash_value_and_voffset, eq_value_and_voffset) 
 (compare_value_and_voffset, compute_vtable_size) 
 (print_one_vtable, gnuv3_print_vtable): New functions. 
 (init_gnuv3_ops): Initialize 'print_vtable' field. 
 * cp-support.c (info_vtable_command): New function. 
 (_initialize_cp_support): Add "info vtable". 	*
 cp-abi.h (cplus_print_vtable): Declare. 	(struct
 cp_abi_ops) <print_vtable>: New field. 	*
 cp-abi.c (cplus_print_vtable): New function. 	* NEWS:
 Update. gdb/testsuite 	* gdb.cp/virtfunc.exp
 (make_one_vtable_result): New proc. 
 (test_info_vtable): Likewise. 	(do_tests): Call
 test_info_vtable. gdb/doc 	* gdb.texinfo (Debugging
 C Plus Plus): Document "info vtable".

---
 gdb/NEWS                          |    3 +
 gdb/cp-abi.c                      |   10 ++
 gdb/cp-abi.h                      |    6 +
 gdb/cp-support.c                  |   17 +++
 gdb/doc/gdb.texinfo               |    6 +
 gdb/gnu-v3-abi.c                  |  241 +++++++++++++++++++++++++++++++++++++
 gdb/testsuite/gdb.cp/virtfunc.exp |   42 +++++++
 7 files changed, 325 insertions(+), 0 deletions(-)

diff --git a/gdb/NEWS b/gdb/NEWS
index 090a065..e9efc96 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -52,6 +52,9 @@
   ** "enable count" can be used to auto-disable a breakpoint after
      several hits.
 
+  ** "info vtable" can be used to show the virtual method tables for
+     C++ and Java objects.
+
 * New targets
 
 Renesas RL78			rl78-*-elf
diff --git a/gdb/cp-abi.c b/gdb/cp-abi.c
index e949088..16b5356 100644
--- a/gdb/cp-abi.c
+++ b/gdb/cp-abi.c
@@ -169,6 +169,16 @@ cplus_method_ptr_to_value (struct value **this_p,
   return (*current_cp_abi.method_ptr_to_value) (this_p, method_ptr);
 }
 
+/* See cp-abi.h.  */
+
+void
+cplus_print_vtable (struct value *value)
+{
+  if (current_cp_abi.print_vtable == NULL)
+    error (_("GDB cannot print the vtable on this target"));
+  return (*current_cp_abi.print_vtable) (value);
+}
+
 int
 cp_pass_by_reference (struct type *type)
 {
diff --git a/gdb/cp-abi.h b/gdb/cp-abi.h
index 0f24e9a..8451450 100644
--- a/gdb/cp-abi.h
+++ b/gdb/cp-abi.h
@@ -173,6 +173,11 @@ struct value *cplus_method_ptr_to_value (struct value **this_p,
 void cplus_make_method_ptr (struct type *type, gdb_byte *CONTENTS,
 			    CORE_ADDR address, int is_virtual);
 
+/* Print the vtable for VALUE, if there is one.  If there is no
+   vtable, print a message, but do not throw.  */
+
+void cplus_print_vtable (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.  */
@@ -213,6 +218,7 @@ struct cp_abi_ops
 			   CORE_ADDR, int);
   struct value * (*method_ptr_to_value) (struct value **,
 					 struct value *);
+  void (*print_vtable) (struct value *);
   CORE_ADDR (*skip_trampoline) (struct frame_info *, CORE_ADDR);
   int (*pass_by_reference) (struct type *type);
 };
diff --git a/gdb/cp-support.c b/gdb/cp-support.c
index 0620aa7..66b3244 100644
--- a/gdb/cp-support.c
+++ b/gdb/cp-support.c
@@ -34,6 +34,7 @@
 #include "exceptions.h"
 #include "expression.h"
 #include "value.h"
+#include "cp-abi.h"
 
 #include "safe-ctype.h"
 
@@ -1563,6 +1564,17 @@ cp_validate_operator (const char *input)
   return 0;
 }
 
+/* Implement "info vtable".  */
+
+static void
+info_vtable_command (char *arg, int from_tty)
+{
+  struct value *value;
+
+  value = parse_and_eval (arg);
+  cplus_print_vtable (value);
+}
+
 void
 _initialize_cp_support (void)
 {
@@ -1581,4 +1593,9 @@ _initialize_cp_support (void)
 	   first_component_command,
 	   _("Print the first class/namespace component of NAME."),
 	   &maint_cplus_cmd_list);
+
+  add_info ("vtable", info_vtable_command,
+	    _("Show the vtable for a C++ object.\n\
+Usage: info vtable EXPRESSION\n\
+Evaluate EXPRESSION and display the vtable for the resulting object."));
 }
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 88dcec1..9eed27e 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -12786,6 +12786,12 @@ Print inheritance relationships as well as other information for type
 @var{typename}.
 @xref{Symbols, ,Examining the Symbol Table}.
 
+@item info vtable @var{expression}.
+The @code{info vtable} command can be used to display the virtual
+method tables of the object computed by @var{expression}.  This shows
+one entry per virtual table; there may be multiple virtual tables when
+multiple inheritance is in use.
+
 @cindex C@t{++} symbol display
 @item set print demangle
 @itemx show print demangle
diff --git a/gdb/gnu-v3-abi.c b/gdb/gnu-v3-abi.c
index f015af6..8710bde 100644
--- a/gdb/gnu-v3-abi.c
+++ b/gdb/gnu-v3-abi.c
@@ -26,6 +26,7 @@
 #include "objfiles.h"
 #include "valprint.h"
 #include "c-lang.h"
+#include "exceptions.h"
 
 #include "gdb_assert.h"
 #include "gdb_string.h"
@@ -725,6 +726,245 @@ gnuv3_method_ptr_to_value (struct value **this_p, struct value *method_ptr)
     return value_from_pointer (lookup_pointer_type (method_type), ptr_value);
 }
 
+/* Objects of this type are stored in a hash table and a vector when
+   printing the vtables for a class.  */
+
+struct value_and_voffset
+{
+  /* The value representing the object.  */
+  struct value *value;
+
+  /* The maximum vtable offset we've found for any object at this
+     offset in the outermost object.  */
+  int max_voffset;
+};
+
+typedef struct value_and_voffset *value_and_voffset_p;
+DEF_VEC_P (value_and_voffset_p);
+
+/* Hash function for value_and_voffset.  */
+
+static hashval_t
+hash_value_and_voffset (const void *p)
+{
+  const struct value_and_voffset *o = p;
+
+  return value_address (o->value) + value_embedded_offset (o->value);
+}
+
+/* Equality function for value_and_voffset.  */
+
+static int
+eq_value_and_voffset (const void *a, const void *b)
+{
+  const struct value_and_voffset *ova = a;
+  const struct value_and_voffset *ovb = b;
+
+  return (value_address (ova->value) + value_embedded_offset (ova->value)
+	  == value_address (ovb->value) + value_embedded_offset (ovb->value));
+}
+
+/* qsort comparison function for value_and_voffset.  */
+
+static int
+compare_value_and_voffset (const void *a, const void *b)
+{
+  const struct value_and_voffset * const *ova = a;
+  CORE_ADDR addra = (value_address ((*ova)->value)
+		     + value_embedded_offset ((*ova)->value));
+  const struct value_and_voffset * const *ovb = b;
+  CORE_ADDR addrb = (value_address ((*ovb)->value)
+		     + value_embedded_offset ((*ovb)->value));
+
+  if (addra < addrb)
+    return -1;
+  if (addra > addrb)
+    return 1;
+  return 0;
+}
+
+/* A helper function used when printing vtables.  This determines the
+   key (most derived) sub-object at each address and also computes the
+   maximum vtable offset seen for the corresponding vtable.  Updates
+   OFFSET_HASH and OFFSET_VEC with a new value_and_voffset object, if
+   needed.  VALUE is the object to examine.  */
+
+static void
+compute_vtable_size (htab_t offset_hash,
+		     VEC (value_and_voffset_p) **offset_vec,
+		     struct value *value)
+{
+  int i;
+  struct type *type = check_typedef (value_type (value));
+  void **slot;
+  struct value_and_voffset search_vo, *current_vo;
+  CORE_ADDR addr = value_address (value) + value_embedded_offset (value);
+
+  /* If the object is not dynamic, then we are done; as it cannot have
+     dynamic base types either.  */
+  if (!gnuv3_dynamic_class (type))
+    return;
+
+  /* Update the hash and the vec, if needed.  */
+  search_vo.value = value;
+  slot = htab_find_slot (offset_hash, &search_vo, INSERT);
+  if (*slot)
+    current_vo = *slot;
+  else
+    {
+      current_vo = XNEW (struct value_and_voffset);
+      current_vo->value = value;
+      current_vo->max_voffset = -1;
+      *slot = current_vo;
+      VEC_safe_push (value_and_voffset_p, *offset_vec, current_vo);
+    }
+
+  /* Update the value_and_voffset object with the highest vtable
+     offset from this class.  */
+  for (i = 0; i < TYPE_NFN_FIELDS (type); ++i)
+    {
+      int j;
+      struct fn_field *fn = TYPE_FN_FIELDLIST1 (type, i);
+
+      for (j = 0; j < TYPE_FN_FIELDLIST_LENGTH (type, i); ++j)
+	{
+	  if (TYPE_FN_FIELD_VIRTUAL_P (fn, j))
+	    {
+	      int voffset = TYPE_FN_FIELD_VOFFSET (fn, j);
+
+	      if (voffset > current_vo->max_voffset)
+		current_vo->max_voffset = voffset;
+	    }
+	}
+    }
+
+  /* Recurse into base classes.  */
+  for (i = 0; i < TYPE_N_BASECLASSES (type); ++i)
+    compute_vtable_size (offset_hash, offset_vec, value_field (value, i));
+}
+
+/* Helper for gnuv3_print_vtable that prints a single vtable.  */
+
+static void
+print_one_vtable (struct gdbarch *gdbarch, struct value *value,
+		  int max_voffset,
+		  struct value_print_options *opts)
+{
+  int i;
+  struct type *type = check_typedef (value_type (value));
+  struct value *vtable;
+  CORE_ADDR vt_addr;
+
+  vtable = gnuv3_get_vtable (gdbarch, type,
+			     value_address (value)
+			     + value_embedded_offset (value));
+  vt_addr = value_address (value_field (vtable,
+					vtable_field_virtual_functions));
+
+  printf_filtered (_("vtable for '%s' @ %s (subobject @ %s):\n"),
+		   TYPE_SAFE_NAME (type),
+		   paddress (gdbarch, vt_addr),
+		   paddress (gdbarch, (value_address (value)
+				       + value_embedded_offset (value))));
+
+  for (i = 0; i <= max_voffset; ++i)
+    {
+      struct value *vfn;
+      CORE_ADDR addr;
+      volatile struct gdb_exception ex;
+
+      printf_filtered ("[%d]: ", i);
+
+      vfn = value_subscript (value_field (vtable,
+					  vtable_field_virtual_functions),
+			     i);
+
+      if (gdbarch_vtable_function_descriptors (gdbarch))
+	vfn = value_addr (vfn);
+
+      TRY_CATCH (ex, RETURN_MASK_ERROR)
+	{
+	  addr = value_as_address (vfn);
+	}
+      if (ex.reason < 0)
+	printf_filtered (_("<error: %s>"), ex.message);
+      else
+	print_function_pointer_address (gdbarch, addr, gdb_stdout,
+					opts->addressprint);
+      printf_filtered ("\n");
+    }
+}
+
+/* Implementation of the print_vtable method.  */
+
+static void
+gnuv3_print_vtable (struct value *value)
+{
+  struct gdbarch *gdbarch;
+  struct type *type;
+  struct value *vtable;
+  struct value_print_options opts;
+  htab_t offset_hash;
+  struct cleanup *cleanup;
+  VEC (value_and_voffset_p) *result_vec;
+  struct value_and_voffset *iter;
+  int i, count;
+
+  value = coerce_ref (value);
+  type = check_typedef (value_type (value));
+  if (TYPE_CODE (type) == TYPE_CODE_PTR)
+    {
+      value = value_ind (value);
+      type = check_typedef (value_type (value));
+    }
+
+  get_user_print_options (&opts);
+
+  /* Respect 'set print object'.  */
+  if (opts.objectprint)
+    {
+      value = value_full_object (value, NULL, 0, 0, 0);
+      type = check_typedef (value_type (value));
+    }
+
+  gdbarch = get_type_arch (type);
+  vtable = gnuv3_get_vtable (gdbarch, type,
+			     value_as_address (value_addr (value)));
+
+  if (!vtable)
+    {
+      printf_filtered (_("This object does not have a vtable\n"));
+      return;
+    }
+
+  offset_hash = htab_create_alloc (1, hash_value_and_voffset,
+				   eq_value_and_voffset,
+				   xfree, xcalloc, xfree);
+  cleanup = make_cleanup_htab_delete (offset_hash);
+  make_cleanup (VEC_cleanup (value_and_voffset_p), &result_vec);
+
+  compute_vtable_size (offset_hash, &result_vec, value);
+
+  qsort (VEC_address (value_and_voffset_p, result_vec),
+	 VEC_length (value_and_voffset_p, result_vec),
+	 sizeof (value_and_voffset_p),
+	 compare_value_and_voffset);
+
+  count = 0;
+  for (i = 0; VEC_iterate (value_and_voffset_p, result_vec, i, iter); ++i)
+    {
+      if (iter->max_voffset >= 0)
+	{
+	  if (count > 0)
+	    printf_filtered ("\n");
+	  print_one_vtable (gdbarch, iter->value, iter->max_voffset, &opts);
+	  ++count;
+	}
+    }
+
+  do_cleanups (cleanup);
+}
+
 /* 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.  */
 
@@ -873,6 +1113,7 @@ init_gnuv3_ops (void)
   gnu_v3_abi_ops.method_ptr_size = gnuv3_method_ptr_size;
   gnu_v3_abi_ops.make_method_ptr = gnuv3_make_method_ptr;
   gnu_v3_abi_ops.method_ptr_to_value = gnuv3_method_ptr_to_value;
+  gnu_v3_abi_ops.print_vtable = gnuv3_print_vtable;
   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/gdb.cp/virtfunc.exp b/gdb/testsuite/gdb.cp/virtfunc.exp
index dd41c6d..00dd89e 100644
--- a/gdb/testsuite/gdb.cp/virtfunc.exp
+++ b/gdb/testsuite/gdb.cp/virtfunc.exp
@@ -226,6 +226,47 @@ proc test_virtual_calls {} {
     }
 }
 
+# A helper proc that creates a regular expression matching a
+# particular vtable.  NAME is the type name.  Each element of ARGS is
+# the name of a function in the vtable.
+
+proc make_one_vtable_result {name args} {
+    global hex
+
+    set nls "\[\r\n\]+"
+
+    set result "vtable for '${name}' @ $hex .subobject @ $hex.:$nls"
+    set count 0
+    foreach func $args {
+	append result ".${count}.: $hex <$func..>${nls}"
+	incr count
+    }
+
+    return $result
+}
+
+# Test "info vtable".
+
+proc test_info_vtable {} {
+    global hex
+
+    set nls "\[\r\n\]+"
+
+    set vt_A [make_one_vtable_result A A::f]
+    set vt_B [make_one_vtable_result B B::f]
+    set vt_V [make_one_vtable_result V VB::vvb V::vv]
+    set vt_V2 [make_one_vtable_result V VB::vvb "virtual thunk to E::vv"]
+    set vt_D [make_one_vtable_result D D::vg D::vd]
+    set vt_D2 [make_one_vtable_result D "non-virtual thunk to E::vg" D::vd]
+    set vt_E [make_one_vtable_result E E::f E::vg E::vv]
+
+    gdb_test "info vtable a" "${vt_A}${vt_V}"
+    gdb_test "info vtable b" "${vt_B}${vt_V}"
+    gdb_test "info vtable c" "${vt_V}"
+    gdb_test "info vtable d" "${vt_D}${vt_V}"
+    gdb_test "info vtable e" "${vt_E}${vt_D2}${vt_V2}"
+}
+
 proc do_tests {} {
     global srcdir subdir binfile
     global gdb_prompt
@@ -244,6 +285,7 @@ proc do_tests {} {
 	return
     }
     test_ptype_of_classes
+    test_info_vtable
 
     gdb_breakpoint test_calls
     gdb_test "continue" ".*Breakpoint .* test_calls.*" ""
-- 
1.7.7.6


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]