From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 29525 invoked by alias); 28 Feb 2012 21:37:06 -0000 Received: (qmail 29516 invoked by uid 22791); 28 Feb 2012 21:37:04 -0000 X-SWARE-Spam-Status: No, hits=-6.9 required=5.0 tests=AWL,BAYES_00,RCVD_IN_DNSWL_HI,SPF_HELO_PASS,TW_DR,TW_VF,T_RP_MATCHES_RCVD X-Spam-Check-By: sourceware.org Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Tue, 28 Feb 2012 21:36:45 +0000 Received: from int-mx10.intmail.prod.int.phx2.redhat.com (int-mx10.intmail.prod.int.phx2.redhat.com [10.5.11.23]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id q1SLaiEu005042 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Tue, 28 Feb 2012 16:36:44 -0500 Received: from ns3.rdu.redhat.com (ns3.rdu.redhat.com [10.11.255.199]) by int-mx10.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id q1SLaiV6017651; Tue, 28 Feb 2012 16:36:44 -0500 Received: from barimba (ovpn01.gateway.prod.ext.phx2.redhat.com [10.5.9.1]) by ns3.rdu.redhat.com (8.13.8/8.13.8) with ESMTP id q1SLagOx024361; Tue, 28 Feb 2012 16:36:43 -0500 From: Tom Tromey To: gdb-patches@sourceware.org Subject: [4/5] implement "info vtable" Date: Tue, 28 Feb 2012 21:48:00 -0000 Message-ID: <87mx82prmd.fsf@fleche.redhat.com> MIME-Version: 1.0 Content-Type: text/plain Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2012-02/txt/msg00680.txt.bz2 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 [1]: 0x400d42 [2]: 0x400d8e vtable for 'D' @ 0x4014a8 (subobject @ 0x602590): [0]: 0x400d3c [1]: 0x400dc8 vtable for 'V' @ 0x4014f0 (subobject @ 0x6025b0): [0]: 0x400df4 [1]: 0x400d84 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) : 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 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) : 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 (_(""), 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