[RFC] embryo of type/main_type pretty_printing...

Joel Brobecker
2009-12-20T07:41:44Z

[-- 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)