Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
* [RFC] embryo of type/main_type pretty_printing...
@ 2009-12-20  7:41 Joel Brobecker
  2009-12-22 18:42 ` Tom Tromey
  2009-12-22 19:24 ` Daniel Jacobowitz
  0 siblings, 2 replies; 6+ messages in thread
From: Joel Brobecker @ 2009-12-20  7:41 UTC (permalink / raw)
  To: gdb-patches

[-- Attachment #1: Type: text/plain, Size: 1749 bytes --]

Hello,

As discussed with Daniel and Tom on IRC, I started looking at writing
pretty printers for struct type and struct main_type.  I have not fully
tested it yet, just with various integer types (not even range types).
It's just a prototype at this stage.  But before finishing this up,
I want to finish up the patch I have been working on on-and-off, that
will provide a discriminant for the type-specific stuff, making it
easier to print the right component for type_specific.

Eventually, I think it'd be nice if we printed the data even more
intelligently, omitting fields that are not relevant for the given
type_code for instance. Or perhaps, we might want to leave the
C-like representation, and show something more synthetic, tailored
for each type kind.

Here is a transcript with the current version:

    (top-gdb) p *type
    $2 = 
    {pointer_type = 0x0,
     reference_type = 0x0,
     chain = 0x0,
     instance_flags = 0x443 [CONST|VOLATILE|UNSIGNED|STATIC],
     length = 4,
     main_type = 0xc8dcd0}

Not much to show off, in this case, except that the pretty-printer
prints a decoded version of the instance_flags. Always nice when
one looks at them.

And then, trying to print *type.main_type:

    $4 = 
    {name = 0xc8dd20 "int",
     tag_name = 0x0,
     code = TYPE_CODE_INT,
     flags = [unsigned|static|objfile_owned],
     owner = 0xcfd450 (objfile),
     target_type = 0x0,
     vptr_basetype = 0x0}

I've only tested with TYPE_CODE_INT, right now, so it's probably
still buggy as is with other types, in particular when fields or
bounds are involved. I will look at that if no one else beats me
to it, but after I have introduced the type_specific discriminant.

Enjoy! Suggestions and comments welcome.

-- 
Joel

[-- Attachment #2: gdb-gdb.py --]
[-- Type: text/x-python, Size: 9587 bytes --]

import gdb
import os.path

class TypeFlag:
    """A class that allows us to store a flag name, its short name,
    and its value.

    In the GDB sources, struct type has a component called instance_flags
    whose the value is the addition of various flags.  These flags are
    defined by two emumerates: type_flag_value, and type_instance_flag_value.
    This class helps us recreate a list with all these flags that is
    easy to manipulate and sort.  Because all flag names start with either
    TYPE_FLAG_ or TYPE_INSTANCE_FLAG_, a short_name attribute is provided
    that strips this prefix.

    ATTRIBUTES
      name:  The enumeration name (eg: "TYPE_FLAG_UNSIGNED").
      value: The associated value.
      short_name: The enumeration name, with the suffix stripped.
    """
    def __init__(self, name, value):
        self.name = name
        self.value = value
        self.short_name = name.replace("TYPE_FLAG_", '')
        if self.short_name == name:
            self.short_name = name.replace("TYPE_INSTANCE_FLAG_", '')
    def __cmp__(self, other):
        """Sort by value order."""
        return self.value.__cmp__(other.value)

# A list of all existing TYPE_FLAGS_* and TYPE_INSTANCE_FLAGS_*
# enumerations, stored as TypeFlags objects.  Lazy-initialized.
TYPE_FLAGS = None

class TypeFlagsPrinter:
    """A class that prints a decoded form of an instance_flags value.

    This class uses a global named TYPE_FLAGS, which is a list of
    all defined TypeFlag values.  Using a global allows us to compute
    this list only once.

    This class relies on a couple of enumeration types being defined.
    If not, then printing of the instance_flag is going to be degraded,
    but it's not a fatal error.
    """
    def __init__(self, val):
        self.val = val
    def __str__(self):
        global TYPE_FLAGS
        if TYPE_FLAGS is None:
            self.init_TYPE_FLAGS()
        if not self.val:
            return "0"
        if TYPE_FLAGS:
            flag_list = [flag.short_name for flag in TYPE_FLAGS
                         if self.val & flag.value]
        else:
            flag_list = ["???"]
        return "0x%x [%s]" % (self.val, "|".join(flag_list))
    def init_TYPE_FLAGS(self):
        """Initialize the TYPE_FLAGS global as a list of TypeFlag objects.
        This operation requires the search of a couple of enumeration types.
        If not found, a warning is printed on stdout, and TYPE_FLAGS is
        set to the empty list.

        The resulting list is sorted by increasing value, to facilitate
        printing of the list of flags used in an instance_flags value.
        """
        global TYPE_FLAGS
        TYPE_FLAGS = []
        try:
            flags = gdb.lookup_type("enum type_flag_value")
        except:
            print "Warning: Cannot find enum type_flag_value type."
            print "         `struct type' pretty-printer will be degraded"
            return
        try:
            iflags = gdb.lookup_type("enum type_instance_flag_value")
        except:
            print "Warning: Cannot find enum type_instance_flag_value type."
            print "         `struct type' pretty-printer will be degraded"
            return
        # Note: TYPE_FLAG_MIN is a duplicate of TYPE_FLAG_UNSIGNED,
        # so exclude it from the list we are building.
        TYPE_FLAGS = [TypeFlag(field.name, field.bitpos)
                      for field in flags.fields()
                      if field.name != 'TYPE_FLAG_MIN']
        TYPE_FLAGS += [TypeFlag(field.name, field.bitpos)
                       for field in iflags.fields()]
        TYPE_FLAGS.sort()

class StructTypePrettyPrinter:
    """Pretty-print an object of type struct type"""
    def __init__(self, val):
        self.val = val
    def to_string(self):
        fields = []
        fields.append("pointer_type = %s" % self.val['pointer_type'])
        fields.append("reference_type = %s" % self.val['reference_type'])
        fields.append("chain = %s" % self.val['reference_type'])
        fields.append("instance_flags = %s"
                      % TypeFlagsPrinter(self.val['instance_flags']))
        fields.append("length = %d" % self.val['length'])
        fields.append("main_type = %s" % self.val['main_type'])
        return "\n{" + ",\n ".join(fields) + "}"

class StructMainTypePrettyPrinter:
    """Pretty-print an objet of type main_type"""
    def __init__(self, val):
        self.val = val
    def flags_to_string(self):
        """struct main_type contains a series of components that
        are one-bit ints whose name start with "flag_".  For instance:
        flag_unsigned, flag_stub, etc.  In essence, these components are
        really boolean flags, and this method prints a short synthetic
        version of the value of all these flags.  For instance, if
        flag_unsigned and flag_static are the only components set to 1,
        this function will return "unsigned|static".
        """
        fields = [field.name.replace("flag_", "")
                  for field in self.val.type.fields()
                  if field.name.startswith("flag_")
                     and self.val[field.name]]
        return "|".join(fields)
    def owner_to_string(self):
        """Return an image of component "owner".
        """
        if self.val['flag_objfile_owned'] != 0:
            return "%s (objfile)" % self.val['owner']['objfile']
        else:
            return "%s (gdbarch)" % self.val['owner']['gdbarch']
    def struct_field_location_img(self, field_val):
        """Return an image of the loc component inside the given field
        gdb.Value.
        """
        loc_val = field_val['loc']
        loc_kind = str(field_val['loc_kind'])
        if loc_kind == "FIELD_LOC_KIND_BITPOS":
            return 'bitpos = %d' % loc_val['bitpos']
        elif loc_kind == "FIELD_LOC_KIND_PHYSADDR":
            return 'physaddr = 0x%x' % loc_val['physaddr']
        elif loc_kind == "FIELD_LOC_KIND_PHYSNAME":
            return 'physname = %s' % loc_val['physname']
        elif loc_kind == "FIELD_LOC_KIND_DWARF_BLOCK":
            return 'dwarf_block = %s' % loc_val['dwarf_block']
        else:
            return 'loc = ??? (unsupported loc_kind value)'
    def struct_field_img(self, fieldno):
        """Return an image of the main_type field number FIELDNO.
        """
        f = self.val['flds_bnds']['fields'][fieldno]
        label = "field[%d]:" % fieldno
        if f['artificial']:
            label += " (artificial)"
        fields = []
        fields.append("name = %s" % f['name'])
        fields.append("type = %s" % f['type'])
        fields.append("loc_kind = %s" % f['loc_kind'])
        fields.append("bitsize = %d" % f['bitsize'])
        fields.append(self.struct_field_location_img(f))
        return label + "\n" + "  {" + ",\n   ".join(fields) + "}"
    def bounds_img(self):
        """Return an image of the main_type bounds.
        """
        b = self.val['flds_bnds']['bounds'].dereference()
        low = str(b['low'])
        if b['low_undefined'] != 0:
            low += " (undefined)"
        high = str(b['high'])
        if b['high_undefined'] != 0:
            high += " (undefined)"
        return "bounds = {%s, %s}" % (low, high)
    def to_string(self):
        """Return a pretty-printed image of our main_type.
        """
        fields = []
        fields.append("name = %s" % self.val['name'])
        fields.append("tag_name = %s" % self.val['tag_name'])
        fields.append("code = %s" % self.val['code'])
        fields.append("flags = [%s]" % self.flags_to_string())
        fields.append("owner = %s" % self.owner_to_string())
        fields.append("target_type = %s" % self.val['target_type'])
        fields.append("vptr_basetype = %s" % self.val['vptr_basetype'])
        if self.val['nfields'] > 0:
            for fieldno in range(self.val['nfields']):
                fields.append("field[%d]:")
                fields.append(self.struct_field_img(fieldno))
        if self.val.type.code == gdb.TYPE_CODE_RANGE:
            fields.append(self.bounds_img())
        # FIXME: We need to print the type_specific field as well.
        # But I will wait for a patch that introduces a discriminant.
        # This will simplify the selection of the right component in
        # that union.
        return "\n{" + ",\n ".join(fields) + "}"

def type_lookup_function(val):
    """A routine that returns the correct pretty printer for VAL
    if appropriate.  Returns None otherwise.
    """
    if val.type.tag == "type":
        return StructTypePrettyPrinter(val)
    elif val.type.tag == "main_type":
        return StructMainTypePrettyPrinter(val)
    return None

def register_pretty_printer(objfile):
    """A routine to register a pretty-printer against the given OBJFILE.
    """
    objfile.pretty_printers.append(type_lookup_function)

if __name__ == "__main__":
    if gdb.current_objfile() is not None:
        # This is the case where this script is being "auto-loaded"
        # for a given objfile.  Register the pretty-printer for that
        # objfile.
        register_pretty_printer(gdb.current_objfile())
    else:
        # We need to locate the objfile corresponding to the GDB
        # executable, and register the pretty-printer for that objfile.
        # FIXME: The condition used to match the objfile is too simplistic
        # and will not work on Windows.
        for objfile in gdb.objfiles():
            if os.path.basename(objfile.filename) == "gdb":
                objfile.pretty_printers.append(type_lookup_function)

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [RFC] embryo of type/main_type pretty_printing...
  2009-12-20  7:41 [RFC] embryo of type/main_type pretty_printing Joel Brobecker
@ 2009-12-22 18:42 ` Tom Tromey
  2009-12-23  7:08   ` Joel Brobecker
  2009-12-22 19:24 ` Daniel Jacobowitz
  1 sibling, 1 reply; 6+ messages in thread
From: Tom Tromey @ 2009-12-22 18:42 UTC (permalink / raw)
  To: Joel Brobecker; +Cc: gdb-patches

>>>>> "Joel" == Joel Brobecker <brobecker@adacore.com> writes:

Joel> As discussed with Daniel and Tom on IRC, I started looking at
Joel> writing pretty printers for struct type and struct main_type.

Excellent.

I like the approach you took; I think it is elegant.

Joel> class StructTypePrettyPrinter:
[...]
Joel>     def to_string(self):
[...]
Joel>         return "\n{" + ",\n ".join(fields) + "}"

It would be better to have a 'children' method that returns a container
(tuple or list, whatever is convenient).  See the Pretty Printing node
in the manual for details of the contents.

This is better for a few reasons.  From the CLI, it will mean that the
results are printed more nicely: they will respect indentation and "set
print pretty".  From MI, they will show up as children of the type
object.

Regardless of whether you decide to change this, I think you should
check this in.  That will make it simpler for us to improve it.

Tom


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [RFC] embryo of type/main_type pretty_printing...
  2009-12-20  7:41 [RFC] embryo of type/main_type pretty_printing Joel Brobecker
  2009-12-22 18:42 ` Tom Tromey
@ 2009-12-22 19:24 ` Daniel Jacobowitz
  1 sibling, 0 replies; 6+ messages in thread
From: Daniel Jacobowitz @ 2009-12-22 19:24 UTC (permalink / raw)
  To: Joel Brobecker; +Cc: gdb-patches

On Sun, Dec 20, 2009 at 11:41:21AM +0400, Joel Brobecker wrote:
> Hello,
> 
> As discussed with Daniel and Tom on IRC, I started looking at writing
> pretty printers for struct type and struct main_type.  I have not fully
> tested it yet, just with various integer types (not even range types).
> It's just a prototype at this stage.  But before finishing this up,
> I want to finish up the patch I have been working on on-and-off, that
> will provide a discriminant for the type-specific stuff, making it
> easier to print the right component for type_specific.

Cool!

> Eventually, I think it'd be nice if we printed the data even more
> intelligently, omitting fields that are not relevant for the given
> type_code for instance. Or perhaps, we might want to leave the
> C-like representation, and show something more synthetic, tailored
> for each type kind.

Yeah, I was thinking maybe a summary followed by a more verbose
version... just an idea.

I took a look at the patch.

It would be nice if we could simplify the amount of code you had to
write in order to make this an easily generalizable framework.  The
specific change you've made so far is to assign a special printer
to the flags and instance flags members of the structs.  If we could
do that declaratively you wouldn't have to bake in knowledge about the
whole field.  I think this is something we can do entirely in Python.

Another rule that would be nice to apply to an element would be
"automatically dereference main_type".  Can we do that?  It's always
what I'm looking for because it has the code and the name.

>     def __str__(self):
>         global TYPE_FLAGS
>         if TYPE_FLAGS is None:
>             self.init_TYPE_FLAGS()
>         if not self.val:
>             return "0"
>         if TYPE_FLAGS:
>             flag_list = [flag.short_name for flag in TYPE_FLAGS
>                          if self.val & flag.value]
>         else:
>             flag_list = ["???"]
>         return "0x%x [%s]" % (self.val, "|".join(flag_list))

A future enhancement to general flag printing would be to show unknown
bits in hex - then it's obvious something odd is going on.

>     def to_string(self):
>         fields = []
>         fields.append("pointer_type = %s" % self.val['pointer_type'])
>         fields.append("reference_type = %s" % self.val['reference_type'])
>         fields.append("chain = %s" % self.val['reference_type'])
>         fields.append("instance_flags = %s"
>                       % TypeFlagsPrinter(self.val['instance_flags']))
>         fields.append("length = %d" % self.val['length'])
>         fields.append("main_type = %s" % self.val['main_type'])
>         return "\n{" + ",\n ".join(fields) + "}"

Typo on printing chain.  It'd be nice if we could examine the type to
get the field list.

>         # We need to locate the objfile corresponding to the GDB
>         # executable, and register the pretty-printer for that objfile.
>         # FIXME: The condition used to match the objfile is too simplistic
>         # and will not work on Windows.
>         for objfile in gdb.objfiles():
>             if os.path.basename(objfile.filename) == "gdb":
>                 objfile.pretty_printers.append(type_lookup_function)

Want to key off a function?  gdb_main, perhaps.

Like Tom, I think this can go straight in even as a work-in-progress.
It's for developers, and it's better than not.

-- 
Daniel Jacobowitz
CodeSourcery


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [RFC] embryo of type/main_type pretty_printing...
  2009-12-22 18:42 ` Tom Tromey
@ 2009-12-23  7:08   ` Joel Brobecker
  2010-01-05 23:12     ` Tom Tromey
  0 siblings, 1 reply; 6+ messages in thread
From: Joel Brobecker @ 2009-12-23  7:08 UTC (permalink / raw)
  To: gdb-patches

Thanks, Daniel and Tom, for taking the time to review the script!
Once checked in, please feel free to rework/enhance as you see fit.
I'll definitely get to it again, but I really want to look at my
DW_AT_GNAT_descriptive_type patch, which will make the gnat-stuff
thing much simpler. I'm pretty sure the patch is ready, but I want
to have another good read + testing before posting.

> Regardless of whether you decide to change this, I think you should
> check this in.  That will make it simpler for us to improve it.

I agree that checking it in would be useful.  I just don't know where,
yet. Should we start a "contrib" directory? Or just in the gdb/ directory
itself if we consider this as port of the GDB sources?

I had also had questions about whether we want to enable this script
by default or not. For now, obviously not, since we really have not
finished discussing the output format (not to mention testing). But
this affects, IMO, the location where we want to store the script.
I was thinking about having the "make" install that script in the
build directory besides the gdb exe so that it gets automatically
used.  Or perhaps we can use a different strategy of adding the source
path to one of the search paths that GDB uses... WDYT?

-- 
Joel


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [RFC] embryo of type/main_type pretty_printing...
  2009-12-23  7:08   ` Joel Brobecker
@ 2010-01-05 23:12     ` Tom Tromey
  2010-01-06  3:51       ` Joel Brobecker
  0 siblings, 1 reply; 6+ messages in thread
From: Tom Tromey @ 2010-01-05 23:12 UTC (permalink / raw)
  To: Joel Brobecker; +Cc: gdb-patches

>>>>> "Joel" == Joel Brobecker <brobecker@adacore.com> writes:

Joel> I agree that checking it in would be useful.  I just don't know where,
Joel> yet. Should we start a "contrib" directory? Or just in the gdb/ directory
Joel> itself if we consider this as port of the GDB sources?

Just in the gdb directory is fine by me.

Joel> I had also had questions about whether we want to enable this script
Joel> by default or not. For now, obviously not, since we really have not
Joel> finished discussing the output format (not to mention testing). But
Joel> this affects, IMO, the location where we want to store the script.
Joel> I was thinking about having the "make" install that script in the
Joel> build directory besides the gdb exe so that it gets automatically
Joel> used.  Or perhaps we can use a different strategy of adding the source
Joel> path to one of the search paths that GDB uses... WDYT?

In the build tree we can have gdbinit.in refer to this code.

I would probably defer installing it for now, but either way is fine by
me.

Tom


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [RFC] embryo of type/main_type pretty_printing...
  2010-01-05 23:12     ` Tom Tromey
@ 2010-01-06  3:51       ` Joel Brobecker
  0 siblings, 0 replies; 6+ messages in thread
From: Joel Brobecker @ 2010-01-06  3:51 UTC (permalink / raw)
  To: gdb-patches

OK - I just checked in gdb-gdb.py in the gdb/ directory, after having
added a copyright header. I'm finishing up reviving the mips-irix port
this week, and will start again on it next week :).

-- 
Joel


^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2010-01-06  3:51 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-12-20  7:41 [RFC] embryo of type/main_type pretty_printing Joel Brobecker
2009-12-22 18:42 ` Tom Tromey
2009-12-23  7:08   ` Joel Brobecker
2010-01-05 23:12     ` Tom Tromey
2010-01-06  3:51       ` Joel Brobecker
2009-12-22 19:24 ` Daniel Jacobowitz

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox