This is the mail archive of the mailing list for the Archer project.

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

pretty-printing update

I've been working on pretty printing for the past week; I thought I'd
send a note about the current status.

The basics work ok.  On the user side, I added a "/r" ("raw") format
to "print" so that users can disable pretty-printing.

On the Python side, I made a few fixes to the Value class and
implemented enough of the Type class to let my example printers work.

Pretty-printers are registered in a global 'gdb.pretty_printers'
dictionary.  This dictionary maps regular expressions (which are
matched against canonical type names) to printing functions.

A printer function is passed a Value, and can return a string or any
other value convertible from Python to gdb values (including another
Value object).

The Python branch also has some code to auto-load Python code when an
objfile is opened.  The intent here is to install pretty-printing code
alongside the separate debuginfo.  (This area might need a bit of
tweaking, perhaps picking a better file name to read.)

I've appended my sample printers.  These print a number of objects
defined in libstdc++.

There are a couple problems right now.

The biggest one is that the output is not always actually pretty.  It
is an improvement, in that you can easily see the contents of
containers and whatnot, but the formatting is odd.  Consider this
example, where 'intmap' is a std::map<int, char*>:

(gdb) p intmap
$2 = number of elements = 2
[5] = 0x804c057 "liver"
[23] = 0x804c046 "maude"

The internal problem is that the value-printing code does not know
that gdb printed "$2 = ".

I'm not totally sure how, or whether, I want to fix this.  Initially I
had wanted to print things using re-enterable syntax, like:

(gdb) p str
$1._M_dataplus._M_p = "the string value"

... the idea being that the left-hand side would tell you a bit about
what you were "really" looking at.

Doing that means refactoring a lot of the value-printing code, though.
That would be ok, except I'm also not sure it always gives good
results.  E.g., we may sometimes want to print interesting derived
values, like the length of a linked list -- but in this case there is
no good choice for the LHS.

The other problem is that, right now, pretty-printing of fields is not
disabled by '/r'.  This is because I didn't make a pass through all
the val_print hierarchy, adding a 'raw' argument.  This code is a
mish-mash of arguments and globals, and I'm considering refactoring
the mess so that we pass around a single format-controlling struct

If you've got any input on these, I'm all ears.

If you want to try it, all the code is on archer-tromey-python.


# Pretty-printers for libstc++.

def printstdlist(val):
    "Print a std::list"
    itype = val.type().template_argument(0)
    nodetype = gdb.Type('std::_List_node<%s>' % itype).pointer()
    head = val['_M_impl']['_M_node']
    base = head['_M_next']
    head = head.address()
    max = gdb.get_parameter('print elements')
    n = 0
    result = []
    while base != head and (max == 0 or n < max):
        elt = base.cast(nodetype).dereference()
        datum = elt['_M_data']
        if n > 0:
            result += '\n'
        result.append ('[%d] = %s' % (n, str (datum)))
        n = n + 1
        base = elt['_M_next']
    if len (result) == 0:
        result.append('<<empty list>>')
    return "\n".join(result)

def printstdvector(val):
    "Print a std::vector"
    start = val['_M_impl']['_M_start']
    finish = val['_M_impl']['_M_finish']
    end = val['_M_impl']['_M_end_of_storage']
    max = gdb.get_parameter('print elements')
    result = []
    result.append('vector length = %d' % int (finish - start))
    result.append('vector capacity = %d' % int (end - start))
    i = 0
    while start < finish and (max == 0 or i < max):
        result.append('[%d] = %s' % (i, str(start.dereference())))
        start = start + 1
        i = i + 1
    return "\n".join(result)

def printstdstack(val):
    "Print a std::stack or std::queue"
    # Maybe we should print stack and queue in opposite orders?
    cur = val['c']['_M_impl']['_M_start']['_M_cur']
    finish = val['c']['_M_impl']['_M_finish']['_M_cur']
    i = 0
    max = gdb.get_parameter('print elements')
    result = ['stack size = %d' % int (finish -cur)]
    while cur != finish and (max == 0 or i < max):
        result.append('[%d] = %s' % (i, str (cur.dereference())))
        cur = cur + 1
        i = i + 1
    return "\n".join(result)

def rbtree_to_list(val):
    "Turn an rbtree into a size and a list of elements."
    maxprint = gdb.get_parameter('print elements')
    size = val['_M_t']['_M_impl']['_M_node_count']
    if maxprint == 0 or maxprint > size:
        maxprint = size
    node = val['_M_t']['_M_impl']['_M_header']['_M_left']
    i = 0
    result = []
    while i < maxprint:
        if node.dereference()['_M_right']:
            node = node.dereference()['_M_right']
            while node.dereference()['_M_left']:
                node = node.dereference()['_M_left']
            parent = node.dereference()['_M_parent']
            while node == parent.dereference()['_M_right']:
                node = parent
                parent = parent.dereference()['_M_parent']
            if node.dereference()['_M_right'] != parent:
                node = parent
        i = i + 1
    return (size, result)

def printstdmap(val):
    "Print a std::map"
    keytype = val.type().template_argument(0)
    valuetype = val.type().template_argument(1)
    nodetype = gdb.Type('std::_Rb_tree_node< std::pair< const %s, %s > >' % (keytype, valuetype))
    nodetype = nodetype.pointer()
    (size, nodes) = rbtree_to_list (val)
    result = ['number of elements = %d' % int (size)]
    for node in nodes:
        pair = node.cast(nodetype).dereference()['_M_value_field']
        result.append('[%s] = %s' % (str (pair['first']), str (pair['second'])))
    return "\n".join(result)

def printstdset(val):
    "Print a std::set"
    keytype = val.type().template_argument(0)
    nodetype = gdb.Type("std::_Rb_tree_node< %s >" % keytype).pointer()
    (size, nodes) = rbtree_to_list (val)
    result = ['number of elements = %d' % int (size)]
    for node in nodes:
        elt = node.cast(nodetype).dereference()['_M_value_field']
        result.append('  %s' % str (elt))
    return "\n".join(result)

def printstdstring(val):
    "Print a std::string"
    return val['_M_dataplus']['_M_p']

gdb.pretty_printers['^std::list<.*>$'] = printstdlist
gdb.pretty_printers['^std::vector<.*>$'] = printstdvector
gdb.pretty_printers['^std::map<.*>$'] = printstdmap
gdb.pretty_printers['^std::set<.*>$'] = printstdset
gdb.pretty_printers['^std::basic_string<char.*>$'] = printstdstring
gdb.pretty_printers['^std::stack<.*>$'] = printstdstack
gdb.pretty_printers['^std::queue<.*>$'] = printstdstack

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]