@@ -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.
+
* New "--readnever" command line option instructs GDB to not read each
symbol file's symbolic debug information. This makes startup faster
but at the expense of not being able to perform symbolic debugging.
@@ -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,15 +890,98 @@ 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;
}
-/* Return true is an access label (i.e., "public:", "private:",
+/* Print information about field at index FIELD_IDX of the union type
+ TYPE. Since union fields don't have the concept of offsets, we
+ just print their sizes.
+
+ 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 field at index FIELD_IDX of the struct type
+ TYPE. ENDPOS is the one-past-the-end bit position of the previous
+ field (where we expect this field to be if there is no hole). At
+ the end, ENDPOS is updated to the one-past-the-end bit position of
+ the current field. OFFSET_BITPOS is the offset value we carry over
+ when we are printing a struct that is inside another struct; this
+ is useful so that the offset is constantly incremented (if we
+ didn't carry it over, the offset would be reset to zero when
+ printing the inner struct).
+
+ 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;
+
+ /* We check for *ENDPOS > 0 because there is a specific scenario
+ when *ENDPOS can be zero and BITPOS can be > 0: when we are
+ dealing with a struct/class with a virtual method. Because of
+ the vtable, the first field of the struct/class will have an
+ offset of sizeof (void *) (the size of the vtable). If we do not
+ check for *ENDPOS > 0 here, GDB will report a hole before the
+ first field, which is not accurate. */
+ 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 if an access label (i.e., "public:", "private:",
"protected:") needs to be printed for TYPE. */
static bool
@@ -1032,6 +1138,8 @@ c_type_print_base_struct_union (struct type *type, struct ui_file *stream,
int len = TYPE_NFIELDS (type);
vptr_fieldno = get_vptr_fieldno (type, &basetype);
+ unsigned int endpos = 0;
+
for (int i = TYPE_N_BASECLASSES (type); i < len; i++)
{
QUIT;
@@ -1048,18 +1156,51 @@ c_type_print_base_struct_union (struct type *type, struct ui_file *stream,
section_type = output_access_specifier
(stream, section_type, level,
TYPE_FIELD_PROTECTED (type, i),
- TYPE_FIELD_PRIVATE (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 (field_is_static (&TYPE_FIELD (type, i)))
+ 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, show - 1, level + 4,
+ stream, newshow, level + 4,
&local_flags);
- if (!field_is_static (&TYPE_FIELD (type, i))
- && TYPE_FIELD_PACKED (type, i))
+ 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,
@@ -1122,9 +1263,10 @@ c_type_print_base_struct_union (struct type *type, struct ui_file *stream,
section_type = output_access_specifier
(stream, section_type, level,
TYPE_FN_FIELD_PROTECTED (f, j),
- TYPE_FN_FIELD_PRIVATE (f, j));
+ TYPE_FN_FIELD_PRIVATE (f, j), flags);
- print_spaces_filtered (level + 4, stream);
+ 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))
@@ -1143,9 +1285,16 @@ c_type_print_base_struct_union (struct type *type, struct ui_file *stream,
&& !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))
@@ -1226,9 +1375,16 @@ c_type_print_base_struct_union (struct type *type, struct ui_file *stream,
for (int i = 0; i < TYPE_NESTED_TYPES_COUNT (type); ++i)
{
- print_spaces_filtered (level + 4, stream);
+ unsigned int old_po = semi_local_flags.print_offsets;
+
+ /* Temporarily disable print_offsets, because it
+ would mess with indentation. */
+ print_spaces_filtered_with_print_options (level + 4, stream,
+ flags);
+ semi_local_flags.print_offsets = 0;
c_print_type (TYPE_NESTED_TYPES_FIELD_TYPE (type, i),
"", stream, show, level + 4, &semi_local_flags);
+ semi_local_flags.print_offsets = old_po;
fprintf_filtered (stream, ";\n");
}
}
@@ -1254,11 +1410,17 @@ c_type_print_base_struct_union (struct type *type, struct ui_file *stream,
section_type = output_access_specifier
(stream, section_type, level,
TYPE_TYPEDEF_FIELD_PROTECTED (type, i),
- TYPE_TYPEDEF_FIELD_PRIVATE (type, i));
+ TYPE_TYPEDEF_FIELD_PRIVATE (type, i), flags);
}
- print_spaces_filtered (level + 4, stream);
+ print_spaces_filtered_with_print_options (level + 4, stream,
+ flags);
fprintf_filtered (stream, "typedef ");
+ unsigned int old_po = semi_local_flags.print_offsets;
+ /* Temporarily disable print_offsets, because it
+ would mess with indentation. */
+ semi_local_flags.print_offsets = 0;
+
/* We want to print typedefs with substitutions
from the template parameters or globally-known
typedefs but not local typedefs. */
@@ -1266,13 +1428,21 @@ c_type_print_base_struct_union (struct type *type, struct ui_file *stream,
TYPE_TYPEDEF_FIELD_NAME (type, i),
stream, show - 1, level + 4,
&semi_local_flags);
+ semi_local_flags.print_offsets = old_po;
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);
}
@@ -1300,7 +1470,6 @@ c_type_print_base (struct type *type, struct ui_file *stream,
{
int i;
int len;
- int j;
QUIT;
@@ -17211,6 +17211,62 @@ 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.
+
+For example, given the following declarations:
+
+@smallexample
+struct tuv
+@{
+ int a1;
+ char *a2;
+ int a3;
+@};
+
+struct xyz
+@{
+ int f1;
+ char f2;
+ void *f3;
+ struct tuv f4;
+@};
+
+union qwe
+@{
+ struct tuv fff1;
+ struct xyz fff2;
+@};
+@end smallexample
+
+Issuing a @command{ptype /o union qwe} would print:
+
+@smallexample
+(@value{GDBP}) ptype /o union qwe
+/* offset | size */
+union qwe @{
+/* 24 */ struct tuv @{
+/* 0 | 4 */ int a1;
+/* XXX 4-byte hole */
+/* 8 | 8 */ char *a2;
+/* 16 | 4 */ int a3;
+ @} /* total size: 24 bytes */ fff1;
+/* 40 */ struct xyz @{
+/* 0 | 4 */ int f1;
+/* 4 | 1 */ char f2;
+/* XXX 3-byte hole */
+/* 8 | 8 */ void *f3;
+/* 16 | 24 */ struct tuv @{
+/* 16 | 4 */ int a1;
+/* XXX 4-byte hole */
+/* 24 | 8 */ char *a2;
+/* 32 | 4 */ int a3;
+ @} /* total size: 24 bytes */ f4;
+ @} /* total size: 40 bytes */ fff2;
+@} /* total size: 40 bytes */
+@end smallexample
@end table
@kindex ptype
new file mode 100644
@@ -0,0 +1,138 @@
+/* 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;
+};
+
+/* A union with two nested structs. */
+
+union qwe
+{
+ struct tuv fff1;
+
+ struct xyz fff2;
+};
+
+/* A struct with an union. */
+
+struct poi
+{
+ int f1;
+
+ union qwe f2;
+
+ uint16_t f3;
+
+ struct pqr f4;
+};
+
+int
+main (int argc, char *argv[])
+{
+ struct abc foo;
+ struct pqr bar;
+ union qwe c;
+ struct poi d;
+ uint8_t i;
+
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,158 @@
+# 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
+
+# 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"
+
+# Test that the offset is properly reset when we are printing an union
+# and go inside two inner structs.
+# This also tests a struct inside a struct inside an union.
+gdb_test "ptype /o union qwe" \
+ [multi_line \
+"/\\\* offset | size \\\*/" \
+"/\\\* 24 \\\*/ struct tuv {" \
+"/\\\* 0 | 4 \\\*/ int a1;" \
+"/\\\* XXX 4-byte hole \\\*/" \
+"/\\\* 8 | 8 \\\*/ char \\\*a2;" \
+"/\\\* 16 | 4 \\\*/ int a3;" \
+" } /\\\* total size: 24 bytes \\\*/ fff1;" \
+"/\\\* 40 \\\*/ struct xyz {" \
+"/\\\* 0 | 4 \\\*/ int f1;" \
+"/\\\* 4 | 1 \\\*/ char f2;" \
+"/\\\* XXX 3-byte hole \\\*/" \
+"/\\\* 8 | 8 \\\*/ void \\\*f3;" \
+"/\\\* 16 | 24 \\\*/ struct tuv {" \
+"/\\\* 16 | 4 \\\*/ int a1;" \
+"/\\\* XXX 4-byte hole \\\*/" \
+"/\\\* 24 | 8 \\\*/ char \\\*a2;" \
+"/\\\* 32 | 4 \\\*/ int a3;" \
+" } /\\\* total size: 24 bytes \\\*/ f4;" \
+" } /\\\* total size: 40 bytes \\\*/ fff2;" \
+"} /\\\* total size: 40 bytes \\\*/"] \
+ "ptype offset union qwe"
+
+# Test printing a struct that contains an union, and that also
+# contains a struct.
+gdb_test "ptype /o struct poi" \
+ [multi_line \
+"/\\\* offset | size \\\*/" \
+"/\\\* 0 | 4 \\\*/ int f1;" \
+"/\\\* XXX 4-byte hole \\\*/" \
+"/\\\* 8 | 40 \\\*/ union qwe {" \
+"/\\\* 24 \\\*/ struct tuv {" \
+"/\\\* 8 | 4 \\\*/ int a1;" \
+"/\\\* XXX 4-byte hole \\\*/" \
+"/\\\* 16 | 8 \\\*/ char \\\*a2;" \
+"/\\\* 24 | 4 \\\*/ int a3;" \
+" } /\\\* total size: 24 bytes \\\*/ fff1;" \
+"/\\\* 40 \\\*/ 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 \\\*/ fff2;" \
+" } /\\\* total size: 40 bytes \\\*/ f2;" \
+"/\\\* 48 | 2 \\\*/ uint16_t f3;" \
+"/\\\* XXX 6-byte hole */" \
+"/\\\* 56 | 56 \\\*/ struct pqr {" \
+"/\\\* 56 | 4 \\\*/ int ff1;" \
+"/\\\* XXX 4-byte hole \\\*/" \
+"/\\\* 64 | 40 \\\*/ struct xyz {" \
+"/\\\* 64 | 4 \\\*/ int f1;" \
+"/\\\* 68 | 1 \\\*/ char f2;" \
+"/\\\* XXX 3-byte hole \\\*/" \
+"/\\\* 72 | 8 \\\*/ void \\\*f3;" \
+"/\\\* 80 | 24 \\\*/ struct tuv {" \
+"/\\\* 80 | 4 \\\*/ int a1;" \
+"/\\\* XXX 4-byte hole \\\*/" \
+"/\\\* 88 | 8 \\\*/ char \\\*a2;" \
+"/\\\* 96 | 4 \\\*/ int a3;" \
+" } /\\\* total size: 24 bytes \\\*/ f4;" \
+" } /\\\* total size: 40 bytes \\\*/ ff2;" \
+"/\\\* 104 | 1 \\\*/ char ff3;" \
+" } /\\\* total size: 56 bytes \\\*/ f4;" \
+"} /\\\* total size: 112 bytes \\\*/"] \
+ "ptype offset struct poi"
+
+# Test that we don't print any header when issuing a "ptype /o" on a
+# non-struct, non-union, non-class type.
+gdb_test "ptype /o int" "int" "ptype offset int"
+gdb_test "ptype /o uint8_t" "char" "ptype offset uint8_t"
@@ -42,7 +42,9 @@ const struct type_print_options type_print_raw_options =
1, /* raw */
1, /* print_methods */
1, /* print_typedefs */
+ 0, /* print_offsets */
0, /* print_nested_type_limit */
+ 0, /* offset_bitpos */
NULL, /* local_typedefs */
NULL, /* global_table */
NULL /* global_printers */
@@ -55,7 +57,9 @@ static struct type_print_options default_ptype_flags =
0, /* raw */
1, /* print_methods */
1, /* print_typedefs */
+ 0, /* print_offsets */
0, /* print_nested_type_limit */
+ 0, /* offset_bitpos */
NULL, /* local_typedefs */
NULL, /* global_table */
NULL /* global_printers */
@@ -440,6 +444,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);
}
@@ -499,6 +506,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)
@@ -759,7 +771,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,
@@ -35,9 +35,18 @@ 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 number of nested type definitions to print. -1 == all. */
int print_nested_type_limit;
+ /* 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;