From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 96788 invoked by alias); 4 Dec 2017 15:03:56 -0000 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 Received: (qmail 96757 invoked by uid 89); 4 Dec 2017 15:03:55 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-26.9 required=5.0 tests=BAYES_00,GIT_PATCH_0,GIT_PATCH_1,GIT_PATCH_2,GIT_PATCH_3,KAM_SHORT,SPF_HELO_PASS,T_RP_MATCHES_RCVD autolearn=ham version=3.3.2 spammy=field5, field4, field2, field3 X-HELO: mx1.redhat.com Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Mon, 04 Dec 2017 15:03:48 +0000 Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 26DE87CB9C; Mon, 4 Dec 2017 15:03:47 +0000 (UTC) Received: from localhost (unused-10-15-17-193.yyz.redhat.com [10.15.17.193]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 9B95B619D5; Mon, 4 Dec 2017 15:03:46 +0000 (UTC) From: Sergio Durigan Junior To: GDB Patches Cc: Tom Tromey , Eli Zaretskii Subject: Re: [PATCH v2] Implement pahole-like 'ptype /o' option References: <20171121160709.23248-1-sergiodj@redhat.com> <20171128212137.15655-1-sergiodj@redhat.com> Date: Mon, 04 Dec 2017 15:03:00 -0000 In-Reply-To: <20171128212137.15655-1-sergiodj@redhat.com> (Sergio Durigan Junior's message of "Tue, 28 Nov 2017 16:21:37 -0500") Message-ID: <871ska7cx9.fsf@redhat.com> User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/25.2 (gnu/linux) MIME-Version: 1.0 Content-Type: text/plain X-IsSubscribed: yes X-SW-Source: 2017-12/txt/msg00072.txt.bz2 On Tuesday, November 28 2017, I wrote: > Changes from v1: > > - Address Tom's comments (the command now prints offset information > about unions, and the offset info is carried on for nested structs). > > - Address Eli's comments. > > - Extended testcase. > > - A "bit" of cleanup on 'c_type_print_base'. Ping. > > This commit implements the pahole-like '/o' option for 'ptype', which > prints the offsets and sizes of struct fields, reporting whenever > there is a hole found. > > The output is heavily based on pahole(1), with a few modifications > here and there to adjust it to our reality. Here's an example: > > (gdb) ptype /o stap_probe > /* offset | size */ > struct stap_probe { > /* 0 | 40 */ struct probe { > /* 0 | 8 */ const probe_ops *pops; > /* 8 | 8 */ gdbarch *arch; > /* 16 | 8 */ const char *name; > /* 24 | 8 */ const char *provider; > /* 32 | 8 */ CORE_ADDR address; > } /* total size: 40 bytes */ p; > /* 40 | 8 */ CORE_ADDR sem_addr; > /* 48:31 | 4 */ unsigned int args_parsed : 1; > /* XXX 7-bit hole */ > /* XXX 7-byte hole */ > /* 56 | 8 */ union { > /* 8 */ const char *text; > /* 8 */ VEC_stap_probe_arg_s *vec; > } /* total size: 8 bytes */ args_u; > } /* total size: 64 bytes */ > > A big part of this patch handles the formatting logic of 'ptype', > which is a bit messy. I tried to be not very invasive, but I had to > do some cleanups here and there to make life easier. > > This patch is the start of a long-term work I'll do to flush the local > patches we carry for Fedora GDB. In this specific case, I'm aiming at > upstreaming the feature implemented by the 'pahole.py' script that is > shipped with Fedora GDB: > > > > This has been regression-tested on the BuildBot. There's a new > testcase for it, along with an update to the documentation. I also > thought it was worth mentioning this feature in the NEWS file. > > gdb/ChangeLog: > 2017-11-28 Sergio Durigan Junior > > PR cli/16224 > * NEWS (Changes since GDB 8.0): Mention new '/o' flag. > * c-typeprint.c (OFFSET_SPC_LEN): New define. > (print_spaces_filtered_with_print_options): New function. > (output_access_specifier): Take new argument FLAGS. Modify > function to call 'print_spaces_filtered_with_print_options'. > (c_print_type_union_field_offset): New function. > (c_print_type_struct_field_offset): New function. > (need_access_label_p): New function, with contents from > 'c_type_print_base'. > (c_type_print_base_struct_union): Likewise. > (c_type_print_base): Print offsets and sizes for struct > fields. Struct/union handling code move to functions > mentioned above. > * typeprint.c (const struct type_print_options > type_print_raw_options): Initialize 'print_offsets' and > 'offset_bitpos'. > (static struct type_print_options default_ptype_flags): > Likewise. > (whatis_exp): Handle '/o' option. > (_initialize_typeprint): Add '/o' flag to ptype's help. > * typeprint.h (struct type_print_options) : New > field. > : Likewise. > > gdb/testsuite/ChangeLog: > 2017-11-28 Sergio Durigan Junior > > PR cli/16224 > * gdb.base/ptype-offsets.cc: New file. > * gdb.base/ptype-offsets.exp: New file. > > gdb/doc/ChangeLog: > 2017-11-28 Sergio Durigan Junior > > PR cli/16224 > * gdb.texinfo (ptype): Add new flag '/o'. > --- > gdb/NEWS | 3 + > gdb/c-typeprint.c | 1016 +++++++++++++++++------------- > gdb/doc/gdb.texinfo | 4 + > gdb/testsuite/gdb.base/ptype-offsets.cc | 113 ++++ > gdb/testsuite/gdb.base/ptype-offsets.exp | 77 +++ > gdb/typeprint.c | 15 +- > gdb/typeprint.h | 9 + > 7 files changed, 812 insertions(+), 425 deletions(-) > create mode 100644 gdb/testsuite/gdb.base/ptype-offsets.cc > create mode 100644 gdb/testsuite/gdb.base/ptype-offsets.exp > > diff --git a/gdb/NEWS b/gdb/NEWS > index 754ce103bd..1247021046 100644 > --- a/gdb/NEWS > +++ b/gdb/NEWS > @@ -3,6 +3,9 @@ > > *** Changes since GDB 8.0 > > +* The 'ptype' command now accepts a '/o' flag, which prints the > + offsets and sizes of fields in a struct, like the pahole(1) tool. > + > * GDB now uses the GNU MPFR library, if available, to emulate target > floating-point arithmetic during expression evaluation when the target > uses different floating-point formats than the host. At least version > diff --git a/gdb/c-typeprint.c b/gdb/c-typeprint.c > index ed5a1a4b8a..39334ccf88 100644 > --- a/gdb/c-typeprint.c > +++ b/gdb/c-typeprint.c > @@ -32,6 +32,14 @@ > #include "cp-abi.h" > #include "cp-support.h" > > +/* When printing the offsets of a struct and its fields (i.e., 'ptype > + /o'; type_print_options::print_offsets), we use this many > + characters when printing the offset information at the beginning of > + the line. This is needed in order to generate the correct amount > + of whitespaces when no offset info should be printed for a certain > + field. */ > +#define OFFSET_SPC_LEN 23 > + > /* A list of access specifiers used for printing. */ > > enum access_specifier > @@ -836,21 +844,36 @@ c_type_print_template_args (const struct type_print_options *flags, > fputs_filtered (_("] "), stream); > } > > +/* Use 'print_spaces_filtered', but take into consideration the > + type_print_options FLAGS in order to determine how many whitespaces > + will be printed. */ > + > +static void > +print_spaces_filtered_with_print_options (int level, struct ui_file *stream, > + const struct type_print_options *flags) > +{ > + if (!flags->print_offsets) > + print_spaces_filtered (level, stream); > + else > + print_spaces_filtered (level + OFFSET_SPC_LEN, stream); > +} > + > /* Output an access specifier to STREAM, if needed. LAST_ACCESS is the > last access specifier output (typically returned by this function). */ > > static enum access_specifier > output_access_specifier (struct ui_file *stream, > enum access_specifier last_access, > - int level, bool is_protected, bool is_private) > + int level, bool is_protected, bool is_private, > + const struct type_print_options *flags) > { > if (is_protected) > { > if (last_access != s_protected) > { > last_access = s_protected; > - fprintfi_filtered (level + 2, stream, > - "protected:\n"); > + print_spaces_filtered_with_print_options (level + 2, stream, flags); > + fprintf_filtered (stream, "protected:\n"); > } > } > else if (is_private) > @@ -858,8 +881,8 @@ output_access_specifier (struct ui_file *stream, > if (last_access != s_private) > { > last_access = s_private; > - fprintfi_filtered (level + 2, stream, > - "private:\n"); > + print_spaces_filtered_with_print_options (level + 2, stream, flags); > + fprintf_filtered (stream, "private:\n"); > } > } > else > @@ -867,14 +890,569 @@ output_access_specifier (struct ui_file *stream, > if (last_access != s_public) > { > last_access = s_public; > - fprintfi_filtered (level + 2, stream, > - "public:\n"); > + print_spaces_filtered_with_print_options (level + 2, stream, flags); > + fprintf_filtered (stream, "public:\n"); > } > } > > return last_access; > } > > +/* Print information about the offset of TYPE inside its union. > + FIELD_IDX represents the index of this TYPE inside the union. We > + just print the type size, and nothing more. > + > + The output is strongly based on pahole(1). */ > + > +static void > +c_print_type_union_field_offset (struct type *type, unsigned int field_idx, > + struct ui_file *stream) > +{ > + struct type *ftype = check_typedef (TYPE_FIELD_TYPE (type, field_idx)); > + > + fprintf_filtered (stream, "/* %4u */", TYPE_LENGTH (ftype)); > +} > + > +/* Print information about the offset of TYPE inside its struct. > + FIELD_IDX represents the index of this TYPE inside the struct, and > + ENDPOS is the end position of the previous type (this is how we > + calculate whether there are holes in the struct). At the end, > + ENDPOS is updated. > + > + The output is strongly based on pahole(1). */ > + > +static void > +c_print_type_struct_field_offset (struct type *type, unsigned int field_idx, > + unsigned int *endpos, struct ui_file *stream, > + unsigned int offset_bitpos) > +{ > + struct type *ftype = check_typedef (TYPE_FIELD_TYPE (type, field_idx)); > + unsigned int bitpos = TYPE_FIELD_BITPOS (type, field_idx); > + unsigned int fieldsize_byte = TYPE_LENGTH (ftype); > + unsigned int fieldsize_bit; > + > + if (*endpos > 0 && *endpos < bitpos) > + { > + /* If ENDPOS is smaller than the current type's bitpos, it means > + there's a hole in the struct, so we report it here. */ > + unsigned int hole = bitpos - *endpos; > + unsigned int hole_byte = hole / TARGET_CHAR_BIT; > + unsigned int hole_bit = hole % TARGET_CHAR_BIT; > + > + if (hole_bit > 0) > + fprintf_filtered (stream, "/* XXX %2u-bit hole */\n", hole_bit); > + > + if (hole_byte > 0) > + fprintf_filtered (stream, "/* XXX %2u-byte hole */\n", hole_byte); > + } > + > + /* The position of the field, relative to the beginning of the > + struct. Assume this number will have 4 digits. */ > + fprintf_filtered (stream, "/* %4u", > + (bitpos + offset_bitpos) / TARGET_CHAR_BIT); > + > + if (TYPE_FIELD_PACKED (type, field_idx)) > + { > + /* We're dealing with a bitfield. Print how many bits are left > + to be used. */ > + fieldsize_bit = TYPE_FIELD_BITSIZE (type, field_idx); > + fprintf_filtered (stream, ":%u", > + fieldsize_byte * TARGET_CHAR_BIT - fieldsize_bit); > + } > + else > + { > + fieldsize_bit = fieldsize_byte * TARGET_CHAR_BIT; > + fprintf_filtered (stream, " "); > + } > + > + fprintf_filtered (stream, " | %4u */", fieldsize_byte); > + > + *endpos = bitpos + fieldsize_bit; > +} > + > +/* Return true is an access label (i.e., "public:", "private:", > + "protected:") needs to be printed for TYPE. */ > + > +static bool > +need_access_label_p (struct type *type) > +{ > + bool need_access_label = false; > + int i, j; > + int len, len2; > + > + if (TYPE_DECLARED_CLASS (type)) > + { > + QUIT; > + len = TYPE_NFIELDS (type); > + for (i = TYPE_N_BASECLASSES (type); i < len; i++) > + if (!TYPE_FIELD_PRIVATE (type, i)) > + { > + need_access_label = true; > + break; > + } > + QUIT; > + if (!need_access_label) > + { > + len2 = TYPE_NFN_FIELDS (type); > + for (j = 0; j < len2; j++) > + { > + len = TYPE_FN_FIELDLIST_LENGTH (type, j); > + for (i = 0; i < len; i++) > + if (!TYPE_FN_FIELD_PRIVATE (TYPE_FN_FIELDLIST1 (type, > + j), i)) > + { > + need_access_label = true; > + break; > + } > + if (need_access_label) > + break; > + } > + } > + QUIT; > + if (!need_access_label) > + { > + for (i = 0; i < TYPE_TYPEDEF_FIELD_COUNT (type); ++i) > + { > + if (!TYPE_TYPEDEF_FIELD_PRIVATE (type, i)) > + { > + need_access_label = true; > + break; > + } > + } > + } > + } > + else > + { > + QUIT; > + len = TYPE_NFIELDS (type); > + for (i = TYPE_N_BASECLASSES (type); i < len; i++) > + if (TYPE_FIELD_PRIVATE (type, i) > + || TYPE_FIELD_PROTECTED (type, i)) > + { > + need_access_label = true; > + break; > + } > + QUIT; > + if (!need_access_label) > + { > + len2 = TYPE_NFN_FIELDS (type); > + for (j = 0; j < len2; j++) > + { > + QUIT; > + len = TYPE_FN_FIELDLIST_LENGTH (type, j); > + for (i = 0; i < len; i++) > + if (TYPE_FN_FIELD_PROTECTED (TYPE_FN_FIELDLIST1 (type, > + j), i) > + || TYPE_FN_FIELD_PRIVATE (TYPE_FN_FIELDLIST1 (type, > + j), > + i)) > + { > + need_access_label = true; > + break; > + } > + if (need_access_label) > + break; > + } > + } > + QUIT; > + if (!need_access_label) > + { > + for (i = 0; i < TYPE_TYPEDEF_FIELD_COUNT (type); ++i) > + { > + if (TYPE_TYPEDEF_FIELD_PROTECTED (type, i) > + || TYPE_TYPEDEF_FIELD_PRIVATE (type, i)) > + { > + need_access_label = true; > + break; > + } > + } > + } > + } > + return need_access_label; > +} > + > +/* Helper for 'c_type_print_base' that handles structs and unions. > + For a description of the arguments, see 'c_type_print_base'. */ > + > +static void > +c_type_print_base_struct_union (struct type *type, struct ui_file *stream, > + int show, int level, > + const struct type_print_options *flags) > +{ > + struct type_print_options local_flags = *flags; > + struct type_print_options semi_local_flags = *flags; > + struct cleanup *local_cleanups = make_cleanup (null_cleanup, NULL); > + > + local_flags.local_typedefs = NULL; > + semi_local_flags.local_typedefs = NULL; > + > + if (!flags->raw) > + { > + if (flags->local_typedefs) > + local_flags.local_typedefs > + = copy_typedef_hash (flags->local_typedefs); > + else > + local_flags.local_typedefs = create_typedef_hash (); > + > + make_cleanup_free_typedef_hash (local_flags.local_typedefs); > + } > + > + c_type_print_modifier (type, stream, 0, 1); > + if (TYPE_CODE (type) == TYPE_CODE_UNION) > + fprintf_filtered (stream, "union "); > + else if (TYPE_DECLARED_CLASS (type)) > + fprintf_filtered (stream, "class "); > + else > + fprintf_filtered (stream, "struct "); > + > + /* Print the tag if it exists. The HP aCC compiler emits a > + spurious "{unnamed struct}"/"{unnamed union}"/"{unnamed > + enum}" tag for unnamed struct/union/enum's, which we don't > + want to print. */ > + if (TYPE_TAG_NAME (type) != NULL > + && !startswith (TYPE_TAG_NAME (type), "{unnamed")) > + { > + /* When printing the tag name, we are still effectively > + printing in the outer context, hence the use of FLAGS > + here. */ > + print_name_maybe_canonical (TYPE_TAG_NAME (type), flags, stream); > + if (show > 0) > + fputs_filtered (" ", stream); > + } > + > + if (show < 0) > + { > + /* If we just printed a tag name, no need to print anything > + else. */ > + if (TYPE_TAG_NAME (type) == NULL) > + fprintf_filtered (stream, "{...}"); > + } > + else if (show > 0 || TYPE_TAG_NAME (type) == NULL) > + { > + struct type *basetype; > + > + c_type_print_template_args (&local_flags, type, stream); > + > + /* Add in template parameters when printing derivation info. */ > + add_template_parameters (local_flags.local_typedefs, type); > + cp_type_print_derivation_info (stream, type, &local_flags); > + > + /* This holds just the global typedefs and the template > + parameters. */ > + semi_local_flags.local_typedefs > + = copy_typedef_hash (local_flags.local_typedefs); > + if (semi_local_flags.local_typedefs) > + make_cleanup_free_typedef_hash (semi_local_flags.local_typedefs); > + > + /* Now add in the local typedefs. */ > + recursively_update_typedef_hash (local_flags.local_typedefs, type); > + > + fprintf_filtered (stream, "{\n"); > + if (TYPE_NFIELDS (type) == 0 && TYPE_NFN_FIELDS (type) == 0 > + && TYPE_TYPEDEF_FIELD_COUNT (type) == 0) > + { > + if (TYPE_STUB (type)) > + fprintfi_filtered (level + 4, stream, > + _("\n")); > + else > + fprintfi_filtered (level + 4, stream, > + _("\n")); > + } > + > + /* Start off with no specific section type, so we can print > + one for the first field we find, and use that section type > + thereafter until we find another type. */ > + enum access_specifier section_type = s_none; > + > + /* For a class, if all members are private, there's no need > + for a "private:" label; similarly, for a struct or union > + masquerading as a class, if all members are public, there's > + no need for a "public:" label. */ > + bool need_access_label = need_access_label_p (type); > + > + /* If there is a base class for this type, > + do not print the field that it occupies. */ > + > + int len = TYPE_NFIELDS (type); > + int vptr_fieldno = get_vptr_fieldno (type, &basetype); > + unsigned int endpos = 0; > + > + for (int i = TYPE_N_BASECLASSES (type); i < len; i++) > + { > + QUIT; > + > + /* If we have a virtual table pointer, omit it. Even if > + virtual table pointers are not specifically marked in > + the debug info, they should be artificial. */ > + if ((i == vptr_fieldno && type == basetype) > + || TYPE_FIELD_ARTIFICIAL (type, i)) > + continue; > + > + if (need_access_label) > + { > + section_type = output_access_specifier > + (stream, section_type, level, > + TYPE_FIELD_PROTECTED (type, i), > + TYPE_FIELD_PRIVATE (type, i), > + flags); > + } > + > + bool is_static = field_is_static (&TYPE_FIELD (type, i)); > + > + if (flags->print_offsets) > + { > + if (!is_static) > + { > + if (TYPE_CODE (type) == TYPE_CODE_STRUCT) > + c_print_type_struct_field_offset (type, i, &endpos, stream, > + flags->offset_bitpos); > + else if (TYPE_CODE (type) == TYPE_CODE_UNION) > + c_print_type_union_field_offset (type, i, stream); > + } > + else > + print_spaces_filtered (OFFSET_SPC_LEN, stream); > + } > + > + print_spaces_filtered (level + 4, stream); > + if (is_static) > + fprintf_filtered (stream, "static "); > + > + int newshow = show - 1; > + > + if (flags->print_offsets > + && (TYPE_CODE (TYPE_FIELD_TYPE (type, i)) == TYPE_CODE_STRUCT > + || TYPE_CODE (TYPE_FIELD_TYPE (type, i)) == TYPE_CODE_UNION)) > + { > + /* If we're printing offsets and this field's type is > + either a struct or an union, then we're interested in > + expanding it. */ > + ++newshow; > + > + /* Make sure we carry our offset when we expand the > + struct. */ > + local_flags.offset_bitpos > + = flags->offset_bitpos + TYPE_FIELD_BITPOS (type, i); > + } > + > + c_print_type (TYPE_FIELD_TYPE (type, i), > + TYPE_FIELD_NAME (type, i), > + stream, newshow, level + 4, > + &local_flags); > + if (!is_static > + && TYPE_FIELD_PACKED (type, i)) > + { > + /* It is a bitfield. This code does not attempt > + to look at the bitpos and reconstruct filler, > + unnamed fields. This would lead to misleading > + results if the compiler does not put out fields > + for such things (I don't know what it does). */ > + fprintf_filtered (stream, " : %d", > + TYPE_FIELD_BITSIZE (type, i)); > + } > + fprintf_filtered (stream, ";\n"); > + } > + > + /* If there are both fields and methods, put a blank line > + between them. Make sure to count only method that we > + will display; artificial methods will be hidden. */ > + len = TYPE_NFN_FIELDS (type); > + if (!flags->print_methods) > + len = 0; > + int real_len = 0; > + for (int i = 0; i < len; i++) > + { > + struct fn_field *f = TYPE_FN_FIELDLIST1 (type, i); > + int len2 = TYPE_FN_FIELDLIST_LENGTH (type, i); > + int j; > + > + for (j = 0; j < len2; j++) > + if (!TYPE_FN_FIELD_ARTIFICIAL (f, j)) > + real_len++; > + } > + if (real_len > 0 && section_type != s_none) > + fprintf_filtered (stream, "\n"); > + > + /* C++: print out the methods. */ > + for (int i = 0; i < len; i++) > + { > + struct fn_field *f = TYPE_FN_FIELDLIST1 (type, i); > + int j, len2 = TYPE_FN_FIELDLIST_LENGTH (type, i); > + const char *method_name = TYPE_FN_FIELDLIST_NAME (type, i); > + const char *name = type_name_no_tag (type); > + int is_constructor = name && strcmp (method_name, > + name) == 0; > + > + for (j = 0; j < len2; j++) > + { > + const char *mangled_name; > + gdb::unique_xmalloc_ptr mangled_name_holder; > + char *demangled_name; > + const char *physname = TYPE_FN_FIELD_PHYSNAME (f, j); > + int is_full_physname_constructor = > + TYPE_FN_FIELD_CONSTRUCTOR (f, j) > + || is_constructor_name (physname) > + || is_destructor_name (physname) > + || method_name[0] == '~'; > + > + /* Do not print out artificial methods. */ > + if (TYPE_FN_FIELD_ARTIFICIAL (f, j)) > + continue; > + > + QUIT; > + section_type = output_access_specifier > + (stream, section_type, level, > + TYPE_FN_FIELD_PROTECTED (f, j), > + TYPE_FN_FIELD_PRIVATE (f, j), > + flags); > + > + print_spaces_filtered_with_print_options (level + 4, stream, > + flags); > + if (TYPE_FN_FIELD_VIRTUAL_P (f, j)) > + fprintf_filtered (stream, "virtual "); > + else if (TYPE_FN_FIELD_STATIC_P (f, j)) > + fprintf_filtered (stream, "static "); > + if (TYPE_TARGET_TYPE (TYPE_FN_FIELD_TYPE (f, j)) == 0) > + { > + /* Keep GDB from crashing here. */ > + fprintf_filtered (stream, > + _(" %s;\n"), > + TYPE_FN_FIELD_PHYSNAME (f, j)); > + break; > + } > + else if (!is_constructor /* Constructors don't > + have declared > + types. */ > + && !is_full_physname_constructor /* " " */ > + && !is_type_conversion_operator (type, i, j)) > + { > + unsigned int old_po = local_flags.print_offsets; > + > + /* Temporarily disable print_offsets, because it > + would mess with indentation. */ > + local_flags.print_offsets = 0; > + c_print_type (TYPE_TARGET_TYPE (TYPE_FN_FIELD_TYPE (f, j)), > + "", stream, -1, 0, > + &local_flags); > + local_flags.print_offsets = old_po; > + fputs_filtered (" ", stream); > + } > + if (TYPE_FN_FIELD_STUB (f, j)) > + { > + /* Build something we can demangle. */ > + mangled_name_holder.reset (gdb_mangle_name (type, i, j)); > + mangled_name = mangled_name_holder.get (); > + } > + else > + mangled_name = TYPE_FN_FIELD_PHYSNAME (f, j); > + > + demangled_name = > + gdb_demangle (mangled_name, > + DMGL_ANSI | DMGL_PARAMS); > + if (demangled_name == NULL) > + { > + /* In some cases (for instance with the HP > + demangling), if a function has more than 10 > + arguments, the demangling will fail. > + Let's try to reconstruct the function > + signature from the symbol information. */ > + if (!TYPE_FN_FIELD_STUB (f, j)) > + { > + int staticp = TYPE_FN_FIELD_STATIC_P (f, j); > + struct type *mtype = TYPE_FN_FIELD_TYPE (f, j); > + > + cp_type_print_method_args (mtype, > + "", > + method_name, > + staticp, > + stream, &local_flags); > + } > + else > + fprintf_filtered (stream, > + _(""), > + mangled_name); > + } > + else > + { > + char *p; > + char *demangled_no_class > + = remove_qualifiers (demangled_name); > + > + /* Get rid of the `static' appended by the > + demangler. */ > + p = strstr (demangled_no_class, " static"); > + if (p != NULL) > + { > + int length = p - demangled_no_class; > + char *demangled_no_static; > + > + demangled_no_static > + = (char *) xmalloc (length + 1); > + strncpy (demangled_no_static, > + demangled_no_class, length); > + *(demangled_no_static + length) = '\0'; > + fputs_filtered (demangled_no_static, stream); > + xfree (demangled_no_static); > + } > + else > + fputs_filtered (demangled_no_class, stream); > + xfree (demangled_name); > + } > + > + fprintf_filtered (stream, ";\n"); > + } > + } > + > + /* Print typedefs defined in this class. */ > + > + if (TYPE_TYPEDEF_FIELD_COUNT (type) != 0 && flags->print_typedefs) > + { > + if (TYPE_NFIELDS (type) != 0 || TYPE_NFN_FIELDS (type) != 0) > + fprintf_filtered (stream, "\n"); > + > + for (int i = 0; i < TYPE_TYPEDEF_FIELD_COUNT (type); i++) > + { > + struct type *target = TYPE_TYPEDEF_FIELD_TYPE (type, i); > + > + /* Dereference the typedef declaration itself. */ > + gdb_assert (TYPE_CODE (target) == TYPE_CODE_TYPEDEF); > + target = TYPE_TARGET_TYPE (target); > + > + if (need_access_label) > + { > + section_type = output_access_specifier > + (stream, section_type, level, > + TYPE_TYPEDEF_FIELD_PROTECTED (type, i), > + TYPE_TYPEDEF_FIELD_PRIVATE (type, i), > + flags); > + } > + print_spaces_filtered_with_print_options (level + 4, > + stream, flags); > + fprintf_filtered (stream, "typedef "); > + > + /* We want to print typedefs with substitutions > + from the template parameters or globally-known > + typedefs but not local typedefs. */ > + c_print_type (target, > + TYPE_TYPEDEF_FIELD_NAME (type, i), > + stream, show - 1, level + 4, > + &semi_local_flags); > + fprintf_filtered (stream, ";\n"); > + } > + } > + > + if (flags->print_offsets && level > 0) > + print_spaces_filtered (OFFSET_SPC_LEN, stream); > + > + fprintfi_filtered (level, stream, "}"); > + } > + > + if (show > 0 && flags->print_offsets) > + fprintf_filtered (stream, " /* total size: %4u bytes */", > + TYPE_LENGTH (type)); > + > + do_cleanups (local_cleanups); > +} > + > /* Print the name of the type (or the ultimate pointer target, > function value or array element), or the description of a structure > or union. > @@ -898,10 +1476,8 @@ c_type_print_base (struct type *type, struct ui_file *stream, > int show, int level, const struct type_print_options *flags) > { > int i; > - int len, real_len; > - enum access_specifier section_type; > - int need_access_label = 0; > - int j, len2; > + int len; > + int j; > > QUIT; > > @@ -918,15 +1494,16 @@ c_type_print_base (struct type *type, struct ui_file *stream, > folk tend to expect things like "class5 *foo" rather than "struct > class5 *foo". */ > > - if (show <= 0 > - && TYPE_NAME (type) != NULL) > + struct type *ttype = check_typedef (type); > + > + if (show <= 0 && TYPE_NAME (type) != NULL) > { > c_type_print_modifier (type, stream, 0, 1); > print_name_maybe_canonical (TYPE_NAME (type), flags, stream); > return; > } > > - type = check_typedef (type); > + type = ttype; > > switch (TYPE_CODE (type)) > { > @@ -958,416 +1535,7 @@ c_type_print_base (struct type *type, struct ui_file *stream, > > case TYPE_CODE_STRUCT: > case TYPE_CODE_UNION: > - { > - struct type_print_options local_flags = *flags; > - struct type_print_options semi_local_flags = *flags; > - struct cleanup *local_cleanups = make_cleanup (null_cleanup, NULL); > - > - local_flags.local_typedefs = NULL; > - semi_local_flags.local_typedefs = NULL; > - > - if (!flags->raw) > - { > - if (flags->local_typedefs) > - local_flags.local_typedefs > - = copy_typedef_hash (flags->local_typedefs); > - else > - local_flags.local_typedefs = create_typedef_hash (); > - > - make_cleanup_free_typedef_hash (local_flags.local_typedefs); > - } > - > - c_type_print_modifier (type, stream, 0, 1); > - if (TYPE_CODE (type) == TYPE_CODE_UNION) > - fprintf_filtered (stream, "union "); > - else if (TYPE_DECLARED_CLASS (type)) > - fprintf_filtered (stream, "class "); > - else > - fprintf_filtered (stream, "struct "); > - > - /* Print the tag if it exists. The HP aCC compiler emits a > - spurious "{unnamed struct}"/"{unnamed union}"/"{unnamed > - enum}" tag for unnamed struct/union/enum's, which we don't > - want to print. */ > - if (TYPE_TAG_NAME (type) != NULL > - && !startswith (TYPE_TAG_NAME (type), "{unnamed")) > - { > - /* When printing the tag name, we are still effectively > - printing in the outer context, hence the use of FLAGS > - here. */ > - print_name_maybe_canonical (TYPE_TAG_NAME (type), flags, stream); > - if (show > 0) > - fputs_filtered (" ", stream); > - } > - > - if (show < 0) > - { > - /* If we just printed a tag name, no need to print anything > - else. */ > - if (TYPE_TAG_NAME (type) == NULL) > - fprintf_filtered (stream, "{...}"); > - } > - else if (show > 0 || TYPE_TAG_NAME (type) == NULL) > - { > - struct type *basetype; > - int vptr_fieldno; > - > - c_type_print_template_args (&local_flags, type, stream); > - > - /* Add in template parameters when printing derivation info. */ > - add_template_parameters (local_flags.local_typedefs, type); > - cp_type_print_derivation_info (stream, type, &local_flags); > - > - /* This holds just the global typedefs and the template > - parameters. */ > - semi_local_flags.local_typedefs > - = copy_typedef_hash (local_flags.local_typedefs); > - if (semi_local_flags.local_typedefs) > - make_cleanup_free_typedef_hash (semi_local_flags.local_typedefs); > - > - /* Now add in the local typedefs. */ > - recursively_update_typedef_hash (local_flags.local_typedefs, type); > - > - fprintf_filtered (stream, "{\n"); > - if (TYPE_NFIELDS (type) == 0 && TYPE_NFN_FIELDS (type) == 0 > - && TYPE_TYPEDEF_FIELD_COUNT (type) == 0) > - { > - if (TYPE_STUB (type)) > - fprintfi_filtered (level + 4, stream, > - _("\n")); > - else > - fprintfi_filtered (level + 4, stream, > - _("\n")); > - } > - > - /* Start off with no specific section type, so we can print > - one for the first field we find, and use that section type > - thereafter until we find another type. */ > - > - section_type = s_none; > - > - /* For a class, if all members are private, there's no need > - for a "private:" label; similarly, for a struct or union > - masquerading as a class, if all members are public, there's > - no need for a "public:" label. */ > - > - if (TYPE_DECLARED_CLASS (type)) > - { > - QUIT; > - len = TYPE_NFIELDS (type); > - for (i = TYPE_N_BASECLASSES (type); i < len; i++) > - if (!TYPE_FIELD_PRIVATE (type, i)) > - { > - need_access_label = 1; > - break; > - } > - QUIT; > - if (!need_access_label) > - { > - len2 = TYPE_NFN_FIELDS (type); > - for (j = 0; j < len2; j++) > - { > - len = TYPE_FN_FIELDLIST_LENGTH (type, j); > - for (i = 0; i < len; i++) > - if (!TYPE_FN_FIELD_PRIVATE (TYPE_FN_FIELDLIST1 (type, > - j), i)) > - { > - need_access_label = 1; > - break; > - } > - if (need_access_label) > - break; > - } > - } > - QUIT; > - if (!need_access_label) > - { > - for (i = 0; i < TYPE_TYPEDEF_FIELD_COUNT (type); ++i) > - { > - if (!TYPE_TYPEDEF_FIELD_PRIVATE (type, i)) > - { > - need_access_label = 1; > - break; > - } > - } > - } > - } > - else > - { > - QUIT; > - len = TYPE_NFIELDS (type); > - for (i = TYPE_N_BASECLASSES (type); i < len; i++) > - if (TYPE_FIELD_PRIVATE (type, i) > - || TYPE_FIELD_PROTECTED (type, i)) > - { > - need_access_label = 1; > - break; > - } > - QUIT; > - if (!need_access_label) > - { > - len2 = TYPE_NFN_FIELDS (type); > - for (j = 0; j < len2; j++) > - { > - QUIT; > - len = TYPE_FN_FIELDLIST_LENGTH (type, j); > - for (i = 0; i < len; i++) > - if (TYPE_FN_FIELD_PROTECTED (TYPE_FN_FIELDLIST1 (type, > - j), i) > - || TYPE_FN_FIELD_PRIVATE (TYPE_FN_FIELDLIST1 (type, > - j), > - i)) > - { > - need_access_label = 1; > - break; > - } > - if (need_access_label) > - break; > - } > - } > - QUIT; > - if (!need_access_label) > - { > - for (i = 0; i < TYPE_TYPEDEF_FIELD_COUNT (type); ++i) > - { > - if (TYPE_TYPEDEF_FIELD_PROTECTED (type, i) > - || TYPE_TYPEDEF_FIELD_PRIVATE (type, i)) > - { > - need_access_label = 1; > - break; > - } > - } > - } > - } > - > - /* If there is a base class for this type, > - do not print the field that it occupies. */ > - > - len = TYPE_NFIELDS (type); > - vptr_fieldno = get_vptr_fieldno (type, &basetype); > - for (i = TYPE_N_BASECLASSES (type); i < len; i++) > - { > - QUIT; > - > - /* If we have a virtual table pointer, omit it. Even if > - virtual table pointers are not specifically marked in > - the debug info, they should be artificial. */ > - if ((i == vptr_fieldno && type == basetype) > - || TYPE_FIELD_ARTIFICIAL (type, i)) > - continue; > - > - if (need_access_label) > - { > - section_type = output_access_specifier > - (stream, section_type, level, > - TYPE_FIELD_PROTECTED (type, i), > - TYPE_FIELD_PRIVATE (type, i)); > - } > - > - print_spaces_filtered (level + 4, stream); > - if (field_is_static (&TYPE_FIELD (type, i))) > - fprintf_filtered (stream, "static "); > - c_print_type (TYPE_FIELD_TYPE (type, i), > - TYPE_FIELD_NAME (type, i), > - stream, show - 1, level + 4, > - &local_flags); > - if (!field_is_static (&TYPE_FIELD (type, i)) > - && TYPE_FIELD_PACKED (type, i)) > - { > - /* It is a bitfield. This code does not attempt > - to look at the bitpos and reconstruct filler, > - unnamed fields. This would lead to misleading > - results if the compiler does not put out fields > - for such things (I don't know what it does). */ > - fprintf_filtered (stream, " : %d", > - TYPE_FIELD_BITSIZE (type, i)); > - } > - fprintf_filtered (stream, ";\n"); > - } > - > - /* If there are both fields and methods, put a blank line > - between them. Make sure to count only method that we > - will display; artificial methods will be hidden. */ > - len = TYPE_NFN_FIELDS (type); > - if (!flags->print_methods) > - len = 0; > - real_len = 0; > - for (i = 0; i < len; i++) > - { > - struct fn_field *f = TYPE_FN_FIELDLIST1 (type, i); > - int len2 = TYPE_FN_FIELDLIST_LENGTH (type, i); > - int j; > - > - for (j = 0; j < len2; j++) > - if (!TYPE_FN_FIELD_ARTIFICIAL (f, j)) > - real_len++; > - } > - if (real_len > 0 && section_type != s_none) > - fprintf_filtered (stream, "\n"); > - > - /* C++: print out the methods. */ > - for (i = 0; i < len; i++) > - { > - struct fn_field *f = TYPE_FN_FIELDLIST1 (type, i); > - int j, len2 = TYPE_FN_FIELDLIST_LENGTH (type, i); > - const char *method_name = TYPE_FN_FIELDLIST_NAME (type, i); > - const char *name = type_name_no_tag (type); > - int is_constructor = name && strcmp (method_name, > - name) == 0; > - > - for (j = 0; j < len2; j++) > - { > - const char *mangled_name; > - gdb::unique_xmalloc_ptr mangled_name_holder; > - char *demangled_name; > - const char *physname = TYPE_FN_FIELD_PHYSNAME (f, j); > - int is_full_physname_constructor = > - TYPE_FN_FIELD_CONSTRUCTOR (f, j) > - || is_constructor_name (physname) > - || is_destructor_name (physname) > - || method_name[0] == '~'; > - > - /* Do not print out artificial methods. */ > - if (TYPE_FN_FIELD_ARTIFICIAL (f, j)) > - continue; > - > - QUIT; > - section_type = output_access_specifier > - (stream, section_type, level, > - TYPE_FN_FIELD_PROTECTED (f, j), > - TYPE_FN_FIELD_PRIVATE (f, j)); > - > - print_spaces_filtered (level + 4, stream); > - if (TYPE_FN_FIELD_VIRTUAL_P (f, j)) > - fprintf_filtered (stream, "virtual "); > - else if (TYPE_FN_FIELD_STATIC_P (f, j)) > - fprintf_filtered (stream, "static "); > - if (TYPE_TARGET_TYPE (TYPE_FN_FIELD_TYPE (f, j)) == 0) > - { > - /* Keep GDB from crashing here. */ > - fprintf_filtered (stream, > - _(" %s;\n"), > - TYPE_FN_FIELD_PHYSNAME (f, j)); > - break; > - } > - else if (!is_constructor /* Constructors don't > - have declared > - types. */ > - && !is_full_physname_constructor /* " " */ > - && !is_type_conversion_operator (type, i, j)) > - { > - c_print_type (TYPE_TARGET_TYPE (TYPE_FN_FIELD_TYPE (f, j)), > - "", stream, -1, 0, > - &local_flags); > - fputs_filtered (" ", stream); > - } > - if (TYPE_FN_FIELD_STUB (f, j)) > - { > - /* Build something we can demangle. */ > - mangled_name_holder.reset (gdb_mangle_name (type, i, j)); > - mangled_name = mangled_name_holder.get (); > - } > - else > - mangled_name = TYPE_FN_FIELD_PHYSNAME (f, j); > - > - demangled_name = > - gdb_demangle (mangled_name, > - DMGL_ANSI | DMGL_PARAMS); > - if (demangled_name == NULL) > - { > - /* In some cases (for instance with the HP > - demangling), if a function has more than 10 > - arguments, the demangling will fail. > - Let's try to reconstruct the function > - signature from the symbol information. */ > - if (!TYPE_FN_FIELD_STUB (f, j)) > - { > - int staticp = TYPE_FN_FIELD_STATIC_P (f, j); > - struct type *mtype = TYPE_FN_FIELD_TYPE (f, j); > - > - cp_type_print_method_args (mtype, > - "", > - method_name, > - staticp, > - stream, &local_flags); > - } > - else > - fprintf_filtered (stream, > - _(""), > - mangled_name); > - } > - else > - { > - char *p; > - char *demangled_no_class > - = remove_qualifiers (demangled_name); > - > - /* Get rid of the `static' appended by the > - demangler. */ > - p = strstr (demangled_no_class, " static"); > - if (p != NULL) > - { > - int length = p - demangled_no_class; > - char *demangled_no_static; > - > - demangled_no_static > - = (char *) xmalloc (length + 1); > - strncpy (demangled_no_static, > - demangled_no_class, length); > - *(demangled_no_static + length) = '\0'; > - fputs_filtered (demangled_no_static, stream); > - xfree (demangled_no_static); > - } > - else > - fputs_filtered (demangled_no_class, stream); > - xfree (demangled_name); > - } > - > - fprintf_filtered (stream, ";\n"); > - } > - } > - > - /* Print typedefs defined in this class. */ > - > - if (TYPE_TYPEDEF_FIELD_COUNT (type) != 0 && flags->print_typedefs) > - { > - if (TYPE_NFIELDS (type) != 0 || TYPE_NFN_FIELDS (type) != 0) > - fprintf_filtered (stream, "\n"); > - > - for (i = 0; i < TYPE_TYPEDEF_FIELD_COUNT (type); i++) > - { > - struct type *target = TYPE_TYPEDEF_FIELD_TYPE (type, i); > - > - /* Dereference the typedef declaration itself. */ > - gdb_assert (TYPE_CODE (target) == TYPE_CODE_TYPEDEF); > - target = TYPE_TARGET_TYPE (target); > - > - if (need_access_label) > - { > - section_type = output_access_specifier > - (stream, section_type, level, > - TYPE_TYPEDEF_FIELD_PROTECTED (type, i), > - TYPE_TYPEDEF_FIELD_PRIVATE (type, i)); > - } > - print_spaces_filtered (level + 4, stream); > - fprintf_filtered (stream, "typedef "); > - > - /* We want to print typedefs with substitutions > - from the template parameters or globally-known > - typedefs but not local typedefs. */ > - c_print_type (target, > - TYPE_TYPEDEF_FIELD_NAME (type, i), > - stream, show - 1, level + 4, > - &semi_local_flags); > - fprintf_filtered (stream, ";\n"); > - } > - } > - > - fprintfi_filtered (level, stream, "}"); > - } > - > - do_cleanups (local_cleanups); > - } > + c_type_print_base_struct_union (type, stream, show, level, flags); > break; > > case TYPE_CODE_ENUM: > diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo > index 675f6e7bc8..f7a45dd5dd 100644 > --- a/gdb/doc/gdb.texinfo > +++ b/gdb/doc/gdb.texinfo > @@ -17095,6 +17095,10 @@ names are substituted when printing other types. > @item T > Print typedefs defined in the class. This is the default, but the flag > exists in case you change the default with @command{set print type typedefs}. > + > +@item o > +Print the offsets and sizes of fields in a struct, similar to what the > +@command{pahole} tool does. > @end table > > @kindex ptype > diff --git a/gdb/testsuite/gdb.base/ptype-offsets.cc b/gdb/testsuite/gdb.base/ptype-offsets.cc > new file mode 100644 > index 0000000000..f9a57fd3db > --- /dev/null > +++ b/gdb/testsuite/gdb.base/ptype-offsets.cc > @@ -0,0 +1,113 @@ > +/* This testcase is part of GDB, the GNU debugger. > + > + Copyright 2017 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 . */ > + > +/* This file will be used to test 'ptype /o' on x86_64 only. */ > + > +#include > + > +/* A struct with many types of fields, in order to test 'ptype > + /o'. */ > + > +struct abc > +{ > + /* Virtual destructor. */ > + virtual ~abc () > + {} > + > + /* 8-byte address. Because of the virtual destructor above, this > + field's offset will be 8. */ > + void *field1; > + > + /* No hole here. */ > + > + /* 4-byte int bitfield of 1-bit. */ > + unsigned int field2 : 1; > + > + /* 31-bit hole here. */ > + > + /* 4-byte int. */ > + int field3; > + > + /* No hole here. */ > + > + /* 1-byte char. */ > + char field4; > + > + /* 7-byte hole here. */ > + > + /* 8-byte int. */ > + uint64_t field5; > + > + /* We just print the offset and size of a union, ignoring its > + fields. */ > + union > + { > + /* 8-byte address. */ > + void *field6; > + > + /* 4-byte int. */ > + int field7; > + } field8; > + > + /* Empty constructor. */ > + abc () > + {} > +}; > + > +/* This struct will be nested inside 'struct xyz'. */ > + > +struct tuv > +{ > + int a1; > + > + char *a2; > + > + int a3; > +}; > + > +/* This struct will be nested inside 'struct pqr'. */ > + > +struct xyz > +{ > + int f1; > + > + char f2; > + > + void *f3; > + > + struct tuv f4; > +}; > + > +/* A struct with a nested struct. */ > + > +struct pqr > +{ > + int ff1; > + > + struct xyz ff2; > + > + char ff3; > +}; > + > +int > +main (int argc, char *argv[]) > +{ > + struct abc foo; > + struct pqr bar; > + > + return 0; > +} > diff --git a/gdb/testsuite/gdb.base/ptype-offsets.exp b/gdb/testsuite/gdb.base/ptype-offsets.exp > new file mode 100644 > index 0000000000..4f84416dc5 > --- /dev/null > +++ b/gdb/testsuite/gdb.base/ptype-offsets.exp > @@ -0,0 +1,77 @@ > +# This testcase is part of GDB, the GNU debugger. > + > +# Copyright 2017 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 . > + > +standard_testfile .cc ptype-offsets.cc > + > +# Test only works on x86_64 LP64 targets. That's how we guarantee > +# that the expected holes will be present in the struct. > +if { !([istarget "x86_64-*-*"] && [is_lp64_target]) } { > + untested "test work only on x86_64 lp64" > + return 0 > +} > + > +if { [prepare_for_testing "failed to prepare" $testfile $srcfile \ > + { debug c++ optimize=-O0 }] } { > + return -1 > +} > + > +# Test general offset printing, ctor/dtor printing, union, formatting. > +gdb_test "ptype /o struct abc" \ > + [multi_line \ > +"type = struct abc {" \ > +"/\\\* offset | size \\\*/" \ > +" public:" \ > +"/\\\* 8 | 8 \\\*/ void \\\*field1;" \ > +"/\\\* 16:31 | 4 \\\*/ unsigned int field2 : 1;" \ > +"/\\\* XXX 7-bit hole \\\*/" \ > +"/\\\* XXX 3-byte hole \\\*/" \ > +"/\\\* 20 | 4 \\\*/ int field3;" \ > +"/\\\* 24 | 1 \\\*/ char field4;" \ > +"/\\\* XXX 7-byte hole \\\*/" \ > +"/\\\* 32 | 8 \\\*/ uint64_t field5;" \ > +"/\\\* 40 | 8 \\\*/ union {" \ > +"/\\\* 8 \\\*/ void \\\*field6;" \ > +"/\\\* 4 \\\*/ int field7;" \ > +" } /\\\* total size: 8 bytes \\\*/ field8;" \ > +"" \ > +" abc\\(void\\);" \ > +" ~abc\\(\\);" \ > +"} /\\\* total size: 48 bytes \\\*/"] \ > + "ptype offset struct abc" > + > +# Test nested structs. > +gdb_test "ptype /o struct pqr" \ > + [multi_line \ > +"type = struct pqr {" \ > +"/\\\* offset | size \\\*/" \ > +"/\\\* 0 | 4 \\\*/ int f1;" \ > +"/\\\* XXX 4-byte hole \\\*/" \ > +"/\\\* 8 | 16 \\\*/ struct xyz {" \ > +"/\\\* 8 | 4 \\\*/ int f1;" \ > +"/\\\* 12 | 1 \\\*/ char f2;" \ > +"/\\\* XXX 3-byte hole \\\*/" \ > +"/\\\* 16 | 8 \\\*/ void \\\*f3;" \ > +"/\\\* 24 | 24 \\\*/ struct tuv {" \ > +"/\\\* 24 | 4 \\\*/ int a1;" \ > +"/\\\* XXX 4-byte hole \\\*/" \ > +"/\\\* 32 | 8 \\\*/ char *a2;" \ > +"/\\\* 40 | 4 \\\*/ int a3;" \ > +" } /\\\* total size: 24 bytes \\\*/ f4;" \ > +" } /\\\* total size: 40 bytes \\\*/ ff2;" \ > +"/\\\* 48 | 1 \\\*/ char ff3;" \ > +"} /\\\* total size: 56 bytes \\\*/"] \ > + "ptype offset struct pqr" > diff --git a/gdb/typeprint.c b/gdb/typeprint.c > index 427af17ad7..1463e802ad 100644 > --- a/gdb/typeprint.c > +++ b/gdb/typeprint.c > @@ -42,6 +42,8 @@ const struct type_print_options type_print_raw_options = > 1, /* raw */ > 1, /* print_methods */ > 1, /* print_typedefs */ > + 0, /* print_offsets */ > + 0, /* offset_bitpos */ > NULL, /* local_typedefs */ > NULL, /* global_table */ > NULL /* global_printers */ > @@ -54,6 +56,8 @@ static struct type_print_options default_ptype_flags = > 0, /* raw */ > 1, /* print_methods */ > 1, /* print_typedefs */ > + 0, /* print_offsets */ > + 0, /* offset_bitpos */ > NULL, /* local_typedefs */ > NULL, /* global_table */ > NULL /* global_printers */ > @@ -438,6 +442,9 @@ whatis_exp (const char *exp, int show) > case 'T': > flags.print_typedefs = 1; > break; > + case 'o': > + flags.print_offsets = 1; > + break; > default: > error (_("unrecognized flag '%c'"), *exp); > } > @@ -497,6 +504,11 @@ whatis_exp (const char *exp, int show) > real_type = value_rtti_type (val, &full, &top, &using_enc); > } > > + if (flags.print_offsets && > + (TYPE_CODE (type) == TYPE_CODE_STRUCT > + || TYPE_CODE (type) == TYPE_CODE_UNION)) > + fprintf_filtered (gdb_stdout, "/* offset | size */\n"); > + > printf_filtered ("type = "); > > if (!flags.raw) > @@ -722,7 +734,8 @@ Available FLAGS are:\n\ > /m do not print methods defined in a class\n\ > /M print methods defined in a class\n\ > /t do not print typedefs defined in a class\n\ > - /T print typedefs defined in a class")); > + /T print typedefs defined in a class\n\ > + /o print offsets and sizes of fields in a struct (like pahole)\n")); > set_cmd_completer (c, expression_completer); > > c = add_com ("whatis", class_vars, whatis_command, > diff --git a/gdb/typeprint.h b/gdb/typeprint.h > index a458aa4e2f..a2a5285012 100644 > --- a/gdb/typeprint.h > +++ b/gdb/typeprint.h > @@ -35,6 +35,15 @@ struct type_print_options > /* True means print typedefs in a class. */ > unsigned int print_typedefs : 1; > > + /* True means to print offsets, a la 'pahole'. */ > + unsigned int print_offsets : 1; > + > + /* The offset to be applied to bitpos when PRINT_OFFSETS is true. > + This is needed for when we are printing nested structs and want > + to make sure that the printed offset for each field carries off > + the offset of the outter struct. */ > + unsigned int offset_bitpos; > + > /* If not NULL, a local typedef hash table used when printing a > type. */ > struct typedef_hash_table *local_typedefs; > -- > 2.13.3 -- Sergio GPG key ID: 237A 54B1 0287 28BF 00EF 31F4 D0EB 7628 65FC 5E36 Please send encrypted e-mail if possible http://sergiodj.net/