* 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:
parent
2479f722a4
commit
c4aeac856a
11 changed files with 359 additions and 0 deletions
|
@ -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
|
||||
|
|
3
gdb/NEWS
3
gdb/NEWS
|
@ -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
|
||||
|
|
10
gdb/cp-abi.c
10
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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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."));
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
241
gdb/gnu-v3-abi.c
241
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 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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -111,6 +111,7 @@ D d;
|
|||
E e;
|
||||
V v;
|
||||
VB vb;
|
||||
VA va;
|
||||
|
||||
|
||||
A* pAa = &a;
|
||||
|
|
|
@ -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.*" ""
|
||||
|
|
Loading…
Reference in a new issue