* 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_vtbl_command): New function.
	(_initialize_cp_support): Add "info vtbl".
	* 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_vtbl): Likewise.
	(do_tests): Call test_info_vtbl.
	* gdb.cp/virtfunc.cc (va): New global.
gdb/doc
	* gdb.texinfo (Debugging C Plus Plus): Document "info vtbl".
This commit is contained in:
Tom Tromey 2012-03-15 15:43:18 +00:00
parent 2479f722a4
commit c4aeac856a
11 changed files with 359 additions and 0 deletions

View File

@ -1,3 +1,17 @@
2012-03-15 Tom Tromey <tromey@redhat.com>
* 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_vtbl_command): New function.
(_initialize_cp_support): Add "info vtbl".
* 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.
2012-03-15 Tom Tromey <tromey@redhat.com>
* d-lang.c (d_language_defn) <la_iterate_over_symbols>: Set to

View File

@ -70,6 +70,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

View File

@ -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)
{

View File

@ -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);
};

View File

@ -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_vtbl_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,10 @@ _initialize_cp_support (void)
first_component_command,
_("Print the first class/namespace component of NAME."),
&maint_cplus_cmd_list);
add_info ("vtbl", info_vtbl_command,
_("Show the vtable for a C++ object.\n\
Usage: info vtbl EXPRESSION\n\
Evaluate EXPRESSION and display the virtual function table for the\n\
resulting object."));
}

View File

@ -1,3 +1,7 @@
2012-03-15 Tom Tromey <tromey@redhat.com>
* gdb.texinfo (Debugging C Plus Plus): Document "info vtbl".
2012-03-13 Doug Evans <dje@google.com>
* gdb.texinfo (Help): Change apropos example to use "alias" instead

View File

@ -12788,6 +12788,12 @@ Print inheritance relationships as well as other information for type
@var{typename}.
@xref{Symbols, ,Examining the Symbol Table}.
@item info vtbl @var{expression}.
The @code{info vtbl} 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

View File

@ -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 virtual function table\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;
}

View File

@ -1,3 +1,10 @@
2012-03-15 Tom Tromey <tromey@redhat.com>
* gdb.cp/virtfunc.exp (make_one_vtable_result): New proc.
(test_info_vtbl): Likewise.
(do_tests): Call test_info_vtbl.
* gdb.cp/virtfunc.cc (va): New global.
2012-03-15 Thomas Schwinge <thomas@codesourcery.com>
* gdb.dwarf2/dw2-ada-ffffffff.S: Use .4byte instead of .long for

View File

@ -111,6 +111,7 @@ D d;
E e;
V v;
VB vb;
VA va;
A* pAa = &a;

View File

@ -226,6 +226,54 @@ 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 vtbl".
proc test_info_vtbl {} {
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 vtbl a" "${vt_A}${vt_V}"
gdb_test "info vtbl b" "${vt_B}${vt_V}"
gdb_test "info vtbl c" "${vt_V}"
gdb_test "info vtbl d" "${vt_D}${vt_V}"
gdb_test "info vtbl e" "${vt_E}${vt_D2}${vt_V2}"
gdb_test "info vtbl pEe" "${vt_E}${vt_D2}${vt_V2}"
gdb_test "info vtbl" "Argument required.*"
gdb_test "info vtbl va" \
"This object does not have a virtual function table.*"
gdb_test "info vtbl all_count" \
"This object does not have a virtual function table.*"
}
proc do_tests {} {
global srcdir subdir binfile
global gdb_prompt
@ -244,6 +292,7 @@ proc do_tests {} {
return
}
test_ptype_of_classes
test_info_vtbl
gdb_breakpoint test_calls
gdb_test "continue" ".*Breakpoint .* test_calls.*" ""