This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
Re: [PATCH v2] Implement pahole-like 'ptype /o' option
- From: Sergio Durigan Junior <sergiodj at redhat dot com>
- To: GDB Patches <gdb-patches at sourceware dot org>
- Cc: Tom Tromey <tom at tromey dot com>, Eli Zaretskii <eliz at gnu dot org>
- Date: Fri, 08 Dec 2017 16:32:11 -0500
- Subject: Re: [PATCH v2] Implement pahole-like 'ptype /o' option
- Authentication-results: sourceware.org; auth=none
- References: <20171121160709.23248-1-sergiodj@redhat.com> <20171128212137.15655-1-sergiodj@redhat.com> <871ska7cx9.fsf@redhat.com>
On Monday, December 04 2017, I wrote:
> 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.
Ping^2.
Eli has already approved the documentation part.
>>
>> 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:
>>
>> <https://src.fedoraproject.org/rpms/gdb/blob/master/f/gdb-archer.patch#_311>
>>
>> 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 <sergiodj@redhat.com>
>>
>> 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) <print_offsets>: New
>> field.
>> <offset_bitpos>: Likewise.
>>
>> gdb/testsuite/ChangeLog:
>> 2017-11-28 Sergio Durigan Junior <sergiodj@redhat.com>
>>
>> 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 <sergiodj@redhat.com>
>>
>> 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,
>> + _("<incomplete type>\n"));
>> + else
>> + fprintfi_filtered (level + 4, stream,
>> + _("<no data fields>\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<char> 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,
>> + _("<undefined type> %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,
>> + _("<badly mangled name '%s'>"),
>> + 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,
>> - _("<incomplete type>\n"));
>> - else
>> - fprintfi_filtered (level + 4, stream,
>> - _("<no data fields>\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<char> 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,
>> - _("<undefined type> %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,
>> - _("<badly mangled name '%s'>"),
>> - 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 <http://www.gnu.org/licenses/>. */
>> +
>> +/* This file will be used to test 'ptype /o' on x86_64 only. */
>> +
>> +#include <stdint.h>
>> +
>> +/* 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 <http://www.gnu.org/licenses/>.
>> +
>> +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/
--
Sergio
GPG key ID: 237A 54B1 0287 28BF 00EF 31F4 D0EB 7628 65FC 5E36
Please send encrypted e-mail if possible
http://sergiodj.net/