[v2] Implement pahole-like 'ptype /o' option

Message ID 20171128212137.15655-1-sergiodj@redhat.com
State New, archived
Headers

Commit Message

Sergio Durigan Junior Nov. 28, 2017, 9:21 p.m. UTC
  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
  

Comments

Eli Zaretskii Nov. 29, 2017, 3:27 a.m. UTC | #1
> 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.
  
Sergio Durigan Junior Dec. 4, 2017, 3:03 p.m. UTC | #2
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
  
Eli Zaretskii Dec. 4, 2017, 3:40 p.m. UTC | #3
> 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?
  
Sergio Durigan Junior Dec. 4, 2017, 4:47 p.m. UTC | #4
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 Durigan Junior Dec. 8, 2017, 9:32 p.m. UTC | #5
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/
  
Simon Marchi Dec. 11, 2017, 3:43 p.m. UTC | #6
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
  
Sergio Durigan Junior Dec. 11, 2017, 6:58 p.m. UTC | #7
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
  
Simon Marchi Dec. 11, 2017, 8:45 p.m. UTC | #8
>>> +/* 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
  
Sergio Durigan Junior Dec. 11, 2017, 9:07 p.m. UTC | #9
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,
  
Pedro Alves Dec. 11, 2017, 10:42 p.m. UTC | #10
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
  
Sergio Durigan Junior Dec. 11, 2017, 10:50 p.m. UTC | #11
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.
  
Pedro Alves Dec. 11, 2017, 11:45 p.m. UTC | #12
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
  
Sergio Durigan Junior Dec. 12, 2017, 12:25 a.m. UTC | #13
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,
  
Pedro Alves Dec. 12, 2017, 12:51 a.m. UTC | #14
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
  
Simon Marchi Dec. 12, 2017, 1:25 a.m. UTC | #15
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
  
John Baldwin Dec. 12, 2017, 3:50 p.m. UTC | #16
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.
  
Sergio Durigan Junior Dec. 12, 2017, 5:04 p.m. UTC | #17
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 */
  

Patch

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;