* [PATCH] Implement pahole-like 'ptype /o' option @ 2017-11-21 16:07 Sergio Durigan Junior 2017-11-21 16:16 ` Sergio Durigan Junior ` (8 more replies) 0 siblings, 9 replies; 75+ messages in thread From: Sergio Durigan Junior @ 2017-11-21 16:07 UTC (permalink / raw) To: GDB Patches; +Cc: Sergio Durigan Junior 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 struct stap_probe { /* offset | size */ /* 0 | 40 */ probe 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 { const char *text; VEC_stap_probe_arg_s *vec; } args_u; } /* total size: 64 bytes */ The idea is to print offsets and sizes only for structs, so unions and other types are mostly ignored. 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 intend to submit a follow-up patch cleaning a few things up in this code. 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-21 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_offset): New function. (c_type_print_base): Print offsets and sizes for struct fields. * typeprint.c (const struct type_print_options type_print_raw_options): Initialize 'print_offsets'. (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. gdb/testsuite/ChangeLog: 2017-11-21 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-21 Sergio Durigan Junior <sergiodj@redhat.com> PR cli/16224 * gdb.texinfo (ptype): Add new flag '/o'. --- gdb/NEWS | 3 + gdb/c-typeprint.c | 144 ++++++++++++++++++++++++++++--- gdb/doc/gdb.texinfo | 4 + gdb/testsuite/gdb.base/ptype-offsets.cc | 77 +++++++++++++++++ gdb/testsuite/gdb.base/ptype-offsets.exp | 52 +++++++++++ gdb/typeprint.c | 8 +- gdb/typeprint.h | 3 + 7 files changed, 276 insertions(+), 15 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 dc070facb8..7aef0331a5 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 supports access to the guarded-storage-control registers and the software-based guarded-storage broadcast control registers on IBM z14. diff --git a/gdb/c-typeprint.c b/gdb/c-typeprint.c index ed5a1a4b8a..d69c789cb9 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,69 @@ 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 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_offset (struct type *type, unsigned int field_idx, + unsigned int *endpos, struct ui_file *stream) +{ + 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 / 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; +} + /* Print the name of the type (or the ultimate pointer target, function value or array element), or the description of a structure or union. @@ -1145,6 +1223,11 @@ c_type_print_base (struct type *type, struct ui_file *stream, len = TYPE_NFIELDS (type); vptr_fieldno = get_vptr_fieldno (type, &basetype); + unsigned int endpos = 0; + if (flags->print_offsets + && TYPE_CODE (type) == TYPE_CODE_STRUCT) + fprintf_filtered (stream, + "/* offset | size */\n"); for (i = TYPE_N_BASECLASSES (type); i < len; i++) { QUIT; @@ -1161,17 +1244,32 @@ c_type_print_base (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 && TYPE_CODE (type) == TYPE_CODE_STRUCT) + c_print_type_offset (type, i, &endpos, stream); + else + { + /* We don't print offset info for union + fields. */ + 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 "); 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)) + if (!is_static && TYPE_FIELD_PACKED (type, i)) { /* It is a bitfield. This code does not attempt @@ -1235,9 +1333,11 @@ c_type_print_base (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)) @@ -1256,9 +1356,15 @@ c_type_print_base (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)) @@ -1347,9 +1453,11 @@ c_type_print_base (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 "); /* We want to print typedefs with substitutions @@ -1363,9 +1471,17 @@ c_type_print_base (struct type *type, struct ui_file *stream, } } + if (flags->print_offsets && level > 0) + print_spaces_filtered (OFFSET_SPC_LEN, stream); + fprintfi_filtered (level, stream, "}"); } + if (show > 0 && flags->print_offsets + && TYPE_CODE (type) == TYPE_CODE_STRUCT) + fprintf_filtered (stream, " /* total size: %4u bytes */", + TYPE_LENGTH (type)); + do_cleanups (local_cleanups); } break; diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 29d47892fc..a2dc03cfd2 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -17071,6 +17071,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..f6f845db74 --- /dev/null +++ b/gdb/testsuite/gdb.base/ptype-offsets.cc @@ -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/>. */ + +/* 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; + + /* 3-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 () + {} +}; + +int +main (int argc, char *argv[]) +{ + struct abc foo; + + 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..c06c124152 --- /dev/null +++ b/gdb/testsuite/gdb.base/ptype-offsets.exp @@ -0,0 +1,52 @@ +# 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 +} + +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 {" \ +" void \\\*field6;" \ +" int field7;" \ +" } field8;" \ +"" \ +" abc\\(void\\);" \ +" ~abc\\(\\);" \ +"} /\\\* total size: 48 bytes \\\*/"] diff --git a/gdb/typeprint.c b/gdb/typeprint.c index 427af17ad7..36e2ea9a53 100644 --- a/gdb/typeprint.c +++ b/gdb/typeprint.c @@ -42,6 +42,7 @@ const struct type_print_options type_print_raw_options = 1, /* raw */ 1, /* print_methods */ 1, /* print_typedefs */ + 0, /* print_offsets */ NULL, /* local_typedefs */ NULL, /* global_table */ NULL /* global_printers */ @@ -54,6 +55,7 @@ static struct type_print_options default_ptype_flags = 0, /* raw */ 1, /* print_methods */ 1, /* print_typedefs */ + 0, /* print_offsets */ NULL, /* local_typedefs */ NULL, /* global_table */ NULL /* global_printers */ @@ -438,6 +440,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); } @@ -722,7 +727,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..65cef15abe 100644 --- a/gdb/typeprint.h +++ b/gdb/typeprint.h @@ -35,6 +35,9 @@ 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; + /* If not NULL, a local typedef hash table used when printing a type. */ struct typedef_hash_table *local_typedefs; -- 2.13.3 ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH] Implement pahole-like 'ptype /o' option 2017-11-21 16:07 [PATCH] Implement pahole-like 'ptype /o' option Sergio Durigan Junior @ 2017-11-21 16:16 ` Sergio Durigan Junior 2017-11-21 16:50 ` Eli Zaretskii ` (7 subsequent siblings) 8 siblings, 0 replies; 75+ messages in thread From: Sergio Durigan Junior @ 2017-11-21 16:16 UTC (permalink / raw) To: GDB Patches On Tuesday, November 21 2017, I wrote: [...] > diff --git a/gdb/testsuite/gdb.base/ptype-offsets.cc b/gdb/testsuite/gdb.base/ptype-offsets.cc > new file mode 100644 > index 0000000000..f6f845db74 > --- /dev/null > +++ b/gdb/testsuite/gdb.base/ptype-offsets.cc > @@ -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/>. */ > + > +/* 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; > + > + /* 3-byte hole here. */ This should be "7-byte hole". I've updated my local tree. -- Sergio GPG key ID: 237A 54B1 0287 28BF 00EF 31F4 D0EB 7628 65FC 5E36 Please send encrypted e-mail if possible http://sergiodj.net/ ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH] Implement pahole-like 'ptype /o' option 2017-11-21 16:07 [PATCH] Implement pahole-like 'ptype /o' option Sergio Durigan Junior 2017-11-21 16:16 ` Sergio Durigan Junior @ 2017-11-21 16:50 ` Eli Zaretskii 2017-11-21 17:00 ` Sergio Durigan Junior 2017-11-26 19:27 ` Tom Tromey ` (6 subsequent siblings) 8 siblings, 1 reply; 75+ messages in thread From: Eli Zaretskii @ 2017-11-21 16:50 UTC (permalink / raw) To: Sergio Durigan Junior; +Cc: gdb-patches > From: Sergio Durigan Junior <sergiodj@redhat.com> > Cc: Sergio Durigan Junior <sergiodj@redhat.com> > Date: Tue, 21 Nov 2017 11:07:09 -0500 > > gdb/NEWS | 3 + > gdb/c-typeprint.c | 144 ++++++++++++++++++++++++++++--- > gdb/doc/gdb.texinfo | 4 + > gdb/testsuite/gdb.base/ptype-offsets.cc | 77 +++++++++++++++++ > gdb/testsuite/gdb.base/ptype-offsets.exp | 52 +++++++++++ > gdb/typeprint.c | 8 +- > gdb/typeprint.h | 3 + > 7 files changed, 276 insertions(+), 15 deletions(-) > create mode 100644 gdb/testsuite/gdb.base/ptype-offsets.cc > create mode 100644 gdb/testsuite/gdb.base/ptype-offsets.exp The documentation parts are okay, but ... > +@item o > +Print the offsets and sizes of fields in a struct, similar to what the > +@command{pahole} tool does. ... how about an example showing the output of this? I, for one, have never heard of 'pahole', so the reference to it won't help the likes of myself to understand what will be produced. And here: > + /o print offsets and sizes of fields in a struct (like pahole)\n")); I wonder whether we should mention 'pahole' at all. Thanks. ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH] Implement pahole-like 'ptype /o' option 2017-11-21 16:50 ` Eli Zaretskii @ 2017-11-21 17:00 ` Sergio Durigan Junior 2017-11-21 19:14 ` Eli Zaretskii 0 siblings, 1 reply; 75+ messages in thread From: Sergio Durigan Junior @ 2017-11-21 17:00 UTC (permalink / raw) To: Eli Zaretskii; +Cc: gdb-patches On Tuesday, November 21 2017, Eli Zaretskii wrote: >> From: Sergio Durigan Junior <sergiodj@redhat.com> >> Cc: Sergio Durigan Junior <sergiodj@redhat.com> >> Date: Tue, 21 Nov 2017 11:07:09 -0500 >> >> gdb/NEWS | 3 + >> gdb/c-typeprint.c | 144 ++++++++++++++++++++++++++++--- >> gdb/doc/gdb.texinfo | 4 + >> gdb/testsuite/gdb.base/ptype-offsets.cc | 77 +++++++++++++++++ >> gdb/testsuite/gdb.base/ptype-offsets.exp | 52 +++++++++++ >> gdb/typeprint.c | 8 +- >> gdb/typeprint.h | 3 + >> 7 files changed, 276 insertions(+), 15 deletions(-) >> create mode 100644 gdb/testsuite/gdb.base/ptype-offsets.cc >> create mode 100644 gdb/testsuite/gdb.base/ptype-offsets.exp > > The documentation parts are okay, but ... > >> +@item o >> +Print the offsets and sizes of fields in a struct, similar to what the >> +@command{pahole} tool does. > > ... how about an example showing the output of this? I, for one, have > never heard of 'pahole', so the reference to it won't help the likes > of myself to understand what will be produced. Good point. I will include an example output, then. > And here: > >> + /o print offsets and sizes of fields in a struct (like pahole)\n")); > > I wonder whether we should mention 'pahole' at all. I thought that it was good to mention 'pahole' here because that was the main motivation for writing this patch. My feeling is that it's benefitial to mention 'pahole' because even if the user doesn't know what it is, this will be a good opportunity for them to learn :-). But that's my personal opinion; I won't fight if you think the reference should be removed. Thanks, -- Sergio GPG key ID: 237A 54B1 0287 28BF 00EF 31F4 D0EB 7628 65FC 5E36 Please send encrypted e-mail if possible http://sergiodj.net/ ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH] Implement pahole-like 'ptype /o' option 2017-11-21 17:00 ` Sergio Durigan Junior @ 2017-11-21 19:14 ` Eli Zaretskii 0 siblings, 0 replies; 75+ messages in thread From: Eli Zaretskii @ 2017-11-21 19:14 UTC (permalink / raw) To: Sergio Durigan Junior; +Cc: gdb-patches > From: Sergio Durigan Junior <sergiodj@redhat.com> > Cc: gdb-patches@sourceware.org > Date: Tue, 21 Nov 2017 12:00:14 -0500 > > >> + /o print offsets and sizes of fields in a struct (like pahole)\n")); > > > > I wonder whether we should mention 'pahole' at all. > > I thought that it was good to mention 'pahole' here because that was the > main motivation for writing this patch. My feeling is that it's > benefitial to mention 'pahole' because even if the user doesn't know > what it is, this will be a good opportunity for them to learn :-). > > But that's my personal opinion; I won't fight if you think the reference > should be removed. I don't have strong a opinion. Let's see what others think. If no one chimes in, feel free to leave it unchanged. ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH] Implement pahole-like 'ptype /o' option 2017-11-21 16:07 [PATCH] Implement pahole-like 'ptype /o' option Sergio Durigan Junior 2017-11-21 16:16 ` Sergio Durigan Junior 2017-11-21 16:50 ` Eli Zaretskii @ 2017-11-26 19:27 ` Tom Tromey 2017-11-27 19:54 ` Sergio Durigan Junior 2017-11-28 21:21 ` [PATCH v2] " Sergio Durigan Junior ` (5 subsequent siblings) 8 siblings, 1 reply; 75+ messages in thread From: Tom Tromey @ 2017-11-26 19:27 UTC (permalink / raw) To: Sergio Durigan Junior; +Cc: GDB Patches >>>>> "Sergio" == Sergio Durigan Junior <sergiodj@redhat.com> writes: Sergio> This commit implements the pahole-like '/o' option for 'ptype', which Sergio> prints the offsets and sizes of struct fields, reporting whenever Sergio> there is a hole found. Thanks for doing this! Sergio> The idea is to print offsets and sizes only for structs, so unions and Sergio> other types are mostly ignored. I tried out the patch, since I want to add support for this feature to the Rust language code. I have two comments. First, I think it would be valuable to descend into embedded structures. This would make it simpler to review layout of the entire outer struct. That is, like this: struct t { int x; int y; }; struct s { int x; char c; struct t t; } s; The old pahole.py did do this, like (gdb) pahole struct s struct s { /* 0 4 */ int x /* 4 1 */ char c /* XXX 24 bit hole, try to pack */ /* 8 8 */ struct t { /* 0 4 */ int x /* 4 4 */ int y } t } ... though the old code's output is kind of confusing, restarting the offset at 0 in the embedded struct. Second, and similarly, descending into unions does seem to make sense sometimes (pahole.py didn't do this, but it seems like it should have). Continuing the above example, consider: union u { struct s s; int i; }; Here ptype shows: (gdb) ptype/o union u type = union u { struct s s; int i; } (The formatting looks weird without the layout info here...) I think one specific case where it is interesting to see the union's layout is when you want to know why a union field in a struct is as large as it is. That is, what is the largest member of the union, in case you want to try to shrink it? Tom ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH] Implement pahole-like 'ptype /o' option 2017-11-26 19:27 ` Tom Tromey @ 2017-11-27 19:54 ` Sergio Durigan Junior 2017-11-27 22:20 ` Tom Tromey 0 siblings, 1 reply; 75+ messages in thread From: Sergio Durigan Junior @ 2017-11-27 19:54 UTC (permalink / raw) To: Tom Tromey; +Cc: GDB Patches On Sunday, November 26 2017, Tom Tromey wrote: >>>>>> "Sergio" == Sergio Durigan Junior <sergiodj@redhat.com> writes: > > Sergio> This commit implements the pahole-like '/o' option for 'ptype', which > Sergio> prints the offsets and sizes of struct fields, reporting whenever > Sergio> there is a hole found. > > Thanks for doing this! Thanks for the insights :-). > Sergio> The idea is to print offsets and sizes only for structs, so unions and > Sergio> other types are mostly ignored. > > I tried out the patch, since I want to add support for this feature to > the Rust language code. I have two comments. > > First, I think it would be valuable to descend into embedded structures. > This would make it simpler to review layout of the entire outer struct. > That is, like this: > > struct t { > int x; > int y; > }; > > struct s { > int x; > char c; > struct t t; > } s; > > The old pahole.py did do this, like > > (gdb) pahole struct s > struct s { > /* 0 4 */ int x > /* 4 1 */ char c > /* XXX 24 bit hole, try to pack */ > /* 8 8 */ struct t { > /* 0 4 */ int x > /* 4 4 */ int y > } t > } I agree. In order to test the patch, I've even hacked GDB and made 'ptype' print more levels instead of just 1. However, due to the idiosincrasies of the formatting function + the fact that this feature would be better off as a second patch, I left it as TODO. I think for 'ptype /o' the best would be to stick with 2 levels, because otherwise the output gets too messed up. But I've been thinking about sending another patch to enable the multi-level ptype anyway. > ... though the old code's output is kind of confusing, restarting the > offset at 0 in the embedded struct. The way my code is written, it also restarts the offset at 0. Do you think it'd be better to keep using the offset from the outter struct? > Second, and similarly, descending into unions does seem to make sense > sometimes (pahole.py didn't do this, but it seems like it should have). > > Continuing the above example, consider: > > union u { > struct s s; > int i; > }; > > Here ptype shows: > > (gdb) ptype/o union u > type = union u { > struct s s; > int i; > } > > (The formatting looks weird without the layout info here...) > > I think one specific case where it is interesting to see the union's > layout is when you want to know why a union field in a struct is as > large as it is. That is, what is the largest member of the union, in > case you want to try to shrink it? Good point. I've implemented this on the patch. Thanks, -- Sergio GPG key ID: 237A 54B1 0287 28BF 00EF 31F4 D0EB 7628 65FC 5E36 Please send encrypted e-mail if possible http://sergiodj.net/ ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH] Implement pahole-like 'ptype /o' option 2017-11-27 19:54 ` Sergio Durigan Junior @ 2017-11-27 22:20 ` Tom Tromey 2017-11-28 0:39 ` Sergio Durigan Junior 0 siblings, 1 reply; 75+ messages in thread From: Tom Tromey @ 2017-11-27 22:20 UTC (permalink / raw) To: Sergio Durigan Junior; +Cc: Tom Tromey, GDB Patches >>>>> "Sergio" == Sergio Durigan Junior <sergiodj@redhat.com> writes: Sergio> I think for 'ptype /o' the best would be to stick with 2 levels, because Sergio> otherwise the output gets too messed up. But I've been thinking about Sergio> sending another patch to enable the multi-level ptype anyway. I see what you mean, though I wonder if it's better to just overflow and let users with deeply-nested types just widen their terminals. Alternatively the code could wrap_here() at strategic points. >> ... though the old code's output is kind of confusing, restarting the >> offset at 0 in the embedded struct. Sergio> The way my code is written, it also restarts the offset at 0. Do you Sergio> think it'd be better to keep using the offset from the outter struct? I am not 100% certain but I tend to think it's more understandable to have the offsets refer to the outermost structure. Tom ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH] Implement pahole-like 'ptype /o' option 2017-11-27 22:20 ` Tom Tromey @ 2017-11-28 0:39 ` Sergio Durigan Junior 0 siblings, 0 replies; 75+ messages in thread From: Sergio Durigan Junior @ 2017-11-28 0:39 UTC (permalink / raw) To: Tom Tromey; +Cc: GDB Patches On Monday, November 27 2017, Tom Tromey wrote: >>>>>> "Sergio" == Sergio Durigan Junior <sergiodj@redhat.com> writes: > > Sergio> I think for 'ptype /o' the best would be to stick with 2 levels, because > Sergio> otherwise the output gets too messed up. But I've been thinking about > Sergio> sending another patch to enable the multi-level ptype anyway. > > I see what you mean, though I wonder if it's better to just overflow and > let users with deeply-nested types just widen their terminals. OK, so your idea is to always "un-nest" structures/unions when doing 'ptype /o' (just like pahole.py does). That works for me, but I'd like to hear other opinions as well. > Alternatively the code could wrap_here() at strategic points. Yeah, I'll see if I can do that, but I don't know. >>> ... though the old code's output is kind of confusing, restarting the >>> offset at 0 in the embedded struct. > > Sergio> The way my code is written, it also restarts the offset at 0. Do you > Sergio> think it'd be better to keep using the offset from the outter struct? > > I am not 100% certain but I tend to think it's more understandable to > have the offsets refer to the outermost structure. OK, that can be arranged :-). Thanks, -- Sergio GPG key ID: 237A 54B1 0287 28BF 00EF 31F4 D0EB 7628 65FC 5E36 Please send encrypted e-mail if possible http://sergiodj.net/ ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v2] Implement pahole-like 'ptype /o' option 2017-11-21 16:07 [PATCH] Implement pahole-like 'ptype /o' option Sergio Durigan Junior ` (2 preceding siblings ...) 2017-11-26 19:27 ` Tom Tromey @ 2017-11-28 21:21 ` Sergio Durigan Junior 2017-11-29 3:28 ` Eli Zaretskii ` (2 more replies) 2017-12-11 19:58 ` [PATCH v3 0/2] Implement pahole-like 'ptype /o' option (and do some code reorg) Sergio Durigan Junior ` (4 subsequent siblings) 8 siblings, 3 replies; 75+ messages in thread From: Sergio Durigan Junior @ 2017-11-28 21:21 UTC (permalink / raw) To: GDB Patches; +Cc: Tom Tromey, Eli Zaretskii, Sergio Durigan Junior 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'. 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 ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH v2] Implement pahole-like 'ptype /o' option 2017-11-28 21:21 ` [PATCH v2] " Sergio Durigan Junior @ 2017-11-29 3:28 ` Eli Zaretskii 2017-12-04 15:03 ` Sergio Durigan Junior 2017-12-11 15:43 ` Simon Marchi 2 siblings, 0 replies; 75+ messages in thread From: Eli Zaretskii @ 2017-11-29 3:28 UTC (permalink / raw) To: Sergio Durigan Junior; +Cc: gdb-patches, tom > From: Sergio Durigan Junior <sergiodj@redhat.com> > Cc: Tom Tromey <tom@tromey.com>, > Eli Zaretskii <eliz@gnu.org>, > Sergio Durigan Junior <sergiodj@redhat.com> > Date: Tue, 28 Nov 2017 16:21:37 -0500 > > 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'. OK for the documentation parts. Thanks. ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH v2] Implement pahole-like 'ptype /o' option 2017-11-28 21:21 ` [PATCH v2] " Sergio Durigan Junior 2017-11-29 3:28 ` Eli Zaretskii @ 2017-12-04 15:03 ` Sergio Durigan Junior 2017-12-04 15:41 ` Eli Zaretskii 2017-12-08 21:32 ` Sergio Durigan Junior 2017-12-11 15:43 ` Simon Marchi 2 siblings, 2 replies; 75+ messages in thread From: Sergio Durigan Junior @ 2017-12-04 15:03 UTC (permalink / raw) To: GDB Patches; +Cc: Tom Tromey, Eli Zaretskii 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: > > <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/ ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH v2] Implement pahole-like 'ptype /o' option 2017-12-04 15:03 ` Sergio Durigan Junior @ 2017-12-04 15:41 ` Eli Zaretskii 2017-12-04 16:47 ` Sergio Durigan Junior 2017-12-08 21:32 ` Sergio Durigan Junior 1 sibling, 1 reply; 75+ messages in thread From: Eli Zaretskii @ 2017-12-04 15:41 UTC (permalink / raw) To: Sergio Durigan Junior; +Cc: gdb-patches, tom > From: Sergio Durigan Junior <sergiodj@redhat.com> > Cc: Tom Tromey <tom@tromey.com>, Eli Zaretskii <eliz@gnu.org> > Date: Mon, 04 Dec 2017 10:03:46 -0500 > > 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. I believe I already approved the documentation parts. Right? ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH v2] Implement pahole-like 'ptype /o' option 2017-12-04 15:41 ` Eli Zaretskii @ 2017-12-04 16:47 ` Sergio Durigan Junior 0 siblings, 0 replies; 75+ messages in thread From: Sergio Durigan Junior @ 2017-12-04 16:47 UTC (permalink / raw) To: Eli Zaretskii; +Cc: gdb-patches, tom On Monday, December 04 2017, Eli Zaretskii wrote: >> From: Sergio Durigan Junior <sergiodj@redhat.com> >> Cc: Tom Tromey <tom@tromey.com>, Eli Zaretskii <eliz@gnu.org> >> Date: Mon, 04 Dec 2017 10:03:46 -0500 >> >> 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. > > I believe I already approved the documentation parts. Right? Sorry, yes Eli, you have approved the docs already. Now I just need someone to look at the code ;-). Thanks, -- Sergio GPG key ID: 237A 54B1 0287 28BF 00EF 31F4 D0EB 7628 65FC 5E36 Please send encrypted e-mail if possible http://sergiodj.net/ ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH v2] Implement pahole-like 'ptype /o' option 2017-12-04 15:03 ` Sergio Durigan Junior 2017-12-04 15:41 ` Eli Zaretskii @ 2017-12-08 21:32 ` Sergio Durigan Junior 1 sibling, 0 replies; 75+ messages in thread From: Sergio Durigan Junior @ 2017-12-08 21:32 UTC (permalink / raw) To: GDB Patches; +Cc: Tom Tromey, Eli Zaretskii 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/ ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH v2] Implement pahole-like 'ptype /o' option 2017-11-28 21:21 ` [PATCH v2] " Sergio Durigan Junior 2017-11-29 3:28 ` Eli Zaretskii 2017-12-04 15:03 ` Sergio Durigan Junior @ 2017-12-11 15:43 ` Simon Marchi 2017-12-11 18:59 ` Sergio Durigan Junior 2 siblings, 1 reply; 75+ messages in thread From: Simon Marchi @ 2017-12-11 15:43 UTC (permalink / raw) To: Sergio Durigan Junior, GDB Patches; +Cc: Tom Tromey, Eli Zaretskii On 2017-11-28 04:21 PM, Sergio Durigan Junior 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'. > > > 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. Seems like you'll need to do a bit of conflict handling with the code from Record and output access specifiers for nested typedefs c191a6875b118fce30e7dc4d9e4bd20eff850270 :( > 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) Missing spaces here. > +{ > + 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); Is there a reason for introducing this new variable and doing the check_typedef before the if? > + > + 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); Moving this code to a new function is a good idea, but can you do it in its own preliminary patch? It is hard to review changes when code is moved and changed at the same time. > 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 You can reduce this to just standard_testfile ptype-offsets.cc or standard_testfile .cc It is harmless, but specifying .cc and ptype-offsets.cc will cause you to have two variables, srcfile and srcfile2, with the value "ptype-offsets.cc", and from what I see you don't use srcfile2. > + > +# 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" work -> works > + return 0 > +} > + > +if { [prepare_for_testing "failed to prepare" $testfile $srcfile \ > + { debug c++ optimize=-O0 }] } { optimize=-O0 seems unnecessary to me, I've never seen it specified explicitly in a test. > + 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" You could save some backslashes by using {} as string delimiters, if you prefer. Otherwise, maybe a new proc multi_line_string_to_regexp, which combines multi_line and string_to_regexp, would be nice. I mention this as a suggestion for future improvement, I wouldn't want to delay the merge of this patch just for that. > + > +# 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" You could throw a few more tests in there, for example a union with two structs, to verify that the offset does go back to 0 when showing the second struct: /* offset | size */ type = union my_union { /* 8 */ struct my_struct_1 { /* 0 | 4 */ int a; /* 4 | 4 */ int b; } /* total size: 8 bytes */ s1; /* 8 */ struct my_struct_2 { /* 0 | 4 */ int c; /* 4 | 4 */ int d; } /* total size: 8 bytes */ s2; } /* total size: 8 bytes */ I also noticed that the offset is not shown in front of the struct-in-union, as show above, but it is in the case of struct-in-struct: /* offset | size */ type = struct my_struct_3 { /* 0 | 8 */ struct my_struct_1 { /* 0 | 4 */ int a; /* 4 | 4 */ int b; } /* total size: 8 bytes */ s1; /* 8 | 8 */ struct my_struct_2 { /* 8 | 4 */ int c; /* 12 | 4 */ int d; } /* total size: 8 bytes */ s2; } /* total size: 16 bytes */ Is this difference on purpose? Finally, a test case on a non-struct/union type, to check that the header is not printed. > 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; > Thanks! Simon ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH v2] Implement pahole-like 'ptype /o' option 2017-12-11 15:43 ` Simon Marchi @ 2017-12-11 18:59 ` Sergio Durigan Junior 2017-12-11 20:45 ` Simon Marchi 0 siblings, 1 reply; 75+ messages in thread From: Sergio Durigan Junior @ 2017-12-11 18:59 UTC (permalink / raw) To: Simon Marchi; +Cc: GDB Patches, Tom Tromey, Eli Zaretskii Thanks for the review, Simon. Comments below. On Monday, December 11 2017, Simon Marchi wrote: > On 2017-11-28 04:21 PM, Sergio Durigan Junior 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'. >> >> >> 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. > > Seems like you'll need to do a bit of conflict handling with the code from > > Record and output access specifiers for nested typedefs > c191a6875b118fce30e7dc4d9e4bd20eff850270 > > :( Yeah, I'll take care of that right now. Sorry for now sending an updated version. >> 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) > > Missing spaces here. This is actually on purpose. If I indent the line, it will have more than 80 chars. I believe this is a well known method for avoiding this problem...? >> +{ >> + 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); > > Is there a reason for introducing this new variable and doing the check_typedef before the if? This is a leftover from a previous version. The patch now unwraps typedef's inside the c_print_type_union_field_offset and c_print_type_struct_field_offset functions. Thanks for the catch, I removed it. >> + >> + 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); > > Moving this code to a new function is a good idea, but can you do it in its own > preliminary patch? It is hard to review changes when code is moved and changed > at the same time. OK, I've splitted the patch as requested. >> 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 > > You can reduce this to just > > standard_testfile ptype-offsets.cc > > or > > standard_testfile .cc > > It is harmless, but specifying .cc and ptype-offsets.cc will cause you to have two > variables, srcfile and srcfile2, with the value "ptype-offsets.cc", and from what > I see you don't use srcfile2. Hm, you're right. Fixed. > >> + >> +# 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" > > work -> works Fixed. >> + return 0 >> +} >> + >> +if { [prepare_for_testing "failed to prepare" $testfile $srcfile \ >> + { debug c++ optimize=-O0 }] } { > > optimize=-O0 seems unnecessary to me, I've never seen it specified explicitly in a test. There are very few tests that use it (2, currently). I understand it's not very common to explicitly specify -O0, but I put it there because I decided to be on the safe side. If the compiler performs any optimization at all, it could mess with the layout of the structs. >> + 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" > > You could save some backslashes by using {} as string delimiters, if you prefer. > Otherwise, maybe a new proc multi_line_string_to_regexp, which combines multi_line > and string_to_regexp, would be nice. I mention this as a suggestion for future > improvement, I wouldn't want to delay the merge of this patch just for that. Thanks, I'll see about simplifying this. > >> + >> +# 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" > > You could throw a few more tests in there, for example a union with two structs, > to verify that the offset does go back to 0 when showing the second struct: > > /* offset | size */ > type = union my_union { > /* 8 */ struct my_struct_1 { > /* 0 | 4 */ int a; > /* 4 | 4 */ int b; > } /* total size: 8 bytes */ s1; > /* 8 */ struct my_struct_2 { > /* 0 | 4 */ int c; > /* 4 | 4 */ int d; > } /* total size: 8 bytes */ s2; > } /* total size: 8 bytes */ > Cool, I'll add a few more tests. > I also noticed that the offset is not shown in front of the struct-in-union, > as show above, but it is in the case of struct-in-struct: > > /* offset | size */ > type = struct my_struct_3 { > /* 0 | 8 */ struct my_struct_1 { > /* 0 | 4 */ int a; > /* 4 | 4 */ int b; > } /* total size: 8 bytes */ s1; > /* 8 | 8 */ struct my_struct_2 { > /* 8 | 4 */ int c; > /* 12 | 4 */ int d; > } /* total size: 8 bytes */ s2; > } /* total size: 16 bytes */ > > Is this difference on purpose? Yes; offsets are not shown for fields inside unions (not only structs, but all types of fields), because it doesn't make much sense: they'd be 0 every time. This is also inspired from pahole's output. > Finally, a test case on a non-struct/union type, to check that the header is not printed. Added. >> 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; >> > > Thanks! > > Simon -- Sergio GPG key ID: 237A 54B1 0287 28BF 00EF 31F4 D0EB 7628 65FC 5E36 Please send encrypted e-mail if possible http://sergiodj.net/ ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH v2] Implement pahole-like 'ptype /o' option 2017-12-11 18:59 ` Sergio Durigan Junior @ 2017-12-11 20:45 ` Simon Marchi 2017-12-11 21:07 ` Sergio Durigan Junior 0 siblings, 1 reply; 75+ messages in thread From: Simon Marchi @ 2017-12-11 20:45 UTC (permalink / raw) To: Sergio Durigan Junior; +Cc: GDB Patches, Tom Tromey, Eli Zaretskii >>> +/* 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) >> >> Missing spaces here. > > This is actually on purpose. If I indent the line, it will have more > than 80 chars. I believe this is a well known method for avoiding this > problem...? I am not aware of that. In this case I would put the parameter list on the next, I'm not sure if it's 100% GNU-style approved, but nobody complained when I did it so far :) static void print_spaces_filtered_with_print_options (int level, struct ui_file *stream, const struct type_print_options *flags); It helps with long function names. In this case, I would probably just drop the "struct" to save a few chars, because C++. >>> +if { [prepare_for_testing "failed to prepare" $testfile $srcfile \ >>> + { debug c++ optimize=-O0 }] } { >> >> optimize=-O0 seems unnecessary to me, I've never seen it specified explicitly in a test. > > There are very few tests that use it (2, currently). I understand it's > not very common to explicitly specify -O0, but I put it there because I > decided to be on the safe side. If the compiler performs any > optimization at all, it could mess with the layout of the structs. If GCC decided to optimize by default, so many things would break in the GDB testsuite. We would then probably make gdb_compile add -O0, so that we wouldn't need to do it in all tests. The point is that IMO, tests should expect no optimization by default. >> I also noticed that the offset is not shown in front of the struct-in-union, >> as show above, but it is in the case of struct-in-struct: >> >> /* offset | size */ >> type = struct my_struct_3 { >> /* 0 | 8 */ struct my_struct_1 { >> /* 0 | 4 */ int a; >> /* 4 | 4 */ int b; >> } /* total size: 8 bytes */ s1; >> /* 8 | 8 */ struct my_struct_2 { >> /* 8 | 4 */ int c; >> /* 12 | 4 */ int d; >> } /* total size: 8 bytes */ s2; >> } /* total size: 16 bytes */ >> >> Is this difference on purpose? > > Yes; offsets are not shown for fields inside unions (not only structs, > but all types of fields), because it doesn't make much sense: they'd be > 0 every time. This is also inspired from pahole's output. Not if that union is itself in a struct. For example with this: struct hello { int i; union { struct { int x, y; } a; struct { int x, y; } b; }; }; (gdb) ptype /o struct hello /* offset | size */ type = struct hello { /* 0 | 4 */ int i; /* 4 | 8 */ union { /* 8 */ struct { /* 4 | 4 */ int x; /* 8 | 4 */ int y; } /* total size: 8 bytes */ a; /* 8 */ struct { /* 4 | 4 */ int x; /* 8 | 4 */ int y; } /* total size: 8 bytes */ b; } /* total size: 8 bytes */; } /* total size: 12 bytes */ But I don't mind it, it just stuck out as a little inconsistency. I'll look at v3 now. Simon ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH v2] Implement pahole-like 'ptype /o' option 2017-12-11 20:45 ` Simon Marchi @ 2017-12-11 21:07 ` Sergio Durigan Junior 2017-12-11 22:42 ` Pedro Alves 0 siblings, 1 reply; 75+ messages in thread From: Sergio Durigan Junior @ 2017-12-11 21:07 UTC (permalink / raw) To: Simon Marchi; +Cc: GDB Patches, Tom Tromey, Eli Zaretskii On Monday, December 11 2017, Simon Marchi wrote: >>>> +/* 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) >>> >>> Missing spaces here. >> >> This is actually on purpose. If I indent the line, it will have more >> than 80 chars. I believe this is a well known method for avoiding this >> problem...? > > I am not aware of that. In this case I would put the parameter list on the next, > I'm not sure if it's 100% GNU-style approved, but nobody complained when I did it > so far :) > > static void > print_spaces_filtered_with_print_options > (int level, struct ui_file *stream, const struct type_print_options *flags); > > It helps with long function names. In this case, I would probably just drop the > "struct" to save a few chars, because C++. Fair enough. I use this trick for function prototypes, but not for the definitions. >>>> +if { [prepare_for_testing "failed to prepare" $testfile $srcfile \ >>>> + { debug c++ optimize=-O0 }] } { >>> >>> optimize=-O0 seems unnecessary to me, I've never seen it specified explicitly in a test. >> >> There are very few tests that use it (2, currently). I understand it's >> not very common to explicitly specify -O0, but I put it there because I >> decided to be on the safe side. If the compiler performs any >> optimization at all, it could mess with the layout of the structs. > > If GCC decided to optimize by default, so many things would break in the GDB testsuite. > We would then probably make gdb_compile add -O0, so that we wouldn't need to do it in > all tests. The point is that IMO, tests should expect no optimization by default. OK. >>> I also noticed that the offset is not shown in front of the struct-in-union, >>> as show above, but it is in the case of struct-in-struct: >>> >>> /* offset | size */ >>> type = struct my_struct_3 { >>> /* 0 | 8 */ struct my_struct_1 { >>> /* 0 | 4 */ int a; >>> /* 4 | 4 */ int b; >>> } /* total size: 8 bytes */ s1; >>> /* 8 | 8 */ struct my_struct_2 { >>> /* 8 | 4 */ int c; >>> /* 12 | 4 */ int d; >>> } /* total size: 8 bytes */ s2; >>> } /* total size: 16 bytes */ >>> >>> Is this difference on purpose? >> >> Yes; offsets are not shown for fields inside unions (not only structs, >> but all types of fields), because it doesn't make much sense: they'd be >> 0 every time. This is also inspired from pahole's output. > > Not if that union is itself in a struct. For example with this: > > struct hello > { > int i; > union { > struct { > int x, y; > } a; > struct { > int x, y; > } b; > }; > }; > > (gdb) ptype /o struct hello > /* offset | size */ > type = struct hello { > /* 0 | 4 */ int i; > /* 4 | 8 */ union { > /* 8 */ struct { > /* 4 | 4 */ int x; > /* 8 | 4 */ int y; > } /* total size: 8 bytes */ a; > /* 8 */ struct { > /* 4 | 4 */ int x; > /* 8 | 4 */ int y; > } /* total size: 8 bytes */ b; > } /* total size: 8 bytes */; > } /* total size: 12 bytes */ > > > But I don't mind it, it just stuck out as a little inconsistency. I don't see the inconsistency. If a field is inside a struct, it has its offset *and* size printed. No matter if the field is an int, another struct, or an union. If a field is inside an union, it has only its size printed. In the case above, it makes sense to have the offsets printed for the fields inside the two structs (inside the union), because there might be holes to report (well, one can argue that it doesn't matter if there are holes or not in this case, because if the other struct is bigger then the union size will stay the same). However, it doesn't make sense to print the offsets for the two structs themselves, because they are members of the union. I hope it makes more sense now. Thanks, -- Sergio GPG key ID: 237A 54B1 0287 28BF 00EF 31F4 D0EB 7628 65FC 5E36 Please send encrypted e-mail if possible http://sergiodj.net/ ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH v2] Implement pahole-like 'ptype /o' option 2017-12-11 21:07 ` Sergio Durigan Junior @ 2017-12-11 22:42 ` Pedro Alves 2017-12-11 22:50 ` Sergio Durigan Junior 0 siblings, 1 reply; 75+ messages in thread From: Pedro Alves @ 2017-12-11 22:42 UTC (permalink / raw) To: Sergio Durigan Junior, Simon Marchi Cc: GDB Patches, Tom Tromey, Eli Zaretskii On 12/11/2017 09:07 PM, Sergio Durigan Junior wrote: >>> This is actually on purpose. If I indent the line, it will have more >>> than 80 chars. I believe this is a well known method for avoiding this >>> problem...? >> >> I am not aware of that. In this case I would put the parameter list on the next, >> I'm not sure if it's 100% GNU-style approved, but nobody complained when I did it >> so far :) >> >> static void >> print_spaces_filtered_with_print_options >> (int level, struct ui_file *stream, const struct type_print_options *flags); >> >> It helps with long function names. In this case, I would probably just drop the >> "struct" to save a few chars, because C++. > > Fair enough. I use this trick for function prototypes, but not for the > definitions. Simon's format is what I've been using for a long while too. >> But I don't mind it, it just stuck out as a little inconsistency. > > I don't see the inconsistency. > > If a field is inside a struct, it has its offset *and* size printed. No > matter if the field is an int, another struct, or an union. > > If a field is inside an union, it has only its size printed. > > In the case above, it makes sense to have the offsets printed for the > fields inside the two structs (inside the union), because there might be > holes to report (well, one can argue that it doesn't matter if there are > holes or not in this case, because if the other struct is bigger then > the union size will stay the same). However, it doesn't make sense to > print the offsets for the two structs themselves, because they are > members of the union. > > I hope it makes more sense now. But why do we need the special case? Does it help anything? So far, it seems it only added confusion. The option is "/o" for "print offsets". Why not print offsets always? BTW, shouldn't the documentation in the manual include an example of GDB's output? Thanks, Pedro Alves ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH v2] Implement pahole-like 'ptype /o' option 2017-12-11 22:42 ` Pedro Alves @ 2017-12-11 22:50 ` Sergio Durigan Junior 2017-12-11 23:46 ` Pedro Alves 0 siblings, 1 reply; 75+ messages in thread From: Sergio Durigan Junior @ 2017-12-11 22:50 UTC (permalink / raw) To: Pedro Alves; +Cc: Simon Marchi, GDB Patches, Tom Tromey, Eli Zaretskii On Monday, December 11 2017, Pedro Alves wrote: > On 12/11/2017 09:07 PM, Sergio Durigan Junior wrote: > >>>> This is actually on purpose. If I indent the line, it will have more >>>> than 80 chars. I believe this is a well known method for avoiding this >>>> problem...? >>> >>> I am not aware of that. In this case I would put the parameter list on the next, >>> I'm not sure if it's 100% GNU-style approved, but nobody complained when I did it >>> so far :) >>> >>> static void >>> print_spaces_filtered_with_print_options >>> (int level, struct ui_file *stream, const struct type_print_options *flags); >>> >>> It helps with long function names. In this case, I would probably just drop the >>> "struct" to save a few chars, because C++. >> >> Fair enough. I use this trick for function prototypes, but not for the >> definitions. > > Simon's format is what I've been using for a long while too. Well, I could post a few examples of the format I chose, but I'm pretty sure this would be worthless. As I said, I will change my code. >>> But I don't mind it, it just stuck out as a little inconsistency. >> >> I don't see the inconsistency. >> >> If a field is inside a struct, it has its offset *and* size printed. No >> matter if the field is an int, another struct, or an union. >> >> If a field is inside an union, it has only its size printed. >> >> In the case above, it makes sense to have the offsets printed for the >> fields inside the two structs (inside the union), because there might be >> holes to report (well, one can argue that it doesn't matter if there are >> holes or not in this case, because if the other struct is bigger then >> the union size will stay the same). However, it doesn't make sense to >> print the offsets for the two structs themselves, because they are >> members of the union. >> >> I hope it makes more sense now. > > But why do we need the special case? Does it help anything? > So far, it seems it only added confusion. What do you mean by "special case"? This is what pahole does, and as I've said a few times, the output of 'ptype /o' has been based on pahole's output. I don't consider this a special case; I consider it to be the natural thing to do, because offsets don't make much sense in unions. > The option is "/o" for "print offsets". Why not print offsets always? I hope I explained it above. > BTW, shouldn't the documentation in the manual include an example > of GDB's output? I can include an example, OK. -- Sergio GPG key ID: 237A 54B1 0287 28BF 00EF 31F4 D0EB 7628 65FC 5E36 Please send encrypted e-mail if possible http://sergiodj.net/ ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH v2] Implement pahole-like 'ptype /o' option 2017-12-11 22:50 ` Sergio Durigan Junior @ 2017-12-11 23:46 ` Pedro Alves 2017-12-12 0:25 ` Sergio Durigan Junior 0 siblings, 1 reply; 75+ messages in thread From: Pedro Alves @ 2017-12-11 23:46 UTC (permalink / raw) To: Sergio Durigan Junior Cc: Simon Marchi, GDB Patches, Tom Tromey, Eli Zaretskii On 12/11/2017 10:50 PM, Sergio Durigan Junior wrote: >>> Fair enough. I use this trick for function prototypes, but not for the >>> definitions. >> >> Simon's format is what I've been using for a long while too. > > Well, I could post a few examples of the format I chose, but I'm pretty > sure this would be worthless. As I said, I will change my code. > By stating the format that I've been using too, my intention is to show that Simon's format isn't his own unique snowflake either. If there are many examples of the format you chose, I've not seem many. Maybe it depends on area of code one is touching, or IOW, on the preferences of who wrote specific areas of the codebase. IMO, the right way to go about this is to decide on a format, document it in the wiki and then we can point everyone at it with a URL. (ISTR that GCC's coding conventions documents yet another way to break the line, and in that case, not even GCC follows it.) IMO, the format you've chosen isn't ideal because it requires manual right-alignment for every line, while breaking before the parens makes emacs's TAB automatically align the following lines. The latter also gives a lot more space for the params again; it's sort of a "well, I have to do something, so might as well start way back from the left again). >>>> But I don't mind it, it just stuck out as a little inconsistency. >>> >>> I don't see the inconsistency. >>> >>> If a field is inside a struct, it has its offset *and* size printed. No >>> matter if the field is an int, another struct, or an union. >>> >>> If a field is inside an union, it has only its size printed. >>> >>> In the case above, it makes sense to have the offsets printed for the >>> fields inside the two structs (inside the union), because there might be >>> holes to report (well, one can argue that it doesn't matter if there are >>> holes or not in this case, because if the other struct is bigger then >>> the union size will stay the same). However, it doesn't make sense to >>> print the offsets for the two structs themselves, because they are >>> members of the union. >>> >>> I hope it makes more sense now. >> >> But why do we need the special case? Does it help anything? >> So far, it seems it only added confusion. > > What do you mean by "special case"? Special case: "If a field is inside a union, it has only its size printed; otherwise print the offset and size." No special case: "Always print the offset and size." > > This is what pahole does, and as I've said a few times, the output of > 'ptype /o' has been based on pahole's output. That's abundantly clear, but I don't see why we need to follow "pahole"'s output religiously... > I don't consider this a > special case; I consider it to be the natural thing to do, because > offsets don't make much sense in unions. Of course they do. You can do 'offsetof(foo_union, foo_field)' just fine, for example. Saying that the offsets happen to be the same is not the same as saying that the offsets don't exist. I asked: >> Does it help anything? >> So far, it seems it only added confusion. I think a much better rationale for omitting the offsets would be: Not printing the offsets in the case of union members helps by increasing signal/noise ratio. Why? With patch as is: /* 0 | 32 */ struct general_symbol_info { /* 0 | 8 */ const char *name; /* 8 | 8 */ union { /* 8 */ LONGEST ivalue; /* 8 */ const block *block; /* 8 */ const gdb_byte *bytes; /* 8 */ CORE_ADDR address; /* 8 */ const common_block *common_block; /* 8 */ symbol *chain; } /* total size: 8 bytes */ value; vs always-print-offset: /* 0 | 32 */ struct general_symbol_info { /* 0 | 8 */ const char *name; /* 8 | 8 */ union { /* 8 8 */ LONGEST ivalue; /* 8 8 */ const block *block; /* 8 8 */ const gdb_byte *bytes; /* 8 8 */ CORE_ADDR address; /* 8 8 */ const common_block *common_block; /* 8 8 */ symbol *chain; } /* total size: 8 bytes */ value; Note that it can be argued that the version that does _not_ print the offsets includes _more_ information, or less noise, because with that version it's much easier to not get distracted with the offsets of the union fields, which can do nothing about. So from that angle, I see value in not printing the offsets of union members. (Also note that the assertion that offsets of union members would be 0 every time is inaccurate. When printing a union that is itself a field of a struct, and the union is at offset > 0 in the containing struct, the offset to print would be offset > 0.) > >> The option is "/o" for "print offsets". Why not print offsets always? > > I hope I explained it above. > >> BTW, shouldn't the documentation in the manual include an example >> of GDB's output? > > I can include an example, OK. Thanks, Pedro Alves ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH v2] Implement pahole-like 'ptype /o' option 2017-12-11 23:46 ` Pedro Alves @ 2017-12-12 0:25 ` Sergio Durigan Junior 2017-12-12 0:52 ` Pedro Alves 0 siblings, 1 reply; 75+ messages in thread From: Sergio Durigan Junior @ 2017-12-12 0:25 UTC (permalink / raw) To: Pedro Alves; +Cc: Simon Marchi, GDB Patches, Tom Tromey, Eli Zaretskii On Monday, December 11 2017, Pedro Alves wrote: > On 12/11/2017 10:50 PM, Sergio Durigan Junior wrote: > >>>> Fair enough. I use this trick for function prototypes, but not for the >>>> definitions. >>> >>> Simon's format is what I've been using for a long while too. >> >> Well, I could post a few examples of the format I chose, but I'm pretty >> sure this would be worthless. As I said, I will change my code. >> > > By stating the format that I've been using too, my intention > is to show that Simon's format isn't his own unique snowflake > either. I honestly never thought he was the only one doing this. I certainly remember having seen this format choice in other places as well. > If there are many examples of the format you chose, I've not seem > many. Maybe it depends on area of code one is touching, or IOW, on > the preferences of who wrote specific areas of the codebase. I remember seeing some examples of the format I chose. > IMO, the right way to go about this is to decide on a format, > document it in the wiki and then we can point everyone at it with a URL. > (ISTR that GCC's coding conventions documents yet another > way to break the line, and in that case, not even GCC follows it.) > > IMO, the format you've chosen isn't ideal because it requires > manual right-alignment for every line, while breaking before the > parens makes emacs's TAB automatically align the following lines. > The latter also gives a lot more space for the params again; it's > sort of a "well, I have to do something, so might as well start > way back from the left again). No, it's not ideal at all. But at least on my Emacs, the other format has the downside of being always realigned to the column zero when I press TAB on it. But again, I'm not saying this is Emacs fault nor your nor Simon's fault; this is just something I noticed. >>>>> But I don't mind it, it just stuck out as a little inconsistency. >>>> >>>> I don't see the inconsistency. >>>> >>>> If a field is inside a struct, it has its offset *and* size printed. No >>>> matter if the field is an int, another struct, or an union. >>>> >>>> If a field is inside an union, it has only its size printed. >>>> >>>> In the case above, it makes sense to have the offsets printed for the >>>> fields inside the two structs (inside the union), because there might be >>>> holes to report (well, one can argue that it doesn't matter if there are >>>> holes or not in this case, because if the other struct is bigger then >>>> the union size will stay the same). However, it doesn't make sense to >>>> print the offsets for the two structs themselves, because they are >>>> members of the union. >>>> >>>> I hope it makes more sense now. >>> >>> But why do we need the special case? Does it help anything? >>> So far, it seems it only added confusion. >> >> What do you mean by "special case"? > > Special case: > > "If a field is inside a union, it has only its size printed; otherwise > print the offset and size." > > No special case: > > "Always print the offset and size." I don't like the expression "special case" because it diminishes the difference that exist between structs and unions. It is not like I went out of my way to treat this difference and made the code complex; it is also not like the output is extremely complex with it. >> >> This is what pahole does, and as I've said a few times, the output of >> 'ptype /o' has been based on pahole's output. > > That's abundantly clear, but I don't see why we need to follow > "pahole"'s output religiously... Sure, but I don't see why we should deviate from the de facto convention in this specific case. >> I don't consider this a >> special case; I consider it to be the natural thing to do, because >> offsets don't make much sense in unions. > > Of course they do. You can do 'offsetof(foo_union, foo_field)' just > fine, for example. Saying that the offsets happen to be the same > is not the same as saying that the offsets don't exist. I don't remember saying offsets don't exist in unions. What I said is that in this specific case they don't matter/make much sense to be printed. > I asked: > >>> Does it help anything? >>> So far, it seems it only added confusion. > > I think a much better rationale for omitting the offsets > would be: > > Not printing the offsets in the case of union members helps > by increasing signal/noise ratio. I think that's a matter of opinion. I didn't think about reducing the signal/noise ratio; I explicitly thought that printing union offsets was not useful. > Why? > > With patch as is: > > /* 0 | 32 */ struct general_symbol_info { > /* 0 | 8 */ const char *name; > /* 8 | 8 */ union { > /* 8 */ LONGEST ivalue; > /* 8 */ const block *block; > /* 8 */ const gdb_byte *bytes; > /* 8 */ CORE_ADDR address; > /* 8 */ const common_block *common_block; > /* 8 */ symbol *chain; > } /* total size: 8 bytes */ value; > > vs always-print-offset: > > /* 0 | 32 */ struct general_symbol_info { > /* 0 | 8 */ const char *name; > /* 8 | 8 */ union { > /* 8 8 */ LONGEST ivalue; > /* 8 8 */ const block *block; > /* 8 8 */ const gdb_byte *bytes; > /* 8 8 */ CORE_ADDR address; > /* 8 8 */ const common_block *common_block; > /* 8 8 */ symbol *chain; > } /* total size: 8 bytes */ value; > > Note that it can be argued that the version that does _not_ print > the offsets includes _more_ information, or less noise, because > with that version it's much easier to not get distracted with > the offsets of the union fields, which can do nothing about. > > So from that angle, I see value in not printing the offsets > of union members. Since it's still not clear whether the offsets should be printed or not in this case, and I am not a global maintainer, I adjusted the code to print them and will post the patch as a reply to the v4 e-mail. This way you can decide which version is best. Thanks, -- Sergio GPG key ID: 237A 54B1 0287 28BF 00EF 31F4 D0EB 7628 65FC 5E36 Please send encrypted e-mail if possible http://sergiodj.net/ ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH v2] Implement pahole-like 'ptype /o' option 2017-12-12 0:25 ` Sergio Durigan Junior @ 2017-12-12 0:52 ` Pedro Alves 2017-12-12 1:25 ` Simon Marchi 0 siblings, 1 reply; 75+ messages in thread From: Pedro Alves @ 2017-12-12 0:52 UTC (permalink / raw) To: Sergio Durigan Junior Cc: Simon Marchi, GDB Patches, Tom Tromey, Eli Zaretskii On 12/12/2017 12:25 AM, Sergio Durigan Junior wrote: > On Monday, December 11 2017, Pedro Alves wrote: >> IMO, the right way to go about this is to decide on a format, >> document it in the wiki and then we can point everyone at it with a URL. >> (ISTR that GCC's coding conventions documents yet another >> way to break the line, and in that case, not even GCC follows it.) >> >> IMO, the format you've chosen isn't ideal because it requires >> manual right-alignment for every line, while breaking before the >> parens makes emacs's TAB automatically align the following lines. >> The latter also gives a lot more space for the params again; it's >> sort of a "well, I have to do something, so might as well start >> way back from the left again). > > No, it's not ideal at all. But at least on my Emacs, the other format > has the downside of being always realigned to the column zero when I > press TAB on it. But again, I'm not saying this is Emacs fault nor your > nor Simon's fault; this is just something I noticed. Seems easier to fix (just two spaces, and only on one line) than the other approach, which depends a varying number of space/delete strokes. Maybe there's a way to configure emacs not to do that, even? > >>>>>> But I don't mind it, it just stuck out as a little inconsistency. >>>>> >>>>> I don't see the inconsistency. >>>>> >>>>> If a field is inside a struct, it has its offset *and* size printed. No >>>>> matter if the field is an int, another struct, or an union. >>>>> >>>>> If a field is inside an union, it has only its size printed. >>>>> >>>>> In the case above, it makes sense to have the offsets printed for the >>>>> fields inside the two structs (inside the union), because there might be >>>>> holes to report (well, one can argue that it doesn't matter if there are >>>>> holes or not in this case, because if the other struct is bigger then >>>>> the union size will stay the same). However, it doesn't make sense to >>>>> print the offsets for the two structs themselves, because they are >>>>> members of the union. >>>>> >>>>> I hope it makes more sense now. >>>> >>>> But why do we need the special case? Does it help anything? >>>> So far, it seems it only added confusion. >>> >>> What do you mean by "special case"? >> >> Special case: >> >> "If a field is inside a union, it has only its size printed; otherwise >> print the offset and size." >> >> No special case: >> >> "Always print the offset and size." > > I don't like the expression "special case" because it diminishes the > difference that exist between structs and unions. I don't follow, but OK... > It is not like I went > out of my way to treat this difference and made the code complex; it is > also not like the output is extremely complex with it. The point isn't about the implementation complexity, it's about user expectations. Simon was seemingly surprised by "an inconsistency" (i.e., a case is not consistent with the others; i.e., there's special/different case), so I think it's valid to discuss a bit and maybe double check the rationale and see if we can save users from being confused too. If after chatting a bit we come to the conclusion skipping the offsets makes sense, than that's fine. No harm done. >>> I don't consider this a >>> special case; I consider it to be the natural thing to do, because >>> offsets don't make much sense in unions. >> >> Of course they do. You can do 'offsetof(foo_union, foo_field)' just >> fine, for example. Saying that the offsets happen to be the same >> is not the same as saying that the offsets don't exist. > > I don't remember saying offsets don't exist in unions. What I said is > that in this specific case they don't matter/make much sense to be > printed. Guess we're discussing semantics, which is kind of pointless... "Don't make sense" to me is like talking about what's the "weight of a mile", which is truly meaningless. Stating that all union members live at offset 0 is not meaningless, because that's exactly how you define a union! >> So from that angle, I see value in not printing the offsets >> of union members. > > Since it's still not clear whether the offsets should be printed or not > in this case, and I am not a global maintainer, I adjusted the code to > print them and will post the patch as a reply to the v4 e-mail. This > way you can decide which version is best. Fun, just when I agreed with not printing the offsets... :-P :-) Thanks, Pedro Alves ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH v2] Implement pahole-like 'ptype /o' option 2017-12-12 0:52 ` Pedro Alves @ 2017-12-12 1:25 ` Simon Marchi 2017-12-12 15:50 ` John Baldwin 0 siblings, 1 reply; 75+ messages in thread From: Simon Marchi @ 2017-12-12 1:25 UTC (permalink / raw) To: Pedro Alves Cc: Sergio Durigan Junior, Simon Marchi, GDB Patches, Tom Tromey, Eli Zaretskii On 2017-12-11 19:51, Pedro Alves wrote: >>> So from that angle, I see value in not printing the offsets >>> of union members. >> >> Since it's still not clear whether the offsets should be printed or >> not >> in this case, and I am not a global maintainer, I adjusted the code to >> print them and will post the patch as a reply to the v4 e-mail. This >> way you can decide which version is best. > > Fun, just when I agreed with not printing the offsets... :-P :-) Damn, sorry for starting you guys on that track! I just wanted to know if it was intentional or not, I am fine with either. Simon ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH v2] Implement pahole-like 'ptype /o' option 2017-12-12 1:25 ` Simon Marchi @ 2017-12-12 15:50 ` John Baldwin 2017-12-12 17:04 ` Sergio Durigan Junior 0 siblings, 1 reply; 75+ messages in thread From: John Baldwin @ 2017-12-12 15:50 UTC (permalink / raw) To: Simon Marchi, Pedro Alves Cc: Sergio Durigan Junior, Simon Marchi, GDB Patches, Tom Tromey, Eli Zaretskii On 12/11/17 8:25 PM, Simon Marchi wrote: > On 2017-12-11 19:51, Pedro Alves wrote: >>>> So from that angle, I see value in not printing the offsets >>>> of union members. >>> >>> Since it's still not clear whether the offsets should be printed or >>> not >>> in this case, and I am not a global maintainer, I adjusted the code to >>> print them and will post the patch as a reply to the v4 e-mail. This >>> way you can decide which version is best. >> >> Fun, just when I agreed with not printing the offsets... :-P :-) > > Damn, sorry for starting you guys on that track! I just wanted to know > if it was intentional or not, I am fine with either. The only reason I would find the offset usable is to know the offset of a structure member inside of a union. E.g.: struct foo { int x; union { struct { int y; int z; }; int a; }; }; I think it is useful to know the offset of 'foo.z' within the overall structure. -- John Baldwin ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH v2] Implement pahole-like 'ptype /o' option 2017-12-12 15:50 ` John Baldwin @ 2017-12-12 17:04 ` Sergio Durigan Junior 0 siblings, 0 replies; 75+ messages in thread From: Sergio Durigan Junior @ 2017-12-12 17:04 UTC (permalink / raw) To: John Baldwin Cc: Simon Marchi, Pedro Alves, Simon Marchi, GDB Patches, Tom Tromey, Eli Zaretskii On Tuesday, December 12 2017, John Baldwin wrote: > On 12/11/17 8:25 PM, Simon Marchi wrote: >> On 2017-12-11 19:51, Pedro Alves wrote: >>>>> So from that angle, I see value in not printing the offsets >>>>> of union members. >>>> >>>> Since it's still not clear whether the offsets should be printed or >>>> not >>>> in this case, and I am not a global maintainer, I adjusted the code to >>>> print them and will post the patch as a reply to the v4 e-mail. This >>>> way you can decide which version is best. >>> >>> Fun, just when I agreed with not printing the offsets... :-P :-) >> >> Damn, sorry for starting you guys on that track! I just wanted to know >> if it was intentional or not, I am fine with either. > > The only reason I would find the offset usable is to know the offset of > a structure member inside of a union. E.g.: > > struct foo { > int x; > union { > struct { > int y; > int z; > }; > int a; > }; > }; > > I think it is useful to know the offset of 'foo.z' within the overall > structure. You'll have that: (gdb) ptype /o struct foo /* offset | size */ struct foo { /* 0 | 4 */ int x; /* 4 | 8 */ union { /* 8 */ struct { /* 4 | 4 */ int y; /* 8 | 4 */ int z; } /* total size: 8 bytes */; /* 4 */ int a; } /* total size: 8 bytes */; } /* total size: 12 bytes */ -- Sergio GPG key ID: 237A 54B1 0287 28BF 00EF 31F4 D0EB 7628 65FC 5E36 Please send encrypted e-mail if possible http://sergiodj.net/ ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v3 0/2] Implement pahole-like 'ptype /o' option (and do some code reorg) 2017-11-21 16:07 [PATCH] Implement pahole-like 'ptype /o' option Sergio Durigan Junior ` (3 preceding siblings ...) 2017-11-28 21:21 ` [PATCH v2] " Sergio Durigan Junior @ 2017-12-11 19:58 ` Sergio Durigan Junior 2017-12-11 19:58 ` [PATCH v3 1/2] Reorganize code to handle TYPE_CODE_{STRUCT,UNION} on 'c_type_print_base' Sergio Durigan Junior 2017-12-11 19:58 ` [PATCH v3 2/2] Implement pahole-like 'ptype /o' option Sergio Durigan Junior 2017-12-11 23:43 ` [PATCH v4 0/2] Implement pahole-like 'ptype /o' option (and do some code reorg) Sergio Durigan Junior ` (3 subsequent siblings) 8 siblings, 2 replies; 75+ messages in thread From: Sergio Durigan Junior @ 2017-12-11 19:58 UTC (permalink / raw) To: GDB Patches; +Cc: Tom Tromey, Eli Zaretskii, Simon Marchi Changes from v2: - Split the patch between #1 (code reorganization) and #2 (actual implementation of 'ptype /o'). - Add a few more tests; fix a few thinkos in the testcase. - Remove leftover code. Hi, This is the third version of the patch, which is now a series. Functionality hasn't changed; this is mostly a reorganization of the patch into two patches (one for code movement, another for the actual implementation of 'ptype /o'). Each patch contains an explanation of what it does. The docs have already been approved by Eli. ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v3 1/2] Reorganize code to handle TYPE_CODE_{STRUCT,UNION} on 'c_type_print_base' 2017-12-11 19:58 ` [PATCH v3 0/2] Implement pahole-like 'ptype /o' option (and do some code reorg) Sergio Durigan Junior @ 2017-12-11 19:58 ` Sergio Durigan Junior 2017-12-11 20:55 ` Simon Marchi 2017-12-11 19:58 ` [PATCH v3 2/2] Implement pahole-like 'ptype /o' option Sergio Durigan Junior 1 sibling, 1 reply; 75+ messages in thread From: Sergio Durigan Junior @ 2017-12-11 19:58 UTC (permalink / raw) To: GDB Patches Cc: Tom Tromey, Eli Zaretskii, Simon Marchi, Sergio Durigan Junior While doing the 'ptype /o' work, I noticed that 'c_type_print_base' was very long, with a big amount of code just to handle the case of TYPE_CODE_{STRUCT,UNION}. This made working with the function a bit difficult, specially because of the level of indentation. This commit moves this part of the code to their own functions. Now we have a 'c_type_print_base_struct_union' with most of the code, and also 'need_access_label_p', which is a subset of the code that was also a good candidate for having its own function. gdb/ChangeLog: yyyy-mm-dd Sergio Durigan Junior <sergiodj@redhat.com> * c-typeprint.c (need_access_label_p): New function. (c_type_print_base_struct_union): New function. (c_type_print_base): Move code to handle TYPE_CODE_{STRUCT,UNION} to the functions mentioned above. --- gdb/c-typeprint.c | 889 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 455 insertions(+), 434 deletions(-) diff --git a/gdb/c-typeprint.c b/gdb/c-typeprint.c index f3c3e7d706..46c814fae8 100644 --- a/gdb/c-typeprint.c +++ b/gdb/c-typeprint.c @@ -875,6 +875,458 @@ output_access_specifier (struct ui_file *stream, return last_access; } +/* 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; + 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. */ + + 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); + vptr_fieldno = get_vptr_fieldno (type, &basetype); + 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)); + } + + 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; + 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)); + + 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 out nested types. */ + if (TYPE_NESTED_TYPES_COUNT (type) != 0 + && semi_local_flags.print_nested_type_limit != 0) + { + if (semi_local_flags.print_nested_type_limit > 0) + --semi_local_flags.print_nested_type_limit; + + if (TYPE_NFIELDS (type) != 0 || TYPE_NFN_FIELDS (type) != 0) + fprintf_filtered (stream, "\n"); + + for (int i = 0; i < TYPE_NESTED_TYPES_COUNT (type); ++i) + { + print_spaces_filtered (level + 4, stream); + c_print_type (TYPE_NESTED_TYPES_FIELD_TYPE (type, i), + "", stream, show, level + 4, &semi_local_flags); + 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 + || TYPE_NESTED_TYPES_COUNT (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)); + } + 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); +} + /* 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 +1350,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; @@ -958,436 +1408,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 out nested types. */ - if (TYPE_NESTED_TYPES_COUNT (type) != 0 - && semi_local_flags.print_nested_type_limit != 0) - { - if (semi_local_flags.print_nested_type_limit > 0) - --semi_local_flags.print_nested_type_limit; - - if (TYPE_NFIELDS (type) != 0 || TYPE_NFN_FIELDS (type) != 0) - fprintf_filtered (stream, "\n"); - - for (i = 0; i < TYPE_NESTED_TYPES_COUNT (type); ++i) - { - print_spaces_filtered (level + 4, stream); - c_print_type (TYPE_NESTED_TYPES_FIELD_TYPE (type, i), - "", stream, show, level + 4, &semi_local_flags); - 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 - || TYPE_NESTED_TYPES_COUNT (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: -- 2.14.3 ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH v3 1/2] Reorganize code to handle TYPE_CODE_{STRUCT,UNION} on 'c_type_print_base' 2017-12-11 19:58 ` [PATCH v3 1/2] Reorganize code to handle TYPE_CODE_{STRUCT,UNION} on 'c_type_print_base' Sergio Durigan Junior @ 2017-12-11 20:55 ` Simon Marchi 2017-12-11 23:05 ` Sergio Durigan Junior 0 siblings, 1 reply; 75+ messages in thread From: Simon Marchi @ 2017-12-11 20:55 UTC (permalink / raw) To: Sergio Durigan Junior, GDB Patches; +Cc: Tom Tromey, Eli Zaretskii Hi Sergio, LGTM, with some nits. On 2017-12-11 02:57 PM, Sergio Durigan Junior wrote: > diff --git a/gdb/c-typeprint.c b/gdb/c-typeprint.c > index f3c3e7d706..46c814fae8 100644 > --- a/gdb/c-typeprint.c > +++ b/gdb/c-typeprint.c > @@ -875,6 +875,458 @@ output_access_specifier (struct ui_file *stream, > return last_access; > } > > +/* 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; It seems to me that you could return immediately when you detect that a label is needed. It would allow removing a bunch of if (!need_access_label). > @@ -898,10 +1350,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; j is unused. Thanks, Simon ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH v3 1/2] Reorganize code to handle TYPE_CODE_{STRUCT,UNION} on 'c_type_print_base' 2017-12-11 20:55 ` Simon Marchi @ 2017-12-11 23:05 ` Sergio Durigan Junior 0 siblings, 0 replies; 75+ messages in thread From: Sergio Durigan Junior @ 2017-12-11 23:05 UTC (permalink / raw) To: Simon Marchi; +Cc: GDB Patches, Tom Tromey, Eli Zaretskii On Monday, December 11 2017, Simon Marchi wrote: > Hi Sergio, > > LGTM, with some nits. Thanks for the review. > On 2017-12-11 02:57 PM, Sergio Durigan Junior wrote: >> diff --git a/gdb/c-typeprint.c b/gdb/c-typeprint.c >> index f3c3e7d706..46c814fae8 100644 >> --- a/gdb/c-typeprint.c >> +++ b/gdb/c-typeprint.c >> @@ -875,6 +875,458 @@ output_access_specifier (struct ui_file *stream, >> return last_access; >> } >> >> +/* 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; > > It seems to me that you could return immediately when you detect that a label is > needed. It would allow removing a bunch of if (!need_access_label). Done. >> @@ -898,10 +1350,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; > > j is unused. Removed. Thanks, -- Sergio GPG key ID: 237A 54B1 0287 28BF 00EF 31F4 D0EB 7628 65FC 5E36 Please send encrypted e-mail if possible http://sergiodj.net/ ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v3 2/2] Implement pahole-like 'ptype /o' option 2017-12-11 19:58 ` [PATCH v3 0/2] Implement pahole-like 'ptype /o' option (and do some code reorg) Sergio Durigan Junior 2017-12-11 19:58 ` [PATCH v3 1/2] Reorganize code to handle TYPE_CODE_{STRUCT,UNION} on 'c_type_print_base' Sergio Durigan Junior @ 2017-12-11 19:58 ` Sergio Durigan Junior 2017-12-11 21:50 ` Simon Marchi 1 sibling, 1 reply; 75+ messages in thread From: Sergio Durigan Junior @ 2017-12-11 19:58 UTC (permalink / raw) To: GDB Patches Cc: Tom Tromey, Eli Zaretskii, Simon Marchi, Sergio Durigan Junior 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: yyyy-mm-dd 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. (c_type_print_base_struct_union): Print offsets and sizes for struct/union/class fields. * 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: yyyy-mm-dd 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: yyyy-mm-dd Sergio Durigan Junior <sergiodj@redhat.com> PR cli/16224 * gdb.texinfo (ptype): Add new flag '/o'. --- gdb/NEWS | 3 + gdb/c-typeprint.c | 193 ++++++++++++++++++++++++++++--- gdb/doc/gdb.texinfo | 4 + gdb/testsuite/gdb.base/ptype-offsets.cc | 138 ++++++++++++++++++++++ gdb/testsuite/gdb.base/ptype-offsets.exp | 158 +++++++++++++++++++++++++ gdb/typeprint.c | 15 ++- gdb/typeprint.h | 9 ++ 7 files changed, 502 insertions(+), 18 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 c6fe297159..67d40d007b 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. + * 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. diff --git a/gdb/c-typeprint.c b/gdb/c-typeprint.c index 46c814fae8..aca5ec898d 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,86 @@ 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. */ @@ -1083,6 +1178,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; @@ -1099,18 +1196,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, @@ -1173,9 +1303,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)) @@ -1194,9 +1325,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)) @@ -1277,9 +1415,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"); } } @@ -1305,11 +1450,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. */ @@ -1317,13 +1468,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); } diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 062f01d5ff..7e85d92bf4 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -17211,6 +17211,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..52d2277ce7 --- /dev/null +++ b/gdb/testsuite/gdb.base/ptype-offsets.cc @@ -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; +} diff --git a/gdb/testsuite/gdb.base/ptype-offsets.exp b/gdb/testsuite/gdb.base/ptype-offsets.exp new file mode 100644 index 0000000000..fa1c0cb41c --- /dev/null +++ b/gdb/testsuite/gdb.base/ptype-offsets.exp @@ -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" diff --git a/gdb/typeprint.c b/gdb/typeprint.c index 9d9d6f5a49..eb829c2069 100644 --- a/gdb/typeprint.c +++ b/gdb/typeprint.c @@ -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, diff --git a/gdb/typeprint.h b/gdb/typeprint.h index a2058b0120..dd23c1eb6c 100644 --- a/gdb/typeprint.h +++ b/gdb/typeprint.h @@ -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; -- 2.14.3 ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH v3 2/2] Implement pahole-like 'ptype /o' option 2017-12-11 19:58 ` [PATCH v3 2/2] Implement pahole-like 'ptype /o' option Sergio Durigan Junior @ 2017-12-11 21:50 ` Simon Marchi 2017-12-11 23:24 ` Sergio Durigan Junior 0 siblings, 1 reply; 75+ messages in thread From: Simon Marchi @ 2017-12-11 21:50 UTC (permalink / raw) To: Sergio Durigan Junior, GDB Patches; +Cc: Tom Tromey, Eli Zaretskii Hi Sergio, LGTM, with some nits. On 2017-12-11 02:57 PM, Sergio Durigan Junior wrote: > @@ -867,14 +890,86 @@ 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 wording confuses me a bit. TYPE is not inside a struct; the struct contains fields, not types. And TYPE is the struct type IIUC, not the field type. So it should maybe be something like: 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. What does OFFSET_BITPOS do? > + > + 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) Why do you check for *endpos > 0? Did you see a case where *endpos is 0 and bitpos > 0? That would mean that there's a "hole" before the first field. Would we want to show it as a hole anyway? Simon ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH v3 2/2] Implement pahole-like 'ptype /o' option 2017-12-11 21:50 ` Simon Marchi @ 2017-12-11 23:24 ` Sergio Durigan Junior 2017-12-12 1:32 ` Simon Marchi 0 siblings, 1 reply; 75+ messages in thread From: Sergio Durigan Junior @ 2017-12-11 23:24 UTC (permalink / raw) To: Simon Marchi; +Cc: GDB Patches, Tom Tromey, Eli Zaretskii On Monday, December 11 2017, Simon Marchi wrote: > Hi Sergio, > > LGTM, with some nits. > > On 2017-12-11 02:57 PM, Sergio Durigan Junior wrote: >> @@ -867,14 +890,86 @@ 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 wording confuses me a bit. TYPE is not inside a struct; the struct > contains fields, not types. And TYPE is the struct type IIUC, not the > field type. So it should maybe be something like: > > 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. Thanks, I adopted your version. > What does OFFSET_BITPOS do? Ah, I forgot to include it in the comment. It is the offset value we carry over when we are printing an inner struct. This is needed because otherwise we'd print inner structs starting at position 0, which is something that Tom didn't like and suggested changing. >> + >> + 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) > > Why do you check for *endpos > 0? Did you see a case where *endpos is 0 > and bitpos > 0? That would mean that there's a "hole" before the first field. > Would we want to show it as a hole anyway? Yeah, this situation happens when we have a virtual method in a class. Because of the vtable, the first field of the struct will start at offset 8 (for 64-bit architectures), and in this case *endpos will be 0 because we won't have updated it, leading to a confusing message about a 8-byte hole in the beginning of the struct: ... 50 /* offset | size */ 51 type = struct abc { 52 public: 53 /* XXX 8-byte hole */ 54 /* 8 | 8 */ void *field1; ... In order to suppress this first message, I check for *endpos > 0. I will add a comment to the code explaining this scenario. -- Sergio GPG key ID: 237A 54B1 0287 28BF 00EF 31F4 D0EB 7628 65FC 5E36 Please send encrypted e-mail if possible http://sergiodj.net/ ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH v3 2/2] Implement pahole-like 'ptype /o' option 2017-12-11 23:24 ` Sergio Durigan Junior @ 2017-12-12 1:32 ` Simon Marchi 2017-12-12 6:19 ` Sergio Durigan Junior 0 siblings, 1 reply; 75+ messages in thread From: Simon Marchi @ 2017-12-12 1:32 UTC (permalink / raw) To: Sergio Durigan Junior Cc: Simon Marchi, GDB Patches, Tom Tromey, Eli Zaretskii On 2017-12-11 18:24, Sergio Durigan Junior wrote: >>> + >>> + 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) >> >> Why do you check for *endpos > 0? Did you see a case where *endpos is >> 0 >> and bitpos > 0? That would mean that there's a "hole" before the >> first field. >> Would we want to show it as a hole anyway? > > Yeah, this situation happens when we have a virtual method in a class. > Because of the vtable, the first field of the struct will start at > offset 8 (for 64-bit architectures), and in this case *endpos will be 0 > because we won't have updated it, leading to a confusing message about > a > 8-byte hole in the beginning of the struct: > > ... > 50 /* offset | size */ > 51 type = struct abc { > 52 public: > 53 /* XXX 8-byte hole */ > 54 /* 8 | 8 */ void *field1; > ... > > In order to suppress this first message, I check for *endpos > 0. > > I will add a comment to the code explaining this scenario. Ah ok that makes sense. Yeah, a comment would be nice. But now I'm thinking that it would be nice if GDB showed the vtable. If I say the first field at offset 8, it would probably take me some time to get why, and would maybe think it's a bug. But if we showed a fake field, such as: /* 0 | 8 */ /* vtable */ It would be immediately obvious. Simon ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH v3 2/2] Implement pahole-like 'ptype /o' option 2017-12-12 1:32 ` Simon Marchi @ 2017-12-12 6:19 ` Sergio Durigan Junior 2017-12-12 18:14 ` Pedro Alves 0 siblings, 1 reply; 75+ messages in thread From: Sergio Durigan Junior @ 2017-12-12 6:19 UTC (permalink / raw) To: Simon Marchi; +Cc: Simon Marchi, GDB Patches, Tom Tromey, Eli Zaretskii On Monday, December 11 2017, Simon Marchi wrote: > On 2017-12-11 18:24, Sergio Durigan Junior wrote: >>>> + >>>> + 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) >>> >>> Why do you check for *endpos > 0? Did you see a case where *endpos >>> is 0 >>> and bitpos > 0? That would mean that there's a "hole" before the >>> first field. >>> Would we want to show it as a hole anyway? >> >> Yeah, this situation happens when we have a virtual method in a class. >> Because of the vtable, the first field of the struct will start at >> offset 8 (for 64-bit architectures), and in this case *endpos will be 0 >> because we won't have updated it, leading to a confusing message >> about a >> 8-byte hole in the beginning of the struct: >> >> ... >> 50 /* offset | size */ >> 51 type = struct abc { >> 52 public: >> 53 /* XXX 8-byte hole */ >> 54 /* 8 | 8 */ void *field1; >> ... >> >> In order to suppress this first message, I check for *endpos > 0. >> >> I will add a comment to the code explaining this scenario. > > Ah ok that makes sense. Yeah, a comment would be nice. But now I'm > thinking that it would be nice if GDB showed the vtable. If I say the > first field at offset 8, it would probably take me some time to get > why, and would maybe think it's a bug. But if we showed a fake field, > such as: > > /* 0 | 8 */ /* vtable */ > > It would be immediately obvious. OK, I did that. BTW, a little status update. Apparently the patch can't handle bitfields very well. I've found a few cases where the bitfield handling gets confused, printing wrong offsets/sizes/holes. Bitfields can be extremely complex to deal with when it comes to offsets... I spent hours trying to improve the patch, managed to make some progress, but there are still corner cases to fix. For example, the patch doesn't deal well with this case: struct aa { /* 0 | 1 */ char aa; /* 1: 1 | 1 */ unsigned char a : 7; /* 1:15 | 4 */ int b : 10; } /* total size: 4 bytes */ In this case, the bitfield "b" would be combined with the previous bitfield "a", like pahole reports: struct aa { char aa; /* 0 1 */ unsigned char a:7; /* 1: 1 1 */ /* Bitfield combined with previous fields */ int b:10; /* 0: 7 4 */ } Confusing... I'm not sure why pahole reports b's offset to be 0. Also, the patch doesn't understand cases like this: struct tyu { unsigned int a1 : 1; unsigned int a2 : 3; unsigned int a9 : 23; /* PROBLEM HAPPENS HERE */ unsigned char a0 : 2; uint64_t a3; unsigned int a4 : 5; uint64_t a5 : 3; }; In this case, we're switching types in the middle of the bitfield. The bitfield "unsigned char a0" would be combined with the bitfields above it, but the patch doesn't know that: struct tyu { /* 0:31 | 4 */ unsigned int a1 : 1; /* 0:28 | 4 */ unsigned int a2 : 3; /* 0: 5 | 4 */ unsigned int a9 : 23; /* 3: 6 | 1 */ unsigned char a0 : 2; /* XXX 3-bit hole */ /* XXX 4-byte hole */ /* 8 | 8 */ uint64_t a3; /* 16:27 | 4 */ unsigned int a4 : 5; /* 16:56 | 8 */ uint64_t a5 : 3; } /* total size: 24 bytes */ Whereas pahole reports: struct tyu { unsigned int a1:1; /* 0:31 4 */ unsigned int a2:3; /* 0:28 4 */ unsigned int a9:23; /* 0: 5 4 */ /* XXX 253 bits hole, try to pack */ /* Bitfield combined with next fields */ unsigned char a0:2; /* 3: 3 1 */ /* XXX 6 bits hole, try to pack */ /* XXX 4 bytes hole, try to pack */ uint64_t a3; /* 8 8 */ unsigned int a4:5; /* 16:27 4 */ uint64_t a5:3; /* 16:56 8 */ }; Hm, TBH pahole itself seems a bit confused here, saying there is a 253-bit hole... Anyway, long story short, this is much more complex that I thought it would be (TM). I am extremely tired right now and can't continue, but I intend to resume work tomorrow morning. But I'd like to leave a few options on the table: 1) Remove the patch from the 8.1 wishlist, which will unblock the branching. 2) Remove the bitfield handling from the patch, leaving it feature-incomplete, but working. 3) Push the patch with these known limitations, document them, mark them as KFAIL in the testcase, and open a bug to fix them (I personally wouldn't like this). 4) Wait more time until these issues are resolved. WDYT? Thanks, -- Sergio GPG key ID: 237A 54B1 0287 28BF 00EF 31F4 D0EB 7628 65FC 5E36 Please send encrypted e-mail if possible http://sergiodj.net/ ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH v3 2/2] Implement pahole-like 'ptype /o' option 2017-12-12 6:19 ` Sergio Durigan Junior @ 2017-12-12 18:14 ` Pedro Alves 2017-12-12 18:40 ` Sergio Durigan Junior 0 siblings, 1 reply; 75+ messages in thread From: Pedro Alves @ 2017-12-12 18:14 UTC (permalink / raw) To: Sergio Durigan Junior, Simon Marchi Cc: Simon Marchi, GDB Patches, Tom Tromey, Eli Zaretskii On 12/12/2017 06:19 AM, Sergio Durigan Junior wrote: > BTW, a little status update. > > Apparently the patch can't handle bitfields very well. I've found a few > cases where the bitfield handling gets confused, printing wrong > offsets/sizes/holes. Bitfields can be extremely complex to deal with > when it comes to offsets... > > I spent hours trying to improve the patch, managed to make some > progress, but there are still corner cases to fix. For example, the > patch doesn't deal well with this case: > > struct aa { > /* 0 | 1 */ char aa; > /* 1: 1 | 1 */ unsigned char a : 7; > /* 1:15 | 4 */ int b : 10; > } /* total size: 4 bytes */ > > In this case, the bitfield "b" would be combined with the previous > bitfield "a", like pahole reports: > > struct aa { > char aa; /* 0 1 */ > unsigned char a:7; /* 1: 1 1 */ > > /* Bitfield combined with previous fields */ > > int b:10; /* 0: 7 4 */ > } > > Confusing... I'm not sure why pahole reports b's offset to be 0. 0 seems right to me. The bitfield's type is int, with size 4, and it lives at byte offset 0: <2><53>: Abbrev Number: 5 (DW_TAG_member) <54> DW_AT_name : (indirect string, offset: 0x4ae): b <58> DW_AT_decl_file : 1 <59> DW_AT_decl_line : 7 <5a> DW_AT_type : <0x71> <5e> DW_AT_byte_size : 4 <5f> DW_AT_bit_size : 10 <60> DW_AT_bit_offset : 7 <61> DW_AT_data_member_location: 0 <<< Replacing the 0 with 1, like: int b:10; /* 1: 7 4 */ would look incorrect to me, because that'd make '(char*)&aa + 1 + 4' (address of containing object + byte offset + byte size) overshoot the size of the containing object. What is that number after ":" in bitfields supposed to mean in pahole's output (and I assume that that's what you're trying to emulate)? We're missing documentation for that. It seems like it's supposed to mean the number of bits left in the containing anonymous object (i.e., in the 4 bytes of the declared int)? Then "0:7" seems right, given: sizeof (int) * 8 - bitsof(aa.a) - bitsof(aa.a) - bits(aa.b) => sizeof (int) * 8 - 7 - 10 => 7 It took me a while to get to this conclusion (and writing a lot of text that I ended up deleting... :-P), because I originally assumed that this was meant to be the field's bit offset. > > Also, the patch doesn't understand cases like this: > > struct tyu > { > unsigned int a1 : 1; > unsigned int a2 : 3; > unsigned int a9 : 23; > /* PROBLEM HAPPENS HERE */ > unsigned char a0 : 2; > uint64_t a3; > unsigned int a4 : 5; > uint64_t a5 : 3; > }; > > In this case, we're switching types in the middle of the bitfield. The > bitfield "unsigned char a0" would be combined with the bitfields above > it, but the patch doesn't know that: > > struct tyu { > /* 0:31 | 4 */ unsigned int a1 : 1; > /* 0:28 | 4 */ unsigned int a2 : 3; > /* 0: 5 | 4 */ unsigned int a9 : 23; > /* 3: 6 | 1 */ unsigned char a0 : 2; > /* XXX 3-bit hole */ > /* XXX 4-byte hole */ > /* 8 | 8 */ uint64_t a3; > /* 16:27 | 4 */ unsigned int a4 : 5; > /* 16:56 | 8 */ uint64_t a5 : 3; > } /* total size: 24 bytes */ > > Whereas pahole reports: > > struct tyu { > unsigned int a1:1; /* 0:31 4 */ > unsigned int a2:3; /* 0:28 4 */ > unsigned int a9:23; /* 0: 5 4 */ > > /* XXX 253 bits hole, try to pack */ > /* Bitfield combined with next fields */ > > unsigned char a0:2; /* 3: 3 1 */ > > /* XXX 6 bits hole, try to pack */ > /* XXX 4 bytes hole, try to pack */ > > uint64_t a3; /* 8 8 */ > unsigned int a4:5; /* 16:27 4 */ > uint64_t a5:3; /* 16:56 8 */ > }; > > Hm, TBH pahole itself seems a bit confused here, saying there is a > 253-bit hole... A 253-bit hole does look odd. > > > Anyway, long story short, this is much more complex that I thought it > would be (TM). I am extremely tired right now and can't continue, but I > intend to resume work tomorrow morning. But I'd like to leave a few > options on the table: > > 1) Remove the patch from the 8.1 wishlist, which will unblock the > branching. > > 2) Remove the bitfield handling from the patch, leaving it > feature-incomplete, but working. > > 3) Push the patch with these known limitations, document them, mark them > as KFAIL in the testcase, and open a bug to fix them (I personally > wouldn't like this). > > 4) Wait more time until these issues are resolved. > Joel said that we'd re-evaluate Wednesday, so there's still some time. Thanks, Pedro Alves ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH v3 2/2] Implement pahole-like 'ptype /o' option 2017-12-12 18:14 ` Pedro Alves @ 2017-12-12 18:40 ` Sergio Durigan Junior 2017-12-12 20:12 ` Pedro Alves 0 siblings, 1 reply; 75+ messages in thread From: Sergio Durigan Junior @ 2017-12-12 18:40 UTC (permalink / raw) To: Pedro Alves Cc: Simon Marchi, Simon Marchi, GDB Patches, Tom Tromey, Eli Zaretskii Thanks for looking into this. On Tuesday, December 12 2017, Pedro Alves wrote: > On 12/12/2017 06:19 AM, Sergio Durigan Junior wrote: > >> BTW, a little status update. >> >> Apparently the patch can't handle bitfields very well. I've found a few >> cases where the bitfield handling gets confused, printing wrong >> offsets/sizes/holes. Bitfields can be extremely complex to deal with >> when it comes to offsets... >> >> I spent hours trying to improve the patch, managed to make some >> progress, but there are still corner cases to fix. For example, the >> patch doesn't deal well with this case: >> >> struct aa { >> /* 0 | 1 */ char aa; >> /* 1: 1 | 1 */ unsigned char a : 7; >> /* 1:15 | 4 */ int b : 10; >> } /* total size: 4 bytes */ >> >> In this case, the bitfield "b" would be combined with the previous >> bitfield "a", like pahole reports: >> >> struct aa { >> char aa; /* 0 1 */ >> unsigned char a:7; /* 1: 1 1 */ >> >> /* Bitfield combined with previous fields */ >> >> int b:10; /* 0: 7 4 */ >> } >> >> Confusing... I'm not sure why pahole reports b's offset to be 0. > > 0 seems right to me. The bitfield's type is int, with size 4, > and it lives at byte offset 0: > > <2><53>: Abbrev Number: 5 (DW_TAG_member) > <54> DW_AT_name : (indirect string, offset: 0x4ae): b > <58> DW_AT_decl_file : 1 > <59> DW_AT_decl_line : 7 > <5a> DW_AT_type : <0x71> > <5e> DW_AT_byte_size : 4 > <5f> DW_AT_bit_size : 10 > <60> DW_AT_bit_offset : 7 > <61> DW_AT_data_member_location: 0 <<< > > Replacing the 0 with 1, like: > > int b:10; /* 1: 7 4 */ > > would look incorrect to me, because that'd make '(char*)&aa + 1 + 4' > (address of containing object + byte offset + byte size) overshoot the > size of the containing object. OK. The GDB patch is printing "1:15" because the offset is calculated as: (bitpos + offset_bitpos) / TARGET_CHAR_BIT In this particular case, offset_bitpos is zero because we're not inside an inner struct, so we don't care about it. bitpos is TYPE_FIELD_BITPOS (type, field_idx), which is 15 in this case, because sizeof (aa.aa) + bitsof (aa.a) = 15. When we divide it by TARGET_CHAR_BIT, we get 1. It seems using TYPE_FIELD_BITPOS is not reliable when dealing with bitfields. So there is something I'm not accounting here. > What is that number after ":" in bitfields supposed to mean in > pahole's output (and I assume that that's what you're trying > to emulate)? We're missing documentation for that. Yeah. I tried to find documentation on the output, but couldn't. Yesterday I was reading pahole's code. From what I understand, it is the number of bits left in the object. > It seems like it's supposed to mean the number of bits left in the > containing anonymous object (i.e., in the 4 bytes of the declared > int)? Then "0:7" seems right, given: > > sizeof (int) * 8 - bitsof(aa.a) - bitsof(aa.a) - bits(aa.b) > => sizeof (int) * 8 - 7 - 10 > => 7 I guess you meant: sizeof (int) * 8 - bitsof (aa.a) - bitsof(aa.b) Right? But yeah, it makes sense when you consider that the first bitfield got "promoted" from "unsigned char" to "int". I haven't figured out a way to keep track of these type changes in order to calculate the right offsets, remaining space and holes. > It took me a while to get to this conclusion (and writing a lot > of text that I ended up deleting... :-P), because I originally > assumed that this was meant to be the field's bit offset. > >> >> Also, the patch doesn't understand cases like this: >> >> struct tyu >> { >> unsigned int a1 : 1; >> unsigned int a2 : 3; >> unsigned int a9 : 23; >> /* PROBLEM HAPPENS HERE */ >> unsigned char a0 : 2; >> uint64_t a3; >> unsigned int a4 : 5; >> uint64_t a5 : 3; >> }; >> >> In this case, we're switching types in the middle of the bitfield. The >> bitfield "unsigned char a0" would be combined with the bitfields above >> it, but the patch doesn't know that: >> >> struct tyu { >> /* 0:31 | 4 */ unsigned int a1 : 1; >> /* 0:28 | 4 */ unsigned int a2 : 3; >> /* 0: 5 | 4 */ unsigned int a9 : 23; >> /* 3: 6 | 1 */ unsigned char a0 : 2; >> /* XXX 3-bit hole */ >> /* XXX 4-byte hole */ >> /* 8 | 8 */ uint64_t a3; >> /* 16:27 | 4 */ unsigned int a4 : 5; >> /* 16:56 | 8 */ uint64_t a5 : 3; >> } /* total size: 24 bytes */ >> >> Whereas pahole reports: >> >> struct tyu { >> unsigned int a1:1; /* 0:31 4 */ >> unsigned int a2:3; /* 0:28 4 */ >> unsigned int a9:23; /* 0: 5 4 */ >> >> /* XXX 253 bits hole, try to pack */ >> /* Bitfield combined with next fields */ >> >> unsigned char a0:2; /* 3: 3 1 */ >> >> /* XXX 6 bits hole, try to pack */ >> /* XXX 4 bytes hole, try to pack */ >> >> uint64_t a3; /* 8 8 */ >> unsigned int a4:5; /* 16:27 4 */ >> uint64_t a5:3; /* 16:56 8 */ >> }; >> >> Hm, TBH pahole itself seems a bit confused here, saying there is a >> 253-bit hole... > > A 253-bit hole does look odd. Yes. I haven't checked all the offsets too; there may be inconsistencies. >> >> >> Anyway, long story short, this is much more complex that I thought it >> would be (TM). I am extremely tired right now and can't continue, but I >> intend to resume work tomorrow morning. But I'd like to leave a few >> options on the table: >> >> 1) Remove the patch from the 8.1 wishlist, which will unblock the >> branching. >> >> 2) Remove the bitfield handling from the patch, leaving it >> feature-incomplete, but working. >> >> 3) Push the patch with these known limitations, document them, mark them >> as KFAIL in the testcase, and open a bug to fix them (I personally >> wouldn't like this). >> >> 4) Wait more time until these issues are resolved. >> > > Joel said that we'd re-evaluate Wednesday, so there's still some time. OK, I'll keep trying here. Thanks, -- Sergio GPG key ID: 237A 54B1 0287 28BF 00EF 31F4 D0EB 7628 65FC 5E36 Please send encrypted e-mail if possible http://sergiodj.net/ ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH v3 2/2] Implement pahole-like 'ptype /o' option 2017-12-12 18:40 ` Sergio Durigan Junior @ 2017-12-12 20:12 ` Pedro Alves 0 siblings, 0 replies; 75+ messages in thread From: Pedro Alves @ 2017-12-12 20:12 UTC (permalink / raw) To: Sergio Durigan Junior Cc: Simon Marchi, Simon Marchi, GDB Patches, Tom Tromey, Eli Zaretskii On 12/12/2017 06:40 PM, Sergio Durigan Junior wrote: > On Tuesday, December 12 2017, Pedro Alves wrote: > >> On 12/12/2017 06:19 AM, Sergio Durigan Junior wrote: >> >>> Apparently the patch can't handle bitfields very well. I've found a few >>> cases where the bitfield handling gets confused, printing wrong >>> offsets/sizes/holes. Bitfields can be extremely complex to deal with >>> when it comes to offsets... >>> >>> I spent hours trying to improve the patch, managed to make some >>> progress, but there are still corner cases to fix. For example, the >>> patch doesn't deal well with this case: >>> >>> struct aa { >>> /* 0 | 1 */ char aa; >>> /* 1: 1 | 1 */ unsigned char a : 7; >>> /* 1:15 | 4 */ int b : 10; >>> } /* total size: 4 bytes */ >>> >>> In this case, the bitfield "b" would be combined with the previous >>> bitfield "a", like pahole reports: >>> >>> struct aa { >>> char aa; /* 0 1 */ >>> unsigned char a:7; /* 1: 1 1 */ >>> >>> /* Bitfield combined with previous fields */ >>> >>> int b:10; /* 0: 7 4 */ >>> } >>> >>> Confusing... I'm not sure why pahole reports b's offset to be 0. >> >> 0 seems right to me. The bitfield's type is int, with size 4, >> and it lives at byte offset 0: >> >> <2><53>: Abbrev Number: 5 (DW_TAG_member) >> <54> DW_AT_name : (indirect string, offset: 0x4ae): b >> <58> DW_AT_decl_file : 1 >> <59> DW_AT_decl_line : 7 >> <5a> DW_AT_type : <0x71> >> <5e> DW_AT_byte_size : 4 >> <5f> DW_AT_bit_size : 10 >> <60> DW_AT_bit_offset : 7 >> <61> DW_AT_data_member_location: 0 <<< >> >> Replacing the 0 with 1, like: >> >> int b:10; /* 1: 7 4 */ >> >> would look incorrect to me, because that'd make '(char*)&aa + 1 + 4' >> (address of containing object + byte offset + byte size) overshoot the >> size of the containing object. > > OK. The GDB patch is printing "1:15" because the offset is calculated > as: > > (bitpos + offset_bitpos) / TARGET_CHAR_BIT > > In this particular case, offset_bitpos is zero because we're not inside > an inner struct, so we don't care about it. bitpos is TYPE_FIELD_BITPOS > (type, field_idx), which is 15 in this case, because sizeof (aa.aa) + > bitsof (aa.a) = 15. Well, actually it's 15 because that's the bit number of the LSB of that field, and x86 is a !gdbarch_bits_big_endian machine. Because we have: <2><53>: Abbrev Number: 4 (DW_TAG_member) <54> DW_AT_name : b <56> DW_AT_decl_file : 1 <57> DW_AT_decl_line : 7 <58> DW_AT_type : <0x6f> <5c> DW_AT_byte_size : 4 <5d> DW_AT_bit_size : 10 <5e> DW_AT_bit_offset : 7 <5f> DW_AT_data_member_location: 0 and with that, we reach here: static void dwarf2_add_field (struct field_info *fip, struct die_info *die, struct dwarf2_cu *cu) { ... attr = dwarf2_attr (die, DW_AT_bit_offset, cu); if (attr) { if (gdbarch_bits_big_endian (gdbarch)) { ... } else { ... attr = dwarf2_attr (die, DW_AT_byte_size, cu); if (attr) { /* The size of the anonymous object containing the bit field is explicit, so use the indicated size (in bytes). */ anonymous_size = DW_UNSND (attr); } else { ... } SET_FIELD_BITPOS (*fp, (FIELD_BITPOS (*fp) + anonymous_size * bits_per_byte - bit_offset - FIELD_BITSIZE (*fp))); } } which gives us: bitpos = DW_AT_byte_size * 8 - DW_AT_bit_offset - DW_AT_bit_size = 4 * 8 - 7 - 10 = 15 and since x86 is a !gdbarch_bits_big_endian machine, 15 goes here: byte: 0 1 2 3 bits: 7-0 15-8 23-16 31-24 ^ which is what we see if we write a "1" to aa.b: (gdb) p aa.b = 1 $1 = 1 (gdb) x /4xb &aa 0x60103c <aa>: 0x00 0x80 0x00 0x00 ^^^^ > When we divide it by TARGET_CHAR_BIT, we get 1. It > seems using TYPE_FIELD_BITPOS is not reliable when dealing with > bitfields. I noticed: (gdb) p /x & aa.aa $1 = 0x601040 (gdb) p /x & aa.a $2 = 0x601041 (gdb) p /x & aa.b $3 = 0x601040 (gdb) Ignoring the fact that taking the address of bitfields is not valid C/C++ [ :-) ], it seems like the addresses above match what we'd expect the byte offset to be. At least for this example. So maybe what you need is to factor out or duplicate the logic used by the related value printing code. Here in value_primitive_field: if (TYPE_FIELD_BITSIZE (arg_type, fieldno)) { /* Handle packed fields. ... LONGEST bitpos = TYPE_FIELD_BITPOS (arg_type, fieldno); LONGEST container_bitsize = TYPE_LENGTH (type) * 8; v = allocate_value_lazy (type); v->bitsize = TYPE_FIELD_BITSIZE (arg_type, fieldno); if ((bitpos % container_bitsize) + v->bitsize <= container_bitsize && TYPE_LENGTH (type) <= (int) sizeof (LONGEST)) v->bitpos = bitpos % container_bitsize; else v->bitpos = bitpos % 8; v->offset = (value_embedded_offset (arg1) + offset + (bitpos - v->bitpos) / 8); } Above "v->offset" may be the byte offset that you're after. > > So there is something I'm not accounting here. > >> What is that number after ":" in bitfields supposed to mean in >> pahole's output (and I assume that that's what you're trying >> to emulate)? We're missing documentation for that. > > Yeah. I tried to find documentation on the output, but couldn't. > Yesterday I was reading pahole's code. From what I understand, it is > the number of bits left in the object. > >> It seems like it's supposed to mean the number of bits left in the >> containing anonymous object (i.e., in the 4 bytes of the declared >> int)? Then "0:7" seems right, given: >> >> sizeof (int) * 8 - bitsof(aa.a) - bitsof(aa.a) - bits(aa.b) >> => sizeof (int) * 8 - 7 - 10 >> => 7 > > I guess you meant: > > sizeof (int) * 8 - bitsof (aa.a) - bitsof(aa.b) No, because that's be => 32 - 7 - 10 => 15 I meant: sizeof (int) * 8 - bitsof(aa.aa) - bitsof(aa.a) - bits(aa.b) => sizeof (int) * 8 - 8 - 7 - 10 => 7 which gives us the 7 unused bits that pahole reports. > > Right? > > But yeah, it makes sense when you consider that the first bitfield got > "promoted" from "unsigned char" to "int". I haven't figured out a way > to keep track of these type changes in order to calculate the right > offsets, remaining space and holes. Do we really need to track type changes, or do we simply need to detect that the byte offset moved in the negative direction? I.e., here: struct S { char aa; /* 0 1 */ unsigned char a:7; /* 1: 1 1 */ /* Bitfield combined with previous fields */ int b:10; /* 0: 7 4 */ }; The byte offset went "0 -> 1 -> 0", and the "1 -> 0" would mean that the bitfield was "combined"? Thanks, Pedro Alves ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v4 0/2] Implement pahole-like 'ptype /o' option (and do some code reorg) 2017-11-21 16:07 [PATCH] Implement pahole-like 'ptype /o' option Sergio Durigan Junior ` (4 preceding siblings ...) 2017-12-11 19:58 ` [PATCH v3 0/2] Implement pahole-like 'ptype /o' option (and do some code reorg) Sergio Durigan Junior @ 2017-12-11 23:43 ` Sergio Durigan Junior 2017-12-11 23:44 ` [PATCH v4 2/2] Implement pahole-like 'ptype /o' option Sergio Durigan Junior 2017-12-11 23:44 ` [PATCH v4 1/2] Reorganize code to handle TYPE_CODE_{STRUCT,UNION} on 'c_type_print_base' Sergio Durigan Junior 2017-12-13 3:17 ` [PATCH v5 0/2] Implement pahole-like 'ptype /o' option (and do some code reorg) Sergio Durigan Junior ` (2 subsequent siblings) 8 siblings, 2 replies; 75+ messages in thread From: Sergio Durigan Junior @ 2017-12-11 23:43 UTC (permalink / raw) To: GDB Patches Cc: Tom Tromey, Eli Zaretskii, Simon Marchi, Pedro Alves, André Pönitz, Keith Seitz Changes from v3: - Simplified need_access_label_p. - Fixed a few typos. - Improved comments on c_print_type_struct_field_offset and c_print_type_union_field_offset. - Removed unused variable. - Added @smallexample to docs. Eli had approved the documentation bits for the previous patch, but since this one had an addition (the inclusion of an @smallexample showing the output of 'ptype /o'), I believe it'd be good if he could review it again. Thanks, ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v4 2/2] Implement pahole-like 'ptype /o' option 2017-12-11 23:43 ` [PATCH v4 0/2] Implement pahole-like 'ptype /o' option (and do some code reorg) Sergio Durigan Junior @ 2017-12-11 23:44 ` Sergio Durigan Junior 2017-12-12 0:27 ` Sergio Durigan Junior ` (2 more replies) 2017-12-11 23:44 ` [PATCH v4 1/2] Reorganize code to handle TYPE_CODE_{STRUCT,UNION} on 'c_type_print_base' Sergio Durigan Junior 1 sibling, 3 replies; 75+ messages in thread From: Sergio Durigan Junior @ 2017-12-11 23:44 UTC (permalink / raw) To: GDB Patches Cc: Tom Tromey, Eli Zaretskii, Simon Marchi, Pedro Alves, André Pönitz, Keith Seitz, Sergio Durigan Junior 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: yyyy-mm-dd 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. (c_type_print_base_struct_union): Print offsets and sizes for struct/union/class fields. * 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: yyyy-mm-dd 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: yyyy-mm-dd Sergio Durigan Junior <sergiodj@redhat.com> PR cli/16224 * gdb.texinfo (ptype): Add new flag '/o'. --- gdb/NEWS | 3 + gdb/c-typeprint.c | 207 ++++++++++++++++++++++++++++--- gdb/doc/gdb.texinfo | 56 +++++++++ gdb/testsuite/gdb.base/ptype-offsets.cc | 138 +++++++++++++++++++++ gdb/testsuite/gdb.base/ptype-offsets.exp | 158 +++++++++++++++++++++++ gdb/typeprint.c | 15 ++- gdb/typeprint.h | 9 ++ 7 files changed, 566 insertions(+), 20 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 c6fe297159..67d40d007b 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. + * 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. diff --git a/gdb/c-typeprint.c b/gdb/c-typeprint.c index ce10b5e005..23138d8a40 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,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; diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 062f01d5ff..2ed52e6739 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -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 diff --git a/gdb/testsuite/gdb.base/ptype-offsets.cc b/gdb/testsuite/gdb.base/ptype-offsets.cc new file mode 100644 index 0000000000..52d2277ce7 --- /dev/null +++ b/gdb/testsuite/gdb.base/ptype-offsets.cc @@ -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; +} diff --git a/gdb/testsuite/gdb.base/ptype-offsets.exp b/gdb/testsuite/gdb.base/ptype-offsets.exp new file mode 100644 index 0000000000..fa1c0cb41c --- /dev/null +++ b/gdb/testsuite/gdb.base/ptype-offsets.exp @@ -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" diff --git a/gdb/typeprint.c b/gdb/typeprint.c index 9d9d6f5a49..eb829c2069 100644 --- a/gdb/typeprint.c +++ b/gdb/typeprint.c @@ -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, diff --git a/gdb/typeprint.h b/gdb/typeprint.h index a2058b0120..dd23c1eb6c 100644 --- a/gdb/typeprint.h +++ b/gdb/typeprint.h @@ -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; -- 2.14.3 ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH v4 2/2] Implement pahole-like 'ptype /o' option 2017-12-11 23:44 ` [PATCH v4 2/2] Implement pahole-like 'ptype /o' option Sergio Durigan Junior @ 2017-12-12 0:27 ` Sergio Durigan Junior 2017-12-12 0:29 ` Sergio Durigan Junior 2017-12-12 1:59 ` Simon Marchi 2017-12-12 3:39 ` Eli Zaretskii 2 siblings, 1 reply; 75+ messages in thread From: Sergio Durigan Junior @ 2017-12-12 0:27 UTC (permalink / raw) To: GDB Patches Cc: Tom Tromey, Eli Zaretskii, Simon Marchi, Pedro Alves, André Pönitz, Keith Seitz On Monday, December 11 2017, I wrote: > 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. The patch below applies on top of this one and extends the output to include offsets of union fields. -- Sergio GPG key ID: 237A 54B1 0287 28BF 00EF 31F4 D0EB 7628 65FC 5E36 Please send encrypted e-mail if possible http://sergiodj.net/ diff --git a/gdb/c-typeprint.c b/gdb/c-typeprint.c index 23138d8a40..e66129f643 100644 --- a/gdb/c-typeprint.c +++ b/gdb/c-typeprint.c @@ -906,11 +906,15 @@ output_access_specifier (struct ui_file *stream, static void c_print_type_union_field_offset (struct type *type, unsigned int field_idx, - struct ui_file *stream) + 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); - fprintf_filtered (stream, "/* %4u */", TYPE_LENGTH (ftype)); + fprintf_filtered (stream, "/* %4u | %4u */", + (bitpos + offset_bitpos) / TARGET_CHAR_BIT, + TYPE_LENGTH (ftype)); } /* Print information about field at index FIELD_IDX of the struct type @@ -1169,7 +1173,8 @@ c_type_print_base_struct_union (struct type *type, struct ui_file *stream, 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); + c_print_type_union_field_offset (type, i, stream, + flags->offset_bitpos); } else print_spaces_filtered (OFFSET_SPC_LEN, stream); diff --git a/gdb/testsuite/gdb.base/ptype-offsets.exp b/gdb/testsuite/gdb.base/ptype-offsets.exp index fa1c0cb41c..7566dcaaab 100644 --- a/gdb/testsuite/gdb.base/ptype-offsets.exp +++ b/gdb/testsuite/gdb.base/ptype-offsets.exp @@ -44,8 +44,8 @@ gdb_test "ptype /o struct abc" \ "/\\\* XXX 7-byte hole \\\*/" \ "/\\\* 32 | 8 \\\*/ uint64_t field5;" \ "/\\\* 40 | 8 \\\*/ union {" \ -"/\\\* 8 \\\*/ void \\\*field6;" \ -"/\\\* 4 \\\*/ int field7;" \ +"/\\\* 40 | 8 \\\*/ void \\\*field6;" \ +"/\\\* 40 | 4 \\\*/ int field7;" \ " } /\\\* total size: 8 bytes \\\*/ field8;" \ "" \ " abc\\(void\\);" \ @@ -82,13 +82,13 @@ gdb_test "ptype /o struct pqr" \ gdb_test "ptype /o union qwe" \ [multi_line \ "/\\\* offset | size \\\*/" \ -"/\\\* 24 \\\*/ struct tuv {" \ +"/\\\* 0 | 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 | 40 \\\*/ struct xyz {" \ "/\\\* 0 | 4 \\\*/ int f1;" \ "/\\\* 4 | 1 \\\*/ char f2;" \ "/\\\* XXX 3-byte hole \\\*/" \ @@ -111,13 +111,13 @@ gdb_test "ptype /o struct poi" \ "/\\\* 0 | 4 \\\*/ int f1;" \ "/\\\* XXX 4-byte hole \\\*/" \ "/\\\* 8 | 40 \\\*/ union qwe {" \ -"/\\\* 24 \\\*/ struct tuv {" \ +"/\\\* 8 | 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 | 40 \\\*/ struct xyz {" \ "/\\\* 8 | 4 \\\*/ int f1;" \ "/\\\* 12 | 1 \\\*/ char f2;" \ "/\\\* XXX 3-byte hole \\\*/" \ ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH v4 2/2] Implement pahole-like 'ptype /o' option 2017-12-12 0:27 ` Sergio Durigan Junior @ 2017-12-12 0:29 ` Sergio Durigan Junior 0 siblings, 0 replies; 75+ messages in thread From: Sergio Durigan Junior @ 2017-12-12 0:29 UTC (permalink / raw) To: GDB Patches Cc: Tom Tromey, Eli Zaretskii, Simon Marchi, Pedro Alves, André Pönitz, Keith Seitz On Monday, December 11 2017, I wrote: > On Monday, December 11 2017, I wrote: > >> 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. > > The patch below applies on top of this one and extends the output to > include offsets of union fields. > > -- > Sergio > GPG key ID: 237A 54B1 0287 28BF 00EF 31F4 D0EB 7628 65FC 5E36 > Please send encrypted e-mail if possible > http://sergiodj.net/ > > diff --git a/gdb/c-typeprint.c b/gdb/c-typeprint.c > index 23138d8a40..e66129f643 100644 > --- a/gdb/c-typeprint.c > +++ b/gdb/c-typeprint.c > @@ -906,11 +906,15 @@ output_access_specifier (struct ui_file *stream, > > static void > c_print_type_union_field_offset (struct type *type, unsigned int field_idx, > - struct ui_file *stream) > + 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); > > - fprintf_filtered (stream, "/* %4u */", TYPE_LENGTH (ftype)); > + fprintf_filtered (stream, "/* %4u | %4u */", > + (bitpos + offset_bitpos) / TARGET_CHAR_BIT, > + TYPE_LENGTH (ftype)); > } The comment of the function above needs an update: /* 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. OFFSET_BITPOS is the offset value we carry over when we are printing a union that is inside a 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 union). The output is strongly based on pahole(1). */ -- Sergio GPG key ID: 237A 54B1 0287 28BF 00EF 31F4 D0EB 7628 65FC 5E36 Please send encrypted e-mail if possible http://sergiodj.net/ ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH v4 2/2] Implement pahole-like 'ptype /o' option 2017-12-11 23:44 ` [PATCH v4 2/2] Implement pahole-like 'ptype /o' option Sergio Durigan Junior 2017-12-12 0:27 ` Sergio Durigan Junior @ 2017-12-12 1:59 ` Simon Marchi 2017-12-12 3:39 ` Eli Zaretskii 2 siblings, 0 replies; 75+ messages in thread From: Simon Marchi @ 2017-12-12 1:59 UTC (permalink / raw) To: Sergio Durigan Junior, GDB Patches Cc: Tom Tromey, Eli Zaretskii, Simon Marchi, Pedro Alves, André Pönitz, Keith Seitz On 2017-12-11 06:43 PM, Sergio Durigan Junior wrote: > 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. That patch LGTM as-is, as well as with the union offsets :) Simon ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH v4 2/2] Implement pahole-like 'ptype /o' option 2017-12-11 23:44 ` [PATCH v4 2/2] Implement pahole-like 'ptype /o' option Sergio Durigan Junior 2017-12-12 0:27 ` Sergio Durigan Junior 2017-12-12 1:59 ` Simon Marchi @ 2017-12-12 3:39 ` Eli Zaretskii 2 siblings, 0 replies; 75+ messages in thread From: Eli Zaretskii @ 2017-12-12 3:39 UTC (permalink / raw) To: Sergio Durigan Junior Cc: gdb-patches, tom, simon.marchi, palves, apoenitz, keiths > From: Sergio Durigan Junior <sergiodj@redhat.com> > Cc: Tom Tromey <tom@tromey.com>, Eli Zaretskii <eliz@gnu.org>, Simon Marchi <simon.marchi@ericsson.com>, Pedro Alves <palves@redhat.com>, André Pönitz <apoenitz@t-online.de>, Keith Seitz <keiths@redhat.com>, Sergio Durigan Junior <sergiodj@redhat.com> > Date: Mon, 11 Dec 2017 18:43:45 -0500 > > +Issuing a @command{ptype /o union qwe} would print: This should be Issuing the command @kbd{ptype /o union qwe} would print: (The @command markup is for shell commands, like 'ls' and 'grep', not for something the user types on the keyboard.) Otherwise, the documentation parts are okay. Thanks. ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v4 1/2] Reorganize code to handle TYPE_CODE_{STRUCT,UNION} on 'c_type_print_base' 2017-12-11 23:43 ` [PATCH v4 0/2] Implement pahole-like 'ptype /o' option (and do some code reorg) Sergio Durigan Junior 2017-12-11 23:44 ` [PATCH v4 2/2] Implement pahole-like 'ptype /o' option Sergio Durigan Junior @ 2017-12-11 23:44 ` Sergio Durigan Junior 1 sibling, 0 replies; 75+ messages in thread From: Sergio Durigan Junior @ 2017-12-11 23:44 UTC (permalink / raw) To: GDB Patches Cc: Tom Tromey, Eli Zaretskii, Simon Marchi, Pedro Alves, André Pönitz, Keith Seitz, Sergio Durigan Junior While doing the 'ptype /o' work, I noticed that 'c_type_print_base' was very long, with a big amount of code just to handle the case of TYPE_CODE_{STRUCT,UNION}. This made working with the function a bit difficult, specially because of the level of indentation. This commit moves this part of the code to their own functions. Now we have a 'c_type_print_base_struct_union' with most of the code, and also 'need_access_label_p', which is a subset of the code that was also a good candidate for having its own function. gdb/ChangeLog: yyyy-mm-dd Sergio Durigan Junior <sergiodj@redhat.com> * c-typeprint.c (need_access_label_p): New function. (c_type_print_base_struct_union): New function. (c_type_print_base): Move code to handle TYPE_CODE_{STRUCT,UNION} to the functions mentioned above. --- gdb/c-typeprint.c | 838 ++++++++++++++++++++++++++---------------------------- 1 file changed, 404 insertions(+), 434 deletions(-) diff --git a/gdb/c-typeprint.c b/gdb/c-typeprint.c index f3c3e7d706..ce10b5e005 100644 --- a/gdb/c-typeprint.c +++ b/gdb/c-typeprint.c @@ -875,6 +875,407 @@ output_access_specifier (struct ui_file *stream, return last_access; } +/* 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) +{ + if (TYPE_DECLARED_CLASS (type)) + { + QUIT; + for (int i = TYPE_N_BASECLASSES (type); i < TYPE_NFIELDS (type); i++) + if (!TYPE_FIELD_PRIVATE (type, i)) + return true; + QUIT; + for (int j = 0; j < TYPE_NFN_FIELDS (type); j++) + for (int i = 0; i < TYPE_FN_FIELDLIST_LENGTH (type, j); i++) + if (!TYPE_FN_FIELD_PRIVATE (TYPE_FN_FIELDLIST1 (type, + j), i)) + return true; + QUIT; + for (int i = 0; i < TYPE_TYPEDEF_FIELD_COUNT (type); ++i) + if (!TYPE_TYPEDEF_FIELD_PRIVATE (type, i)) + return true; + } + else + { + QUIT; + for (int i = TYPE_N_BASECLASSES (type); i < TYPE_NFIELDS (type); i++) + if (TYPE_FIELD_PRIVATE (type, i) || TYPE_FIELD_PROTECTED (type, i)) + return true; + QUIT; + for (int j = 0; j < TYPE_NFN_FIELDS (type); j++) + { + QUIT; + for (int i = 0; i < TYPE_FN_FIELDLIST_LENGTH (type, j); i++) + if (TYPE_FN_FIELD_PROTECTED (TYPE_FN_FIELDLIST1 (type, + j), i) + || TYPE_FN_FIELD_PRIVATE (TYPE_FN_FIELDLIST1 (type, + j), + i)) + return true; + } + QUIT; + for (int i = 0; i < TYPE_TYPEDEF_FIELD_COUNT (type); ++i) + if (TYPE_TYPEDEF_FIELD_PROTECTED (type, i) + || TYPE_TYPEDEF_FIELD_PRIVATE (type, i)) + return true; + } + + return false; +} + +/* 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; + 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. */ + + 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); + vptr_fieldno = get_vptr_fieldno (type, &basetype); + 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)); + } + + 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; + 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)); + + 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 out nested types. */ + if (TYPE_NESTED_TYPES_COUNT (type) != 0 + && semi_local_flags.print_nested_type_limit != 0) + { + if (semi_local_flags.print_nested_type_limit > 0) + --semi_local_flags.print_nested_type_limit; + + if (TYPE_NFIELDS (type) != 0 || TYPE_NFN_FIELDS (type) != 0) + fprintf_filtered (stream, "\n"); + + for (int i = 0; i < TYPE_NESTED_TYPES_COUNT (type); ++i) + { + print_spaces_filtered (level + 4, stream); + c_print_type (TYPE_NESTED_TYPES_FIELD_TYPE (type, i), + "", stream, show, level + 4, &semi_local_flags); + 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 + || TYPE_NESTED_TYPES_COUNT (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)); + } + 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); +} + /* 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 +1299,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; @@ -958,436 +1357,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 out nested types. */ - if (TYPE_NESTED_TYPES_COUNT (type) != 0 - && semi_local_flags.print_nested_type_limit != 0) - { - if (semi_local_flags.print_nested_type_limit > 0) - --semi_local_flags.print_nested_type_limit; - - if (TYPE_NFIELDS (type) != 0 || TYPE_NFN_FIELDS (type) != 0) - fprintf_filtered (stream, "\n"); - - for (i = 0; i < TYPE_NESTED_TYPES_COUNT (type); ++i) - { - print_spaces_filtered (level + 4, stream); - c_print_type (TYPE_NESTED_TYPES_FIELD_TYPE (type, i), - "", stream, show, level + 4, &semi_local_flags); - 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 - || TYPE_NESTED_TYPES_COUNT (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: -- 2.14.3 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v5 0/2] Implement pahole-like 'ptype /o' option (and do some code reorg) 2017-11-21 16:07 [PATCH] Implement pahole-like 'ptype /o' option Sergio Durigan Junior ` (5 preceding siblings ...) 2017-12-11 23:43 ` [PATCH v4 0/2] Implement pahole-like 'ptype /o' option (and do some code reorg) Sergio Durigan Junior @ 2017-12-13 3:17 ` Sergio Durigan Junior 2017-12-13 3:17 ` [PATCH v5 2/2] Implement pahole-like 'ptype /o' option Sergio Durigan Junior 2017-12-13 3:17 ` [PATCH v5 1/2] Reorganize code to handle TYPE_CODE_{STRUCT,UNION} on 'c_type_print_base' Sergio Durigan Junior 2017-12-14 2:48 ` [PATCH v6 0/2] Implement pahole-like 'ptype /o' option (and do some code reorg) Sergio Durigan Junior 2017-12-15 1:12 ` [PATCH v7 0/2] Implement pahole-like 'ptype /o' option (and do some code reorg) Sergio Durigan Junior 8 siblings, 2 replies; 75+ messages in thread From: Sergio Durigan Junior @ 2017-12-13 3:17 UTC (permalink / raw) To: GDB Patches Cc: Tom Tromey, Eli Zaretskii, Simon Marchi, Pedro Alves, Keith Seitz Changes from v4: - Fixed issue with printing bitfield offsets. Big thanks to Pedro for the help with figuring this out. - Expanded testcase to cover the bitfield offset test. - Fixed @command/@kbd on documentation. - Greatly expanded documentation to explain more about various types of output that the command generates. - Added a marker for /* vtable */ fields. I honestly thought this patch was not going to be ready today. It's now apparently printing the offsets (and remaining bits) of bitfields correctly, at least as far I have tested. Eli, I think you need to review the docs once again because I included a great deal of information about the format printed by the command. Thanks. ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v5 2/2] Implement pahole-like 'ptype /o' option 2017-12-13 3:17 ` [PATCH v5 0/2] Implement pahole-like 'ptype /o' option (and do some code reorg) Sergio Durigan Junior @ 2017-12-13 3:17 ` Sergio Durigan Junior 2017-12-13 4:50 ` Simon Marchi ` (3 more replies) 2017-12-13 3:17 ` [PATCH v5 1/2] Reorganize code to handle TYPE_CODE_{STRUCT,UNION} on 'c_type_print_base' Sergio Durigan Junior 1 sibling, 4 replies; 75+ messages in thread From: Sergio Durigan Junior @ 2017-12-13 3:17 UTC (permalink / raw) To: GDB Patches Cc: Tom Tromey, Eli Zaretskii, Simon Marchi, Pedro Alves, Keith Seitz, Sergio Durigan Junior 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: yyyy-mm-dd Sergio Durigan Junior <sergiodj@redhat.com> Pedro Alves <palves@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_vtable_offset_marker): New function. (c_print_type_union_field_offset): New function. (c_print_type_struct_field_offset): New function. (c_type_print_base_struct_union): Print offsets and sizes for struct/union/class fields. * 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: yyyy-mm-dd 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: yyyy-mm-dd Sergio Durigan Junior <sergiodj@redhat.com> PR cli/16224 * gdb.texinfo (ptype): Add documentation for new flag '/o'. --- gdb/NEWS | 3 + gdb/c-typeprint.c | 248 ++++++++++++++++++++++++++++--- gdb/doc/gdb.texinfo | 115 ++++++++++++++ gdb/testsuite/gdb.base/ptype-offsets.cc | 158 ++++++++++++++++++++ gdb/testsuite/gdb.base/ptype-offsets.exp | 192 ++++++++++++++++++++++++ gdb/typeprint.c | 15 +- gdb/typeprint.h | 9 ++ 7 files changed, 719 insertions(+), 21 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 c6fe297159..67d40d007b 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. + * 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. diff --git a/gdb/c-typeprint.c b/gdb/c-typeprint.c index ce10b5e005..cd21cb2172 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,15 +890,132 @@ 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:", +static void +c_print_type_vtable_offset_marker (struct type *type, unsigned int field_idx, + struct ui_file *stream, + unsigned int offset_bitpos) +{ + unsigned int bitpos = TYPE_FIELD_BITPOS (type, field_idx); + unsigned int offset = (bitpos + offset_bitpos) / TARGET_CHAR_BIT; + + fprintf_filtered (stream, "/* %4u | %4u */ /* vtable */\n", offset, + TYPE_LENGTH (TYPE_FIELD_TYPE (type, field_idx))); +} + +/* 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 = fieldsize_byte * TARGET_CHAR_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); + } + + if (TYPE_FIELD_PACKED (type, field_idx)) + { + /* We're dealing with a bitfield. Print how many bits are left + to be used. */ + unsigned int bitsize = TYPE_FIELD_BITSIZE (type, field_idx); + /* The bitpos relative to the beginning of our container + field. */ + unsigned int relative_bitpos; + + /* The following was copied from + value.c:value_primitive_field. */ + if ((bitpos % fieldsize_bit) + bitsize <= fieldsize_bit) + relative_bitpos = bitpos % fieldsize_bit; + else + relative_bitpos = bitpos % TARGET_CHAR_BIT; + + /* This is the exact offset (in bits) of this bitfield. */ + unsigned int bit_offset + = (bitpos - relative_bitpos) + offset_bitpos; + + /* The position of the field, relative to the beginning of the + struct, and how many bits are left to be used in this + container. */ + fprintf_filtered (stream, "/* %4u:%2u", bit_offset / TARGET_CHAR_BIT, + fieldsize_bit - (relative_bitpos + bitsize)); + fieldsize_bit = bitsize; + } + else + { + /* The position of the field, relative to the beginning of the + struct. */ + fprintf_filtered (stream, "/* %4u", + (bitpos + offset_bitpos) / 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 +1172,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; @@ -1041,25 +1183,63 @@ c_type_print_base_struct_union (struct type *type, struct ui_file *stream, the debug info, they should be artificial. */ if ((i == vptr_fieldno && type == basetype) || TYPE_FIELD_ARTIFICIAL (type, i)) - continue; + { + if (flags->print_offsets) + c_print_type_vtable_offset_marker (type, i, stream, + flags->offset_bitpos); + continue; + } if (need_access_label) { 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 +1302,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 +1324,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 +1414,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 +1449,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 +1467,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 +1509,6 @@ c_type_print_base (struct type *type, struct ui_file *stream, { int i; int len; - int j; QUIT; diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 062f01d5ff..9d58f2b7d0 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -17211,6 +17211,121 @@ 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; +@}; + +struct tyu +@{ + int a1 : 1; + int a2 : 3; + int a3 : 23; + char a4 : 2; + int64_t a5; + int a6 : 5; + int64_t a7 : 3; +@}; +@end smallexample + +Issuing a @kbd{ptype /o struct tuv} would print: + +@smallexample +(@value{GDBP}) ptype /o struct tuv +/* offset | size */ +struct tuv @{ +/* 0 | 4 */ int a1; +/* XXX 4-byte hole */ +/* 8 | 8 */ char *a2; +/* 16 | 4 */ int a3; +@} /* total size: 24 bytes */ +@end smallexample + +Notice the format of the first column of comments. There, you can +find two parts separated by the @code{|} character: the @emph{offset}, +which indicates where the field is located inside the struct, in +bytes, and the @emph{size} of the field. Another interesting line is +the marker of a @emph{hole} in the struct, indicating that it may be +possible to pack the struct and make it use less space by reorganizing +its fields. + +It is also possible to print offsets inside an union: + +@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 + +In this case, since @code{struct tuv} and @code{struct xyz} occupy the +same space (because we are dealing with an union), the offset is not +printed for them. However, you can still examine the offset of each +of these structures' fields. + +Another useful scenario is printing the offsets of a struct containing +bitfields: + +@smallexample +(@value{GDBP}) ptype /o struct tyu +/* offset | size */ +struct tyu @{ +/* 0:31 | 4 */ int a1 : 1; +/* 0:28 | 4 */ int a2 : 3; +/* 0: 5 | 4 */ int a3 : 23; +/* 3: 3 | 1 */ char a4 : 2; +/* XXX 3-bit hole */ +/* XXX 4-byte hole */ +/* 8 | 8 */ int64_t a5; +/* 16:27 | 4 */ int a6 : 5; +/* 16:56 | 8 */ int64_t a7 : 3; +@} /* total size: 24 bytes */ +@end smallexample + +Note how the offset information is now extended to also include how +many bits are left to be used in each bitfield. @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..aadcace517 --- /dev/null +++ b/gdb/testsuite/gdb.base/ptype-offsets.cc @@ -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/>. */ + +/* 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; +}; + +/* A struct with bitfields. */ + +struct tyu +{ + int a1 : 1; + + int a2 : 3; + + int a3 : 23; + + char a4 : 2; + + int64_t a5; + + int a6 : 5; + + int64_t a7 : 3; +}; + +int +main (int argc, char *argv[]) +{ + struct abc foo; + struct pqr bar; + union qwe c; + struct poi d; + struct tyu e; + uint8_t i; + + 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..04a887a703 --- /dev/null +++ b/gdb/testsuite/gdb.base/ptype-offsets.exp @@ -0,0 +1,192 @@ +# 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 printing a struct with several bitfields, laid out in various +# ways. +# +# Because dealing with bitfields and offsets is difficult, it can be +# tricky to confirm that the output of the this command is accurate. +# A nice way to do that is to use GDB's "x" command and print the +# actual memory layout of the struct. In order to differentiate +# betweent bitfields and non-bitfield variables, one can assign "-1" +# to every bitfield in the struct. An example of the output of "x" +# using "struct tyu" is: +# +# (gdb) x/24xb &e +# 0x7fffffffd540: 0xff 0xff 0xff 0x1f 0x00 0x00 0x00 0x00 +# 0x7fffffffd548: 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff +# 0x7fffffffd550: 0xff 0x00 0x00 0x00 0x00 0x00 0x00 0x00 +# +# Thanks to Pedro Alves for coming up with this method (which can be +# used to inspect the other cases, of course). +gdb_test "ptype /o struct tyu" \ + [multi_line \ +"/\\\* offset | size \\\*/" \ +"struct tyu {" \ +"/\\\* 0:31 | 4 \\\*/ int a1 : 1;" \ +"/\\\* 0:28 | 4 \\\*/ int a2 : 3;" \ +"/\\\* 0: 5 | 4 \\\*/ int a3 : 23;" \ +"/\\\* 3: 3 | 1 \\\*/ char a4 : 2;" \ +"/\\\* XXX 3-bit hole \\\*/" \ +"/\\\* XXX 4-byte hole \\\*/" \ +"/\\\* 8 | 8 \\\*/ int64_t a5;" \ +"/\\\* 16:27 | 4 \\\*/ int a6 : 5;" \ +"/\\\* 16:56 | 8 \\\*/ int64_t a7 : 3;" \ +"} /\\\* total size: 24 bytes \\\*/"] \ + "ptype offset struct tyu" + +# 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" diff --git a/gdb/typeprint.c b/gdb/typeprint.c index 9d9d6f5a49..225e94b1bb 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 */ 0, /* print_nested_type_limit */ NULL, /* local_typedefs */ NULL, /* global_table */ @@ -55,6 +57,8 @@ static struct type_print_options default_ptype_flags = 0, /* raw */ 1, /* print_methods */ 1, /* print_typedefs */ + 0, /* print_offsets */ + 0, /* offset_bitpos */ 0, /* print_nested_type_limit */ NULL, /* local_typedefs */ NULL, /* global_table */ @@ -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, diff --git a/gdb/typeprint.h b/gdb/typeprint.h index a2058b0120..08333b3762 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 over + the offset of the outter struct. */ + unsigned int offset_bitpos; + /* The number of nested type definitions to print. -1 == all. */ int print_nested_type_limit; -- 2.14.3 ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH v5 2/2] Implement pahole-like 'ptype /o' option 2017-12-13 3:17 ` [PATCH v5 2/2] Implement pahole-like 'ptype /o' option Sergio Durigan Junior @ 2017-12-13 4:50 ` Simon Marchi 2017-12-13 16:42 ` Sergio Durigan Junior 2017-12-13 16:17 ` Eli Zaretskii ` (2 subsequent siblings) 3 siblings, 1 reply; 75+ messages in thread From: Simon Marchi @ 2017-12-13 4:50 UTC (permalink / raw) To: Sergio Durigan Junior, GDB Patches Cc: Tom Tromey, Eli Zaretskii, Simon Marchi, Pedro Alves, Keith Seitz On 2017-12-12 10:17 PM, Sergio Durigan Junior wrote: > +/* 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 = fieldsize_byte * TARGET_CHAR_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); > + } > + > + if (TYPE_FIELD_PACKED (type, field_idx)) > + { > + /* We're dealing with a bitfield. Print how many bits are left > + to be used. */ > + unsigned int bitsize = TYPE_FIELD_BITSIZE (type, field_idx); > + /* The bitpos relative to the beginning of our container > + field. */ > + unsigned int relative_bitpos; > + > + /* The following was copied from > + value.c:value_primitive_field. */ > + if ((bitpos % fieldsize_bit) + bitsize <= fieldsize_bit) > + relative_bitpos = bitpos % fieldsize_bit; > + else > + relative_bitpos = bitpos % TARGET_CHAR_BIT; > + > + /* This is the exact offset (in bits) of this bitfield. */ > + unsigned int bit_offset > + = (bitpos - relative_bitpos) + offset_bitpos; > + > + /* The position of the field, relative to the beginning of the > + struct, and how many bits are left to be used in this > + container. */ > + fprintf_filtered (stream, "/* %4u:%2u", bit_offset / TARGET_CHAR_BIT, > + fieldsize_bit - (relative_bitpos + bitsize)); > + fieldsize_bit = bitsize; > + } I won't pretend I understand this part. I'll let Pedro look at it, or maybe try again tomorrow, it's getting late here :) > @@ -1041,25 +1183,63 @@ c_type_print_base_struct_union (struct type *type, struct ui_file *stream, > the debug info, they should be artificial. */ > if ((i == vptr_fieldno && type == basetype) > || TYPE_FIELD_ARTIFICIAL (type, i)) > - continue; > + { > + if (flags->print_offsets) > + c_print_type_vtable_offset_marker (type, i, stream, > + flags->offset_bitpos); > + continue; > + } With that version, the comment above the if would need to be updted. Note that the vtable marker won't be printed when looking at a child class. With the following: struct foo { virtual ~foo() = default; int a; short b; }; struct bar : foo { virtual ~bar () = default; int z; short c, d, e; }; we get this: (gdb) ptype /o foo /* offset | size */ type = struct foo { /* 0 | 8 */ /* vtable */ public: /* 8 | 4 */ int a; /* 12 | 2 */ short b; ~foo(); } /* total size: 16 bytes */ (gdb) ptype /o bar /* offset | size */ type = struct bar : public foo { /* 16 | 4 */ int z; /* 20 | 2 */ short c; /* 22 | 2 */ short d; /* 24 | 2 */ short e; public: ~bar(); } /* total size: 32 bytes */ If we print the vtable, I think it would make sense to print it for the child class too. And if we do, it would also make sense to display the fields of base classes too, otherwise there would be a gap in the vtable offset and the first field offset. I think it would be useful in order to show the holes between the fields of the base class and the child class. In the example above, it would show that there's a two byte hole between b and z. Putting z at the end of bar could save some space. But doing so would require to change ptype's behavior significantly, making it recurse in parent classes. Should we do that only if /o is specified? If this makes the patch too complex, I would suggest merging it without the vtable field, and take at adding it after. We don't have to include all the features we can think of in the first iteration. For that use case, it's also interesting to see what pahole does too: struct bar : foo { /* struct foo <ancestor>; */ /* 0 16 */ /* XXX last struct has 2 bytes of padding */ int z; /* 16 4 */ short int c; /* 20 2 */ short int d; /* 22 2 */ short int e; /* 24 2 */ void bar(class bar *, const class bar &); void bar(class bar *); virtual void ~bar(class bar *, int); /* size: 32, cachelines: 1, members: 5 */ /* padding: 6 */ /* paddings: 1, sum paddings: 2 */ /* last cacheline: 32 bytes */ /* BRAIN FART ALERT! 32 != 10 + 0(holes), diff = 22 */ }; I am not sure why it brain farts. Looks like it's comparing the size of the whole structure (including fields of the base class, 32 bytes) with the size of the fields of bar (10 bytes) and is surprised it's not the same size. > diff --git a/gdb/testsuite/gdb.base/ptype-offsets.exp b/gdb/testsuite/gdb.base/ptype-offsets.exp > new file mode 100644 > index 0000000000..04a887a703 > --- /dev/null > +++ b/gdb/testsuite/gdb.base/ptype-offsets.exp > @@ -0,0 +1,192 @@ > +# 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 printing a struct with several bitfields, laid out in various > +# ways. > +# > +# Because dealing with bitfields and offsets is difficult, it can be > +# tricky to confirm that the output of the this command is accurate. > +# A nice way to do that is to use GDB's "x" command and print the > +# actual memory layout of the struct. In order to differentiate > +# betweent bitfields and non-bitfield variables, one can assign "-1" "betweent" > +# to every bitfield in the struct. An example of the output of "x" > +# using "struct tyu" is: > +# > +# (gdb) x/24xb &e > +# 0x7fffffffd540: 0xff 0xff 0xff 0x1f 0x00 0x00 0x00 0x00 > +# 0x7fffffffd548: 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff > +# 0x7fffffffd550: 0xff 0x00 0x00 0x00 0x00 0x00 0x00 0x00 > +# > +# Thanks to Pedro Alves for coming up with this method (which can be > +# used to inspect the other cases, of course). I'm all for thanking Pedro, but I would suggest putting this last remark in the commit message :) Simon ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH v5 2/2] Implement pahole-like 'ptype /o' option 2017-12-13 4:50 ` Simon Marchi @ 2017-12-13 16:42 ` Sergio Durigan Junior 0 siblings, 0 replies; 75+ messages in thread From: Sergio Durigan Junior @ 2017-12-13 16:42 UTC (permalink / raw) To: Simon Marchi Cc: GDB Patches, Tom Tromey, Eli Zaretskii, Simon Marchi, Pedro Alves, Keith Seitz On Tuesday, December 12 2017, Simon Marchi wrote: > On 2017-12-12 10:17 PM, Sergio Durigan Junior wrote: >> +/* 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 = fieldsize_byte * TARGET_CHAR_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); >> + } >> + >> + if (TYPE_FIELD_PACKED (type, field_idx)) >> + { >> + /* We're dealing with a bitfield. Print how many bits are left >> + to be used. */ >> + unsigned int bitsize = TYPE_FIELD_BITSIZE (type, field_idx); >> + /* The bitpos relative to the beginning of our container >> + field. */ >> + unsigned int relative_bitpos; >> + >> + /* The following was copied from >> + value.c:value_primitive_field. */ >> + if ((bitpos % fieldsize_bit) + bitsize <= fieldsize_bit) >> + relative_bitpos = bitpos % fieldsize_bit; >> + else >> + relative_bitpos = bitpos % TARGET_CHAR_BIT; >> + >> + /* This is the exact offset (in bits) of this bitfield. */ >> + unsigned int bit_offset >> + = (bitpos - relative_bitpos) + offset_bitpos; >> + >> + /* The position of the field, relative to the beginning of the >> + struct, and how many bits are left to be used in this >> + container. */ >> + fprintf_filtered (stream, "/* %4u:%2u", bit_offset / TARGET_CHAR_BIT, >> + fieldsize_bit - (relative_bitpos + bitsize)); >> + fieldsize_bit = bitsize; >> + } > > I won't pretend I understand this part. I'll let Pedro look at it, or maybe try > again tomorrow, it's getting late here :) Yeah, it's complicated. >> @@ -1041,25 +1183,63 @@ c_type_print_base_struct_union (struct type *type, struct ui_file *stream, >> the debug info, they should be artificial. */ >> if ((i == vptr_fieldno && type == basetype) >> || TYPE_FIELD_ARTIFICIAL (type, i)) >> - continue; >> + { >> + if (flags->print_offsets) >> + c_print_type_vtable_offset_marker (type, i, stream, >> + flags->offset_bitpos); >> + continue; >> + } > > With that version, the comment above the if would need to be updted. > > Note that the vtable marker won't be printed when looking at a child class. With the > following: > > struct foo > { > virtual ~foo() = default; > int a; > short b; > }; > > struct bar : foo > { > virtual ~bar () = default; > int z; > short c, d, e; > }; > > we get this: > > (gdb) ptype /o foo > /* offset | size */ > type = struct foo { > /* 0 | 8 */ /* vtable */ > public: > /* 8 | 4 */ int a; > /* 12 | 2 */ short b; > > ~foo(); > } /* total size: 16 bytes */ > (gdb) ptype /o bar > /* offset | size */ > type = struct bar : public foo { > /* 16 | 4 */ int z; > /* 20 | 2 */ short c; > /* 22 | 2 */ short d; > /* 24 | 2 */ short e; > public: > ~bar(); > } /* total size: 32 bytes */ > > If we print the vtable, I think it would make sense to print it for the child > class too. And if we do, it would also make sense to display the fields of base > classes too, otherwise there would be a gap in the vtable offset and the first > field offset. I think it would be useful in order to show the holes between the fields > of the base class and the child class. In the example above, it would show that there's > a two byte hole between b and z. Putting z at the end of bar could save some space. > > But doing so would require to change ptype's behavior significantly, making it recurse > in parent classes. Should we do that only if /o is specified? If this makes the patch > too complex, I would suggest merging it without the vtable field, and take at adding it > after. We don't have to include all the features we can think of in the first iteration. I agree that this use case would be good to have. But I also agree that it is a bit complex to implement... (A few moments later) OK, I gave it a try, and I may have something that works. I am attaching the patch to this e-mail so you can have a look. >> diff --git a/gdb/testsuite/gdb.base/ptype-offsets.exp b/gdb/testsuite/gdb.base/ptype-offsets.exp >> new file mode 100644 >> index 0000000000..04a887a703 >> --- /dev/null >> +++ b/gdb/testsuite/gdb.base/ptype-offsets.exp >> @@ -0,0 +1,192 @@ >> +# 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 printing a struct with several bitfields, laid out in various >> +# ways. >> +# >> +# Because dealing with bitfields and offsets is difficult, it can be >> +# tricky to confirm that the output of the this command is accurate. >> +# A nice way to do that is to use GDB's "x" command and print the >> +# actual memory layout of the struct. In order to differentiate >> +# betweent bitfields and non-bitfield variables, one can assign "-1" > > "betweent" Fixed. >> +# to every bitfield in the struct. An example of the output of "x" >> +# using "struct tyu" is: >> +# >> +# (gdb) x/24xb &e >> +# 0x7fffffffd540: 0xff 0xff 0xff 0x1f 0x00 0x00 0x00 0x00 >> +# 0x7fffffffd548: 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff >> +# 0x7fffffffd550: 0xff 0x00 0x00 0x00 0x00 0x00 0x00 0x00 >> +# >> +# Thanks to Pedro Alves for coming up with this method (which can be >> +# used to inspect the other cases, of course). > > I'm all for thanking Pedro, but I would suggest putting this last remark in the commit > message :) Alright, moved to the commit message. Thanks, -- Sergio GPG key ID: 237A 54B1 0287 28BF 00EF 31F4 D0EB 7628 65FC 5E36 Please send encrypted e-mail if possible http://sergiodj.net/ diff --git a/gdb/NEWS b/gdb/NEWS index c6fe297159..67d40d007b 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. + * 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. diff --git a/gdb/c-typeprint.c b/gdb/c-typeprint.c index ce10b5e005..c9906091d5 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,15 +890,132 @@ 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:", +static void +c_print_type_vtable_offset_marker (struct type *type, unsigned int field_idx, + struct ui_file *stream, + unsigned int offset_bitpos) +{ + unsigned int bitpos = TYPE_FIELD_BITPOS (type, field_idx); + unsigned int offset = (bitpos + offset_bitpos) / TARGET_CHAR_BIT; + + fprintf_filtered (stream, "/* %4u | %4u */", offset, + TYPE_LENGTH (TYPE_FIELD_TYPE (type, field_idx))); +} + +/* 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, + struct ui_file *stream, + struct print_offset_data *po_data) +{ + 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 = fieldsize_byte * TARGET_CHAR_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 (po_data->endpos > 0 && po_data->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 - po_data->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); + } + + if (TYPE_FIELD_PACKED (type, field_idx)) + { + /* We're dealing with a bitfield. Print how many bits are left + to be used. */ + unsigned int bitsize = TYPE_FIELD_BITSIZE (type, field_idx); + /* The bitpos relative to the beginning of our container + field. */ + unsigned int relative_bitpos; + + /* The following was copied from + value.c:value_primitive_field. */ + if ((bitpos % fieldsize_bit) + bitsize <= fieldsize_bit) + relative_bitpos = bitpos % fieldsize_bit; + else + relative_bitpos = bitpos % TARGET_CHAR_BIT; + + /* This is the exact offset (in bits) of this bitfield. */ + unsigned int bit_offset + = (bitpos - relative_bitpos) + po_data->offset_bitpos; + + /* The position of the field, relative to the beginning of the + struct, and how many bits are left to be used in this + container. */ + fprintf_filtered (stream, "/* %4u:%2u", bit_offset / TARGET_CHAR_BIT, + fieldsize_bit - (relative_bitpos + bitsize)); + fieldsize_bit = bitsize; + } + else + { + /* The position of the field, relative to the beginning of the + struct. */ + fprintf_filtered (stream, "/* %4u", + (bitpos + po_data->offset_bitpos) / TARGET_CHAR_BIT); + + fprintf_filtered (stream, " "); + } + + fprintf_filtered (stream, " | %4u */", fieldsize_byte); + + po_data->endpos = bitpos + fieldsize_bit; +} + +/* Return true if an access label (i.e., "public:", "private:", "protected:") needs to be printed for TYPE. */ static bool @@ -1004,6 +1144,21 @@ c_type_print_base_struct_union (struct type *type, struct ui_file *stream, recursively_update_typedef_hash (local_flags.local_typedefs, type); fprintf_filtered (stream, "{\n"); + + if (flags->print_offsets) + { + for (int i = 0; i < TYPE_N_BASECLASSES (type); ++i) + { + struct type *bclass = TYPE_BASECLASS (type, i); + + print_spaces_filtered_with_print_options (level + 4, stream, + flags); + c_print_type (bclass, "", stream, show, level + 4, + &local_flags); + fputs_filtered ("\n", stream); + } + } + if (TYPE_NFIELDS (type) == 0 && TYPE_NFN_FIELDS (type) == 0 && TYPE_TYPEDEF_FIELD_COUNT (type) == 0) { @@ -1032,6 +1187,7 @@ 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); + for (int i = TYPE_N_BASECLASSES (type); i < len; i++) { QUIT; @@ -1041,25 +1197,71 @@ c_type_print_base_struct_union (struct type *type, struct ui_file *stream, the debug info, they should be artificial. */ if ((i == vptr_fieldno && type == basetype) || TYPE_FIELD_ARTIFICIAL (type, i)) - continue; + { + if (flags->print_offsets) + { + c_print_type_vtable_offset_marker + (type, i, stream, flags->po_data.offset_bitpos); + print_spaces_filtered (level + 4, stream); + fprintf_filtered (stream, "/* vtable */\n"); + } + continue; + } if (need_access_label) { 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, stream, &local_flags.po_data); + ((struct type_print_options *) flags)->po_data + = local_flags.po_data; + } + 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.po_data.offset_bitpos + = flags->po_data.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 +1324,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 +1346,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 +1436,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 +1471,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 +1489,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 +1531,6 @@ c_type_print_base (struct type *type, struct ui_file *stream, { int i; int len; - int j; QUIT; diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index da3ed28dfe..08e29edb82 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -17216,6 +17216,121 @@ 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; +@}; + +struct tyu +@{ + int a1 : 1; + int a2 : 3; + int a3 : 23; + char a4 : 2; + int64_t a5; + int a6 : 5; + int64_t a7 : 3; +@}; +@end smallexample + +Issuing a @kbd{ptype /o struct tuv} would print: + +@smallexample +(@value{GDBP}) ptype /o struct tuv +/* offset | size */ +struct tuv @{ +/* 0 | 4 */ int a1; +/* XXX 4-byte hole */ +/* 8 | 8 */ char *a2; +/* 16 | 4 */ int a3; +@} /* total size: 24 bytes */ +@end smallexample + +Notice the format of the first column of comments. There, you can +find two parts separated by the @code{|} character: the @emph{offset}, +which indicates where the field is located inside the struct, in +bytes, and the @emph{size} of the field. Another interesting line is +the marker of a @emph{hole} in the struct, indicating that it may be +possible to pack the struct and make it use less space by reorganizing +its fields. + +It is also possible to print offsets inside an union: + +@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 + +In this case, since @code{struct tuv} and @code{struct xyz} occupy the +same space (because we are dealing with an union), the offset is not +printed for them. However, you can still examine the offset of each +of these structures' fields. + +Another useful scenario is printing the offsets of a struct containing +bitfields: + +@smallexample +(@value{GDBP}) ptype /o struct tyu +/* offset | size */ +struct tyu @{ +/* 0:31 | 4 */ int a1 : 1; +/* 0:28 | 4 */ int a2 : 3; +/* 0: 5 | 4 */ int a3 : 23; +/* 3: 3 | 1 */ char a4 : 2; +/* XXX 3-bit hole */ +/* XXX 4-byte hole */ +/* 8 | 8 */ int64_t a5; +/* 16:27 | 4 */ int a6 : 5; +/* 16:56 | 8 */ int64_t a7 : 3; +@} /* total size: 24 bytes */ +@end smallexample + +Note how the offset information is now extended to also include how +many bits are left to be used in each bitfield. @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..aadcace517 --- /dev/null +++ b/gdb/testsuite/gdb.base/ptype-offsets.cc @@ -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/>. */ + +/* 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; +}; + +/* A struct with bitfields. */ + +struct tyu +{ + int a1 : 1; + + int a2 : 3; + + int a3 : 23; + + char a4 : 2; + + int64_t a5; + + int a6 : 5; + + int64_t a7 : 3; +}; + +int +main (int argc, char *argv[]) +{ + struct abc foo; + struct pqr bar; + union qwe c; + struct poi d; + struct tyu e; + uint8_t i; + + 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..74101ac5df --- /dev/null +++ b/gdb/testsuite/gdb.base/ptype-offsets.exp @@ -0,0 +1,189 @@ +# 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 printing a struct with several bitfields, laid out in various +# ways. +# +# Because dealing with bitfields and offsets is difficult, it can be +# tricky to confirm that the output of the this command is accurate. +# A nice way to do that is to use GDB's "x" command and print the +# actual memory layout of the struct. In order to differentiate +# between bitfields and non-bitfield variables, one can assign "-1" to +# every bitfield in the struct. An example of the output of "x" using +# "struct tyu" is: +# +# (gdb) x/24xb &e +# 0x7fffffffd540: 0xff 0xff 0xff 0x1f 0x00 0x00 0x00 0x00 +# 0x7fffffffd548: 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff +# 0x7fffffffd550: 0xff 0x00 0x00 0x00 0x00 0x00 0x00 0x00 +gdb_test "ptype /o struct tyu" \ + [multi_line \ +"/\\\* offset | size \\\*/" \ +"struct tyu {" \ +"/\\\* 0:31 | 4 \\\*/ int a1 : 1;" \ +"/\\\* 0:28 | 4 \\\*/ int a2 : 3;" \ +"/\\\* 0: 5 | 4 \\\*/ int a3 : 23;" \ +"/\\\* 3: 3 | 1 \\\*/ char a4 : 2;" \ +"/\\\* XXX 3-bit hole \\\*/" \ +"/\\\* XXX 4-byte hole \\\*/" \ +"/\\\* 8 | 8 \\\*/ int64_t a5;" \ +"/\\\* 16:27 | 4 \\\*/ int a6 : 5;" \ +"/\\\* 16:56 | 8 \\\*/ int64_t a7 : 3;" \ +"} /\\\* total size: 24 bytes \\\*/"] \ + "ptype offset struct tyu" + +# 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" diff --git a/gdb/typeprint.c b/gdb/typeprint.c index 9d9d6f5a49..72a05b2471 100644 --- a/gdb/typeprint.c +++ b/gdb/typeprint.c @@ -42,6 +42,11 @@ const struct type_print_options type_print_raw_options = 1, /* raw */ 1, /* print_methods */ 1, /* print_typedefs */ + 0, /* print_offsets */ + { + 0, /* offset_bitpos */ + 0, + }, 0, /* print_nested_type_limit */ NULL, /* local_typedefs */ NULL, /* global_table */ @@ -55,6 +60,11 @@ static struct type_print_options default_ptype_flags = 0, /* raw */ 1, /* print_methods */ 1, /* print_typedefs */ + 0, /* print_offsets */ + { + 0, /* offset_bitpos */ + 0, + }, 0, /* print_nested_type_limit */ NULL, /* local_typedefs */ NULL, /* global_table */ @@ -440,6 +450,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 +512,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 +777,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 a2058b0120..ba4ed8e292 100644 --- a/gdb/typeprint.h +++ b/gdb/typeprint.h @@ -24,6 +24,17 @@ struct ui_file; struct typedef_hash_table; struct ext_lang_type_printers; +struct print_offset_data +{ + /* 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 over + the offset of the outter struct. */ + unsigned int offset_bitpos; + + unsigned int endpos; +}; + struct type_print_options { /* True means that no special printing flags should apply. */ @@ -35,6 +46,11 @@ 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; + + struct print_offset_data po_data; + /* The number of nested type definitions to print. -1 == all. */ int print_nested_type_limit; ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH v5 2/2] Implement pahole-like 'ptype /o' option 2017-12-13 3:17 ` [PATCH v5 2/2] Implement pahole-like 'ptype /o' option Sergio Durigan Junior 2017-12-13 4:50 ` Simon Marchi @ 2017-12-13 16:17 ` Eli Zaretskii 2017-12-13 17:14 ` Sergio Durigan Junior 2017-12-13 16:19 ` Pedro Alves 2017-12-13 16:20 ` Pedro Alves 3 siblings, 1 reply; 75+ messages in thread From: Eli Zaretskii @ 2017-12-13 16:17 UTC (permalink / raw) To: Sergio Durigan Junior; +Cc: gdb-patches, tom, simon.marchi, palves, keiths > From: Sergio Durigan Junior <sergiodj@redhat.com> > Cc: Tom Tromey <tom@tromey.com>, Eli Zaretskii <eliz@gnu.org>, Simon Marchi <simon.marchi@ericsson.com>, Pedro Alves <palves@redhat.com>, Keith Seitz <keiths@redhat.com>, Sergio Durigan Junior <sergiodj@redhat.com> > Date: Tue, 12 Dec 2017 22:17:24 -0500 > > This commit implements the pahole-like '/o' option for 'ptype', which > +Issuing a @kbd{ptype /o struct tuv} would print: ^ Please insert "command" after the command, where I indicated. Otherwise the sentence reads awkward. > +Notice the format of the first column of comments. There, you can > +find two parts separated by the @code{|} character: the @emph{offset}, ^^^^^^^^ @samp, not @code. The documentation parts are okay with these fixed. Thanks. ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH v5 2/2] Implement pahole-like 'ptype /o' option 2017-12-13 16:17 ` Eli Zaretskii @ 2017-12-13 17:14 ` Sergio Durigan Junior 0 siblings, 0 replies; 75+ messages in thread From: Sergio Durigan Junior @ 2017-12-13 17:14 UTC (permalink / raw) To: Eli Zaretskii; +Cc: gdb-patches, tom, simon.marchi, palves, keiths On Wednesday, December 13 2017, Eli Zaretskii wrote: >> From: Sergio Durigan Junior <sergiodj@redhat.com> >> Cc: Tom Tromey <tom@tromey.com>, Eli Zaretskii <eliz@gnu.org>, Simon >> Marchi <simon.marchi@ericsson.com>, Pedro Alves <palves@redhat.com>, >> Keith Seitz <keiths@redhat.com>, Sergio Durigan Junior >> <sergiodj@redhat.com> >> Date: Tue, 12 Dec 2017 22:17:24 -0500 >> >> This commit implements the pahole-like '/o' option for 'ptype', which >> +Issuing a @kbd{ptype /o struct tuv} would print: > ^ > Please insert "command" after the command, where I indicated. > Otherwise the sentence reads awkward. Done. >> +Notice the format of the first column of comments. There, you can >> +find two parts separated by the @code{|} character: the @emph{offset}, > ^^^^^^^^ > @samp, not @code. Fixed. > The documentation parts are okay with these fixed. Thanks, -- Sergio GPG key ID: 237A 54B1 0287 28BF 00EF 31F4 D0EB 7628 65FC 5E36 Please send encrypted e-mail if possible http://sergiodj.net/ ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH v5 2/2] Implement pahole-like 'ptype /o' option 2017-12-13 3:17 ` [PATCH v5 2/2] Implement pahole-like 'ptype /o' option Sergio Durigan Junior 2017-12-13 4:50 ` Simon Marchi 2017-12-13 16:17 ` Eli Zaretskii @ 2017-12-13 16:19 ` Pedro Alves 2017-12-13 17:13 ` Sergio Durigan Junior 2017-12-13 16:20 ` Pedro Alves 3 siblings, 1 reply; 75+ messages in thread From: Pedro Alves @ 2017-12-13 16:19 UTC (permalink / raw) To: Sergio Durigan Junior, GDB Patches Cc: Tom Tromey, Eli Zaretskii, Simon Marchi, Keith Seitz On 12/13/2017 03:17 AM, Sergio Durigan Junior wrote: > +/* A struct with an union. */ > + > +struct poi > +{ > + int f1; > + > + union qwe f2; > + > + uint16_t f3; > + > + struct pqr f4; > +}; > + > +/* A struct with bitfields. */ > + > +struct tyu > +{ > + int a1 : 1; > + > + int a2 : 3; > + > + int a3 : 23; > + > + char a4 : 2; > + > + int64_t a5; > + > + int a6 : 5; > + > + int64_t a7 : 3; > +}; I think that the testcase should also make sure to exercise the new offset computations in the case c_print_type_struct_field_offset's 'offset_bitpos' parameter is > 0. Is it already covered? I assume we'll need a test like tyu (struct with bitfields with overlapping underlying objects), but that inherits some other base structure? > +++ b/gdb/testsuite/gdb.base/ptype-offsets.exp > @@ -0,0 +1,192 @@ > +# 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/>. > + Please add an intro comment describing what this testcase is about. > +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 > +} I'm mildly worried about whether the bitfield handling is working correctly on big endian machines. We may want to lift this x86-64-only restriction, by using e.g., alignas(N) or __attribute__((aligned(N)) to take care of most of the differences between architectures and end up with few per-arch code in the .exp. But I'm fine with starting with only x86-64 if you confirm manually on e.g., a big endian PPC64 machine on the compile farm, and we can extend the testcase in that direction after this is merged. > + > +if { [prepare_for_testing "failed to prepare" $testfile $srcfile \ > + { debug c++ optimize=-O0 }] } { > + return -1 > +} Weren't you going to remove that optimize thing? :-) > +# 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. "a union". (two times here; there may be other places.) > +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" Did you try using {} instead of "" for these strings, avoiding all the escaping? > @@ -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 && && goes on the next line. > + (TYPE_CODE (type) == TYPE_CODE_STRUCT > + || TYPE_CODE (type) == TYPE_CODE_UNION)) > + fprintf_filtered (gdb_stdout, "/* offset | size */\n"); > + > printf_filtered ("type = "); Thanks, Pedro Alves ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH v5 2/2] Implement pahole-like 'ptype /o' option 2017-12-13 16:19 ` Pedro Alves @ 2017-12-13 17:13 ` Sergio Durigan Junior 2017-12-13 20:36 ` Sergio Durigan Junior 0 siblings, 1 reply; 75+ messages in thread From: Sergio Durigan Junior @ 2017-12-13 17:13 UTC (permalink / raw) To: Pedro Alves Cc: GDB Patches, Tom Tromey, Eli Zaretskii, Simon Marchi, Keith Seitz On Wednesday, December 13 2017, Pedro Alves wrote: > On 12/13/2017 03:17 AM, Sergio Durigan Junior wrote: > >> +/* A struct with an union. */ >> + >> +struct poi >> +{ >> + int f1; >> + >> + union qwe f2; >> + >> + uint16_t f3; >> + >> + struct pqr f4; >> +}; >> + >> +/* A struct with bitfields. */ >> + >> +struct tyu >> +{ >> + int a1 : 1; >> + >> + int a2 : 3; >> + >> + int a3 : 23; >> + >> + char a4 : 2; >> + >> + int64_t a5; >> + >> + int a6 : 5; >> + >> + int64_t a7 : 3; >> +}; > > I think that the testcase should also make sure to exercise the new > offset computations in the case c_print_type_struct_field_offset's > 'offset_bitpos' parameter is > 0. Is it already covered? > I assume we'll need a test like tyu (struct with bitfields with > overlapping underlying objects), but that inherits some other > base structure? Ah, good point, I'll add this test. > >> +++ b/gdb/testsuite/gdb.base/ptype-offsets.exp >> @@ -0,0 +1,192 @@ >> +# 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/>. >> + > > Please add an intro comment describing what this testcase is about. OK. >> +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 >> +} > > I'm mildly worried about whether the bitfield handling is working > correctly on big endian machines. We may want to lift this > x86-64-only restriction, by using e.g., alignas(N) or > __attribute__((aligned(N)) to take care of most of the differences > between architectures and end up with few per-arch code in > the .exp. But I'm fine with starting with only x86-64 if you > confirm manually on e.g., a big endian PPC64 machine on the > compile farm, and we can extend the testcase in that direction > after this is merged. OK, I'll confirm on PPC64BE. >> + >> +if { [prepare_for_testing "failed to prepare" $testfile $srcfile \ >> + { debug c++ optimize=-O0 }] } { >> + return -1 >> +} > > Weren't you going to remove that optimize thing? :-) Yes, sorry. Removed. >> +# 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. > > "a union". (two times here; there may be other places.) Fixed. >> +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" > > Did you try using {} instead of "" for these strings, > avoiding all the escaping? Yes, Simon also made the same comment. I tried replacing by {} but it didn't work at the first attempt and since I was hacking other stuff at the moment I dind't bother tweaking it and just reverted to using "". If it's something you really want, I can do. Otherwise I'd prefer to leave it like that. >> @@ -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 && > > && goes on the next line. Fixed. >> + (TYPE_CODE (type) == TYPE_CODE_STRUCT >> + || TYPE_CODE (type) == TYPE_CODE_UNION)) >> + fprintf_filtered (gdb_stdout, "/* offset | size */\n"); >> + >> printf_filtered ("type = "); > > Thanks, > Pedro Alves Thanks, -- Sergio GPG key ID: 237A 54B1 0287 28BF 00EF 31F4 D0EB 7628 65FC 5E36 Please send encrypted e-mail if possible http://sergiodj.net/ ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH v5 2/2] Implement pahole-like 'ptype /o' option 2017-12-13 17:13 ` Sergio Durigan Junior @ 2017-12-13 20:36 ` Sergio Durigan Junior 2017-12-13 21:22 ` Pedro Alves 0 siblings, 1 reply; 75+ messages in thread From: Sergio Durigan Junior @ 2017-12-13 20:36 UTC (permalink / raw) To: Pedro Alves Cc: GDB Patches, Tom Tromey, Eli Zaretskii, Simon Marchi, Keith Seitz On Wednesday, December 13 2017, I wrote: > On Wednesday, December 13 2017, Pedro Alves wrote: > >> I'm mildly worried about whether the bitfield handling is working >> correctly on big endian machines. We may want to lift this >> x86-64-only restriction, by using e.g., alignas(N) or >> __attribute__((aligned(N)) to take care of most of the differences >> between architectures and end up with few per-arch code in >> the .exp. But I'm fine with starting with only x86-64 if you >> confirm manually on e.g., a big endian PPC64 machine on the >> compile farm, and we can extend the testcase in that direction >> after this is merged. > > OK, I'll confirm on PPC64BE. It seems like the algorithm for calculating bitfield offsets is not working correctly on BE machines. This is not only for "ptype /o", but also for regular print commands. For example, consider this test: struct tyu { int a1 : 1; int a2 : 3; int a3 : 23; char a4 : 2; int64_t a5; int a6 : 5; int64_t a7 : 3; }; int main (int argc, char *argv[]) { struct tyu e; e.a1 = e.a2 = e.a3 = e.a4 = e.a6 = e.a7 = -1; return 0; } After stopping GDB at the "return 0;" line, here's what we see when we print "e" on x86_64: (gdb) p e $1 = {a1 = -1, a2 = -1, a3 = -1, a4 = -1 '\377', a5 = 140737488344880, a6 = -1, a7 = -1} While on PPC64BE: (gdb) p e $1 = {a1 = -1, a2 = 3, a3 = 3, a4 = 3 '\003', a5 = 70367536153528, a6 = -1, a7 = -1} As for "ptype /o", the offsets printed on x86_64 and PPC64BE are the same: x86_64: /* offset | size */ type = struct tyu { /* 0:31 | 4 */ int a1 : 1; /* 0:28 | 4 */ int a2 : 3; /* 0: 5 | 4 */ int a3 : 23; /* 3: 3 | 1 */ char a4 : 2; /* XXX 3-bit hole */ /* XXX 4-byte hole */ /* 8 | 8 */ int64_t a5; /* 16:27 | 4 */ int a6 : 5; /* 16:56 | 8 */ int64_t a7 : 3; /* total size (bytes): 24 */ } PPC64BE: /* offset | size */ type = struct tyu { /* 0:31 | 4 */ int a1 : 1; /* 0:28 | 4 */ int a2 : 3; /* 0: 5 | 4 */ int a3 : 23; /* 3: 3 | 1 */ char a4 : 2; /* XXX 3-bit hole */ /* XXX 4-byte hole */ /* 8 | 8 */ int64_t a5; /* 16:27 | 4 */ int a6 : 5; /* 16:56 | 8 */ int64_t a7 : 3; /* total size (bytes): 24 */ } -- Sergio GPG key ID: 237A 54B1 0287 28BF 00EF 31F4 D0EB 7628 65FC 5E36 Please send encrypted e-mail if possible http://sergiodj.net/ ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH v5 2/2] Implement pahole-like 'ptype /o' option 2017-12-13 20:36 ` Sergio Durigan Junior @ 2017-12-13 21:22 ` Pedro Alves 2017-12-13 21:30 ` Pedro Alves 2017-12-13 21:34 ` Sergio Durigan Junior 0 siblings, 2 replies; 75+ messages in thread From: Pedro Alves @ 2017-12-13 21:22 UTC (permalink / raw) To: Sergio Durigan Junior Cc: GDB Patches, Tom Tromey, Eli Zaretskii, Simon Marchi, Keith Seitz On 12/13/2017 08:36 PM, Sergio Durigan Junior wrote: > On Wednesday, December 13 2017, I wrote: >> OK, I'll confirm on PPC64BE. Thanks. > > It seems like the algorithm for calculating bitfield offsets is not > working correctly on BE machines. This is not only for "ptype /o", but > also for regular print commands. For example, consider this test: > > struct tyu > { > int a1 : 1; > > int a2 : 3; > > int a3 : 23; > > char a4 : 2; > > int64_t a5; > > int a6 : 5; > > int64_t a7 : 3; > }; > > int > main (int argc, char *argv[]) > { > struct tyu e; > > e.a1 = e.a2 = e.a3 = e.a4 = e.a6 = e.a7 = -1; > > return 0; > } > > After stopping GDB at the "return 0;" line, here's what we see when we > print "e" on x86_64: > > (gdb) p e > $1 = {a1 = -1, a2 = -1, a3 = -1, a4 = -1 '\377', a5 = 140737488344880, a6 = -1, a7 = -1} > > While on PPC64BE: > > (gdb) p e > $1 = {a1 = -1, a2 = 3, a3 = 3, a4 = 3 '\003', a5 = 70367536153528, a6 = -1, a7 = -1} > You didn't initialize e.a5, so even the x86_64 version looks wrong at first. You're seeing stack/register garbage in the padding holes. You should make that "e" a global to make sure all its underlying bytes are clear, including padding. Or memset it. The former is easier. a2, a3 and a4 in the PPC64 version do look odd. Though maybe that's something do to with the expression you used. Does it make a difference if you initialize all fields with separate statements, like: e.a1 = -1; e.a2 = -1; etc. > As for "ptype /o", the offsets printed on x86_64 and PPC64BE are the > same: So it sounds like we could remove the x86-64 check in the testcase and let it run on all lp64 targets? Does it pass cleanly on PPC64? Thanks, Pedro Alves ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH v5 2/2] Implement pahole-like 'ptype /o' option 2017-12-13 21:22 ` Pedro Alves @ 2017-12-13 21:30 ` Pedro Alves 2017-12-13 21:34 ` Sergio Durigan Junior 1 sibling, 0 replies; 75+ messages in thread From: Pedro Alves @ 2017-12-13 21:30 UTC (permalink / raw) To: Sergio Durigan Junior Cc: GDB Patches, Tom Tromey, Eli Zaretskii, Simon Marchi, Keith Seitz On 12/13/2017 09:22 PM, Pedro Alves wrote: > On 12/13/2017 08:36 PM, Sergio Durigan Junior wrote: >> On Wednesday, December 13 2017, I wrote: > >>> OK, I'll confirm on PPC64BE. > > Thanks. > >> >> It seems like the algorithm for calculating bitfield offsets is not >> working correctly on BE machines. This is not only for "ptype /o", but >> also for regular print commands. For example, consider this test: >> >> struct tyu >> { >> int a1 : 1; >> >> int a2 : 3; >> >> int a3 : 23; >> >> char a4 : 2; >> >> int64_t a5; >> >> int a6 : 5; >> >> int64_t a7 : 3; >> }; >> >> int >> main (int argc, char *argv[]) >> { >> struct tyu e; >> >> e.a1 = e.a2 = e.a3 = e.a4 = e.a6 = e.a7 = -1; >> >> return 0; >> } >> >> After stopping GDB at the "return 0;" line, here's what we see when we >> print "e" on x86_64: >> >> (gdb) p e >> $1 = {a1 = -1, a2 = -1, a3 = -1, a4 = -1 '\377', a5 = 140737488344880, a6 = -1, a7 = -1} >> >> While on PPC64BE: >> >> (gdb) p e >> $1 = {a1 = -1, a2 = 3, a3 = 3, a4 = 3 '\003', a5 = 70367536153528, a6 = -1, a7 = -1} >> > > You didn't initialize e.a5, so even the x86_64 version looks > wrong at first. You're seeing stack/register garbage in > the padding holes. > > You should make that "e" a global to make sure all its > underlying bytes are clear, including padding. Or memset it. > The former is easier. > > a2, a3 and a4 in the PPC64 version do look odd. Though > maybe that's something do to with the expression you used. > > Does it make a difference if you initialize all fields > with separate statements, like: > > e.a1 = -1; > e.a2 = -1; > etc. Actually, I notice now that a4 is plain "char", not "signed char" and that looks like is the issue. Plain "char" is unsigned on PPC64. And then given an expression like: e.a1 = e.a2 = e.a3 = e.a4 ... e.a4 ends up with an unsigned value (3). And so a2 and a3 end up with the same value too (3), and then a1 ends up "-1" anyway because it's a 1-bit field, i.e., no matter what you put there, it ends up being -1, because all it fits in it is the sign bit. Thanks, Pedro Alves ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH v5 2/2] Implement pahole-like 'ptype /o' option 2017-12-13 21:22 ` Pedro Alves 2017-12-13 21:30 ` Pedro Alves @ 2017-12-13 21:34 ` Sergio Durigan Junior 1 sibling, 0 replies; 75+ messages in thread From: Sergio Durigan Junior @ 2017-12-13 21:34 UTC (permalink / raw) To: Pedro Alves Cc: GDB Patches, Tom Tromey, Eli Zaretskii, Simon Marchi, Keith Seitz On Wednesday, December 13 2017, Pedro Alves wrote: > On 12/13/2017 08:36 PM, Sergio Durigan Junior wrote: >> On Wednesday, December 13 2017, I wrote: > >>> OK, I'll confirm on PPC64BE. > > Thanks. > >> >> It seems like the algorithm for calculating bitfield offsets is not >> working correctly on BE machines. This is not only for "ptype /o", but >> also for regular print commands. For example, consider this test: >> >> struct tyu >> { >> int a1 : 1; >> >> int a2 : 3; >> >> int a3 : 23; >> >> char a4 : 2; >> >> int64_t a5; >> >> int a6 : 5; >> >> int64_t a7 : 3; >> }; >> >> int >> main (int argc, char *argv[]) >> { >> struct tyu e; >> >> e.a1 = e.a2 = e.a3 = e.a4 = e.a6 = e.a7 = -1; >> >> return 0; >> } >> >> After stopping GDB at the "return 0;" line, here's what we see when we >> print "e" on x86_64: >> >> (gdb) p e >> $1 = {a1 = -1, a2 = -1, a3 = -1, a4 = -1 '\377', a5 = 140737488344880, a6 = -1, a7 = -1} >> >> While on PPC64BE: >> >> (gdb) p e >> $1 = {a1 = -1, a2 = 3, a3 = 3, a4 = 3 '\003', a5 = 70367536153528, a6 = -1, a7 = -1} >> > > You didn't initialize e.a5, so even the x86_64 version looks > wrong at first. You're seeing stack/register garbage in > the padding holes. I should have initialized e.a5 to 0 in order to make the problem easier to spot. I did that now: $1 = {a1 = -1, a2 = 3, a3 = 3, a4 = 3 '\003', a5 = 0, a6 = -1, a7 = -1} > You should make that "e" a global to make sure all its > underlying bytes are clear, including padding. Or memset it. > The former is easier. > > a2, a3 and a4 in the PPC64 version do look odd. Though > maybe that's something do to with the expression you used. > > Does it make a difference if you initialize all fields > with separate statements, like: > > e.a1 = -1; > e.a2 = -1; > etc. After memset'ing the variable to 0, and separating all assignments, I get: [sergiodj@gcc1-power7 build-gdb]$ g++ -g ptype-offsets.cc ptype-offsets.cc: In function ‘int main(int, char**)’: ptype-offsets.cc:170:8: warning: large integer implicitly truncated to unsigned type [-Woverflow] e.a4 = -1; ^ $1 = {a1 = -1, a2 = -1, a3 = -1, a4 = 3 '\003', a5 = 0, a6 = -1, a7 = -1} After changing the declaration of a4 to "signed char a4 : 2;": $1 = {a1 = -1, a2 = -1, a3 = -1, a4 = -1 '\377', a5 = 0, a6 = -1, a7 = -1} >> As for "ptype /o", the offsets printed on x86_64 and PPC64BE are the >> same: > > So it sounds like we could remove the x86-64 check in the > testcase and let it run on all lp64 targets? Does it pass > cleanly on PPC64? I'm checking this right now, because I have to readjust the test due to the changes in the output format. -- Sergio GPG key ID: 237A 54B1 0287 28BF 00EF 31F4 D0EB 7628 65FC 5E36 Please send encrypted e-mail if possible http://sergiodj.net/ ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH v5 2/2] Implement pahole-like 'ptype /o' option 2017-12-13 3:17 ` [PATCH v5 2/2] Implement pahole-like 'ptype /o' option Sergio Durigan Junior ` (2 preceding siblings ...) 2017-12-13 16:19 ` Pedro Alves @ 2017-12-13 16:20 ` Pedro Alves 2017-12-13 17:41 ` Sergio Durigan Junior 3 siblings, 1 reply; 75+ messages in thread From: Pedro Alves @ 2017-12-13 16:20 UTC (permalink / raw) To: Sergio Durigan Junior, GDB Patches Cc: Tom Tromey, Eli Zaretskii, Simon Marchi, Keith Seitz [Sending user-interface suggestions separately from core functionality review.] On 12/13/2017 03:17 AM, Sergio Durigan Junior wrote: > (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 */ I've experimented with (different versions of) of this patch against gdb a bit, and I have to say that I'm finding myself a bit confused by the currently produced output. It feels like the output should be a bit more obvious and easier to skim quickly. For example, I was looking at gdb's minimal_symbol (what I had used when I noticed the bitfield handling in earlier versions was incorrect): (top-gdb) ptype/tom minimal_symbol /* offset | size */ type = struct minimal_symbol { /* 0 | 32 */ struct general_symbol_info { /* 0 | 8 */ const char *name; /* 8 | 8 */ union { /* 8 */ LONGEST ivalue; /* 8 */ const block *block; /* 8 */ const gdb_byte *bytes; /* 8 */ CORE_ADDR address; /* 8 */ const common_block *common_block; /* 8 */ symbol *chain; } /* total size: 8 bytes */ value; /* 16 | 8 */ union { /* 8 */ obstack *obstack; /* 8 */ const char *demangled_name; } /* total size: 8 bytes */ language_specific; /* 24:27 | 4 */ language language : 5; /* 24:26 | 4 */ unsigned int ada_mangled : 1; /* XXX 2-bit hole */ /* XXX 1-byte hole */ /* 26 | 2 */ short section; } /* total size: 32 bytes */ mginfo; /* 32 | 8 */ unsigned long size; /* 40 | 8 */ const char *filename; /* 48:28 | 4 */ minimal_symbol_type type : 4; /* 48:27 | 4 */ unsigned int created_by_gdb : 1; /* 48:26 | 4 */ unsigned int target_flag_1 : 1; /* 48:25 | 4 */ unsigned int target_flag_2 : 1; /* 48:24 | 4 */ unsigned int has_size : 1; /* XXX 7-byte hole */ /* 56 | 8 */ minimal_symbol *hash_next; /* 64 | 8 */ minimal_symbol *demangled_hash_next; } /* total size: 72 bytes */ (top-gdb) and I have the following observations: #1 - left vs right We print the extra offset/size info on the leftside of the screen, shifting the whole type to the right. If we were to put the new info on the right, then a "ptype S" vs "ptype/o S" would look mostly the same, except for the extra info that appears on the right, and it'd map better to what you'd write in actual code (who puts comments on the left side, right?) pahole(1) prints the info on the right side. Was there a particular reason gdb's version prints it on the left hand side? Maybe the Python version did that too (did it? I haven't tried it) because it'd be difficult with the Python implementation otherwise? But maybe it's just me. On to the next point in such case. #2 - shift "type =" header to the right as well? Currently, the leading "type =" and ending "}" lines are aligned on the left, along with the offset/size info. I think shifting those to the right as well might make the output nicer. The idea being that /o would split the output in two more-clearly-separated columns, with the new info panned hard on the left column, and old ptype info on the right column. I.e., currently we get: ~~~~~~~~~~~~~~~~~~~~~~~~~~~ (top-gdb) ptype/o minimal_symbol /* offset | size */ type = struct minimal_symbol { /* 0 | 32 */ struct general_symbol_info { /* 0 | 8 */ const char *name; ... } /* total size: 72 bytes */ ~~~~~~~~~~~~~~~~~~~~~~~~~~~ And I'd propose instead: ~~~~~~~~~~~~~~~~~~~~~~~~~~~ (top-gdb) ptype/o minimal_symbol /* offset | size */ type = struct minimal_symbol { /* 0 | 32 */ struct general_symbol_info { /* 0 | 8 */ const char *name; ... } /* total size: 72 bytes */ ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Or even saving one line: ~~~~~~~~~~~~~~~~~~~~~~~~~~~ (top-gdb) ptype/o minimal_symbol /* offset | size */ type = struct minimal_symbol { /* 0 | 32 */ struct general_symbol_info { /* 0 | 8 */ const char *name; ... } /* total size: 72 bytes */ ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Or in full (compare to the version further above): (top-gdb) ptype/o minimal_symbol /* offset | size */ type = struct minimal_symbol { /* 0 | 32 */ struct general_symbol_info { /* 0 | 8 */ const char *name; /* 8 | 8 */ union { /* 8 */ LONGEST ivalue; /* 8 */ const block *block; /* 8 */ const gdb_byte *bytes; /* 8 */ CORE_ADDR address; /* 8 */ const common_block *common_block; /* 8 */ symbol *chain; } /* total size: 8 bytes */ value; /* 16 | 8 */ union { /* 8 */ obstack *obstack; /* 8 */ const char *demangled_name; } /* total size: 8 bytes */ language_specific; /* 24:27 | 4 */ language language : 5; /* 24:26 | 4 */ unsigned int ada_mangled : 1; /* XXX 2-bit hole */ /* XXX 1-byte hole */ /* 26 | 2 */ short section; } /* total size: 32 bytes */ mginfo; /* 32 | 8 */ unsigned long size; /* 40 | 8 */ const char *filename; /* 48:28 | 4 */ minimal_symbol_type type : 4; /* 48:27 | 4 */ unsigned int created_by_gdb : 1; /* 48:26 | 4 */ unsigned int target_flag_1 : 1; /* 48:25 | 4 */ unsigned int target_flag_2 : 1; /* 48:24 | 4 */ unsigned int has_size : 1; /* XXX 7-byte hole */ /* 56 | 8 */ minimal_symbol *hash_next; /* 64 | 8 */ minimal_symbol *demangled_hash_next; } /* total size: 72 bytes */ (top-gdb) #3 - I also find the position of those "/* total size: 8 bytes */" markers a bit hard to grok/spot. I'd suggest considering putting them on the left column along with the rest of the offset/size info as well, like: (top-gdb) ptype/tom minimal_symbol /* offset | size */ type = struct minimal_symbol { /* 0 | 32 */ struct general_symbol_info { /* 0 | 8 */ const char *name; /* 8 | 8 */ union { /* 8 */ LONGEST ivalue; /* 8 */ const block *block; /* 8 */ const gdb_byte *bytes; /* 8 */ CORE_ADDR address; /* 8 */ const common_block *common_block; /* 8 */ symbol *chain; /* total size: 8 */ } value; /* 16 | 8 */ union { /* 8 */ obstack *obstack; /* 8 */ const char *demangled_name; /* total size: 8 */ } language_specific; /* 24:27 | 4 */ language language : 5; /* 24:26 | 4 */ unsigned int ada_mangled : 1; /* XXX 2-bit hole */ /* XXX 1-byte hole */ /* 26 | 2 */ short section; /* total size: 32 */ } mginfo; /* 32 | 8 */ unsigned long size; /* 40 | 8 */ const char *filename; /* 48:28 | 4 */ minimal_symbol_type type : 4; /* 48:27 | 4 */ unsigned int created_by_gdb : 1; /* 48:26 | 4 */ unsigned int target_flag_1 : 1; /* 48:25 | 4 */ unsigned int target_flag_2 : 1; /* 48:24 | 4 */ unsigned int has_size : 1; /* XXX 7-byte hole */ /* 56 | 8 */ minimal_symbol *hash_next; /* 64 | 8 */ minimal_symbol *demangled_hash_next; } /* total size: 72 bytes */ (top-gdb) Or if you don't like it, consider putting that "total size:" info on a separate line before the struct/union is closed, like pahole does: /* size: 128, cachelines: 2, members: 4 */ /* sum members: 124, holes: 1, sum holes: 4 */ }; That may be even better because it provides a place to put extra info, like pahole does (cacheline info etc.). Thanks, Pedro Alves ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH v5 2/2] Implement pahole-like 'ptype /o' option 2017-12-13 16:20 ` Pedro Alves @ 2017-12-13 17:41 ` Sergio Durigan Junior 0 siblings, 0 replies; 75+ messages in thread From: Sergio Durigan Junior @ 2017-12-13 17:41 UTC (permalink / raw) To: Pedro Alves Cc: GDB Patches, Tom Tromey, Eli Zaretskii, Simon Marchi, Keith Seitz On Wednesday, December 13 2017, Pedro Alves wrote: > [Sending user-interface suggestions separately from core > functionality review.] Thanks. > On 12/13/2017 03:17 AM, Sergio Durigan Junior wrote: >> (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 */ > > I've experimented with (different versions of) of this patch > against gdb a bit, and I have to say that I'm finding myself > a bit confused by the currently produced output. It feels > like the output should be a bit more obvious and easier to > skim quickly. > > For example, I was looking at gdb's minimal_symbol (what I had > used when I noticed the bitfield handling in earlier versions was > incorrect): > > (top-gdb) ptype/tom minimal_symbol > /* offset | size */ > type = struct minimal_symbol { > /* 0 | 32 */ struct general_symbol_info { > /* 0 | 8 */ const char *name; > /* 8 | 8 */ union { > /* 8 */ LONGEST ivalue; > /* 8 */ const block *block; > /* 8 */ const gdb_byte *bytes; > /* 8 */ CORE_ADDR address; > /* 8 */ const common_block *common_block; > /* 8 */ symbol *chain; > } /* total size: 8 bytes */ value; > /* 16 | 8 */ union { > /* 8 */ obstack *obstack; > /* 8 */ const char *demangled_name; > } /* total size: 8 bytes */ language_specific; > /* 24:27 | 4 */ language language : 5; > /* 24:26 | 4 */ unsigned int ada_mangled : 1; > /* XXX 2-bit hole */ > /* XXX 1-byte hole */ > /* 26 | 2 */ short section; > } /* total size: 32 bytes */ mginfo; > /* 32 | 8 */ unsigned long size; > /* 40 | 8 */ const char *filename; > /* 48:28 | 4 */ minimal_symbol_type type : 4; > /* 48:27 | 4 */ unsigned int created_by_gdb : 1; > /* 48:26 | 4 */ unsigned int target_flag_1 : 1; > /* 48:25 | 4 */ unsigned int target_flag_2 : 1; > /* 48:24 | 4 */ unsigned int has_size : 1; > /* XXX 7-byte hole */ > /* 56 | 8 */ minimal_symbol *hash_next; > /* 64 | 8 */ minimal_symbol *demangled_hash_next; > } /* total size: 72 bytes */ > (top-gdb) > > and I have the following observations: > > #1 - left vs right > > We print the extra offset/size info on the leftside of the > screen, shifting the whole type to the right. If we were to put > the new info on the right, then a "ptype S" vs "ptype/o S" would > look mostly the same, except for the extra info that appears > on the right, and it'd map better to what you'd write in > actual code (who puts comments on the left side, right?) > pahole(1) prints the info on the right side. Was there a > particular reason gdb's version prints it on the left hand > side? Maybe the Python version did that too (did it? I haven't > tried it) because it'd be difficult with the Python > implementation otherwise? I agree, and when I started working on this feature the first plan was to print things on the right. I even had a patch that did that. But the main problem was that it was hard to properly align the /* */ blocks. For example, "ptype /o" prints nested structures, which means that the we may print things that are aligned at level + 8, level + 12, or even further. This would make the /* */ blocks be aligned differently, which IMHO makes the output much harder to read. pahole (the tool) solves that by printing nested structures separately, but if we were to do that we'd need a bit refactorization of the current code. About the Python version of pahole, it also prints the /* */ blocks on the left side, though in an uglier way IMHO: (gdb) pahole struct wer struct wer { /* 0 24 */ struct tuv { /* 0 4 */ int a1 /* XXX 32 bit hole, try to pack */ /* 8 8 */ char * a2 /* 16 4 */ int a3 } tuv /* 24 24 */ struct tyu { /* 0 0 */ int a1 /* 0 0 */ int a2 /* 0 2 */ int a3 /* 3 0 */ char a4 /* XXX 35 bit hole, try to pack */ /* 8 8 */ long a5 /* 16 0 */ int a6 /* 16 0 */ long a7 } a1 } > But maybe it's just me. On to the next point in > such case. > > #2 - shift "type =" header to the right as well? > > Currently, the leading "type =" and ending "}" lines > are aligned on the left, along with the offset/size info. > I think shifting those to the right as well might make the > output nicer. The idea being that /o would split the > output in two more-clearly-separated columns, with the new info > panned hard on the left column, and old ptype info on > the right column. > > I.e., currently we get: > > ~~~~~~~~~~~~~~~~~~~~~~~~~~~ > (top-gdb) ptype/o minimal_symbol > /* offset | size */ > type = struct minimal_symbol { > /* 0 | 32 */ struct general_symbol_info { > /* 0 | 8 */ const char *name; > ... > } /* total size: 72 bytes */ > ~~~~~~~~~~~~~~~~~~~~~~~~~~~ > > And I'd propose instead: > > ~~~~~~~~~~~~~~~~~~~~~~~~~~~ > (top-gdb) ptype/o minimal_symbol > /* offset | size */ > type = struct minimal_symbol { > /* 0 | 32 */ struct general_symbol_info { > /* 0 | 8 */ const char *name; > ... > } /* total size: 72 bytes */ > ~~~~~~~~~~~~~~~~~~~~~~~~~~~ > > Or even saving one line: > > ~~~~~~~~~~~~~~~~~~~~~~~~~~~ > (top-gdb) ptype/o minimal_symbol > /* offset | size */ type = struct minimal_symbol { > /* 0 | 32 */ struct general_symbol_info { > /* 0 | 8 */ const char *name; > ... > } /* total size: 72 bytes */ > ~~~~~~~~~~~~~~~~~~~~~~~~~~~ > > > Or in full (compare to the version further above): > > (top-gdb) ptype/o minimal_symbol > /* offset | size */ type = struct minimal_symbol { > /* 0 | 32 */ struct general_symbol_info { > /* 0 | 8 */ const char *name; > /* 8 | 8 */ union { 0> /* 8 */ LONGEST ivalue; > /* 8 */ const block *block; > /* 8 */ const gdb_byte *bytes; > /* 8 */ CORE_ADDR address; > /* 8 */ const common_block *common_block; > /* 8 */ symbol *chain; > } /* total size: 8 bytes */ value; > /* 16 | 8 */ union { > /* 8 */ obstack *obstack; > /* 8 */ const char *demangled_name; > } /* total size: 8 bytes */ language_specific; > /* 24:27 | 4 */ language language : 5; > /* 24:26 | 4 */ unsigned int ada_mangled : 1; > /* XXX 2-bit hole */ > /* XXX 1-byte hole */ > /* 26 | 2 */ short section; > } /* total size: 32 bytes */ mginfo; > /* 32 | 8 */ unsigned long size; > /* 40 | 8 */ const char *filename; > /* 48:28 | 4 */ minimal_symbol_type type : 4; > /* 48:27 | 4 */ unsigned int created_by_gdb : 1; > /* 48:26 | 4 */ unsigned int target_flag_1 : 1; > /* 48:25 | 4 */ unsigned int target_flag_2 : 1; > /* 48:24 | 4 */ unsigned int has_size : 1; > /* XXX 7-byte hole */ > /* 56 | 8 */ minimal_symbol *hash_next; > /* 64 | 8 */ minimal_symbol *demangled_hash_next; > } /* total size: 72 bytes */ > (top-gdb) Yeah, this makes sense. I'll see about doing this. > #3 - I also find the position of those > "/* total size: 8 bytes */" markers a bit hard > to grok/spot. > > I'd suggest considering putting them on the left column > along with the rest of the offset/size info as well, like: > > (top-gdb) ptype/tom minimal_symbol > /* offset | size */ > type = struct minimal_symbol { > /* 0 | 32 */ struct general_symbol_info { > /* 0 | 8 */ const char *name; > /* 8 | 8 */ union { > /* 8 */ LONGEST ivalue; > /* 8 */ const block *block; > /* 8 */ const gdb_byte *bytes; > /* 8 */ CORE_ADDR address; > /* 8 */ const common_block *common_block; > /* 8 */ symbol *chain; > /* total size: 8 */ } value; > /* 16 | 8 */ union { > /* 8 */ obstack *obstack; > /* 8 */ const char *demangled_name; > /* total size: 8 */ } language_specific; > /* 24:27 | 4 */ language language : 5; > /* 24:26 | 4 */ unsigned int ada_mangled : 1; > /* XXX 2-bit hole */ > /* XXX 1-byte hole */ > /* 26 | 2 */ short section; > /* total size: 32 */ } mginfo; > /* 32 | 8 */ unsigned long size; > /* 40 | 8 */ const char *filename; > /* 48:28 | 4 */ minimal_symbol_type type : 4; > /* 48:27 | 4 */ unsigned int created_by_gdb : 1; > /* 48:26 | 4 */ unsigned int target_flag_1 : 1; > /* 48:25 | 4 */ unsigned int target_flag_2 : 1; > /* 48:24 | 4 */ unsigned int has_size : 1; > /* XXX 7-byte hole */ > /* 56 | 8 */ minimal_symbol *hash_next; > /* 64 | 8 */ minimal_symbol *demangled_hash_next; > } /* total size: 72 bytes */ > (top-gdb) > > Or if you don't like it, consider putting that > "total size:" info on a separate line before the struct/union > is closed, like pahole does: > > /* size: 128, cachelines: 2, members: 4 */ > /* sum members: 124, holes: 1, sum holes: 4 */ > }; > > That may be even better because it provides a place > to put extra info, like pahole does (cacheline info etc.). I like the idea of putting them on a separate line; not sure how hard that will be. I'll take a look at implemeting this. Thanks, -- Sergio GPG key ID: 237A 54B1 0287 28BF 00EF 31F4 D0EB 7628 65FC 5E36 Please send encrypted e-mail if possible http://sergiodj.net/ ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v5 1/2] Reorganize code to handle TYPE_CODE_{STRUCT,UNION} on 'c_type_print_base' 2017-12-13 3:17 ` [PATCH v5 0/2] Implement pahole-like 'ptype /o' option (and do some code reorg) Sergio Durigan Junior 2017-12-13 3:17 ` [PATCH v5 2/2] Implement pahole-like 'ptype /o' option Sergio Durigan Junior @ 2017-12-13 3:17 ` Sergio Durigan Junior 1 sibling, 0 replies; 75+ messages in thread From: Sergio Durigan Junior @ 2017-12-13 3:17 UTC (permalink / raw) To: GDB Patches Cc: Tom Tromey, Eli Zaretskii, Simon Marchi, Pedro Alves, Keith Seitz, Sergio Durigan Junior While doing the 'ptype /o' work, I noticed that 'c_type_print_base' was very long, with a big amount of code just to handle the case of TYPE_CODE_{STRUCT,UNION}. This made working with the function a bit difficult, specially because of the level of indentation. This commit moves this part of the code to their own functions. Now we have a 'c_type_print_base_struct_union' with most of the code, and also 'need_access_label_p', which is a subset of the code that was also a good candidate for having its own function. gdb/ChangeLog: yyyy-mm-dd Sergio Durigan Junior <sergiodj@redhat.com> * c-typeprint.c (need_access_label_p): New function. (c_type_print_base_struct_union): New function. (c_type_print_base): Move code to handle TYPE_CODE_{STRUCT,UNION} to the functions mentioned above. --- gdb/c-typeprint.c | 838 ++++++++++++++++++++++++++---------------------------- 1 file changed, 404 insertions(+), 434 deletions(-) diff --git a/gdb/c-typeprint.c b/gdb/c-typeprint.c index f3c3e7d706..ce10b5e005 100644 --- a/gdb/c-typeprint.c +++ b/gdb/c-typeprint.c @@ -875,6 +875,407 @@ output_access_specifier (struct ui_file *stream, return last_access; } +/* 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) +{ + if (TYPE_DECLARED_CLASS (type)) + { + QUIT; + for (int i = TYPE_N_BASECLASSES (type); i < TYPE_NFIELDS (type); i++) + if (!TYPE_FIELD_PRIVATE (type, i)) + return true; + QUIT; + for (int j = 0; j < TYPE_NFN_FIELDS (type); j++) + for (int i = 0; i < TYPE_FN_FIELDLIST_LENGTH (type, j); i++) + if (!TYPE_FN_FIELD_PRIVATE (TYPE_FN_FIELDLIST1 (type, + j), i)) + return true; + QUIT; + for (int i = 0; i < TYPE_TYPEDEF_FIELD_COUNT (type); ++i) + if (!TYPE_TYPEDEF_FIELD_PRIVATE (type, i)) + return true; + } + else + { + QUIT; + for (int i = TYPE_N_BASECLASSES (type); i < TYPE_NFIELDS (type); i++) + if (TYPE_FIELD_PRIVATE (type, i) || TYPE_FIELD_PROTECTED (type, i)) + return true; + QUIT; + for (int j = 0; j < TYPE_NFN_FIELDS (type); j++) + { + QUIT; + for (int i = 0; i < TYPE_FN_FIELDLIST_LENGTH (type, j); i++) + if (TYPE_FN_FIELD_PROTECTED (TYPE_FN_FIELDLIST1 (type, + j), i) + || TYPE_FN_FIELD_PRIVATE (TYPE_FN_FIELDLIST1 (type, + j), + i)) + return true; + } + QUIT; + for (int i = 0; i < TYPE_TYPEDEF_FIELD_COUNT (type); ++i) + if (TYPE_TYPEDEF_FIELD_PROTECTED (type, i) + || TYPE_TYPEDEF_FIELD_PRIVATE (type, i)) + return true; + } + + return false; +} + +/* 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; + 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. */ + + 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); + vptr_fieldno = get_vptr_fieldno (type, &basetype); + 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)); + } + + 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; + 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)); + + 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 out nested types. */ + if (TYPE_NESTED_TYPES_COUNT (type) != 0 + && semi_local_flags.print_nested_type_limit != 0) + { + if (semi_local_flags.print_nested_type_limit > 0) + --semi_local_flags.print_nested_type_limit; + + if (TYPE_NFIELDS (type) != 0 || TYPE_NFN_FIELDS (type) != 0) + fprintf_filtered (stream, "\n"); + + for (int i = 0; i < TYPE_NESTED_TYPES_COUNT (type); ++i) + { + print_spaces_filtered (level + 4, stream); + c_print_type (TYPE_NESTED_TYPES_FIELD_TYPE (type, i), + "", stream, show, level + 4, &semi_local_flags); + 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 + || TYPE_NESTED_TYPES_COUNT (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)); + } + 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); +} + /* 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 +1299,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; @@ -958,436 +1357,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 out nested types. */ - if (TYPE_NESTED_TYPES_COUNT (type) != 0 - && semi_local_flags.print_nested_type_limit != 0) - { - if (semi_local_flags.print_nested_type_limit > 0) - --semi_local_flags.print_nested_type_limit; - - if (TYPE_NFIELDS (type) != 0 || TYPE_NFN_FIELDS (type) != 0) - fprintf_filtered (stream, "\n"); - - for (i = 0; i < TYPE_NESTED_TYPES_COUNT (type); ++i) - { - print_spaces_filtered (level + 4, stream); - c_print_type (TYPE_NESTED_TYPES_FIELD_TYPE (type, i), - "", stream, show, level + 4, &semi_local_flags); - 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 - || TYPE_NESTED_TYPES_COUNT (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: -- 2.14.3 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v6 0/2] Implement pahole-like 'ptype /o' option (and do some code reorg) 2017-11-21 16:07 [PATCH] Implement pahole-like 'ptype /o' option Sergio Durigan Junior ` (6 preceding siblings ...) 2017-12-13 3:17 ` [PATCH v5 0/2] Implement pahole-like 'ptype /o' option (and do some code reorg) Sergio Durigan Junior @ 2017-12-14 2:48 ` Sergio Durigan Junior 2017-12-14 2:48 ` [PATCH v6 1/2] Reorganize code to handle TYPE_CODE_{STRUCT,UNION} on 'c_type_print_base' Sergio Durigan Junior 2017-12-14 2:48 ` [PATCH v6 2/2] Implement pahole-like 'ptype /o' option Sergio Durigan Junior 2017-12-15 1:12 ` [PATCH v7 0/2] Implement pahole-like 'ptype /o' option (and do some code reorg) Sergio Durigan Junior 8 siblings, 2 replies; 75+ messages in thread From: Sergio Durigan Junior @ 2017-12-14 2:48 UTC (permalink / raw) To: GDB Patches Cc: Tom Tromey, Eli Zaretskii, Simon Marchi, Pedro Alves, Keith Seitz Changes from v5: - Dropped support for vtable; too complicated for now, will tackle this later. - Fixed documentation issues. - Improved output format based on Pedro's suggestions. - "ptype /o" now defaults to "/tom"; added note on docs about this. - Added a few more tests. Added a description to the testcase. Use {} instead of "" for strings. Removed "optimize=-O0". ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v6 1/2] Reorganize code to handle TYPE_CODE_{STRUCT,UNION} on 'c_type_print_base' 2017-12-14 2:48 ` [PATCH v6 0/2] Implement pahole-like 'ptype /o' option (and do some code reorg) Sergio Durigan Junior @ 2017-12-14 2:48 ` Sergio Durigan Junior 2017-12-14 2:48 ` [PATCH v6 2/2] Implement pahole-like 'ptype /o' option Sergio Durigan Junior 1 sibling, 0 replies; 75+ messages in thread From: Sergio Durigan Junior @ 2017-12-14 2:48 UTC (permalink / raw) To: GDB Patches Cc: Tom Tromey, Eli Zaretskii, Simon Marchi, Pedro Alves, Keith Seitz, Sergio Durigan Junior While doing the 'ptype /o' work, I noticed that 'c_type_print_base' was very long, with a big amount of code just to handle the case of TYPE_CODE_{STRUCT,UNION}. This made working with the function a bit difficult, specially because of the level of indentation. This commit moves this part of the code to their own functions. Now we have a 'c_type_print_base_struct_union' with most of the code, and also 'need_access_label_p', which is a subset of the code that was also a good candidate for having its own function. gdb/ChangeLog: yyyy-mm-dd Sergio Durigan Junior <sergiodj@redhat.com> * c-typeprint.c (need_access_label_p): New function. (c_type_print_base_struct_union): New function. (c_type_print_base): Move code to handle TYPE_CODE_{STRUCT,UNION} to the functions mentioned above. --- gdb/c-typeprint.c | 837 ++++++++++++++++++++++++++---------------------------- 1 file changed, 403 insertions(+), 434 deletions(-) diff --git a/gdb/c-typeprint.c b/gdb/c-typeprint.c index f3c3e7d706..3854886387 100644 --- a/gdb/c-typeprint.c +++ b/gdb/c-typeprint.c @@ -875,6 +875,407 @@ output_access_specifier (struct ui_file *stream, return last_access; } +/* 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) +{ + if (TYPE_DECLARED_CLASS (type)) + { + QUIT; + for (int i = TYPE_N_BASECLASSES (type); i < TYPE_NFIELDS (type); i++) + if (!TYPE_FIELD_PRIVATE (type, i)) + return true; + QUIT; + for (int j = 0; j < TYPE_NFN_FIELDS (type); j++) + for (int i = 0; i < TYPE_FN_FIELDLIST_LENGTH (type, j); i++) + if (!TYPE_FN_FIELD_PRIVATE (TYPE_FN_FIELDLIST1 (type, + j), i)) + return true; + QUIT; + for (int i = 0; i < TYPE_TYPEDEF_FIELD_COUNT (type); ++i) + if (!TYPE_TYPEDEF_FIELD_PRIVATE (type, i)) + return true; + } + else + { + QUIT; + for (int i = TYPE_N_BASECLASSES (type); i < TYPE_NFIELDS (type); i++) + if (TYPE_FIELD_PRIVATE (type, i) || TYPE_FIELD_PROTECTED (type, i)) + return true; + QUIT; + for (int j = 0; j < TYPE_NFN_FIELDS (type); j++) + { + QUIT; + for (int i = 0; i < TYPE_FN_FIELDLIST_LENGTH (type, j); i++) + if (TYPE_FN_FIELD_PROTECTED (TYPE_FN_FIELDLIST1 (type, + j), i) + || TYPE_FN_FIELD_PRIVATE (TYPE_FN_FIELDLIST1 (type, + j), + i)) + return true; + } + QUIT; + for (int i = 0; i < TYPE_TYPEDEF_FIELD_COUNT (type); ++i) + if (TYPE_TYPEDEF_FIELD_PROTECTED (type, i) + || TYPE_TYPEDEF_FIELD_PRIVATE (type, i)) + return true; + } + + return false; +} + +/* 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; + 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. */ + + 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); + vptr_fieldno = get_vptr_fieldno (type, &basetype); + 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)); + } + + 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; + 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)); + + 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 out nested types. */ + if (TYPE_NESTED_TYPES_COUNT (type) != 0 + && semi_local_flags.print_nested_type_limit != 0) + { + if (semi_local_flags.print_nested_type_limit > 0) + --semi_local_flags.print_nested_type_limit; + + if (TYPE_NFIELDS (type) != 0 || TYPE_NFN_FIELDS (type) != 0) + fprintf_filtered (stream, "\n"); + + for (int i = 0; i < TYPE_NESTED_TYPES_COUNT (type); ++i) + { + print_spaces_filtered (level + 4, stream); + c_print_type (TYPE_NESTED_TYPES_FIELD_TYPE (type, i), + "", stream, show, level + 4, &semi_local_flags); + 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 + || TYPE_NESTED_TYPES_COUNT (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)); + } + 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); +} + /* 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 +1299,7 @@ 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; QUIT; @@ -958,436 +1356,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 out nested types. */ - if (TYPE_NESTED_TYPES_COUNT (type) != 0 - && semi_local_flags.print_nested_type_limit != 0) - { - if (semi_local_flags.print_nested_type_limit > 0) - --semi_local_flags.print_nested_type_limit; - - if (TYPE_NFIELDS (type) != 0 || TYPE_NFN_FIELDS (type) != 0) - fprintf_filtered (stream, "\n"); - - for (i = 0; i < TYPE_NESTED_TYPES_COUNT (type); ++i) - { - print_spaces_filtered (level + 4, stream); - c_print_type (TYPE_NESTED_TYPES_FIELD_TYPE (type, i), - "", stream, show, level + 4, &semi_local_flags); - 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 - || TYPE_NESTED_TYPES_COUNT (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: -- 2.14.3 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v6 2/2] Implement pahole-like 'ptype /o' option 2017-12-14 2:48 ` [PATCH v6 0/2] Implement pahole-like 'ptype /o' option (and do some code reorg) Sergio Durigan Junior 2017-12-14 2:48 ` [PATCH v6 1/2] Reorganize code to handle TYPE_CODE_{STRUCT,UNION} on 'c_type_print_base' Sergio Durigan Junior @ 2017-12-14 2:48 ` Sergio Durigan Junior 2017-12-14 14:19 ` Pedro Alves ` (2 more replies) 1 sibling, 3 replies; 75+ messages in thread From: Sergio Durigan Junior @ 2017-12-14 2:48 UTC (permalink / raw) To: GDB Patches Cc: Tom Tromey, Eli Zaretskii, Simon Marchi, Pedro Alves, Keith Seitz, Sergio Durigan Junior 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: /* offset | size */ type = struct wer : public tuv { public: /* 32 | 24 */ struct tyu { /* 32:31 | 4 */ int a1 : 1; /* 32:28 | 4 */ int a2 : 3; /* 32: 5 | 4 */ int a3 : 23; /* 35: 3 | 1 */ char a4 : 2; /* XXX 3-bit hole */ /* XXX 4-byte hole */ /* 40 | 8 */ int64_t a5; /* 48:27 | 4 */ int a6 : 5; /* 48:56 | 8 */ int64_t a7 : 3; /* total size (bytes): 24 */ } a1; /* total size (bytes): 56 */ } A big part of this patch handles the formatting logic of 'ptype', which is a bit messy. The code to handle bitfield offsets, however, took some time to craft. My thanks to Pedro Alves for figuring things out and pointing me to the right direction, as well as coming up with a way to inspect the layout of structs with bitfields (see testcase for comments). After many discussions both on IRC and at the mailing list, I tried to implement printing vtables and inherited classes. Unfortunately the code grew too complex and there were still a few corner cases failing so I had to drop the attempt. This should be implemented in a future patch. 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: yyyy-mm-dd Sergio Durigan Junior <sergiodj@redhat.com> Pedro Alves <palves@redhat.com> PR cli/16224 * NEWS (Changes since GDB 8.0): Mention new '/o' flag. * c-typeprint.c (OFFSET_SPC_LEN): New define. (c_type_print_varspec_prefix): New argument 'struct print_offset_data *'. (c_type_print_base_1): New function and prototype. (c_print_type_1): New function, with code from 'c_print_type'. (c_print_type): Use 'c_print_type_1'. (c_type_print_varspec_prefix): New argument 'struct print_offset_data *'. Use it. Call 'c_type_print_base_1' instead of 'c_print_type_base'. (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_vtable_offset_marker): New function. (c_print_type_union_field_offset): New function. (c_print_type_struct_field_offset): New function. (c_type_print_base_struct_union): New argument 'struct print_offset_data *'. Print offsets and sizes for struct/union/class fields. * typeprint.c (const struct type_print_options type_print_raw_options): Initialize 'print_offsets'. (static struct type_print_options default_ptype_flags): Likewise. (struct print_offset_data print_offset_default_data): New variable. (whatis_exp): Handle '/o' option. (_initialize_typeprint): Add '/o' flag to ptype's help. * typeprint.h (struct print_offset_data): New struct. (struct type_print_options) <print_offsets>: New field. gdb/testsuite/ChangeLog: yyyy-mm-dd 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: yyyy-mm-dd Sergio Durigan Junior <sergiodj@redhat.com> PR cli/16224 * gdb.texinfo (ptype): Add documentation for new flag '/o'. --- gdb/NEWS | 3 + gdb/c-typeprint.c | 380 +++++++++++++++++++++++++------ gdb/doc/gdb.texinfo | 115 ++++++++++ gdb/testsuite/gdb.base/ptype-offsets.cc | 158 +++++++++++++ gdb/testsuite/gdb.base/ptype-offsets.exp | 223 ++++++++++++++++++ gdb/typeprint.c | 23 +- gdb/typeprint.h | 19 ++ 7 files changed, 855 insertions(+), 66 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 bd5ae36dd0..3a7e704cc6 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. + * 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. diff --git a/gdb/c-typeprint.c b/gdb/c-typeprint.c index 3854886387..f85347f03e 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 @@ -45,12 +53,18 @@ enum access_specifier static void c_type_print_varspec_prefix (struct type *, struct ui_file *, int, int, int, - const struct type_print_options *); + const struct type_print_options *, + struct print_offset_data *); /* Print "const", "volatile", or address space modifiers. */ static void c_type_print_modifier (struct type *, struct ui_file *, int, int); + +static void c_type_print_base_1 (struct type *type, struct ui_file *stream, + int show, int level, + const struct type_print_options *flags, + struct print_offset_data *podata); \f /* A callback function for cp_canonicalize_string_full that uses @@ -82,14 +96,15 @@ print_name_maybe_canonical (const char *name, \f -/* LEVEL is the depth to indent lines by. */ +/* Helper function for c_print_type. */ -void -c_print_type (struct type *type, - const char *varstring, - struct ui_file *stream, - int show, int level, - const struct type_print_options *flags) +static void +c_print_type_1 (struct type *type, + const char *varstring, + struct ui_file *stream, + int show, int level, + const struct type_print_options *flags, + struct print_offset_data *podata) { enum type_code code; int demangled_args; @@ -108,7 +123,7 @@ c_print_type (struct type *type, } else { - c_type_print_base (type, stream, show, level, flags); + c_type_print_base_1 (type, stream, show, level, flags, podata); code = TYPE_CODE (type); if ((varstring != NULL && *varstring != '\0') /* Need a space if going to print stars or brackets; @@ -124,7 +139,7 @@ c_print_type (struct type *type, fputs_filtered (" ", stream); need_post_space = (varstring != NULL && strcmp (varstring, "") != 0); c_type_print_varspec_prefix (type, stream, show, 0, need_post_space, - flags); + flags, podata); } if (varstring != NULL) @@ -143,6 +158,20 @@ c_print_type (struct type *type, } } +/* LEVEL is the depth to indent lines by. */ + +void +c_print_type (struct type *type, + const char *varstring, + struct ui_file *stream, + int show, int level, + const struct type_print_options *flags) +{ + struct print_offset_data podata = print_offset_default_data; + + c_print_type_1 (type, varstring, stream, show, level, flags, &podata); +} + /* Print a typedef using C syntax. TYPE is the underlying type. NEW_SYMBOL is the symbol naming the type. STREAM is the stream on which to print. */ @@ -310,7 +339,8 @@ c_type_print_varspec_prefix (struct type *type, struct ui_file *stream, int show, int passed_a_ptr, int need_post_space, - const struct type_print_options *flags) + const struct type_print_options *flags, + struct print_offset_data *podata) { const char *name; @@ -326,40 +356,40 @@ c_type_print_varspec_prefix (struct type *type, { case TYPE_CODE_PTR: c_type_print_varspec_prefix (TYPE_TARGET_TYPE (type), - stream, show, 1, 1, flags); + stream, show, 1, 1, flags, podata); fprintf_filtered (stream, "*"); c_type_print_modifier (type, stream, 1, need_post_space); break; case TYPE_CODE_MEMBERPTR: c_type_print_varspec_prefix (TYPE_TARGET_TYPE (type), - stream, show, 0, 0, flags); + stream, show, 0, 0, flags, podata); name = type_name_no_tag (TYPE_SELF_TYPE (type)); if (name) print_name_maybe_canonical (name, flags, stream); else - c_type_print_base (TYPE_SELF_TYPE (type), - stream, -1, passed_a_ptr, flags); + c_type_print_base_1 (TYPE_SELF_TYPE (type), + stream, -1, passed_a_ptr, flags, podata); fprintf_filtered (stream, "::*"); break; case TYPE_CODE_METHODPTR: c_type_print_varspec_prefix (TYPE_TARGET_TYPE (type), - stream, show, 0, 0, flags); + stream, show, 0, 0, flags, podata); fprintf_filtered (stream, "("); name = type_name_no_tag (TYPE_SELF_TYPE (type)); if (name) print_name_maybe_canonical (name, flags, stream); else - c_type_print_base (TYPE_SELF_TYPE (type), - stream, -1, passed_a_ptr, flags); + c_type_print_base_1 (TYPE_SELF_TYPE (type), + stream, -1, passed_a_ptr, flags, podata); fprintf_filtered (stream, "::*"); break; case TYPE_CODE_REF: case TYPE_CODE_RVALUE_REF: c_type_print_varspec_prefix (TYPE_TARGET_TYPE (type), - stream, show, 1, 0, flags); + stream, show, 1, 0, flags, podata); fprintf_filtered (stream, TYPE_CODE(type) == TYPE_CODE_REF ? "&" : "&&"); c_type_print_modifier (type, stream, 1, need_post_space); break; @@ -367,21 +397,22 @@ c_type_print_varspec_prefix (struct type *type, case TYPE_CODE_METHOD: case TYPE_CODE_FUNC: c_type_print_varspec_prefix (TYPE_TARGET_TYPE (type), - stream, show, 0, 0, flags); + stream, show, 0, 0, flags, podata); if (passed_a_ptr) fprintf_filtered (stream, "("); break; case TYPE_CODE_ARRAY: c_type_print_varspec_prefix (TYPE_TARGET_TYPE (type), - stream, show, 0, 0, flags); + stream, show, 0, 0, flags, podata); if (passed_a_ptr) fprintf_filtered (stream, "("); break; case TYPE_CODE_TYPEDEF: c_type_print_varspec_prefix (TYPE_TARGET_TYPE (type), - stream, show, passed_a_ptr, 0, flags); + stream, show, passed_a_ptr, 0, flags, + podata); break; case TYPE_CODE_UNDEF: @@ -836,21 +867,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 +904,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 +913,121 @@ 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, + struct ui_file *stream, + unsigned int *endpos, + struct print_offset_data *podata) +{ + 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 = fieldsize_byte * TARGET_CHAR_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); + } + + if (TYPE_FIELD_PACKED (type, field_idx)) + { + /* We're dealing with a bitfield. Print how many bits are left + to be used. */ + unsigned int bitsize = TYPE_FIELD_BITSIZE (type, field_idx); + /* The bitpos relative to the beginning of our container + field. */ + unsigned int relative_bitpos; + + /* The following was copied from + value.c:value_primitive_field. */ + if ((bitpos % fieldsize_bit) + bitsize <= fieldsize_bit) + relative_bitpos = bitpos % fieldsize_bit; + else + relative_bitpos = bitpos % TARGET_CHAR_BIT; + + /* This is the exact offset (in bits) of this bitfield. */ + unsigned int bit_offset + = (bitpos - relative_bitpos) + podata->offset_bitpos; + + /* The position of the field, relative to the beginning of the + struct, and how many bits are left to be used in this + container. */ + fprintf_filtered (stream, "/* %4u:%2u", bit_offset / TARGET_CHAR_BIT, + fieldsize_bit - (relative_bitpos + bitsize)); + fieldsize_bit = bitsize; + } + else + { + /* The position of the field, relative to the beginning of the + struct. */ + fprintf_filtered (stream, "/* %4u", + (bitpos + podata->offset_bitpos) / 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 @@ -932,7 +1084,8 @@ need_access_label_p (struct type *type) 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) + const struct type_print_options *flags, + struct print_offset_data *podata) { struct type_print_options local_flags = *flags; struct type_print_options semi_local_flags = *flags; @@ -1004,6 +1157,7 @@ c_type_print_base_struct_union (struct type *type, struct ui_file *stream, 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) { @@ -1032,6 +1186,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 = podata->endpos; for (int i = TYPE_N_BASECLASSES (type); i < len; i++) { QUIT; @@ -1048,18 +1204,57 @@ 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, stream, &endpos, podata); + } + else if (TYPE_CODE (type) == TYPE_CODE_UNION) + c_print_type_union_field_offset (type, i, stream); + + podata->endpos = endpos; + } + 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 "); - 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)) + + 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. */ + podata->offset_bitpos += TYPE_FIELD_BITPOS (type, i); + } + + unsigned int saved_endpos = podata->endpos; + + c_print_type_1 (TYPE_FIELD_TYPE (type, i), + TYPE_FIELD_NAME (type, i), + stream, newshow, level + 4, + &local_flags, podata); + + 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 +1317,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 +1339,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)) { - c_print_type (TYPE_TARGET_TYPE (TYPE_FN_FIELD_TYPE (f, j)), - "", stream, -1, 0, - &local_flags); + 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_1 (TYPE_TARGET_TYPE (TYPE_FN_FIELD_TYPE (f, j)), + "", stream, -1, 0, + &local_flags, podata); + local_flags.print_offsets = old_po; + fputs_filtered (" ", stream); } if (TYPE_FN_FIELD_STUB (f, j)) @@ -1226,9 +1429,17 @@ 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); - c_print_type (TYPE_NESTED_TYPES_FIELD_TYPE (type, i), - "", stream, show, level + 4, &semi_local_flags); + 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_1 (TYPE_NESTED_TYPES_FIELD_TYPE (type, i), + "", stream, show, level + 4, &semi_local_flags, + podata); + semi_local_flags.print_offsets = old_po; fprintf_filtered (stream, ";\n"); } } @@ -1254,22 +1465,46 @@ 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. */ - c_print_type (target, - TYPE_TYPEDEF_FIELD_NAME (type, i), - stream, show - 1, level + 4, - &semi_local_flags); + c_print_type_1 (target, + TYPE_TYPEDEF_FIELD_NAME (type, i), + stream, show - 1, level + 4, + &semi_local_flags, podata); + semi_local_flags.print_offsets = old_po; fprintf_filtered (stream, ";\n"); } } + if (flags->print_offsets) + { + if (show > 0) + { + fputs_filtered ("\n", stream); + print_spaces_filtered_with_print_options (level + 4, + stream, + flags); + fprintf_filtered (stream, "/* total size (bytes): %4u */\n", + TYPE_LENGTH (type)); + } + + print_spaces_filtered (OFFSET_SPC_LEN, stream); + if (level == 0) + print_spaces_filtered (2, stream); + } + fprintfi_filtered (level, stream, "}"); } @@ -1294,9 +1529,11 @@ c_type_print_base_struct_union (struct type *type, struct ui_file *stream, LEVEL is the number of spaces to indent by. We increase it for some recursive calls. */ -void -c_type_print_base (struct type *type, struct ui_file *stream, - int show, int level, const struct type_print_options *flags) +static void +c_type_print_base_1 (struct type *type, struct ui_file *stream, + int show, int level, + const struct type_print_options *flags, + struct print_offset_data *podata) { int i; int len; @@ -1341,8 +1578,8 @@ c_type_print_base (struct type *type, struct ui_file *stream, if (TYPE_TARGET_TYPE (type) == NULL) type_print_unknown_return_type (stream); else - c_type_print_base (TYPE_TARGET_TYPE (type), - stream, show, level, flags); + c_type_print_base_1 (TYPE_TARGET_TYPE (type), + stream, show, level, flags, podata); break; case TYPE_CODE_ARRAY: case TYPE_CODE_PTR: @@ -1350,13 +1587,14 @@ c_type_print_base (struct type *type, struct ui_file *stream, case TYPE_CODE_REF: case TYPE_CODE_RVALUE_REF: case TYPE_CODE_METHODPTR: - c_type_print_base (TYPE_TARGET_TYPE (type), - stream, show, level, flags); + c_type_print_base_1 (TYPE_TARGET_TYPE (type), + stream, show, level, flags, podata); break; case TYPE_CODE_STRUCT: case TYPE_CODE_UNION: - c_type_print_base_struct_union (type, stream, show, level, flags); + c_type_print_base_struct_union (type, stream, show, level, flags, + podata); break; case TYPE_CODE_ENUM: @@ -1456,10 +1694,10 @@ c_type_print_base (struct type *type, struct ui_file *stream, print_spaces_filtered (level + 4, stream); /* We pass "show" here and not "show - 1" to get enum types printed. There's no other way to see them. */ - c_print_type (TYPE_FIELD_TYPE (type, i), - TYPE_FIELD_NAME (type, i), - stream, show, level + 4, - &local_flags); + c_print_type_1 (TYPE_FIELD_TYPE (type, i), + TYPE_FIELD_NAME (type, i), + stream, show, level + 4, + &local_flags, podata); fprintf_filtered (stream, " @%s", plongest (TYPE_FIELD_BITPOS (type, i))); if (TYPE_FIELD_BITSIZE (type, i) > 1) @@ -1518,3 +1756,15 @@ c_type_print_base (struct type *type, struct ui_file *stream, break; } } + +/* See c_type_print_base_1. */ + +void +c_type_print_base (struct type *type, struct ui_file *stream, + int show, int level, + const struct type_print_options *flags) +{ + struct print_offset_data podata = print_offset_default_data; + + c_type_print_base_1 (type, stream, show, level, flags, &podata); +} diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index da3ed28dfe..ddcae06b60 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -17216,6 +17216,121 @@ 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. This option implies the @code{/tm} flags. + +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; +@}; + +struct tyu +@{ + int a1 : 1; + int a2 : 3; + int a3 : 23; + char a4 : 2; + int64_t a5; + int a6 : 5; + int64_t a7 : 3; +@}; +@end smallexample + +Issuing a @kbd{ptype /o struct tuv} command would print: + +@smallexample +(@value{GDBP}) ptype /o struct tuv +/* offset | size */ +struct tuv @{ +/* 0 | 4 */ int a1; +/* XXX 4-byte hole */ +/* 8 | 8 */ char *a2; +/* 16 | 4 */ int a3; +@} /* total size: 24 bytes */ +@end smallexample + +Notice the format of the first column of comments. There, you can +find two parts separated by the @samp{|} character: the @emph{offset}, +which indicates where the field is located inside the struct, in +bytes, and the @emph{size} of the field. Another interesting line is +the marker of a @emph{hole} in the struct, indicating that it may be +possible to pack the struct and make it use less space by reorganizing +its fields. + +It is also possible to print offsets inside an union: + +@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 + +In this case, since @code{struct tuv} and @code{struct xyz} occupy the +same space (because we are dealing with an union), the offset is not +printed for them. However, you can still examine the offset of each +of these structures' fields. + +Another useful scenario is printing the offsets of a struct containing +bitfields: + +@smallexample +(@value{GDBP}) ptype /o struct tyu +/* offset | size */ +struct tyu @{ +/* 0:31 | 4 */ int a1 : 1; +/* 0:28 | 4 */ int a2 : 3; +/* 0: 5 | 4 */ int a3 : 23; +/* 3: 3 | 1 */ char a4 : 2; +/* XXX 3-bit hole */ +/* XXX 4-byte hole */ +/* 8 | 8 */ int64_t a5; +/* 16:27 | 4 */ int a6 : 5; +/* 16:56 | 8 */ int64_t a7 : 3; +@} /* total size: 24 bytes */ +@end smallexample + +Note how the offset information is now extended to also include how +many bits are left to be used in each bitfield. @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..6aa622419c --- /dev/null +++ b/gdb/testsuite/gdb.base/ptype-offsets.cc @@ -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/>. */ + +/* 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. */ + signed int field3; + + /* No hole here. */ + + /* 1-byte char. */ + signed 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. */ + signed int field7; + } field8; + + /* Empty constructor. */ + abc () + {} +}; + +/* This struct will be nested inside 'struct xyz'. */ + +struct tuv +{ + signed int a1; + + signed char *a2; + + signed int a3; +}; + +/* This struct will be nested inside 'struct pqr'. */ + +struct xyz +{ + signed int f1; + + signed char f2; + + void *f3; + + struct tuv f4; +}; + +/* A struct with a nested struct. */ + +struct pqr +{ + signed int ff1; + + struct xyz ff2; + + signed char ff3; +}; + +/* A union with two nested structs. */ + +union qwe +{ + struct tuv fff1; + + struct xyz fff2; +}; + +/* A struct with an union. */ + +struct poi +{ + signed int f1; + + union qwe f2; + + uint16_t f3; + + struct pqr f4; +}; + +/* A struct with bitfields. */ + +struct tyu +{ + signed int a1 : 1; + + signed int a2 : 3; + + signed int a3 : 23; + + signed char a4 : 2; + + int64_t a5; + + signed int a6 : 5; + + int64_t a7 : 3; +}; + +int +main (int argc, char *argv[]) +{ + struct abc foo; + struct pqr bar; + union qwe c; + struct poi d; + struct tyu e; + uint8_t i; + + 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..0b76671820 --- /dev/null +++ b/gdb/testsuite/gdb.base/ptype-offsets.exp @@ -0,0 +1,223 @@ +# 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 testcase exercises the "ptype /o" feature, which can be used to +# print the offsets and sizes of each field of a struct/union/class. + +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 { ![is_lp64_target] } { + untested "test work only on lp64 targets" + return 0 +} + +if { [prepare_for_testing "failed to prepare" $testfile $srcfile \ + { debug c++ }] } { + return -1 +} + +# Test general offset printing, ctor/dtor printing, union, formatting. +gdb_test "ptype /o struct abc" \ + [multi_line \ +{/\* offset | size \*/ type = struct abc \{} \ +{ 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 \*/ signed char field4;} \ +{/\* XXX 7-byte hole \*/} \ +{/\* 32 | 8 \*/ uint64_t field5;} \ +{/\* 40 | 8 \*/ union \{} \ +{/\* 8 \*/ void \*field6;} \ +{/\* 4 \*/ int field7;} \ +{} \ +{ /\* total size \(bytes\): 8 \*/} \ +{ \} field8;} \ +{} \ +{ /\* total size \(bytes\): 48 \*/} \ +{ \}}] \ + "ptype offset struct abc" + +# Test nested structs. +gdb_test "ptype /o struct pqr" \ + [multi_line \ +{/\* offset | size \*/ type = struct pqr \{} \ +{/\* 0 | 4 \*/ int ff1;} \ +{/\* XXX 4-byte hole \*/} \ +{/\* 8 | 40 \*/ struct xyz \{} \ +{/\* 8 | 4 \*/ int f1;} \ +{/\* 12 | 1 \*/ signed char f2;} \ +{/\* XXX 3-byte hole \*/} \ +{/\* 16 | 8 \*/ void \*f3;} \ +{/\* 24 | 24 \*/ struct tuv \{} \ +{/\* 24 | 4 \*/ int a1;} \ +{/\* XXX 4-byte hole \*/} \ +{/\* 32 | 8 \*/ signed char \*a2;} \ +{/\* 40 | 4 \*/ int a3;} \ +{} \ +{ /\* total size \(bytes\): 24 \*/} \ +{ \} f4;} \ +{} \ +{ /\* total size \(bytes\): 40 \*/} \ +{ \} ff2;} \ +{/\* XXX 28-byte hole \*/} \ +{/\* 72 | 1 \*/ signed char ff3;} \ +{} \ +{ /\* total size \(bytes\): 56 \*/} \ +{ \}}] \ + "ptype offset struct pqr" + +# Test that the offset is properly reset when we are printing a union +# and go inside two inner structs. +# This also tests a struct inside a struct inside a union. +gdb_test "ptype /o union qwe" \ + [multi_line \ +{/\* offset | size \*/ type = union qwe \{} \ +{/\* 24 \*/ struct tuv \{} \ +{/\* 0 | 4 \*/ int a1;} \ +{/\* XXX 4-byte hole \*/} \ +{/\* 8 | 8 \*/ signed char \*a2;} \ +{/\* 16 | 4 \*/ int a3;} \ +{} \ +{ /\* total size \(bytes\): 24 \*/} \ +{ \} fff1;} \ +{/\* 40 \*/ struct xyz \{} \ +{/\* 0 | 4 \*/ int f1;} \ +{/\* 4 | 1 \*/ signed char f2;} \ +{/\* XXX 3-byte hole \*/} \ +{/\* 8 | 8 \*/ void \*f3;} \ +{/\* 16 | 24 \*/ struct tuv \{} \ +{/\* 16 | 4 \*/ int a1;} \ +{/\* XXX 4-byte hole \*/} \ +{/\* 24 | 8 \*/ signed char \*a2;} \ +{/\* 32 | 4 \*/ int a3;} \ +{} \ +{ /\* total size \(bytes\): 24 \*/} \ +{ \} f4;} \ +{} \ +{ /\* total size \(bytes\): 40 \*/} \ +{ \} fff2;} \ +{} \ +{ /\* total size \(bytes\): 40 \*/} \ +{ \}}] \ + "ptype offset union qwe" + +# Test printing a struct that contains a union, and that also +# contains a struct. +gdb_test "ptype /o struct poi" \ + [multi_line \ +{/\* offset | size \*/ type = struct poi \{} \ +{/\* 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 \*/ signed char \*a2;} \ +{/\* 24 | 4 \*/ int a3;} \ +{} \ +{ /\* total size \(bytes\): 24 \*/} \ +{ \} fff1;} \ +{/\* 40 \*/ struct xyz \{} \ +{/\* 8 | 4 \*/ int f1;} \ +{/\* 12 | 1 \*/ signed char f2;} \ +{/\* XXX 3-byte hole \*/} \ +{/\* 16 | 8 \*/ void \*f3;} \ +{/\* 24 | 24 \*/ struct tuv \{} \ +{/\* 24 | 4 \*/ int a1;} \ +{/\* XXX 4-byte hole \*/} \ +{/\* 32 | 8 \*/ signed char \*a2;} \ +{/\* 40 | 4 \*/ int a3;} \ +{} \ +{ /\* total size \(bytes\): 24 \*/} \ +{ \} f4;} \ +{} \ +{ /\* total size \(bytes\): 40 \*/} \ +{ \} fff2;} \ +{} \ +{ /\* total size \(bytes\): 40 \*/} \ +{ \} f2;} \ +{/\* 72 | 2 \*/ uint16_t f3;} \ +{/\* XXX 6-byte hole \*/} \ +{/\* 80 | 56 \*/ struct pqr \{} \ +{/\* 80 | 4 \*/ int ff1;} \ +{/\* XXX 4-byte hole \*/} \ +{/\* 88 | 40 \*/ struct xyz \{} \ +{/\* 88 | 4 \*/ int f1;} \ +{/\* 92 | 1 \*/ signed char f2;} \ +{/\* XXX 3-byte hole \*/} \ +{/\* 96 | 8 \*/ void \*f3;} \ +{/\* 104 | 24 \*/ struct tuv \{} \ +{/\* 104 | 4 \*/ int a1;} \ +{/\* XXX 4-byte hole \*/} \ +{/\* 112 | 8 \*/ signed char \*a2;} \ +{/\* 120 | 4 \*/ int a3;} \ +{} \ +{ /\* total size \(bytes\): 24 \*/} \ +{ \} f4;} \ +{} \ +{ /\* total size \(bytes\): 40 \*/} \ +{ \} ff2;} \ +{/\* 152 | 1 \*/ signed char ff3;} \ +{} \ +{ /\* total size \(bytes\): 56 \*/} \ +{ \} f4;} \ +{} \ +{ /\* total size \(bytes\): 112 \*/} \ +{ \}}] \ + "ptype offset struct poi" + +# Test printing a struct with several bitfields, laid out in various +# ways. +# +# Because dealing with bitfields and offsets is difficult, it can be +# tricky to confirm that the output of the this command is accurate. +# A nice way to do that is to use GDB's "x" command and print the +# actual memory layout of the struct. In order to differentiate +# between bitfields and non-bitfield variables, one can assign "-1" to +# every bitfield in the struct. An example of the output of "x" using +# "struct tyu" is: +# +# (gdb) x/24xb &e +# 0x7fffffffd540: 0xff 0xff 0xff 0x1f 0x00 0x00 0x00 0x00 +# 0x7fffffffd548: 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff +# 0x7fffffffd550: 0xff 0x00 0x00 0x00 0x00 0x00 0x00 0x00 +gdb_test "ptype /o struct tyu" \ + [multi_line \ +{/\* offset | size \*/ type = struct tyu \{} \ +{/\* 0:31 | 4 \*/ int a1 : 1;} \ +{/\* 0:28 | 4 \*/ int a2 : 3;} \ +{/\* 0: 5 | 4 \*/ int a3 : 23;} \ +{/\* 3: 3 | 1 \*/ signed char a4 : 2;} \ +{/\* XXX 3-bit hole \*/} \ +{/\* XXX 4-byte hole \*/} \ +{/\* 8 | 8 \*/ int64_t a5;} \ +{/\* 16:27 | 4 \*/ int a6 : 5;} \ +{/\* 16:56 | 8 \*/ int64_t a7 : 3;} \ +{} \ +{ /\* total size \(bytes\): 24 \*/} \ +{ \}}] \ + "ptype offset struct tyu" + +# 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" diff --git a/gdb/typeprint.c b/gdb/typeprint.c index 9d9d6f5a49..5ad0d5bd17 100644 --- a/gdb/typeprint.c +++ b/gdb/typeprint.c @@ -42,6 +42,7 @@ 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 */ NULL, /* local_typedefs */ NULL, /* global_table */ @@ -55,12 +56,21 @@ static struct type_print_options default_ptype_flags = 0, /* raw */ 1, /* print_methods */ 1, /* print_typedefs */ + 0, /* print_offsets */ 0, /* print_nested_type_limit */ NULL, /* local_typedefs */ NULL, /* global_table */ NULL /* global_printers */ }; +/* The default values for a struct print_offset_data. */ + +struct print_offset_data print_offset_default_data = +{ + 0, /* offset_bitpos */ + 0, /* endpos */ +}; + \f /* A hash table holding typedef_field objects. This is more @@ -440,6 +450,11 @@ whatis_exp (const char *exp, int show) case 'T': flags.print_typedefs = 1; break; + case 'o': + flags.print_offsets = 1; + flags.print_typedefs = 0; + flags.print_methods = 0; + break; default: error (_("unrecognized flag '%c'"), *exp); } @@ -499,6 +514,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 */ "); + printf_filtered ("type = "); if (!flags.raw) @@ -759,7 +779,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 a2058b0120..be882d0d03 100644 --- a/gdb/typeprint.h +++ b/gdb/typeprint.h @@ -24,6 +24,22 @@ struct ui_file; struct typedef_hash_table; struct ext_lang_type_printers; +struct print_offset_data +{ + /* 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 over + the offset of the outter struct. */ + unsigned int offset_bitpos; + + /* ENDPOS is the one-past-the-end bit position of the previous field + (where we expect the current field to be if there is no + hole). */ + unsigned int endpos; +}; + +extern struct print_offset_data print_offset_default_data; + struct type_print_options { /* True means that no special printing flags should apply. */ @@ -35,6 +51,9 @@ 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; -- 2.14.3 ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH v6 2/2] Implement pahole-like 'ptype /o' option 2017-12-14 2:48 ` [PATCH v6 2/2] Implement pahole-like 'ptype /o' option Sergio Durigan Junior @ 2017-12-14 14:19 ` Pedro Alves 2017-12-14 20:31 ` Sergio Durigan Junior 2017-12-14 14:50 ` Pedro Alves 2017-12-14 16:30 ` Eli Zaretskii 2 siblings, 1 reply; 75+ messages in thread From: Pedro Alves @ 2017-12-14 14:19 UTC (permalink / raw) To: Sergio Durigan Junior, GDB Patches Cc: Tom Tromey, Eli Zaretskii, Simon Marchi, Keith Seitz Something broke in v5 -> v6 in the offset and hole computation. I'm seeing this: v6: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ (top-gdb) ptype/o minimal_symbol /* offset | size */ type = struct minimal_symbol { /* 0 | 32 */ struct general_symbol_info { /* 0 | 8 */ const char *name; /* 8 | 8 */ union { /* 8 */ LONGEST ivalue; /* 8 */ const block *block; /* 8 */ const gdb_byte *bytes; /* 8 */ CORE_ADDR address; /* 8 */ const common_block *common_block; /* 8 */ symbol *chain; /* total size (bytes): 8 */ } value; /* 24 | 8 */ union { /* 8 */ obstack *obstack; /* 8 */ const char *demangled_name; /* total size (bytes): 8 */ } language_specific; /* 48:27 | 4 */ language language : 5; /* 48:26 | 4 */ unsigned int ada_mangled : 1; /* XXX 2-bit hole */ /* XXX 1-byte hole */ /* 50 | 2 */ short section; /* total size (bytes): 32 */ } mginfo; /* 56 | 8 */ unsigned long size; /* 64 | 8 */ const char *filename; /* 72:28 | 4 */ minimal_symbol_type type : 4; /* 72:27 | 4 */ unsigned int created_by_gdb : 1; /* 72:26 | 4 */ unsigned int target_flag_1 : 1; /* 72:25 | 4 */ unsigned int target_flag_2 : 1; /* 72:24 | 4 */ unsigned int has_size : 1; /* XXX 7-byte hole */ /* 80 | 8 */ minimal_symbol *hash_next; /* 88 | 8 */ minimal_symbol *demangled_hash_next; /* total size (bytes): 72 */ } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This says that "language_specific" is at 24, with size 8 => next byte is at 32. "language" is at 48. How come no "XXX hole" is printed? Similarly in the "8 + 8 -> 24" jump. It turns out the byte offsets are wrong. Here's the actual byte offset for language_specific is: (top-gdb) p /d &((minimal_symbol *) 0) ->mginfo.language_specific $1 = 16 while v6 says 24. v5 seemed to get it right. Here's what it would show: (top-gdb) ptype /o minimal_symbol /* offset | size */ type = struct minimal_symbol { /* 0 | 32 */ struct general_symbol_info { /* 0 | 8 */ const char *name; /* 8 | 8 */ union { /* 8 */ LONGEST ivalue; /* 8 */ const block *block; /* 8 */ const gdb_byte *bytes; /* 8 */ CORE_ADDR address; /* 8 */ const common_block *common_block; /* 8 */ symbol *chain; } /* total size: 8 bytes */ value; /* 16 | 8 */ union { /* 8 */ obstack *obstack; /* 8 */ const char *demangled_name; } /* total size: 8 bytes */ language_specific; /* 24:27 | 4 */ language language : 5; /* 24:26 | 4 */ unsigned int ada_mangled : 1; /* XXX 2-bit hole */ /* XXX 1-byte hole */ /* 26 | 2 */ short section; } /* total size: 32 bytes */ mginfo; /* 32 | 8 */ unsigned long size; /* 40 | 8 */ const char *filename; /* 48:28 | 4 */ minimal_symbol_type type : 4; /* 48:27 | 4 */ unsigned int created_by_gdb : 1; /* 48:26 | 4 */ unsigned int target_flag_1 : 1; /* 48:25 | 4 */ unsigned int target_flag_2 : 1; /* 48:24 | 4 */ unsigned int has_size : 1; /* XXX 7-byte hole */ /* 56 | 8 */ minimal_symbol *hash_next; /* 64 | 8 */ minimal_symbol *demangled_hash_next; } /* total size: 72 bytes */ (top-gdb) Surprised this wasn't caught by the testcases. I do like the new output format a lot better. Thanks much for implementing the suggestions! Pedro Alves ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH v6 2/2] Implement pahole-like 'ptype /o' option 2017-12-14 14:19 ` Pedro Alves @ 2017-12-14 20:31 ` Sergio Durigan Junior 0 siblings, 0 replies; 75+ messages in thread From: Sergio Durigan Junior @ 2017-12-14 20:31 UTC (permalink / raw) To: Pedro Alves Cc: GDB Patches, Tom Tromey, Eli Zaretskii, Simon Marchi, Keith Seitz On Thursday, December 14 2017, Pedro Alves wrote: > Something broke in v5 -> v6 in the offset and hole computation. Thanks for catching this. Indeed, the way the offset_bitpos is propagated has changed, and the patch is wrongly calculating it before printing inner fields of structs/unions. I've fixed the logic and now the behavious is back to the old one. All the tests that were passing before are still passing now, which means that I wasn't testing this specific scenario. For that reason, I've added one more test to cover this. Thanks, -- Sergio GPG key ID: 237A 54B1 0287 28BF 00EF 31F4 D0EB 7628 65FC 5E36 Please send encrypted e-mail if possible http://sergiodj.net/ ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH v6 2/2] Implement pahole-like 'ptype /o' option 2017-12-14 2:48 ` [PATCH v6 2/2] Implement pahole-like 'ptype /o' option Sergio Durigan Junior 2017-12-14 14:19 ` Pedro Alves @ 2017-12-14 14:50 ` Pedro Alves 2017-12-14 20:29 ` Sergio Durigan Junior 2017-12-14 16:30 ` Eli Zaretskii 2 siblings, 1 reply; 75+ messages in thread From: Pedro Alves @ 2017-12-14 14:50 UTC (permalink / raw) To: Sergio Durigan Junior, GDB Patches Cc: Tom Tromey, Eli Zaretskii, Simon Marchi, Keith Seitz On 12/14/2017 02:48 AM, Sergio Durigan Junior wrote: > A big part of this patch handles the formatting logic of 'ptype', > which is a bit messy. The code to handle bitfield offsets, however, > took some time to craft. My thanks to Pedro Alves for figuring things > out and pointing me to the right direction, as well as coming up with > a way to inspect the layout of structs with bitfields (see testcase > for comments). Why, thanks. :-) > +Issuing a @kbd{ptype /o struct tuv} command would print: > + > +@smallexample > +(@value{GDBP}) ptype /o struct tuv > +/* offset | size */ > +struct tuv @{ > +/* 0 | 4 */ int a1; > +/* XXX 4-byte hole */ > +/* 8 | 8 */ char *a2; > +/* 16 | 4 */ int a3; > +@} /* total size: 24 bytes */ > +@end smallexample These examples need to be updated per the new output format. > + 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. */ No longer true... > + > +#include <stdint.h> > + > + > +# Test only works on x86_64 LP64 targets. That's how we guarantee Remove reference to x86_64; it's no longer true. > +# that the expected holes will be present in the struct. > +if { ![is_lp64_target] } { > + untested "test work only on lp64 targets" > + return 0 > +} > + > +if { [prepare_for_testing "failed to prepare" $testfile $srcfile \ > + { debug c++ }] } { > + return -1 > +} I think we're missing a test for "ptype /oTM", to make sure that we can still override the fact that /o implies /tm ? I'd add at least one for "/oTM" and one for "/TMo". The latter ends up being the same as "/o". > +/* The default values for a struct print_offset_data. */ > + > +struct print_offset_data print_offset_default_data = > +{ > + 0, /* offset_bitpos */ > + 0, /* endpos */ > +}; > + Do we really need this object ? How about just defining the default values in-class ? See below. > --- a/gdb/typeprint.h > +++ b/gdb/typeprint.h > @@ -24,6 +24,22 @@ struct ui_file; > struct typedef_hash_table; > struct ext_lang_type_printers; > > +struct print_offset_data > +{ > + /* 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 over > + the offset of the outter struct. */ > + unsigned int offset_bitpos; So here: unsigned int offset_bitpos = 0; > + > + /* ENDPOS is the one-past-the-end bit position of the previous field > + (where we expect the current field to be if there is no > + hole). */ > + unsigned int endpos; and unsigned int endpos = 0; Then you default-constructed print_offset_data objects have the fields automatically zeroed: print_offset_data podata; while at it, wouldn't it be better to name this one "end_bitpos" or something like that with "bit" in it as well? Thanks, Pedro Alves ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH v6 2/2] Implement pahole-like 'ptype /o' option 2017-12-14 14:50 ` Pedro Alves @ 2017-12-14 20:29 ` Sergio Durigan Junior 0 siblings, 0 replies; 75+ messages in thread From: Sergio Durigan Junior @ 2017-12-14 20:29 UTC (permalink / raw) To: Pedro Alves Cc: GDB Patches, Tom Tromey, Eli Zaretskii, Simon Marchi, Keith Seitz Thanks for the review. On Thursday, December 14 2017, Pedro Alves wrote: > On 12/14/2017 02:48 AM, Sergio Durigan Junior wrote: > >> +Issuing a @kbd{ptype /o struct tuv} command would print: >> + >> +@smallexample >> +(@value{GDBP}) ptype /o struct tuv >> +/* offset | size */ >> +struct tuv @{ >> +/* 0 | 4 */ int a1; >> +/* XXX 4-byte hole */ >> +/* 8 | 8 */ char *a2; >> +/* 16 | 4 */ int a3; >> +@} /* total size: 24 bytes */ >> +@end smallexample > > These examples need to be updated per the new output format. Done. >> + 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. */ > > No longer true... Removed. >> + >> +#include <stdint.h> >> + > >> + >> +# Test only works on x86_64 LP64 targets. That's how we guarantee > > Remove reference to x86_64; it's no longer true. Done. >> +# that the expected holes will be present in the struct. >> +if { ![is_lp64_target] } { >> + untested "test work only on lp64 targets" >> + return 0 >> +} >> + >> +if { [prepare_for_testing "failed to prepare" $testfile $srcfile \ >> + { debug c++ }] } { >> + return -1 >> +} > > I think we're missing a test for "ptype /oTM", to make sure that > we can still override the fact that /o implies /tm ? > I'd add at least one for "/oTM" and one for "/TMo". The > latter ends up being the same as "/o". Hm, OK. Added the tests. >> +/* The default values for a struct print_offset_data. */ >> + >> +struct print_offset_data print_offset_default_data = >> +{ >> + 0, /* offset_bitpos */ >> + 0, /* endpos */ >> +}; >> + > > Do we really need this object ? How about just defining > the default values in-class ? See below. Indeed. I removed it in favour of initializing in-class. >> --- a/gdb/typeprint.h >> +++ b/gdb/typeprint.h >> @@ -24,6 +24,22 @@ struct ui_file; >> struct typedef_hash_table; >> struct ext_lang_type_printers; >> >> +struct print_offset_data >> +{ >> + /* 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 over >> + the offset of the outter struct. */ >> + unsigned int offset_bitpos; > > So here: > > unsigned int offset_bitpos = 0; > >> + >> + /* ENDPOS is the one-past-the-end bit position of the previous field >> + (where we expect the current field to be if there is no >> + hole). */ >> + unsigned int endpos; > > and > > unsigned int endpos = 0; > > Then you default-constructed print_offset_data objects have > the fields automatically zeroed: > > print_offset_data podata; > > while at it, wouldn't it be better to name this one "end_bitpos" or > something like that with "bit" in it as well? Right; endpos is too generic indeed. Renamed to end_bitpos. Thanks, -- Sergio GPG key ID: 237A 54B1 0287 28BF 00EF 31F4 D0EB 7628 65FC 5E36 Please send encrypted e-mail if possible http://sergiodj.net/ ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH v6 2/2] Implement pahole-like 'ptype /o' option 2017-12-14 2:48 ` [PATCH v6 2/2] Implement pahole-like 'ptype /o' option Sergio Durigan Junior 2017-12-14 14:19 ` Pedro Alves 2017-12-14 14:50 ` Pedro Alves @ 2017-12-14 16:30 ` Eli Zaretskii 2 siblings, 0 replies; 75+ messages in thread From: Eli Zaretskii @ 2017-12-14 16:30 UTC (permalink / raw) To: Sergio Durigan Junior; +Cc: gdb-patches, tom, simon.marchi, palves, keiths > From: Sergio Durigan Junior <sergiodj@redhat.com> > Cc: Tom Tromey <tom@tromey.com>, > Eli Zaretskii <eliz@gnu.org>, > Simon Marchi <simon.marchi@ericsson.com>, > Pedro Alves <palves@redhat.com>, > Keith Seitz <keiths@redhat.com>, > Sergio Durigan Junior <sergiodj@redhat.com> > Date: Wed, 13 Dec 2017 21:48:16 -0500 > > gdb/ChangeLog: > yyyy-mm-dd Sergio Durigan Junior <sergiodj@redhat.com> > Pedro Alves <palves@redhat.com> > > PR cli/16224 > * NEWS (Changes since GDB 8.0): Mention new '/o' flag. > * c-typeprint.c (OFFSET_SPC_LEN): New define. > (c_type_print_varspec_prefix): New argument 'struct > print_offset_data *'. > (c_type_print_base_1): New function and prototype. > (c_print_type_1): New function, with code from 'c_print_type'. > (c_print_type): Use 'c_print_type_1'. > (c_type_print_varspec_prefix): New argument 'struct > print_offset_data *'. Use it. Call 'c_type_print_base_1' > instead of 'c_print_type_base'. > (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_vtable_offset_marker): New function. > (c_print_type_union_field_offset): New function. > (c_print_type_struct_field_offset): New function. > (c_type_print_base_struct_union): New argument 'struct > print_offset_data *'. Print offsets and sizes for > struct/union/class fields. > * typeprint.c (const struct type_print_options > type_print_raw_options): Initialize 'print_offsets'. > (static struct type_print_options default_ptype_flags): > Likewise. > (struct print_offset_data print_offset_default_data): New > variable. > (whatis_exp): Handle '/o' option. > (_initialize_typeprint): Add '/o' flag to ptype's help. > * typeprint.h (struct print_offset_data): New struct. > (struct type_print_options) <print_offsets>: New field. > > gdb/testsuite/ChangeLog: > yyyy-mm-dd 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: > yyyy-mm-dd Sergio Durigan Junior <sergiodj@redhat.com> > > PR cli/16224 > * gdb.texinfo (ptype): Add documentation for new flag '/o'. OK for the documentation parts, thanks. ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v7 0/2] Implement pahole-like 'ptype /o' option (and do some code reorg) 2017-11-21 16:07 [PATCH] Implement pahole-like 'ptype /o' option Sergio Durigan Junior ` (7 preceding siblings ...) 2017-12-14 2:48 ` [PATCH v6 0/2] Implement pahole-like 'ptype /o' option (and do some code reorg) Sergio Durigan Junior @ 2017-12-15 1:12 ` Sergio Durigan Junior 2017-12-15 1:12 ` [PATCH v7 1/2] Reorganize code to handle TYPE_CODE_{STRUCT,UNION} on 'c_type_print_base' Sergio Durigan Junior ` (2 more replies) 8 siblings, 3 replies; 75+ messages in thread From: Sergio Durigan Junior @ 2017-12-15 1:12 UTC (permalink / raw) To: GDB Patches Cc: Tom Tromey, Eli Zaretskii, Simon Marchi, Pedro Alves, Keith Seitz Changes from v6: - Updated examples in the documentation. - Removed stale comments about the test being x86_64-specific from tests. - Added tests for "ptype /oTM", "ptype /TMo", and for the regression Pedro caught. - Removed "print_offset_default_data". - Fixed regression reported by Pedro. - s/endpos/end_bitpos/ - Filtered out the languages that don't implement the feature so that their type printing output doesn't get clobbered by "ptype /o" when they use "common" code (which is not actually common; it lives inside c-typeprint.c). I decided to do that by checking the current language on typeprint.c:whatis_exp, before setting the "print_offset" bit. The documentation parts have been approved by Eli, and haven't changed. ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v7 1/2] Reorganize code to handle TYPE_CODE_{STRUCT,UNION} on 'c_type_print_base' 2017-12-15 1:12 ` [PATCH v7 0/2] Implement pahole-like 'ptype /o' option (and do some code reorg) Sergio Durigan Junior @ 2017-12-15 1:12 ` Sergio Durigan Junior 2017-12-15 1:13 ` [PATCH v7 2/2] Implement pahole-like 'ptype /o' option Sergio Durigan Junior 2017-12-15 20:11 ` [PATCH v7 0/2] Implement pahole-like 'ptype /o' option (and do some code reorg) Sergio Durigan Junior 2 siblings, 0 replies; 75+ messages in thread From: Sergio Durigan Junior @ 2017-12-15 1:12 UTC (permalink / raw) To: GDB Patches Cc: Tom Tromey, Eli Zaretskii, Simon Marchi, Pedro Alves, Keith Seitz, Sergio Durigan Junior While doing the 'ptype /o' work, I noticed that 'c_type_print_base' was very long, with a big amount of code just to handle the case of TYPE_CODE_{STRUCT,UNION}. This made working with the function a bit difficult, specially because of the level of indentation. This commit moves this part of the code to their own functions. Now we have a 'c_type_print_base_struct_union' with most of the code, and also 'need_access_label_p', which is a subset of the code that was also a good candidate for having its own function. gdb/ChangeLog: yyyy-mm-dd Sergio Durigan Junior <sergiodj@redhat.com> * c-typeprint.c (need_access_label_p): New function. (c_type_print_base_struct_union): New function. (c_type_print_base): Move code to handle TYPE_CODE_{STRUCT,UNION} to the functions mentioned above. --- gdb/c-typeprint.c | 837 ++++++++++++++++++++++++++---------------------------- 1 file changed, 403 insertions(+), 434 deletions(-) diff --git a/gdb/c-typeprint.c b/gdb/c-typeprint.c index f3c3e7d706..3854886387 100644 --- a/gdb/c-typeprint.c +++ b/gdb/c-typeprint.c @@ -875,6 +875,407 @@ output_access_specifier (struct ui_file *stream, return last_access; } +/* 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) +{ + if (TYPE_DECLARED_CLASS (type)) + { + QUIT; + for (int i = TYPE_N_BASECLASSES (type); i < TYPE_NFIELDS (type); i++) + if (!TYPE_FIELD_PRIVATE (type, i)) + return true; + QUIT; + for (int j = 0; j < TYPE_NFN_FIELDS (type); j++) + for (int i = 0; i < TYPE_FN_FIELDLIST_LENGTH (type, j); i++) + if (!TYPE_FN_FIELD_PRIVATE (TYPE_FN_FIELDLIST1 (type, + j), i)) + return true; + QUIT; + for (int i = 0; i < TYPE_TYPEDEF_FIELD_COUNT (type); ++i) + if (!TYPE_TYPEDEF_FIELD_PRIVATE (type, i)) + return true; + } + else + { + QUIT; + for (int i = TYPE_N_BASECLASSES (type); i < TYPE_NFIELDS (type); i++) + if (TYPE_FIELD_PRIVATE (type, i) || TYPE_FIELD_PROTECTED (type, i)) + return true; + QUIT; + for (int j = 0; j < TYPE_NFN_FIELDS (type); j++) + { + QUIT; + for (int i = 0; i < TYPE_FN_FIELDLIST_LENGTH (type, j); i++) + if (TYPE_FN_FIELD_PROTECTED (TYPE_FN_FIELDLIST1 (type, + j), i) + || TYPE_FN_FIELD_PRIVATE (TYPE_FN_FIELDLIST1 (type, + j), + i)) + return true; + } + QUIT; + for (int i = 0; i < TYPE_TYPEDEF_FIELD_COUNT (type); ++i) + if (TYPE_TYPEDEF_FIELD_PROTECTED (type, i) + || TYPE_TYPEDEF_FIELD_PRIVATE (type, i)) + return true; + } + + return false; +} + +/* 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; + 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. */ + + 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); + vptr_fieldno = get_vptr_fieldno (type, &basetype); + 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)); + } + + 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; + 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)); + + 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 out nested types. */ + if (TYPE_NESTED_TYPES_COUNT (type) != 0 + && semi_local_flags.print_nested_type_limit != 0) + { + if (semi_local_flags.print_nested_type_limit > 0) + --semi_local_flags.print_nested_type_limit; + + if (TYPE_NFIELDS (type) != 0 || TYPE_NFN_FIELDS (type) != 0) + fprintf_filtered (stream, "\n"); + + for (int i = 0; i < TYPE_NESTED_TYPES_COUNT (type); ++i) + { + print_spaces_filtered (level + 4, stream); + c_print_type (TYPE_NESTED_TYPES_FIELD_TYPE (type, i), + "", stream, show, level + 4, &semi_local_flags); + 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 + || TYPE_NESTED_TYPES_COUNT (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)); + } + 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); +} + /* 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 +1299,7 @@ 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; QUIT; @@ -958,436 +1356,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 out nested types. */ - if (TYPE_NESTED_TYPES_COUNT (type) != 0 - && semi_local_flags.print_nested_type_limit != 0) - { - if (semi_local_flags.print_nested_type_limit > 0) - --semi_local_flags.print_nested_type_limit; - - if (TYPE_NFIELDS (type) != 0 || TYPE_NFN_FIELDS (type) != 0) - fprintf_filtered (stream, "\n"); - - for (i = 0; i < TYPE_NESTED_TYPES_COUNT (type); ++i) - { - print_spaces_filtered (level + 4, stream); - c_print_type (TYPE_NESTED_TYPES_FIELD_TYPE (type, i), - "", stream, show, level + 4, &semi_local_flags); - 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 - || TYPE_NESTED_TYPES_COUNT (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: -- 2.14.3 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v7 2/2] Implement pahole-like 'ptype /o' option 2017-12-15 1:12 ` [PATCH v7 0/2] Implement pahole-like 'ptype /o' option (and do some code reorg) Sergio Durigan Junior 2017-12-15 1:12 ` [PATCH v7 1/2] Reorganize code to handle TYPE_CODE_{STRUCT,UNION} on 'c_type_print_base' Sergio Durigan Junior @ 2017-12-15 1:13 ` Sergio Durigan Junior 2017-12-15 17:24 ` Pedro Alves 2017-12-15 20:11 ` [PATCH v7 0/2] Implement pahole-like 'ptype /o' option (and do some code reorg) Sergio Durigan Junior 2 siblings, 1 reply; 75+ messages in thread From: Sergio Durigan Junior @ 2017-12-15 1:13 UTC (permalink / raw) To: GDB Patches Cc: Tom Tromey, Eli Zaretskii, Simon Marchi, Pedro Alves, Keith Seitz, Sergio Durigan Junior 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: /* offset | size */ type = struct wer : public tuv { public: /* 32 | 24 */ struct tyu { /* 32:31 | 4 */ int a1 : 1; /* 32:28 | 4 */ int a2 : 3; /* 32: 5 | 4 */ int a3 : 23; /* 35: 3 | 1 */ char a4 : 2; /* XXX 3-bit hole */ /* XXX 4-byte hole */ /* 40 | 8 */ int64_t a5; /* 48:27 | 4 */ int a6 : 5; /* 48:56 | 8 */ int64_t a7 : 3; /* total size (bytes): 24 */ } a1; /* total size (bytes): 56 */ } A big part of this patch handles the formatting logic of 'ptype', which is a bit messy. The code to handle bitfield offsets, however, took some time to craft. My thanks to Pedro Alves for figuring things out and pointing me to the right direction, as well as coming up with a way to inspect the layout of structs with bitfields (see testcase for comments). After many discussions both on IRC and at the mailing list, I tried to implement printing vtables and inherited classes. Unfortunately the code grew too complex and there were still a few corner cases failing so I had to drop the attempt. This should be implemented in a future patch. 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: yyyy-mm-dd Sergio Durigan Junior <sergiodj@redhat.com> Pedro Alves <palves@redhat.com> PR cli/16224 * NEWS (Changes since GDB 8.0): Mention new '/o' flag. * c-typeprint.c (OFFSET_SPC_LEN): New define. (c_type_print_varspec_prefix): New argument 'struct print_offset_data *'. (c_type_print_base_1): New function and prototype. (c_print_type_1): New function, with code from 'c_print_type'. (c_print_type): Use 'c_print_type_1'. (c_type_print_varspec_prefix): New argument 'struct print_offset_data *'. Use it. Call 'c_type_print_base_1' instead of 'c_print_type_base'. (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_vtable_offset_marker): New function. (c_print_type_union_field_offset): New function. (c_print_type_struct_field_offset): New function. (c_type_print_base_struct_union): New argument 'struct print_offset_data *'. Print offsets and sizes for struct/union/class fields. * typeprint.c (const struct type_print_options type_print_raw_options): Initialize 'print_offsets'. (static struct type_print_options default_ptype_flags): Likewise. (struct print_offset_data print_offset_default_data): New variable. (whatis_exp): Handle '/o' option. (_initialize_typeprint): Add '/o' flag to ptype's help. * typeprint.h (struct print_offset_data): New struct. (struct type_print_options) <print_offsets>: New field. gdb/testsuite/ChangeLog: yyyy-mm-dd 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: yyyy-mm-dd Sergio Durigan Junior <sergiodj@redhat.com> PR cli/16224 * gdb.texinfo (ptype): Add documentation for new flag '/o'. --- gdb/NEWS | 3 + gdb/c-typeprint.c | 388 +++++++++++++++++++++++++------ gdb/doc/gdb.texinfo | 124 ++++++++++ gdb/testsuite/gdb.base/ptype-offsets.cc | 193 +++++++++++++++ gdb/testsuite/gdb.base/ptype-offsets.exp | 317 +++++++++++++++++++++++++ gdb/typeprint.c | 23 +- gdb/typeprint.h | 17 ++ 7 files changed, 999 insertions(+), 66 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 bd5ae36dd0..3a7e704cc6 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. + * 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. diff --git a/gdb/c-typeprint.c b/gdb/c-typeprint.c index 3854886387..b977401145 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 @@ -45,12 +53,18 @@ enum access_specifier static void c_type_print_varspec_prefix (struct type *, struct ui_file *, int, int, int, - const struct type_print_options *); + const struct type_print_options *, + struct print_offset_data *); /* Print "const", "volatile", or address space modifiers. */ static void c_type_print_modifier (struct type *, struct ui_file *, int, int); + +static void c_type_print_base_1 (struct type *type, struct ui_file *stream, + int show, int level, + const struct type_print_options *flags, + struct print_offset_data *podata); \f /* A callback function for cp_canonicalize_string_full that uses @@ -82,14 +96,15 @@ print_name_maybe_canonical (const char *name, \f -/* LEVEL is the depth to indent lines by. */ +/* Helper function for c_print_type. */ -void -c_print_type (struct type *type, - const char *varstring, - struct ui_file *stream, - int show, int level, - const struct type_print_options *flags) +static void +c_print_type_1 (struct type *type, + const char *varstring, + struct ui_file *stream, + int show, int level, + const struct type_print_options *flags, + struct print_offset_data *podata) { enum type_code code; int demangled_args; @@ -108,7 +123,7 @@ c_print_type (struct type *type, } else { - c_type_print_base (type, stream, show, level, flags); + c_type_print_base_1 (type, stream, show, level, flags, podata); code = TYPE_CODE (type); if ((varstring != NULL && *varstring != '\0') /* Need a space if going to print stars or brackets; @@ -124,7 +139,7 @@ c_print_type (struct type *type, fputs_filtered (" ", stream); need_post_space = (varstring != NULL && strcmp (varstring, "") != 0); c_type_print_varspec_prefix (type, stream, show, 0, need_post_space, - flags); + flags, podata); } if (varstring != NULL) @@ -143,6 +158,20 @@ c_print_type (struct type *type, } } +/* LEVEL is the depth to indent lines by. */ + +void +c_print_type (struct type *type, + const char *varstring, + struct ui_file *stream, + int show, int level, + const struct type_print_options *flags) +{ + struct print_offset_data podata; + + c_print_type_1 (type, varstring, stream, show, level, flags, &podata); +} + /* Print a typedef using C syntax. TYPE is the underlying type. NEW_SYMBOL is the symbol naming the type. STREAM is the stream on which to print. */ @@ -310,7 +339,8 @@ c_type_print_varspec_prefix (struct type *type, struct ui_file *stream, int show, int passed_a_ptr, int need_post_space, - const struct type_print_options *flags) + const struct type_print_options *flags, + struct print_offset_data *podata) { const char *name; @@ -326,40 +356,40 @@ c_type_print_varspec_prefix (struct type *type, { case TYPE_CODE_PTR: c_type_print_varspec_prefix (TYPE_TARGET_TYPE (type), - stream, show, 1, 1, flags); + stream, show, 1, 1, flags, podata); fprintf_filtered (stream, "*"); c_type_print_modifier (type, stream, 1, need_post_space); break; case TYPE_CODE_MEMBERPTR: c_type_print_varspec_prefix (TYPE_TARGET_TYPE (type), - stream, show, 0, 0, flags); + stream, show, 0, 0, flags, podata); name = type_name_no_tag (TYPE_SELF_TYPE (type)); if (name) print_name_maybe_canonical (name, flags, stream); else - c_type_print_base (TYPE_SELF_TYPE (type), - stream, -1, passed_a_ptr, flags); + c_type_print_base_1 (TYPE_SELF_TYPE (type), + stream, -1, passed_a_ptr, flags, podata); fprintf_filtered (stream, "::*"); break; case TYPE_CODE_METHODPTR: c_type_print_varspec_prefix (TYPE_TARGET_TYPE (type), - stream, show, 0, 0, flags); + stream, show, 0, 0, flags, podata); fprintf_filtered (stream, "("); name = type_name_no_tag (TYPE_SELF_TYPE (type)); if (name) print_name_maybe_canonical (name, flags, stream); else - c_type_print_base (TYPE_SELF_TYPE (type), - stream, -1, passed_a_ptr, flags); + c_type_print_base_1 (TYPE_SELF_TYPE (type), + stream, -1, passed_a_ptr, flags, podata); fprintf_filtered (stream, "::*"); break; case TYPE_CODE_REF: case TYPE_CODE_RVALUE_REF: c_type_print_varspec_prefix (TYPE_TARGET_TYPE (type), - stream, show, 1, 0, flags); + stream, show, 1, 0, flags, podata); fprintf_filtered (stream, TYPE_CODE(type) == TYPE_CODE_REF ? "&" : "&&"); c_type_print_modifier (type, stream, 1, need_post_space); break; @@ -367,21 +397,22 @@ c_type_print_varspec_prefix (struct type *type, case TYPE_CODE_METHOD: case TYPE_CODE_FUNC: c_type_print_varspec_prefix (TYPE_TARGET_TYPE (type), - stream, show, 0, 0, flags); + stream, show, 0, 0, flags, podata); if (passed_a_ptr) fprintf_filtered (stream, "("); break; case TYPE_CODE_ARRAY: c_type_print_varspec_prefix (TYPE_TARGET_TYPE (type), - stream, show, 0, 0, flags); + stream, show, 0, 0, flags, podata); if (passed_a_ptr) fprintf_filtered (stream, "("); break; case TYPE_CODE_TYPEDEF: c_type_print_varspec_prefix (TYPE_TARGET_TYPE (type), - stream, show, passed_a_ptr, 0, flags); + stream, show, passed_a_ptr, 0, flags, + podata); break; case TYPE_CODE_UNDEF: @@ -836,21 +867,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 +904,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 +913,121 @@ 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. + + END_BITPOS 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, + struct ui_file *stream, + unsigned int *end_bitpos, + struct print_offset_data *podata) +{ + 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 = fieldsize_byte * TARGET_CHAR_BIT; + + /* We check for *END_BITPOS > 0 because there is a specific scenario + when *END_BITPOS 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 *END_BITPOS > 0 here, GDB will report a hole before the + first field, which is not accurate. */ + if (*end_bitpos > 0 && *end_bitpos < bitpos) + { + /* If END_BITPOS 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 - *end_bitpos; + 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); + } + + if (TYPE_FIELD_PACKED (type, field_idx)) + { + /* We're dealing with a bitfield. Print how many bits are left + to be used. */ + unsigned int bitsize = TYPE_FIELD_BITSIZE (type, field_idx); + /* The bitpos relative to the beginning of our container + field. */ + unsigned int relative_bitpos; + + /* The following was copied from + value.c:value_primitive_field. */ + if ((bitpos % fieldsize_bit) + bitsize <= fieldsize_bit) + relative_bitpos = bitpos % fieldsize_bit; + else + relative_bitpos = bitpos % TARGET_CHAR_BIT; + + /* This is the exact offset (in bits) of this bitfield. */ + unsigned int bit_offset + = (bitpos - relative_bitpos) + podata->offset_bitpos; + + /* The position of the field, relative to the beginning of the + struct, and how many bits are left to be used in this + container. */ + fprintf_filtered (stream, "/* %4u:%2u", bit_offset / TARGET_CHAR_BIT, + fieldsize_bit - (relative_bitpos + bitsize)); + fieldsize_bit = bitsize; + } + else + { + /* The position of the field, relative to the beginning of the + struct. */ + fprintf_filtered (stream, "/* %4u", + (bitpos + podata->offset_bitpos) / TARGET_CHAR_BIT); + + fprintf_filtered (stream, " "); + } + + fprintf_filtered (stream, " | %4u */", fieldsize_byte); + + *end_bitpos = bitpos + fieldsize_bit; +} + +/* Return true if an access label (i.e., "public:", "private:", "protected:") needs to be printed for TYPE. */ static bool @@ -932,7 +1084,8 @@ need_access_label_p (struct type *type) 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) + const struct type_print_options *flags, + struct print_offset_data *podata) { struct type_print_options local_flags = *flags; struct type_print_options semi_local_flags = *flags; @@ -1004,6 +1157,7 @@ c_type_print_base_struct_union (struct type *type, struct ui_file *stream, 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) { @@ -1032,6 +1186,10 @@ 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 end_bitpos = podata->end_bitpos; + struct print_offset_data local_podata; + for (int i = TYPE_N_BASECLASSES (type); i < len; i++) { QUIT; @@ -1048,18 +1206,63 @@ 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, stream, &end_bitpos, podata); + } + 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 "); - 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)) + + 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/union. */ + local_podata.offset_bitpos + = podata->offset_bitpos + TYPE_FIELD_BITPOS (type, i); + /* We're entering a struct/union. Right now, END_BITPOS + points right *after* the struct/union. However, when + printing the first field of this inner struct/union, + the end_bitpos we're expecting is exactly at the + beginning of the struct/union. Therefore, we + subtract the length of the whole struct/union. */ + local_podata.end_bitpos + = end_bitpos + - TYPE_LENGTH (TYPE_FIELD_TYPE (type, i)) * TARGET_CHAR_BIT; + } + + c_print_type_1 (TYPE_FIELD_TYPE (type, i), + TYPE_FIELD_NAME (type, i), + stream, newshow, level + 4, + &local_flags, &local_podata); + + 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 +1325,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 +1347,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)) { - c_print_type (TYPE_TARGET_TYPE (TYPE_FN_FIELD_TYPE (f, j)), - "", stream, -1, 0, - &local_flags); + 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_1 (TYPE_TARGET_TYPE (TYPE_FN_FIELD_TYPE (f, j)), + "", stream, -1, 0, + &local_flags, podata); + local_flags.print_offsets = old_po; + fputs_filtered (" ", stream); } if (TYPE_FN_FIELD_STUB (f, j)) @@ -1226,9 +1437,17 @@ 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); - c_print_type (TYPE_NESTED_TYPES_FIELD_TYPE (type, i), - "", stream, show, level + 4, &semi_local_flags); + 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_1 (TYPE_NESTED_TYPES_FIELD_TYPE (type, i), + "", stream, show, level + 4, &semi_local_flags, + podata); + semi_local_flags.print_offsets = old_po; fprintf_filtered (stream, ";\n"); } } @@ -1254,22 +1473,46 @@ 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. */ - c_print_type (target, - TYPE_TYPEDEF_FIELD_NAME (type, i), - stream, show - 1, level + 4, - &semi_local_flags); + c_print_type_1 (target, + TYPE_TYPEDEF_FIELD_NAME (type, i), + stream, show - 1, level + 4, + &semi_local_flags, podata); + semi_local_flags.print_offsets = old_po; fprintf_filtered (stream, ";\n"); } } + if (flags->print_offsets) + { + if (show > 0) + { + fputs_filtered ("\n", stream); + print_spaces_filtered_with_print_options (level + 4, + stream, + flags); + fprintf_filtered (stream, "/* total size (bytes): %4u */\n", + TYPE_LENGTH (type)); + } + + print_spaces_filtered (OFFSET_SPC_LEN, stream); + if (level == 0) + print_spaces_filtered (2, stream); + } + fprintfi_filtered (level, stream, "}"); } @@ -1294,9 +1537,11 @@ c_type_print_base_struct_union (struct type *type, struct ui_file *stream, LEVEL is the number of spaces to indent by. We increase it for some recursive calls. */ -void -c_type_print_base (struct type *type, struct ui_file *stream, - int show, int level, const struct type_print_options *flags) +static void +c_type_print_base_1 (struct type *type, struct ui_file *stream, + int show, int level, + const struct type_print_options *flags, + struct print_offset_data *podata) { int i; int len; @@ -1341,8 +1586,8 @@ c_type_print_base (struct type *type, struct ui_file *stream, if (TYPE_TARGET_TYPE (type) == NULL) type_print_unknown_return_type (stream); else - c_type_print_base (TYPE_TARGET_TYPE (type), - stream, show, level, flags); + c_type_print_base_1 (TYPE_TARGET_TYPE (type), + stream, show, level, flags, podata); break; case TYPE_CODE_ARRAY: case TYPE_CODE_PTR: @@ -1350,13 +1595,14 @@ c_type_print_base (struct type *type, struct ui_file *stream, case TYPE_CODE_REF: case TYPE_CODE_RVALUE_REF: case TYPE_CODE_METHODPTR: - c_type_print_base (TYPE_TARGET_TYPE (type), - stream, show, level, flags); + c_type_print_base_1 (TYPE_TARGET_TYPE (type), + stream, show, level, flags, podata); break; case TYPE_CODE_STRUCT: case TYPE_CODE_UNION: - c_type_print_base_struct_union (type, stream, show, level, flags); + c_type_print_base_struct_union (type, stream, show, level, flags, + podata); break; case TYPE_CODE_ENUM: @@ -1456,10 +1702,10 @@ c_type_print_base (struct type *type, struct ui_file *stream, print_spaces_filtered (level + 4, stream); /* We pass "show" here and not "show - 1" to get enum types printed. There's no other way to see them. */ - c_print_type (TYPE_FIELD_TYPE (type, i), - TYPE_FIELD_NAME (type, i), - stream, show, level + 4, - &local_flags); + c_print_type_1 (TYPE_FIELD_TYPE (type, i), + TYPE_FIELD_NAME (type, i), + stream, show, level + 4, + &local_flags, podata); fprintf_filtered (stream, " @%s", plongest (TYPE_FIELD_BITPOS (type, i))); if (TYPE_FIELD_BITSIZE (type, i) > 1) @@ -1518,3 +1764,15 @@ c_type_print_base (struct type *type, struct ui_file *stream, break; } } + +/* See c_type_print_base_1. */ + +void +c_type_print_base (struct type *type, struct ui_file *stream, + int show, int level, + const struct type_print_options *flags) +{ + struct print_offset_data podata; + + c_type_print_base_1 (type, stream, show, level, flags, &podata); +} diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index da3ed28dfe..60ed80c363 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -17216,6 +17216,130 @@ 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. This option implies the @code{/tm} flags. + +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; +@}; + +struct tyu +@{ + int a1 : 1; + int a2 : 3; + int a3 : 23; + char a4 : 2; + int64_t a5; + int a6 : 5; + int64_t a7 : 3; +@}; +@end smallexample + +Issuing a @kbd{ptype /o struct tuv} command would print: + +@smallexample +(@value{GDBP}) ptype /o struct tuv +/* offset | size */ type = struct tuv @{ +/* 0 | 4 */ int a1; +/* XXX 4-byte hole */ +/* 8 | 8 */ char *a2; +/* 16 | 4 */ int a3; + + /* total size (bytes): 24 */ + @} +@end smallexample + +Notice the format of the first column of comments. There, you can +find two parts separated by the @samp{|} character: the @emph{offset}, +which indicates where the field is located inside the struct, in +bytes, and the @emph{size} of the field. Another interesting line is +the marker of a @emph{hole} in the struct, indicating that it may be +possible to pack the struct and make it use less space by reorganizing +its fields. + +It is also possible to print offsets inside an union: + +@smallexample +(@value{GDBP}) ptype /o union qwe +/* offset | size */ type = union qwe @{ +/* 24 */ struct tuv @{ +/* 0 | 4 */ int a1; +/* XXX 4-byte hole */ +/* 8 | 8 */ char *a2; +/* 16 | 4 */ int a3; + + /* total size (bytes): 24 */ + @} 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 (bytes): 24 */ + @} f4; + + /* total size (bytes): 40 */ + @} fff2; + + /* total size (bytes): 40 */ + @} +@end smallexample + +In this case, since @code{struct tuv} and @code{struct xyz} occupy the +same space (because we are dealing with an union), the offset is not +printed for them. However, you can still examine the offset of each +of these structures' fields. + +Another useful scenario is printing the offsets of a struct containing +bitfields: + +@smallexample +(@value{GDBP}) ptype /o struct tyu +/* offset | size */ type = struct tyu @{ +/* 0:31 | 4 */ int a1 : 1; +/* 0:28 | 4 */ int a2 : 3; +/* 0: 5 | 4 */ int a3 : 23; +/* 3: 3 | 1 */ signed char a4 : 2; +/* XXX 3-bit hole */ +/* XXX 4-byte hole */ +/* 8 | 8 */ int64_t a5; +/* 16:27 | 4 */ int a6 : 5; +/* 16:56 | 8 */ int64_t a7 : 3; + + /* total size (bytes): 24 */ + @} +@end smallexample + +Note how the offset information is now extended to also include how +many bits are left to be used in each bitfield. @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..86735203ad --- /dev/null +++ b/gdb/testsuite/gdb.base/ptype-offsets.cc @@ -0,0 +1,193 @@ +/* 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/>. */ + +#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. */ + signed int field3; + + /* No hole here. */ + + /* 1-byte char. */ + signed 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. */ + signed int field7; + } field8; + + /* Empty constructor. */ + abc () + {} + + /* Typedef defined in-struct. */ + typedef int my_int_type; + + my_int_type field9; +}; + +/* This struct will be nested inside 'struct xyz'. */ + +struct tuv +{ + signed int a1; + + signed char *a2; + + signed int a3; +}; + +/* This struct will be nested inside 'struct pqr'. */ + +struct xyz +{ + signed int f1; + + signed char f2; + + void *f3; + + struct tuv f4; +}; + +/* A struct with a nested struct. */ + +struct pqr +{ + signed int ff1; + + struct xyz ff2; + + signed char ff3; +}; + +/* A union with two nested structs. */ + +union qwe +{ + struct tuv fff1; + + struct xyz fff2; +}; + +/* A struct with an union. */ + +struct poi +{ + signed int f1; + + union qwe f2; + + uint16_t f3; + + struct pqr f4; +}; + +/* A struct with bitfields. */ + +struct tyu +{ + signed int a1 : 1; + + signed int a2 : 3; + + signed int a3 : 23; + + signed char a4 : 2; + + int64_t a5; + + signed int a6 : 5; + + int64_t a7 : 3; +}; + +/* A struct with structs and unions. */ + +struct asd +{ + struct jkl + { + signed char *f1; + union + { + void *ff1; + } f2; + union + { + signed char *ff2; + } f3; + int f4 : 5; + unsigned int f5 : 1; + short f6; + } f7; + unsigned long f8; + signed char *f9; + int f10 : 4; + unsigned int f11 : 1; + unsigned int f12 : 1; + unsigned int f13 : 1; + unsigned int f14 : 1; + void *f15; + void *f16; +}; + + +int +main (int argc, char *argv[]) +{ + struct abc foo; + struct pqr bar; + union qwe c; + struct poi d; + struct tyu e; + struct asd f; + uint8_t i; + + 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..67fbd72d64 --- /dev/null +++ b/gdb/testsuite/gdb.base/ptype-offsets.exp @@ -0,0 +1,317 @@ +# 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 testcase exercises the "ptype /o" feature, which can be used to +# print the offsets and sizes of each field of a struct/union/class. + +standard_testfile .cc + +# Test only works on LP64 targets. That's how we guarantee that the +# expected holes will be present in the struct. +if { ![is_lp64_target] } { + untested "test work only on lp64 targets" + return 0 +} + +if { [prepare_for_testing "failed to prepare" $testfile $srcfile \ + { debug c++ }] } { + return -1 +} + +# Test general offset printing, ctor/dtor printing, union, formatting. +gdb_test "ptype /o struct abc" \ + [multi_line \ +{/\* offset | size \*/ type = struct abc \{} \ +{ 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 \*/ signed char field4;} \ +{/\* XXX 7-byte hole \*/} \ +{/\* 32 | 8 \*/ uint64_t field5;} \ +{/\* 40 | 8 \*/ union \{} \ +{/\* 8 \*/ void \*field6;} \ +{/\* 4 \*/ int field7;} \ +{} \ +{ /\* total size \(bytes\): 8 \*/} \ +{ \} field8;} \ +{/\* 48 | 4 \*/ my_int_type field9;} \ +{} \ +{ /\* total size \(bytes\): 56 \*/} \ +{ \}}] \ + "ptype offset struct abc" + +# Test "ptype /oTM". +gdb_test "ptype /oTM struct abc" \ + [multi_line \ +{/\* offset | size \*/ type = struct abc \{} \ +{ 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 \*/ signed char field4;} \ +{/\* XXX 7-byte hole \*/} \ +{/\* 32 | 8 \*/ uint64_t field5;} \ +{/\* 40 | 8 \*/ union \{} \ +{/\* 8 \*/ void \*field6;} \ +{/\* 4 \*/ int field7;} \ +{} \ +{ /\* total size \(bytes\): 8 \*/} \ +{ \} field8;} \ +{/\* 48 | 4 \*/ my_int_type field9;} \ +{} \ +{ abc\(void\);} \ +{ ~abc\(\);} \ +{} \ +{ typedef int my_int_type;} \ +{} \ +{ /\* total size \(bytes\): 56 \*/} \ +{ \}}] \ + "ptype /oTM struct abc" + +# Test "ptype /TMo". This should be the same as "ptype /o". +gdb_test "ptype /TMo struct abc" \ + [multi_line \ +{/\* offset | size \*/ type = struct abc \{} \ +{ 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 \*/ signed char field4;} \ +{/\* XXX 7-byte hole \*/} \ +{/\* 32 | 8 \*/ uint64_t field5;} \ +{/\* 40 | 8 \*/ union \{} \ +{/\* 8 \*/ void \*field6;} \ +{/\* 4 \*/ int field7;} \ +{} \ +{ /\* total size \(bytes\): 8 \*/} \ +{ \} field8;} \ +{/\* 48 | 4 \*/ my_int_type field9;} \ +{} \ +{ /\* total size \(bytes\): 56 \*/} \ +{ \}}] \ + "ptype /TMo struct abc" + +# Test nested structs. +gdb_test "ptype /o struct pqr" \ + [multi_line \ +{/\* offset | size \*/ type = struct pqr \{} \ +{/\* 0 | 4 \*/ int ff1;} \ +{/\* XXX 4-byte hole \*/} \ +{/\* 8 | 40 \*/ struct xyz \{} \ +{/\* 8 | 4 \*/ int f1;} \ +{/\* 12 | 1 \*/ signed char f2;} \ +{/\* XXX 3-byte hole \*/} \ +{/\* 16 | 8 \*/ void \*f3;} \ +{/\* 24 | 24 \*/ struct tuv \{} \ +{/\* 24 | 4 \*/ int a1;} \ +{/\* XXX 4-byte hole \*/} \ +{/\* 32 | 8 \*/ signed char \*a2;} \ +{/\* 40 | 4 \*/ int a3;} \ +{} \ +{ /\* total size \(bytes\): 24 \*/} \ +{ \} f4;} \ +{} \ +{ /\* total size \(bytes\): 40 \*/} \ +{ \} ff2;} \ +{/\* XXX 28-byte hole \*/} \ +{/\* 72 | 1 \*/ signed char ff3;} \ +{} \ +{ /\* total size \(bytes\): 56 \*/} \ +{ \}}] \ + "ptype offset struct pqr" + +# Test that the offset is properly reset when we are printing a union +# and go inside two inner structs. +# This also tests a struct inside a struct inside a union. +gdb_test "ptype /o union qwe" \ + [multi_line \ +{/\* offset | size \*/ type = union qwe \{} \ +{/\* 24 \*/ struct tuv \{} \ +{/\* 0 | 4 \*/ int a1;} \ +{/\* XXX 4-byte hole \*/} \ +{/\* 8 | 8 \*/ signed char \*a2;} \ +{/\* 16 | 4 \*/ int a3;} \ +{} \ +{ /\* total size \(bytes\): 24 \*/} \ +{ \} fff1;} \ +{/\* 40 \*/ struct xyz \{} \ +{/\* 0 | 4 \*/ int f1;} \ +{/\* 4 | 1 \*/ signed char f2;} \ +{/\* XXX 3-byte hole \*/} \ +{/\* 8 | 8 \*/ void \*f3;} \ +{/\* 16 | 24 \*/ struct tuv \{} \ +{/\* 16 | 4 \*/ int a1;} \ +{/\* XXX 4-byte hole \*/} \ +{/\* 24 | 8 \*/ signed char \*a2;} \ +{/\* 32 | 4 \*/ int a3;} \ +{} \ +{ /\* total size \(bytes\): 24 \*/} \ +{ \} f4;} \ +{} \ +{ /\* total size \(bytes\): 40 \*/} \ +{ \} fff2;} \ +{} \ +{ /\* total size \(bytes\): 40 \*/} \ +{ \}}] \ + "ptype offset union qwe" + +# Test printing a struct that contains a union, and that also +# contains a struct. +gdb_test "ptype /o struct poi" \ + [multi_line \ +{/\* offset | size \*/ type = struct poi \{} \ +{/\* 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 \*/ signed char \*a2;} \ +{/\* 24 | 4 \*/ int a3;} \ +{} \ +{ /\* total size \(bytes\): 24 \*/} \ +{ \} fff1;} \ +{/\* 40 \*/ struct xyz \{} \ +{/\* 8 | 4 \*/ int f1;} \ +{/\* 12 | 1 \*/ signed char f2;} \ +{/\* XXX 3-byte hole \*/} \ +{/\* 16 | 8 \*/ void \*f3;} \ +{/\* 24 | 24 \*/ struct tuv \{} \ +{/\* 24 | 4 \*/ int a1;} \ +{/\* XXX 4-byte hole \*/} \ +{/\* 32 | 8 \*/ signed char \*a2;} \ +{/\* 40 | 4 \*/ int a3;} \ +{} \ +{ /\* total size \(bytes\): 24 \*/} \ +{ \} f4;} \ +{} \ +{ /\* total size \(bytes\): 40 \*/} \ +{ \} fff2;} \ +{} \ +{ /\* total size \(bytes\): 40 \*/} \ +{ \} f2;} \ +{/\* 72 | 2 \*/ uint16_t f3;} \ +{/\* XXX 6-byte hole \*/} \ +{/\* 80 | 56 \*/ struct pqr \{} \ +{/\* 80 | 4 \*/ int ff1;} \ +{/\* XXX 4-byte hole \*/} \ +{/\* 88 | 40 \*/ struct xyz \{} \ +{/\* 88 | 4 \*/ int f1;} \ +{/\* 92 | 1 \*/ signed char f2;} \ +{/\* XXX 3-byte hole \*/} \ +{/\* 96 | 8 \*/ void \*f3;} \ +{/\* 104 | 24 \*/ struct tuv \{} \ +{/\* 104 | 4 \*/ int a1;} \ +{/\* XXX 4-byte hole \*/} \ +{/\* 112 | 8 \*/ signed char \*a2;} \ +{/\* 120 | 4 \*/ int a3;} \ +{} \ +{ /\* total size \(bytes\): 24 \*/} \ +{ \} f4;} \ +{} \ +{ /\* total size \(bytes\): 40 \*/} \ +{ \} ff2;} \ +{/\* 152 | 1 \*/ signed char ff3;} \ +{} \ +{ /\* total size \(bytes\): 56 \*/} \ +{ \} f4;} \ +{} \ +{ /\* total size \(bytes\): 112 \*/} \ +{ \}}] \ + "ptype offset struct poi" + +# Test printing a struct with several bitfields, laid out in various +# ways. +# +# Because dealing with bitfields and offsets is difficult, it can be +# tricky to confirm that the output of the this command is accurate. +# A nice way to do that is to use GDB's "x" command and print the +# actual memory layout of the struct. In order to differentiate +# between bitfields and non-bitfield variables, one can assign "-1" to +# every bitfield in the struct. An example of the output of "x" using +# "struct tyu" is: +# +# (gdb) x/24xb &e +# 0x7fffffffd540: 0xff 0xff 0xff 0x1f 0x00 0x00 0x00 0x00 +# 0x7fffffffd548: 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff +# 0x7fffffffd550: 0xff 0x00 0x00 0x00 0x00 0x00 0x00 0x00 +gdb_test "ptype /o struct tyu" \ + [multi_line \ +{/\* offset | size \*/ type = struct tyu \{} \ +{/\* 0:31 | 4 \*/ int a1 : 1;} \ +{/\* 0:28 | 4 \*/ int a2 : 3;} \ +{/\* 0: 5 | 4 \*/ int a3 : 23;} \ +{/\* 3: 3 | 1 \*/ signed char a4 : 2;} \ +{/\* XXX 3-bit hole \*/} \ +{/\* XXX 4-byte hole \*/} \ +{/\* 8 | 8 \*/ int64_t a5;} \ +{/\* 16:27 | 4 \*/ int a6 : 5;} \ +{/\* 16:56 | 8 \*/ int64_t a7 : 3;} \ +{} \ +{ /\* total size \(bytes\): 24 \*/} \ +{ \}}] \ + "ptype offset struct tyu" + +gdb_test "ptype /o struct asd" \ + [multi_line \ +{/\* offset | size \*/ type = struct asd \{} \ +{/\* 0 | 32 \*/ struct asd::jkl \{} \ +{/\* 0 | 8 \*/ signed char \*f1;} \ +{/\* 8 | 8 \*/ union \{} \ +{/\* 8 \*/ void \*ff1;} \ +{} \ +{ /\* total size \(bytes\): 8 \*/} \ +{ \} f2;} \ +{/\* 16 | 8 \*/ union \{} \ +{/\* 8 \*/ signed char \*ff2;} \ +{} \ +{ /\* total size \(bytes\): 8 \*/} \ +{ \} f3;} \ +{/\* 24:27 | 4 \*/ int f4 : 5;} \ +{/\* 24:26 | 4 \*/ unsigned int f5 : 1;} \ +{/\* XXX 2-bit hole \*/} \ +{/\* XXX 1-byte hole \*/} \ +{/\* 26 | 2 \*/ short f6;} \ +{} \ +{ /\* total size \(bytes\): 32 \*/} \ +{ \} f7;} \ +{/\* 32 | 8 \*/ unsigned long f8;} \ +{/\* 40 | 8 \*/ signed char \*f9;} \ +{/\* 48:28 | 4 \*/ int f10 : 4;} \ +{/\* 48:27 | 4 \*/ unsigned int f11 : 1;} \ +{/\* 48:26 | 4 \*/ unsigned int f12 : 1;} \ +{/\* 48:25 | 4 \*/ unsigned int f13 : 1;} \ +{/\* 48:24 | 4 \*/ unsigned int f14 : 1;} \ +{/\* XXX 7-byte hole \*/} \ +{/\* 56 | 8 \*/ void \*f15;} \ +{/\* 64 | 8 \*/ void \*f16;} \ +{} \ +{ /\* total size \(bytes\): 72 \*/} \ +{ \}}] \ + "ptype offset struct asd" + +# 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" diff --git a/gdb/typeprint.c b/gdb/typeprint.c index 9d9d6f5a49..9f44f3329a 100644 --- a/gdb/typeprint.c +++ b/gdb/typeprint.c @@ -42,6 +42,7 @@ 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 */ NULL, /* local_typedefs */ NULL, /* global_table */ @@ -55,6 +56,7 @@ static struct type_print_options default_ptype_flags = 0, /* raw */ 1, /* print_methods */ 1, /* print_typedefs */ + 0, /* print_offsets */ 0, /* print_nested_type_limit */ NULL, /* local_typedefs */ NULL, /* global_table */ @@ -440,6 +442,19 @@ whatis_exp (const char *exp, int show) case 'T': flags.print_typedefs = 1; break; + case 'o': + { + /* Filter out languages which don't implement the + feature. */ + if (current_language->la_language == language_c + || current_language->la_language == language_cplus) + { + flags.print_offsets = 1; + flags.print_typedefs = 0; + flags.print_methods = 0; + } + break; + } default: error (_("unrecognized flag '%c'"), *exp); } @@ -499,6 +514,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 */ "); + printf_filtered ("type = "); if (!flags.raw) @@ -759,7 +779,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 a2058b0120..2e19006f22 100644 --- a/gdb/typeprint.h +++ b/gdb/typeprint.h @@ -24,6 +24,20 @@ struct ui_file; struct typedef_hash_table; struct ext_lang_type_printers; +struct print_offset_data +{ + /* 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 over + the offset of the outter struct. */ + unsigned int offset_bitpos = 0; + + /* END_BITPOS is the one-past-the-end bit position of the previous + field (where we expect the current field to be if there is no + hole). */ + unsigned int end_bitpos = 0; +}; + struct type_print_options { /* True means that no special printing flags should apply. */ @@ -35,6 +49,9 @@ 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; -- 2.14.3 ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH v7 2/2] Implement pahole-like 'ptype /o' option 2017-12-15 1:13 ` [PATCH v7 2/2] Implement pahole-like 'ptype /o' option Sergio Durigan Junior @ 2017-12-15 17:24 ` Pedro Alves 2017-12-15 20:04 ` Sergio Durigan Junior 0 siblings, 1 reply; 75+ messages in thread From: Pedro Alves @ 2017-12-15 17:24 UTC (permalink / raw) To: Sergio Durigan Junior, GDB Patches Cc: Tom Tromey, Eli Zaretskii, Simon Marchi, Keith Seitz Hi Sergio, I found a couple more nits to pick, but this is close. This is OK with the issues below addressed. Please address them, and push this in. Please post the end result for the archives. On 12/15/2017 01:12 AM, Sergio Durigan Junior wrote: > @@ -867,15 +913,121 @@ 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. > + > + END_BITPOS 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. There's still a reference to ENDPOS above that I assume should be END_BITPOS. Now that I'm looking at this, do we still need this parameter, given print_offset_data->end_bitpos exists? What's the relationship between the two? It's a bit confusing to have two things for the same. I think the comment could/should clarify this. > + > + 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). This parameter no longer exists (it's a field of print_offset_data now, right?). The comment should be adjusted, moved elsewhere, or deleted. > + > + The output is strongly based on pahole(1). */ > + > +static void > +c_print_type_struct_field_offset (struct type *type, unsigned int field_idx, > + struct ui_file *stream, > + unsigned int *end_bitpos, > + struct print_offset_data *podata) > +{ > @@ -1143,9 +1347,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)) > { > - c_print_type (TYPE_TARGET_TYPE (TYPE_FN_FIELD_TYPE (f, j)), > - "", stream, -1, 0, > - &local_flags); > + 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_1 (TYPE_TARGET_TYPE (TYPE_FN_FIELD_TYPE (f, j)), > + "", stream, -1, 0, > + &local_flags, podata); > + local_flags.print_offsets = old_po; This pattern appears in several places. Would it make sense to add a c_print_type_no_offsets routine that handled the print_offsets save/restore? > +++ b/gdb/testsuite/gdb.base/ptype-offsets.exp > @@ -0,0 +1,317 @@ > +# 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 testcase exercises the "ptype /o" feature, which can be used to > +# print the offsets and sizes of each field of a struct/union/class. > + > +standard_testfile .cc > + > +# Test only works on LP64 targets. That's how we guarantee that the > +# expected holes will be present in the struct. > +if { ![is_lp64_target] } { > + untested "test work only on lp64 targets" > + return 0 > +} > + > +if { [prepare_for_testing "failed to prepare" $testfile $srcfile \ > + { debug c++ }] } { > + return -1 > +} > + > +# Test general offset printing, ctor/dtor printing, union, formatting. > +gdb_test "ptype /o struct abc" \ > + [multi_line \ ... > +{ \}}] \ > + "ptype offset struct abc" I notice that you're replacing the "/o" in most test names/messages. Is there a reason for that? Why not just let the test name default to the command invocation? I.e., remove the 3rd argument in all these calls to gdb_test. > + > +# Test "ptype /oTM". > +gdb_test "ptype /oTM struct abc" \ > + [multi_line \ .. > + "ptype /oTM struct abc" Here the "/" persisted, so I'm curious why the other cases have "/o" replaced by "offset". I'd just remove the explicit test messages. It just seems like potential for getting out of sync otherwise. > +# Because dealing with bitfields and offsets is difficult, it can be > +# tricky to confirm that the output of the this command is accurate. typo: "of the this" > @@ -499,6 +514,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 */ "); I noticed that "whatis" also prints this header: (top-gdb) whatis/o minimal_symbol /* offset | size */ type = minimal_symbol I guess it shouldn't, for it's pointless. You could either use the "show" parameter here, or just ignore /o for whatis where the options are parsed, further above. And then please add a test covering that. :-) You'll have to use gdb_test_multiple with an anchor, because gdb_test "whatis/o minimal_symbol" \ "type = minimal_symbol" would match the bad output anyway. So something like: set test "whatis /o minimal_symbol" gdb_test_multiple $test $test { -ex "^$test\r\ntype = minimal_symbol\r\n$gdb_prompt $" { pass $test } As mentioned, OK with these issues addressed. Please push and post. Thanks, Pedro Alves ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH v7 2/2] Implement pahole-like 'ptype /o' option 2017-12-15 17:24 ` Pedro Alves @ 2017-12-15 20:04 ` Sergio Durigan Junior 0 siblings, 0 replies; 75+ messages in thread From: Sergio Durigan Junior @ 2017-12-15 20:04 UTC (permalink / raw) To: Pedro Alves Cc: GDB Patches, Tom Tromey, Eli Zaretskii, Simon Marchi, Keith Seitz On Friday, December 15 2017, Pedro Alves wrote: > Hi Sergio, > > I found a couple more nits to pick, but this is close. > > This is OK with the issues below addressed. Please address > them, and push this in. Please post the end result for the > archives. Thanks for review. I'll post the patch below. > On 12/15/2017 01:12 AM, Sergio Durigan Junior wrote: > >> @@ -867,15 +913,121 @@ 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. >> + >> + END_BITPOS 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. > > There's still a reference to ENDPOS above that I assume should > be END_BITPOS. Now that I'm looking at this, do we still need > this parameter, given print_offset_data->end_bitpos exists? > What's the relationship between the two? It's a bit confusing > to have two things for the same. I think the comment could/should > clarify this. Yeah, you're right, I was using END_BITPOS as a separate value because of the problem stated here: /* We're entering a struct/union. Right now, PODATA->END_BITPOS points right *after* the struct/union. However, when printing the first field of this inner struct/union, the end_bitpos we're expecting is exactly at the beginning of the struct/union. Therefore, we subtract the length of the whole struct/union. */ local_podata.end_bitpos = podata->end_bitpos - TYPE_LENGTH (TYPE_FIELD_TYPE (type, i)) * TARGET_CHAR_BIT; However, it's just a matter of using PODATA->END_BITPOS instead, no big deal. So I've made the change, adapted the comments and references, and it's all working as expected. >> + >> + 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). > > This parameter no longer exists (it's a field of print_offset_data now, > right?). The comment should be adjusted, moved elsewhere, or deleted. Yes, that's correct. I'll adjust it. >> + >> + The output is strongly based on pahole(1). */ >> + >> +static void >> +c_print_type_struct_field_offset (struct type *type, unsigned int field_idx, >> + struct ui_file *stream, >> + unsigned int *end_bitpos, >> + struct print_offset_data *podata) >> +{ > > >> @@ -1143,9 +1347,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)) >> { >> - c_print_type (TYPE_TARGET_TYPE (TYPE_FN_FIELD_TYPE (f, j)), >> - "", stream, -1, 0, >> - &local_flags); >> + 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_1 (TYPE_TARGET_TYPE (TYPE_FN_FIELD_TYPE (f, j)), >> + "", stream, -1, 0, >> + &local_flags, podata); >> + local_flags.print_offsets = old_po; > > This pattern appears in several places. Would it make sense to > add a c_print_type_no_offsets routine that handled the > print_offsets save/restore? Sure thing. I'll add such function. > >> +++ b/gdb/testsuite/gdb.base/ptype-offsets.exp >> @@ -0,0 +1,317 @@ >> +# 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 testcase exercises the "ptype /o" feature, which can be used to >> +# print the offsets and sizes of each field of a struct/union/class. >> + >> +standard_testfile .cc >> + >> +# Test only works on LP64 targets. That's how we guarantee that the >> +# expected holes will be present in the struct. >> +if { ![is_lp64_target] } { >> + untested "test work only on lp64 targets" >> + return 0 >> +} >> + >> +if { [prepare_for_testing "failed to prepare" $testfile $srcfile \ >> + { debug c++ }] } { >> + return -1 >> +} >> + >> +# Test general offset printing, ctor/dtor printing, union, formatting. >> +gdb_test "ptype /o struct abc" \ >> + [multi_line \ > > ... > >> +{ \}}] \ >> + "ptype offset struct abc" > > I notice that you're replacing the "/o" in most test > names/messages. Is there a reason for that? Why > not just let the test name default to the command > invocation? I.e., remove the 3rd argument in all > these calls to gdb_test. I thought about that, but I guess I'm too used to always include the test message, plus I wasn't sure if there would be some case when the test command would be the same. But sure, I will do that. > >> + >> +# Test "ptype /oTM". >> +gdb_test "ptype /oTM struct abc" \ >> + [multi_line \ > > .. > >> + "ptype /oTM struct abc" > > Here the "/" persisted, so I'm curious why the other cases > have "/o" replaced by "offset". > > I'd just remove the explicit test messages. It just seems > like potential for getting out of sync otherwise. Done. >> +# Because dealing with bitfields and offsets is difficult, it can be >> +# tricky to confirm that the output of the this command is accurate. > > typo: "of the this" Fixed. >> @@ -499,6 +514,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 */ "); > > I noticed that "whatis" also prints this header: > > (top-gdb) whatis/o minimal_symbol > /* offset | size */ type = minimal_symbol > > I guess it shouldn't, for it's pointless. You could either > use the "show" parameter here, or just ignore /o for whatis > where the options are parsed, further above. Aha, good catch. Kind of forgot about "whatis" ;-). I decided to use the 'show' parameter, checking if it is > 0, but did that further above, because I don't want print_offsets to be 1. > And then please add a test covering that. :-) > > You'll have to use gdb_test_multiple with an anchor, > because > > gdb_test "whatis/o minimal_symbol" \ > "type = minimal_symbol" > > would match the bad output anyway. > > So something like: > > set test "whatis /o minimal_symbol" > gdb_test_multiple $test $test { > -ex "^$test\r\ntype = minimal_symbol\r\n$gdb_prompt $" { > pass $test > } Cool, I didn't remember you could match the test command with gdb_test_multiple. BTW, I replaced -ex by -re, but it works OK. > As mentioned, OK with these issues addressed. Please push and post. Thanks, here's what I'll push. -- Sergio GPG key ID: 237A 54B1 0287 28BF 00EF 31F4 D0EB 7628 65FC 5E36 Please send encrypted e-mail if possible http://sergiodj.net/ From ad7a465f76ace7e8e907b1616206bd6bd29c98cb Mon Sep 17 00:00:00 2001 From: Sergio Durigan Junior <sergiodj@redhat.com> Date: Mon, 20 Nov 2017 16:34:59 -0500 Subject: [PATCH] Implement pahole-like 'ptype /o' option 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: /* offset | size */ type = struct wer : public tuv { public: /* 32 | 24 */ struct tyu { /* 32:31 | 4 */ int a1 : 1; /* 32:28 | 4 */ int a2 : 3; /* 32: 5 | 4 */ int a3 : 23; /* 35: 3 | 1 */ char a4 : 2; /* XXX 3-bit hole */ /* XXX 4-byte hole */ /* 40 | 8 */ int64_t a5; /* 48:27 | 4 */ int a6 : 5; /* 48:56 | 8 */ int64_t a7 : 3; /* total size (bytes): 24 */ } a1; /* total size (bytes): 56 */ } A big part of this patch handles the formatting logic of 'ptype', which is a bit messy. The code to handle bitfield offsets, however, took some time to craft. My thanks to Pedro Alves for figuring things out and pointing me to the right direction, as well as coming up with a way to inspect the layout of structs with bitfields (see testcase for comments). After many discussions both on IRC and at the mailing list, I tried to implement printing vtables and inherited classes. Unfortunately the code grew too complex and there were still a few corner cases failing so I had to drop the attempt. This should be implemented in a future patch. 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-12-15 Sergio Durigan Junior <sergiodj@redhat.com> Pedro Alves <palves@redhat.com> PR cli/16224 * NEWS (Changes since GDB 8.0): Mention new '/o' flag. * c-typeprint.c (OFFSET_SPC_LEN): New define. (c_type_print_varspec_prefix): New argument 'struct print_offset_data *'. (c_type_print_base_1): New function and prototype. (c_print_type_1): New function, with code from 'c_print_type'. (c_print_type): Use 'c_print_type_1'. (c_type_print_varspec_prefix): New argument 'struct print_offset_data *'. Use it. Call 'c_type_print_base_1' instead of 'c_print_type_base'. (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_vtable_offset_marker): New function. (c_print_type_union_field_offset): New function. (c_print_type_struct_field_offset): New function. (c_print_type_no_offsets): New function. (c_type_print_base_struct_union): New argument 'struct print_offset_data *'. Print offsets and sizes for struct/union/class fields. * typeprint.c (const struct type_print_options type_print_raw_options): Initialize 'print_offsets'. (static struct type_print_options default_ptype_flags): Likewise. (struct print_offset_data print_offset_default_data): New variable. (whatis_exp): Handle '/o' option. (_initialize_typeprint): Add '/o' flag to ptype's help. * typeprint.h (struct print_offset_data): New struct. (struct type_print_options) <print_offsets>: New field. gdb/testsuite/ChangeLog: 2017-12-15 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-12-15 Sergio Durigan Junior <sergiodj@redhat.com> PR cli/16224 * gdb.texinfo (ptype): Add documentation for new flag '/o'. --- gdb/NEWS | 3 + gdb/c-typeprint.c | 391 ++++++++++++++++++++++++++----- gdb/doc/gdb.texinfo | 124 ++++++++++ gdb/testsuite/gdb.base/ptype-offsets.cc | 193 +++++++++++++++ gdb/testsuite/gdb.base/ptype-offsets.exp | 318 +++++++++++++++++++++++++ gdb/typeprint.c | 24 +- gdb/typeprint.h | 17 ++ 7 files changed, 1004 insertions(+), 66 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 bd5ae36dd0..3a7e704cc6 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. + * 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. diff --git a/gdb/c-typeprint.c b/gdb/c-typeprint.c index 3854886387..c68936d7db 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 @@ -45,12 +53,18 @@ enum access_specifier static void c_type_print_varspec_prefix (struct type *, struct ui_file *, int, int, int, - const struct type_print_options *); + const struct type_print_options *, + struct print_offset_data *); /* Print "const", "volatile", or address space modifiers. */ static void c_type_print_modifier (struct type *, struct ui_file *, int, int); + +static void c_type_print_base_1 (struct type *type, struct ui_file *stream, + int show, int level, + const struct type_print_options *flags, + struct print_offset_data *podata); \f /* A callback function for cp_canonicalize_string_full that uses @@ -82,14 +96,15 @@ print_name_maybe_canonical (const char *name, \f -/* LEVEL is the depth to indent lines by. */ +/* Helper function for c_print_type. */ -void -c_print_type (struct type *type, - const char *varstring, - struct ui_file *stream, - int show, int level, - const struct type_print_options *flags) +static void +c_print_type_1 (struct type *type, + const char *varstring, + struct ui_file *stream, + int show, int level, + const struct type_print_options *flags, + struct print_offset_data *podata) { enum type_code code; int demangled_args; @@ -108,7 +123,7 @@ c_print_type (struct type *type, } else { - c_type_print_base (type, stream, show, level, flags); + c_type_print_base_1 (type, stream, show, level, flags, podata); code = TYPE_CODE (type); if ((varstring != NULL && *varstring != '\0') /* Need a space if going to print stars or brackets; @@ -124,7 +139,7 @@ c_print_type (struct type *type, fputs_filtered (" ", stream); need_post_space = (varstring != NULL && strcmp (varstring, "") != 0); c_type_print_varspec_prefix (type, stream, show, 0, need_post_space, - flags); + flags, podata); } if (varstring != NULL) @@ -143,6 +158,20 @@ c_print_type (struct type *type, } } +/* LEVEL is the depth to indent lines by. */ + +void +c_print_type (struct type *type, + const char *varstring, + struct ui_file *stream, + int show, int level, + const struct type_print_options *flags) +{ + struct print_offset_data podata; + + c_print_type_1 (type, varstring, stream, show, level, flags, &podata); +} + /* Print a typedef using C syntax. TYPE is the underlying type. NEW_SYMBOL is the symbol naming the type. STREAM is the stream on which to print. */ @@ -310,7 +339,8 @@ c_type_print_varspec_prefix (struct type *type, struct ui_file *stream, int show, int passed_a_ptr, int need_post_space, - const struct type_print_options *flags) + const struct type_print_options *flags, + struct print_offset_data *podata) { const char *name; @@ -326,40 +356,40 @@ c_type_print_varspec_prefix (struct type *type, { case TYPE_CODE_PTR: c_type_print_varspec_prefix (TYPE_TARGET_TYPE (type), - stream, show, 1, 1, flags); + stream, show, 1, 1, flags, podata); fprintf_filtered (stream, "*"); c_type_print_modifier (type, stream, 1, need_post_space); break; case TYPE_CODE_MEMBERPTR: c_type_print_varspec_prefix (TYPE_TARGET_TYPE (type), - stream, show, 0, 0, flags); + stream, show, 0, 0, flags, podata); name = type_name_no_tag (TYPE_SELF_TYPE (type)); if (name) print_name_maybe_canonical (name, flags, stream); else - c_type_print_base (TYPE_SELF_TYPE (type), - stream, -1, passed_a_ptr, flags); + c_type_print_base_1 (TYPE_SELF_TYPE (type), + stream, -1, passed_a_ptr, flags, podata); fprintf_filtered (stream, "::*"); break; case TYPE_CODE_METHODPTR: c_type_print_varspec_prefix (TYPE_TARGET_TYPE (type), - stream, show, 0, 0, flags); + stream, show, 0, 0, flags, podata); fprintf_filtered (stream, "("); name = type_name_no_tag (TYPE_SELF_TYPE (type)); if (name) print_name_maybe_canonical (name, flags, stream); else - c_type_print_base (TYPE_SELF_TYPE (type), - stream, -1, passed_a_ptr, flags); + c_type_print_base_1 (TYPE_SELF_TYPE (type), + stream, -1, passed_a_ptr, flags, podata); fprintf_filtered (stream, "::*"); break; case TYPE_CODE_REF: case TYPE_CODE_RVALUE_REF: c_type_print_varspec_prefix (TYPE_TARGET_TYPE (type), - stream, show, 1, 0, flags); + stream, show, 1, 0, flags, podata); fprintf_filtered (stream, TYPE_CODE(type) == TYPE_CODE_REF ? "&" : "&&"); c_type_print_modifier (type, stream, 1, need_post_space); break; @@ -367,21 +397,22 @@ c_type_print_varspec_prefix (struct type *type, case TYPE_CODE_METHOD: case TYPE_CODE_FUNC: c_type_print_varspec_prefix (TYPE_TARGET_TYPE (type), - stream, show, 0, 0, flags); + stream, show, 0, 0, flags, podata); if (passed_a_ptr) fprintf_filtered (stream, "("); break; case TYPE_CODE_ARRAY: c_type_print_varspec_prefix (TYPE_TARGET_TYPE (type), - stream, show, 0, 0, flags); + stream, show, 0, 0, flags, podata); if (passed_a_ptr) fprintf_filtered (stream, "("); break; case TYPE_CODE_TYPEDEF: c_type_print_varspec_prefix (TYPE_TARGET_TYPE (type), - stream, show, passed_a_ptr, 0, flags); + stream, show, passed_a_ptr, 0, flags, + podata); break; case TYPE_CODE_UNDEF: @@ -836,21 +867,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 +904,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 +913,121 @@ 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. + + PODATA->END_BITPOS 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. + + PODATA->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, + struct ui_file *stream, + struct print_offset_data *podata) +{ + 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 = fieldsize_byte * TARGET_CHAR_BIT; + + /* We check for PODATA->END_BITPOS > 0 because there is a specific + scenario when PODATA->END_BITPOS 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 PODATA->END_BITPOS > 0 here, GDB will report + a hole before the first field, which is not accurate. */ + if (podata->end_bitpos > 0 && podata->end_bitpos < bitpos) + { + /* If PODATA->END_BITPOS 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 - podata->end_bitpos; + 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); + } + + if (TYPE_FIELD_PACKED (type, field_idx)) + { + /* We're dealing with a bitfield. Print how many bits are left + to be used. */ + unsigned int bitsize = TYPE_FIELD_BITSIZE (type, field_idx); + /* The bitpos relative to the beginning of our container + field. */ + unsigned int relative_bitpos; + + /* The following was copied from + value.c:value_primitive_field. */ + if ((bitpos % fieldsize_bit) + bitsize <= fieldsize_bit) + relative_bitpos = bitpos % fieldsize_bit; + else + relative_bitpos = bitpos % TARGET_CHAR_BIT; + + /* This is the exact offset (in bits) of this bitfield. */ + unsigned int bit_offset + = (bitpos - relative_bitpos) + podata->offset_bitpos; + + /* The position of the field, relative to the beginning of the + struct, and how many bits are left to be used in this + container. */ + fprintf_filtered (stream, "/* %4u:%2u", bit_offset / TARGET_CHAR_BIT, + fieldsize_bit - (relative_bitpos + bitsize)); + fieldsize_bit = bitsize; + } + else + { + /* The position of the field, relative to the beginning of the + struct. */ + fprintf_filtered (stream, "/* %4u", + (bitpos + podata->offset_bitpos) / TARGET_CHAR_BIT); + + fprintf_filtered (stream, " "); + } + + fprintf_filtered (stream, " | %4u */", fieldsize_byte); + + podata->end_bitpos = bitpos + fieldsize_bit; +} + +/* Return true if an access label (i.e., "public:", "private:", "protected:") needs to be printed for TYPE. */ static bool @@ -926,13 +1078,35 @@ need_access_label_p (struct type *type) return false; } +/* Helper function that temporarily disables FLAGS->PRINT_OFFSETS, + calls 'c_print_type_1', and then reenables FLAGS->PRINT_OFFSETS if + applicable. */ + +static void +c_print_type_no_offsets (struct type *type, + const char *varstring, + struct ui_file *stream, + int show, int level, + struct type_print_options *flags, + struct print_offset_data *podata) +{ + unsigned int old_po = flags->print_offsets; + + /* Temporarily disable print_offsets, because it would mess with + indentation. */ + flags->print_offsets = 0; + c_print_type_1 (type, varstring, stream, show, level, flags, podata); + flags->print_offsets = old_po; +} + /* 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) + const struct type_print_options *flags, + struct print_offset_data *podata) { struct type_print_options local_flags = *flags; struct type_print_options semi_local_flags = *flags; @@ -1004,6 +1178,7 @@ c_type_print_base_struct_union (struct type *type, struct ui_file *stream, 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) { @@ -1032,6 +1207,9 @@ 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); + + struct print_offset_data local_podata; + for (int i = TYPE_N_BASECLASSES (type); i < len; i++) { QUIT; @@ -1048,18 +1226,64 @@ 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, stream, podata); + } + 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 "); - 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)) + + 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/union. */ + local_podata.offset_bitpos + = podata->offset_bitpos + TYPE_FIELD_BITPOS (type, i); + /* We're entering a struct/union. Right now, + PODATA->END_BITPOS points right *after* the + struct/union. However, when printing the first field + of this inner struct/union, the end_bitpos we're + expecting is exactly at the beginning of the + struct/union. Therefore, we subtract the length of + the whole struct/union. */ + local_podata.end_bitpos + = podata->end_bitpos + - TYPE_LENGTH (TYPE_FIELD_TYPE (type, i)) * TARGET_CHAR_BIT; + } + + c_print_type_1 (TYPE_FIELD_TYPE (type, i), + TYPE_FIELD_NAME (type, i), + stream, newshow, level + 4, + &local_flags, &local_podata); + + 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 +1346,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 +1368,10 @@ c_type_print_base_struct_union (struct type *type, struct ui_file *stream, && !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); + c_print_type_no_offsets + (TYPE_TARGET_TYPE (TYPE_FN_FIELD_TYPE (f, j)), + "", stream, -1, 0, &local_flags, podata); + fputs_filtered (" ", stream); } if (TYPE_FN_FIELD_STUB (f, j)) @@ -1226,9 +1452,11 @@ 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); - c_print_type (TYPE_NESTED_TYPES_FIELD_TYPE (type, i), - "", stream, show, level + 4, &semi_local_flags); + print_spaces_filtered_with_print_options (level + 4, stream, + flags); + c_print_type_no_offsets (TYPE_NESTED_TYPES_FIELD_TYPE (type, i), + "", stream, show, level + 4, + &semi_local_flags, podata); fprintf_filtered (stream, ";\n"); } } @@ -1254,22 +1482,40 @@ 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 "); /* 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); + c_print_type_no_offsets (target, + TYPE_TYPEDEF_FIELD_NAME (type, i), + stream, show - 1, level + 4, + &semi_local_flags, podata); fprintf_filtered (stream, ";\n"); } } + if (flags->print_offsets) + { + if (show > 0) + { + fputs_filtered ("\n", stream); + print_spaces_filtered_with_print_options (level + 4, + stream, + flags); + fprintf_filtered (stream, "/* total size (bytes): %4u */\n", + TYPE_LENGTH (type)); + } + + print_spaces_filtered (OFFSET_SPC_LEN, stream); + if (level == 0) + print_spaces_filtered (2, stream); + } + fprintfi_filtered (level, stream, "}"); } @@ -1294,9 +1540,11 @@ c_type_print_base_struct_union (struct type *type, struct ui_file *stream, LEVEL is the number of spaces to indent by. We increase it for some recursive calls. */ -void -c_type_print_base (struct type *type, struct ui_file *stream, - int show, int level, const struct type_print_options *flags) +static void +c_type_print_base_1 (struct type *type, struct ui_file *stream, + int show, int level, + const struct type_print_options *flags, + struct print_offset_data *podata) { int i; int len; @@ -1341,8 +1589,8 @@ c_type_print_base (struct type *type, struct ui_file *stream, if (TYPE_TARGET_TYPE (type) == NULL) type_print_unknown_return_type (stream); else - c_type_print_base (TYPE_TARGET_TYPE (type), - stream, show, level, flags); + c_type_print_base_1 (TYPE_TARGET_TYPE (type), + stream, show, level, flags, podata); break; case TYPE_CODE_ARRAY: case TYPE_CODE_PTR: @@ -1350,13 +1598,14 @@ c_type_print_base (struct type *type, struct ui_file *stream, case TYPE_CODE_REF: case TYPE_CODE_RVALUE_REF: case TYPE_CODE_METHODPTR: - c_type_print_base (TYPE_TARGET_TYPE (type), - stream, show, level, flags); + c_type_print_base_1 (TYPE_TARGET_TYPE (type), + stream, show, level, flags, podata); break; case TYPE_CODE_STRUCT: case TYPE_CODE_UNION: - c_type_print_base_struct_union (type, stream, show, level, flags); + c_type_print_base_struct_union (type, stream, show, level, flags, + podata); break; case TYPE_CODE_ENUM: @@ -1456,10 +1705,10 @@ c_type_print_base (struct type *type, struct ui_file *stream, print_spaces_filtered (level + 4, stream); /* We pass "show" here and not "show - 1" to get enum types printed. There's no other way to see them. */ - c_print_type (TYPE_FIELD_TYPE (type, i), - TYPE_FIELD_NAME (type, i), - stream, show, level + 4, - &local_flags); + c_print_type_1 (TYPE_FIELD_TYPE (type, i), + TYPE_FIELD_NAME (type, i), + stream, show, level + 4, + &local_flags, podata); fprintf_filtered (stream, " @%s", plongest (TYPE_FIELD_BITPOS (type, i))); if (TYPE_FIELD_BITSIZE (type, i) > 1) @@ -1518,3 +1767,15 @@ c_type_print_base (struct type *type, struct ui_file *stream, break; } } + +/* See c_type_print_base_1. */ + +void +c_type_print_base (struct type *type, struct ui_file *stream, + int show, int level, + const struct type_print_options *flags) +{ + struct print_offset_data podata; + + c_type_print_base_1 (type, stream, show, level, flags, &podata); +} diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index da3ed28dfe..60ed80c363 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -17216,6 +17216,130 @@ 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. This option implies the @code{/tm} flags. + +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; +@}; + +struct tyu +@{ + int a1 : 1; + int a2 : 3; + int a3 : 23; + char a4 : 2; + int64_t a5; + int a6 : 5; + int64_t a7 : 3; +@}; +@end smallexample + +Issuing a @kbd{ptype /o struct tuv} command would print: + +@smallexample +(@value{GDBP}) ptype /o struct tuv +/* offset | size */ type = struct tuv @{ +/* 0 | 4 */ int a1; +/* XXX 4-byte hole */ +/* 8 | 8 */ char *a2; +/* 16 | 4 */ int a3; + + /* total size (bytes): 24 */ + @} +@end smallexample + +Notice the format of the first column of comments. There, you can +find two parts separated by the @samp{|} character: the @emph{offset}, +which indicates where the field is located inside the struct, in +bytes, and the @emph{size} of the field. Another interesting line is +the marker of a @emph{hole} in the struct, indicating that it may be +possible to pack the struct and make it use less space by reorganizing +its fields. + +It is also possible to print offsets inside an union: + +@smallexample +(@value{GDBP}) ptype /o union qwe +/* offset | size */ type = union qwe @{ +/* 24 */ struct tuv @{ +/* 0 | 4 */ int a1; +/* XXX 4-byte hole */ +/* 8 | 8 */ char *a2; +/* 16 | 4 */ int a3; + + /* total size (bytes): 24 */ + @} 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 (bytes): 24 */ + @} f4; + + /* total size (bytes): 40 */ + @} fff2; + + /* total size (bytes): 40 */ + @} +@end smallexample + +In this case, since @code{struct tuv} and @code{struct xyz} occupy the +same space (because we are dealing with an union), the offset is not +printed for them. However, you can still examine the offset of each +of these structures' fields. + +Another useful scenario is printing the offsets of a struct containing +bitfields: + +@smallexample +(@value{GDBP}) ptype /o struct tyu +/* offset | size */ type = struct tyu @{ +/* 0:31 | 4 */ int a1 : 1; +/* 0:28 | 4 */ int a2 : 3; +/* 0: 5 | 4 */ int a3 : 23; +/* 3: 3 | 1 */ signed char a4 : 2; +/* XXX 3-bit hole */ +/* XXX 4-byte hole */ +/* 8 | 8 */ int64_t a5; +/* 16:27 | 4 */ int a6 : 5; +/* 16:56 | 8 */ int64_t a7 : 3; + + /* total size (bytes): 24 */ + @} +@end smallexample + +Note how the offset information is now extended to also include how +many bits are left to be used in each bitfield. @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..86735203ad --- /dev/null +++ b/gdb/testsuite/gdb.base/ptype-offsets.cc @@ -0,0 +1,193 @@ +/* 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/>. */ + +#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. */ + signed int field3; + + /* No hole here. */ + + /* 1-byte char. */ + signed 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. */ + signed int field7; + } field8; + + /* Empty constructor. */ + abc () + {} + + /* Typedef defined in-struct. */ + typedef int my_int_type; + + my_int_type field9; +}; + +/* This struct will be nested inside 'struct xyz'. */ + +struct tuv +{ + signed int a1; + + signed char *a2; + + signed int a3; +}; + +/* This struct will be nested inside 'struct pqr'. */ + +struct xyz +{ + signed int f1; + + signed char f2; + + void *f3; + + struct tuv f4; +}; + +/* A struct with a nested struct. */ + +struct pqr +{ + signed int ff1; + + struct xyz ff2; + + signed char ff3; +}; + +/* A union with two nested structs. */ + +union qwe +{ + struct tuv fff1; + + struct xyz fff2; +}; + +/* A struct with an union. */ + +struct poi +{ + signed int f1; + + union qwe f2; + + uint16_t f3; + + struct pqr f4; +}; + +/* A struct with bitfields. */ + +struct tyu +{ + signed int a1 : 1; + + signed int a2 : 3; + + signed int a3 : 23; + + signed char a4 : 2; + + int64_t a5; + + signed int a6 : 5; + + int64_t a7 : 3; +}; + +/* A struct with structs and unions. */ + +struct asd +{ + struct jkl + { + signed char *f1; + union + { + void *ff1; + } f2; + union + { + signed char *ff2; + } f3; + int f4 : 5; + unsigned int f5 : 1; + short f6; + } f7; + unsigned long f8; + signed char *f9; + int f10 : 4; + unsigned int f11 : 1; + unsigned int f12 : 1; + unsigned int f13 : 1; + unsigned int f14 : 1; + void *f15; + void *f16; +}; + + +int +main (int argc, char *argv[]) +{ + struct abc foo; + struct pqr bar; + union qwe c; + struct poi d; + struct tyu e; + struct asd f; + uint8_t i; + + 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..55b1eb2b1a --- /dev/null +++ b/gdb/testsuite/gdb.base/ptype-offsets.exp @@ -0,0 +1,318 @@ +# 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 testcase exercises the "ptype /o" feature, which can be used to +# print the offsets and sizes of each field of a struct/union/class. + +standard_testfile .cc + +# Test only works on LP64 targets. That's how we guarantee that the +# expected holes will be present in the struct. +if { ![is_lp64_target] } { + untested "test work only on lp64 targets" + return 0 +} + +if { [prepare_for_testing "failed to prepare" $testfile $srcfile \ + { debug c++ }] } { + return -1 +} + +# Test general offset printing, ctor/dtor printing, union, formatting. +gdb_test "ptype /o struct abc" \ + [multi_line \ +{/\* offset | size \*/ type = struct abc \{} \ +{ 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 \*/ signed char field4;} \ +{/\* XXX 7-byte hole \*/} \ +{/\* 32 | 8 \*/ uint64_t field5;} \ +{/\* 40 | 8 \*/ union \{} \ +{/\* 8 \*/ void \*field6;} \ +{/\* 4 \*/ int field7;} \ +{} \ +{ /\* total size \(bytes\): 8 \*/} \ +{ \} field8;} \ +{/\* 48 | 4 \*/ my_int_type field9;} \ +{} \ +{ /\* total size \(bytes\): 56 \*/} \ +{ \}}] + +# Test "ptype /oTM". +gdb_test "ptype /oTM struct abc" \ + [multi_line \ +{/\* offset | size \*/ type = struct abc \{} \ +{ 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 \*/ signed char field4;} \ +{/\* XXX 7-byte hole \*/} \ +{/\* 32 | 8 \*/ uint64_t field5;} \ +{/\* 40 | 8 \*/ union \{} \ +{/\* 8 \*/ void \*field6;} \ +{/\* 4 \*/ int field7;} \ +{} \ +{ /\* total size \(bytes\): 8 \*/} \ +{ \} field8;} \ +{/\* 48 | 4 \*/ my_int_type field9;} \ +{} \ +{ abc\(void\);} \ +{ ~abc\(\);} \ +{} \ +{ typedef int my_int_type;} \ +{} \ +{ /\* total size \(bytes\): 56 \*/} \ +{ \}}] + +# Test "ptype /TMo". This should be the same as "ptype /o". +gdb_test "ptype /TMo struct abc" \ + [multi_line \ +{/\* offset | size \*/ type = struct abc \{} \ +{ 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 \*/ signed char field4;} \ +{/\* XXX 7-byte hole \*/} \ +{/\* 32 | 8 \*/ uint64_t field5;} \ +{/\* 40 | 8 \*/ union \{} \ +{/\* 8 \*/ void \*field6;} \ +{/\* 4 \*/ int field7;} \ +{} \ +{ /\* total size \(bytes\): 8 \*/} \ +{ \} field8;} \ +{/\* 48 | 4 \*/ my_int_type field9;} \ +{} \ +{ /\* total size \(bytes\): 56 \*/} \ +{ \}}] + +# Test nested structs. +gdb_test "ptype /o struct pqr" \ + [multi_line \ +{/\* offset | size \*/ type = struct pqr \{} \ +{/\* 0 | 4 \*/ int ff1;} \ +{/\* XXX 4-byte hole \*/} \ +{/\* 8 | 40 \*/ struct xyz \{} \ +{/\* 8 | 4 \*/ int f1;} \ +{/\* 12 | 1 \*/ signed char f2;} \ +{/\* XXX 3-byte hole \*/} \ +{/\* 16 | 8 \*/ void \*f3;} \ +{/\* 24 | 24 \*/ struct tuv \{} \ +{/\* 24 | 4 \*/ int a1;} \ +{/\* XXX 4-byte hole \*/} \ +{/\* 32 | 8 \*/ signed char \*a2;} \ +{/\* 40 | 4 \*/ int a3;} \ +{} \ +{ /\* total size \(bytes\): 24 \*/} \ +{ \} f4;} \ +{} \ +{ /\* total size \(bytes\): 40 \*/} \ +{ \} ff2;} \ +{/\* XXX 28-byte hole \*/} \ +{/\* 72 | 1 \*/ signed char ff3;} \ +{} \ +{ /\* total size \(bytes\): 56 \*/} \ +{ \}}] + +# Test that the offset is properly reset when we are printing a union +# and go inside two inner structs. +# This also tests a struct inside a struct inside a union. +gdb_test "ptype /o union qwe" \ + [multi_line \ +{/\* offset | size \*/ type = union qwe \{} \ +{/\* 24 \*/ struct tuv \{} \ +{/\* 0 | 4 \*/ int a1;} \ +{/\* XXX 4-byte hole \*/} \ +{/\* 8 | 8 \*/ signed char \*a2;} \ +{/\* 16 | 4 \*/ int a3;} \ +{} \ +{ /\* total size \(bytes\): 24 \*/} \ +{ \} fff1;} \ +{/\* 40 \*/ struct xyz \{} \ +{/\* 0 | 4 \*/ int f1;} \ +{/\* 4 | 1 \*/ signed char f2;} \ +{/\* XXX 3-byte hole \*/} \ +{/\* 8 | 8 \*/ void \*f3;} \ +{/\* 16 | 24 \*/ struct tuv \{} \ +{/\* 16 | 4 \*/ int a1;} \ +{/\* XXX 4-byte hole \*/} \ +{/\* 24 | 8 \*/ signed char \*a2;} \ +{/\* 32 | 4 \*/ int a3;} \ +{} \ +{ /\* total size \(bytes\): 24 \*/} \ +{ \} f4;} \ +{} \ +{ /\* total size \(bytes\): 40 \*/} \ +{ \} fff2;} \ +{} \ +{ /\* total size \(bytes\): 40 \*/} \ +{ \}}] + +# Test printing a struct that contains a union, and that also +# contains a struct. +gdb_test "ptype /o struct poi" \ + [multi_line \ +{/\* offset | size \*/ type = struct poi \{} \ +{/\* 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 \*/ signed char \*a2;} \ +{/\* 24 | 4 \*/ int a3;} \ +{} \ +{ /\* total size \(bytes\): 24 \*/} \ +{ \} fff1;} \ +{/\* 40 \*/ struct xyz \{} \ +{/\* 8 | 4 \*/ int f1;} \ +{/\* 12 | 1 \*/ signed char f2;} \ +{/\* XXX 3-byte hole \*/} \ +{/\* 16 | 8 \*/ void \*f3;} \ +{/\* 24 | 24 \*/ struct tuv \{} \ +{/\* 24 | 4 \*/ int a1;} \ +{/\* XXX 4-byte hole \*/} \ +{/\* 32 | 8 \*/ signed char \*a2;} \ +{/\* 40 | 4 \*/ int a3;} \ +{} \ +{ /\* total size \(bytes\): 24 \*/} \ +{ \} f4;} \ +{} \ +{ /\* total size \(bytes\): 40 \*/} \ +{ \} fff2;} \ +{} \ +{ /\* total size \(bytes\): 40 \*/} \ +{ \} f2;} \ +{/\* 72 | 2 \*/ uint16_t f3;} \ +{/\* XXX 6-byte hole \*/} \ +{/\* 80 | 56 \*/ struct pqr \{} \ +{/\* 80 | 4 \*/ int ff1;} \ +{/\* XXX 4-byte hole \*/} \ +{/\* 88 | 40 \*/ struct xyz \{} \ +{/\* 88 | 4 \*/ int f1;} \ +{/\* 92 | 1 \*/ signed char f2;} \ +{/\* XXX 3-byte hole \*/} \ +{/\* 96 | 8 \*/ void \*f3;} \ +{/\* 104 | 24 \*/ struct tuv \{} \ +{/\* 104 | 4 \*/ int a1;} \ +{/\* XXX 4-byte hole \*/} \ +{/\* 112 | 8 \*/ signed char \*a2;} \ +{/\* 120 | 4 \*/ int a3;} \ +{} \ +{ /\* total size \(bytes\): 24 \*/} \ +{ \} f4;} \ +{} \ +{ /\* total size \(bytes\): 40 \*/} \ +{ \} ff2;} \ +{/\* 152 | 1 \*/ signed char ff3;} \ +{} \ +{ /\* total size \(bytes\): 56 \*/} \ +{ \} f4;} \ +{} \ +{ /\* total size \(bytes\): 112 \*/} \ +{ \}}] + +# Test printing a struct with several bitfields, laid out in various +# ways. +# +# Because dealing with bitfields and offsets is difficult, it can be +# tricky to confirm that the output of this command is accurate. A +# nice way to do that is to use GDB's "x" command and print the actual +# memory layout of the struct. In order to differentiate between +# bitfields and non-bitfield variables, one can assign "-1" to every +# bitfield in the struct. An example of the output of "x" using +# "struct tyu" is: +# +# (gdb) x/24xb &e +# 0x7fffffffd540: 0xff 0xff 0xff 0x1f 0x00 0x00 0x00 0x00 +# 0x7fffffffd548: 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff +# 0x7fffffffd550: 0xff 0x00 0x00 0x00 0x00 0x00 0x00 0x00 +gdb_test "ptype /o struct tyu" \ + [multi_line \ +{/\* offset | size \*/ type = struct tyu \{} \ +{/\* 0:31 | 4 \*/ int a1 : 1;} \ +{/\* 0:28 | 4 \*/ int a2 : 3;} \ +{/\* 0: 5 | 4 \*/ int a3 : 23;} \ +{/\* 3: 3 | 1 \*/ signed char a4 : 2;} \ +{/\* XXX 3-bit hole \*/} \ +{/\* XXX 4-byte hole \*/} \ +{/\* 8 | 8 \*/ int64_t a5;} \ +{/\* 16:27 | 4 \*/ int a6 : 5;} \ +{/\* 16:56 | 8 \*/ int64_t a7 : 3;} \ +{} \ +{ /\* total size \(bytes\): 24 \*/} \ +{ \}}] + +gdb_test "ptype /o struct asd" \ + [multi_line \ +{/\* offset | size \*/ type = struct asd \{} \ +{/\* 0 | 32 \*/ struct asd::jkl \{} \ +{/\* 0 | 8 \*/ signed char \*f1;} \ +{/\* 8 | 8 \*/ union \{} \ +{/\* 8 \*/ void \*ff1;} \ +{} \ +{ /\* total size \(bytes\): 8 \*/} \ +{ \} f2;} \ +{/\* 16 | 8 \*/ union \{} \ +{/\* 8 \*/ signed char \*ff2;} \ +{} \ +{ /\* total size \(bytes\): 8 \*/} \ +{ \} f3;} \ +{/\* 24:27 | 4 \*/ int f4 : 5;} \ +{/\* 24:26 | 4 \*/ unsigned int f5 : 1;} \ +{/\* XXX 2-bit hole \*/} \ +{/\* XXX 1-byte hole \*/} \ +{/\* 26 | 2 \*/ short f6;} \ +{} \ +{ /\* total size \(bytes\): 32 \*/} \ +{ \} f7;} \ +{/\* 32 | 8 \*/ unsigned long f8;} \ +{/\* 40 | 8 \*/ signed char \*f9;} \ +{/\* 48:28 | 4 \*/ int f10 : 4;} \ +{/\* 48:27 | 4 \*/ unsigned int f11 : 1;} \ +{/\* 48:26 | 4 \*/ unsigned int f12 : 1;} \ +{/\* 48:25 | 4 \*/ unsigned int f13 : 1;} \ +{/\* 48:24 | 4 \*/ unsigned int f14 : 1;} \ +{/\* XXX 7-byte hole \*/} \ +{/\* 56 | 8 \*/ void \*f15;} \ +{/\* 64 | 8 \*/ void \*f16;} \ +{} \ +{ /\* total size \(bytes\): 72 \*/} \ +{ \}}] + +# 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" +gdb_test "ptype /o uint8_t" "char" + +# Test that the "whatis" command doesn't print anything related to the +# "offsets" feature, even when receiving the "/o" parameter. +set test "whatis /o asd" +gdb_test_multiple "$test" "$test" { + -re "^$test\r\ntype = asd\r\n$gdb_prompt $" { + pass $test + } +} diff --git a/gdb/typeprint.c b/gdb/typeprint.c index 9d9d6f5a49..15a62bef3c 100644 --- a/gdb/typeprint.c +++ b/gdb/typeprint.c @@ -42,6 +42,7 @@ 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 */ NULL, /* local_typedefs */ NULL, /* global_table */ @@ -55,6 +56,7 @@ static struct type_print_options default_ptype_flags = 0, /* raw */ 1, /* print_methods */ 1, /* print_typedefs */ + 0, /* print_offsets */ 0, /* print_nested_type_limit */ NULL, /* local_typedefs */ NULL, /* global_table */ @@ -440,6 +442,20 @@ whatis_exp (const char *exp, int show) case 'T': flags.print_typedefs = 1; break; + case 'o': + { + /* Filter out languages which don't implement the + feature. */ + if (show > 0 && + (current_language->la_language == language_c + || current_language->la_language == language_cplus)) + { + flags.print_offsets = 1; + flags.print_typedefs = 0; + flags.print_methods = 0; + } + break; + } default: error (_("unrecognized flag '%c'"), *exp); } @@ -499,6 +515,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 */ "); + printf_filtered ("type = "); if (!flags.raw) @@ -759,7 +780,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 a2058b0120..2e19006f22 100644 --- a/gdb/typeprint.h +++ b/gdb/typeprint.h @@ -24,6 +24,20 @@ struct ui_file; struct typedef_hash_table; struct ext_lang_type_printers; +struct print_offset_data +{ + /* 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 over + the offset of the outter struct. */ + unsigned int offset_bitpos = 0; + + /* END_BITPOS is the one-past-the-end bit position of the previous + field (where we expect the current field to be if there is no + hole). */ + unsigned int end_bitpos = 0; +}; + struct type_print_options { /* True means that no special printing flags should apply. */ @@ -35,6 +49,9 @@ 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; -- 2.14.3 ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH v7 0/2] Implement pahole-like 'ptype /o' option (and do some code reorg) 2017-12-15 1:12 ` [PATCH v7 0/2] Implement pahole-like 'ptype /o' option (and do some code reorg) Sergio Durigan Junior 2017-12-15 1:12 ` [PATCH v7 1/2] Reorganize code to handle TYPE_CODE_{STRUCT,UNION} on 'c_type_print_base' Sergio Durigan Junior 2017-12-15 1:13 ` [PATCH v7 2/2] Implement pahole-like 'ptype /o' option Sergio Durigan Junior @ 2017-12-15 20:11 ` Sergio Durigan Junior 2 siblings, 0 replies; 75+ messages in thread From: Sergio Durigan Junior @ 2017-12-15 20:11 UTC (permalink / raw) To: GDB Patches Cc: Tom Tromey, Eli Zaretskii, Simon Marchi, Pedro Alves, Keith Seitz On Thursday, December 14 2017, I wrote: > Changes from v6: > > - Updated examples in the documentation. > > - Removed stale comments about the test being x86_64-specific from > tests. > > - Added tests for "ptype /oTM", "ptype /TMo", and for the regression > Pedro caught. > > - Removed "print_offset_default_data". > > - Fixed regression reported by Pedro. > > - s/endpos/end_bitpos/ > > - Filtered out the languages that don't implement the feature so that > their type printing output doesn't get clobbered by "ptype /o" when > they use "common" code (which is not actually common; it lives > inside c-typeprint.c). I decided to do that by checking the current > language on typeprint.c:whatis_exp, before setting the > "print_offset" bit. > > > The documentation parts have been approved by Eli, and haven't > changed. Patches have been pushed: a27ed7d613ec91c3a79965d6bdab1fa96d559c85 7c1618381fdaa0697a211721ac31844f884797ac Thanks for all the help with reviews and suggestions. -- Sergio GPG key ID: 237A 54B1 0287 28BF 00EF 31F4 D0EB 7628 65FC 5E36 Please send encrypted e-mail if possible http://sergiodj.net/ ^ permalink raw reply [flat|nested] 75+ messages in thread
end of thread, other threads:[~2017-12-15 20:11 UTC | newest] Thread overview: 75+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2017-11-21 16:07 [PATCH] Implement pahole-like 'ptype /o' option Sergio Durigan Junior 2017-11-21 16:16 ` Sergio Durigan Junior 2017-11-21 16:50 ` Eli Zaretskii 2017-11-21 17:00 ` Sergio Durigan Junior 2017-11-21 19:14 ` Eli Zaretskii 2017-11-26 19:27 ` Tom Tromey 2017-11-27 19:54 ` Sergio Durigan Junior 2017-11-27 22:20 ` Tom Tromey 2017-11-28 0:39 ` Sergio Durigan Junior 2017-11-28 21:21 ` [PATCH v2] " Sergio Durigan Junior 2017-11-29 3:28 ` Eli Zaretskii 2017-12-04 15:03 ` Sergio Durigan Junior 2017-12-04 15:41 ` Eli Zaretskii 2017-12-04 16:47 ` Sergio Durigan Junior 2017-12-08 21:32 ` Sergio Durigan Junior 2017-12-11 15:43 ` Simon Marchi 2017-12-11 18:59 ` Sergio Durigan Junior 2017-12-11 20:45 ` Simon Marchi 2017-12-11 21:07 ` Sergio Durigan Junior 2017-12-11 22:42 ` Pedro Alves 2017-12-11 22:50 ` Sergio Durigan Junior 2017-12-11 23:46 ` Pedro Alves 2017-12-12 0:25 ` Sergio Durigan Junior 2017-12-12 0:52 ` Pedro Alves 2017-12-12 1:25 ` Simon Marchi 2017-12-12 15:50 ` John Baldwin 2017-12-12 17:04 ` Sergio Durigan Junior 2017-12-11 19:58 ` [PATCH v3 0/2] Implement pahole-like 'ptype /o' option (and do some code reorg) Sergio Durigan Junior 2017-12-11 19:58 ` [PATCH v3 1/2] Reorganize code to handle TYPE_CODE_{STRUCT,UNION} on 'c_type_print_base' Sergio Durigan Junior 2017-12-11 20:55 ` Simon Marchi 2017-12-11 23:05 ` Sergio Durigan Junior 2017-12-11 19:58 ` [PATCH v3 2/2] Implement pahole-like 'ptype /o' option Sergio Durigan Junior 2017-12-11 21:50 ` Simon Marchi 2017-12-11 23:24 ` Sergio Durigan Junior 2017-12-12 1:32 ` Simon Marchi 2017-12-12 6:19 ` Sergio Durigan Junior 2017-12-12 18:14 ` Pedro Alves 2017-12-12 18:40 ` Sergio Durigan Junior 2017-12-12 20:12 ` Pedro Alves 2017-12-11 23:43 ` [PATCH v4 0/2] Implement pahole-like 'ptype /o' option (and do some code reorg) Sergio Durigan Junior 2017-12-11 23:44 ` [PATCH v4 2/2] Implement pahole-like 'ptype /o' option Sergio Durigan Junior 2017-12-12 0:27 ` Sergio Durigan Junior 2017-12-12 0:29 ` Sergio Durigan Junior 2017-12-12 1:59 ` Simon Marchi 2017-12-12 3:39 ` Eli Zaretskii 2017-12-11 23:44 ` [PATCH v4 1/2] Reorganize code to handle TYPE_CODE_{STRUCT,UNION} on 'c_type_print_base' Sergio Durigan Junior 2017-12-13 3:17 ` [PATCH v5 0/2] Implement pahole-like 'ptype /o' option (and do some code reorg) Sergio Durigan Junior 2017-12-13 3:17 ` [PATCH v5 2/2] Implement pahole-like 'ptype /o' option Sergio Durigan Junior 2017-12-13 4:50 ` Simon Marchi 2017-12-13 16:42 ` Sergio Durigan Junior 2017-12-13 16:17 ` Eli Zaretskii 2017-12-13 17:14 ` Sergio Durigan Junior 2017-12-13 16:19 ` Pedro Alves 2017-12-13 17:13 ` Sergio Durigan Junior 2017-12-13 20:36 ` Sergio Durigan Junior 2017-12-13 21:22 ` Pedro Alves 2017-12-13 21:30 ` Pedro Alves 2017-12-13 21:34 ` Sergio Durigan Junior 2017-12-13 16:20 ` Pedro Alves 2017-12-13 17:41 ` Sergio Durigan Junior 2017-12-13 3:17 ` [PATCH v5 1/2] Reorganize code to handle TYPE_CODE_{STRUCT,UNION} on 'c_type_print_base' Sergio Durigan Junior 2017-12-14 2:48 ` [PATCH v6 0/2] Implement pahole-like 'ptype /o' option (and do some code reorg) Sergio Durigan Junior 2017-12-14 2:48 ` [PATCH v6 1/2] Reorganize code to handle TYPE_CODE_{STRUCT,UNION} on 'c_type_print_base' Sergio Durigan Junior 2017-12-14 2:48 ` [PATCH v6 2/2] Implement pahole-like 'ptype /o' option Sergio Durigan Junior 2017-12-14 14:19 ` Pedro Alves 2017-12-14 20:31 ` Sergio Durigan Junior 2017-12-14 14:50 ` Pedro Alves 2017-12-14 20:29 ` Sergio Durigan Junior 2017-12-14 16:30 ` Eli Zaretskii 2017-12-15 1:12 ` [PATCH v7 0/2] Implement pahole-like 'ptype /o' option (and do some code reorg) Sergio Durigan Junior 2017-12-15 1:12 ` [PATCH v7 1/2] Reorganize code to handle TYPE_CODE_{STRUCT,UNION} on 'c_type_print_base' Sergio Durigan Junior 2017-12-15 1:13 ` [PATCH v7 2/2] Implement pahole-like 'ptype /o' option Sergio Durigan Junior 2017-12-15 17:24 ` Pedro Alves 2017-12-15 20:04 ` Sergio Durigan Junior 2017-12-15 20:11 ` [PATCH v7 0/2] Implement pahole-like 'ptype /o' option (and do some code reorg) Sergio Durigan Junior
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).