Skip to content

7. Pretty printing, part 1

Consider this simple C++ program:

#include <string>
std::string str = "hello world";
int main ()
{
  return 0;
}

Compile it and start it under gdb.  Look what happens when you print the string:

(gdb) print str
$1 = {static npos = 4294967295,
  _M_dataplus = {<std::allocator<char>> = {<__gnu_cxx::new_allocator<char>> = {<No data fields>}, <No data fields>}, _M_p = 0x804a014 "hello world"}}

Crazy!  And worse, if you’ve done any debugging of a program using libstdc++, you’ll know this is one of the better cases — various clever implementation techniques in the library will send you scrambling to the gcc source tree, just to figure out how to print the contents of some container.  At least with string, you eventually got to see the contents.

Here’s how that looks in python-gdb:

(gdb) print str
$1 = hello world

Aside from the missing quotes (oops on me), you can see this is much nicer.  And, if you really want to see the raw bits, you can use “print /r“.

So, how do we do this?  Python, of course!  More concretely, you can register a pretty-printer class by matching the name of a type; any time gdb tries to print a value whose type matches that regular expression, your printer will be used instead.

Here’s a quick implementation of the std::string printer (the real implementation is more complicated because it handles wide strings, and encodings — but those details would obscure more than they reveal):

class StdStringPrinter:
    def __init__(self, val):
        self.val = val

    def to_string(self):
        return self.val['_M_dataplus']['_M_p'].string()
gdb.pretty_printers['^std::basic_string<char,.*>$'] = StdStringPrinter

The printer itself is easy to follow — an initializer that takes a value as an argument, and stores it for later; and a to_string method that returns the appropriate bit of the object.

This example also shows registration.  We associate a regular expression, matching the full type name, with the constructor.

One thing to note here is that the pretty-printer knows the details of the implementation of the class.  This means that, in the long term, printers must be maintained alongside the applications and libraries they work with.  (Right now, the libstdc++ printers are in archer.  But, that will change.)

Also, you can see how useful this will be with the auto-loading feature.  If your program uses libstdc++ — or uses a library that uses libstdc++ — the helpful pretty-printers will automatically be loaded, and by default you will see the contents of containers, not their implementation details.

See how we registered the printer in gdb.pretty_printers?  It turns out that this is second-best — it is nice for a demo or a quick hack, but in production code we want something more robust.

Why?  In the near future, gdb will be able to debug multiple processes at once.  In that case, you might have different processes using different versions of the same library.  But, since printers are registered by type name, and since different versions of the same library probably use the same type names, you need another way to differentiate printers.

Naturally, we’ve implemented this.  Each gdb.Objfile — the Python wrapper class for gdb’s internal objfile structure (which we briefly discussed in an earlier post) — has its own pretty_printers dictionary.  When the “-gdb.py” file is auto-loaded, gdb makes sure to set the “current objfile”, which you can retrieve with “gdb.get_current_objfile“.  Pulling it all together, your auto-loaded code could look something like:

import gdb.libstdcxx.v6.printers
gdb.libstdcxx.v6.printers.register_libstdcxx_printers(gdb.get_current_objfile())

Where the latter is defined as:

def register_libstdcxx_printers(objfile):
   objfile.pretty_printers['^std::basic_string<char,.*>$'] = StdStringPrinter

When printing a value, gdb first searches the pretty_printers dictionaries associated with the program’s objfiles — and when gdb has multiple inferiors, it will restrict its search to the current one, which is exactly what you want.  A program using libstdc++.so.6 will print using the v6 printers, and (presumably) a program using libstdc++.so.7 will use the v7 printers.

As I mentioned in the previous post, we don’t currently have a good solution for statically-linked executables.  That is, we don’t have an automatic way to pick up the correct printers.  You can always write a custom auto-load file that imports the right library printers.  I think at the very least we’ll publish some guidelines for naming printer packages and registration functions, so that this could be automated by an IDE.

The above is just the simplest form of a pretty-printer.  We also have special support for pretty-printing containers.  We’ll learn about that, and about using pretty-printers with the MI interface, next time.

27 Comments

  1. SteveC wrote:

    “Crazy!…One thing to note here is that the pretty-printer knows the details of the implementation of the class. This means that, in the long term, printers must be maintained alongside the applications and libraries they work with.”

    Man, the insane lengths to which C++ can drive a man are something to behold.

    To me,it seems fairly obvious that the problems which C++ tries to solve are much simpler and fewer than the problems which it introduces.

    Writing and maintaining custom debugger extensions? Puke.

    Thursday, December 4, 2008 at 6:30 pm | Permalink
  2. Alexander Larsson wrote:

    So, in the gnome backtrace stuff I pretty printed all GObjects (with the runtime introspected type name). To do this I have to do some introspection on each type to see if it is a GObject.

    But the pretty printer stuff above just matches a typename, so its not enough for me. Any chance of getting a more generic hook?

    Thursday, December 4, 2008 at 6:34 pm | Permalink
  3. tom wrote:

    Alexander — We might be able to work something out. Send some email to the Archer list, with details… in particular I’m curious to know how these variables are declared.

    Thursday, December 4, 2008 at 6:45 pm | Permalink
  4. Alex Burr wrote:

    This is all very cool. I guess this can be used to mitigate the ghastly template hack needed to get covariance in c++ (see http://www.cs.technion.ac.il/~vitus/papers/TypeSafeCovariance.pdf).

    Thursday, December 4, 2008 at 8:58 pm | Permalink
  5. Alexander Larsson wrote:

    Don’t I need to join the list to post? I’m on too many list.

    As an example, take a “GtkWidget *” variable.
    GtkWidget is generally a typedef to struct _GtkWidget, which as the first element has its parent class struct, in this case “GtkObject object”, etc. At the top of the hierarchy you get “GTypeInstance”, which means the type is derived from this and we can use g_type_name_from_instance() to get the runtime introspected type name of the actual object.

    Its really quite similar to having a C++ pretty printer that catches all types that derive from a certain class.

    Thursday, December 4, 2008 at 9:10 pm | Permalink
  6. tom wrote:

    You don’t have to join the list. But I don’t mind discussing it here, either.

    It seems like it would not be too hard to write a generic printer that understands GObjects. It could dig into the value and pull out the type name, etc. FWIW — it is a little better in general to do this entirely in Python rather than relying on inferior function calls, because then the support will also work on core files.

    You can also register a printer for pointer types if you want to do something special there; for instance print a pointer as if it were cast to its runtime type.

    FWIW, gdb has this kind of thing built-in for C++, if you “set print object on”.

    So, if you had such a generic printer, then you would simply register all the GObject subclasses in the pretty_printers dictionary, all pointing at the same constructor.

    If you wanted to get really tricky you could iterate over all the symbols, looking for new types matching the profile (“inherits from GTypeInstance”) and register those dynamically. python-gdb doesn’t have the necessary symbol table iterator for this yet, though.

    Friday, December 5, 2008 at 6:35 pm | Permalink
  7. Alexander Larsson wrote:

    I already have the code to pull out the runtime introspected typename, not using inferiour calls.

    However, I don’t want to register all GObject derived names. First of all, there would be hundreds of them, then it wouldn’t automatically pick up new ones form libraries etc.

    However, if you can register a pretty printer for all pointer types that could work. It would just check if the pointer points to a GObject and if not bail.

    Friday, December 5, 2008 at 6:50 pm | Permalink
  8. tom wrote:

    Yeah, right now things aren’t ideal for your situation. You can register a handler for all pointer types, but there’s no way to say “I give up, call the next printer” — there’s not even a decent notion of next printer.

    Also you’d probably want to register printers for the struct types themselves, so that “p foo” followed by “p *foo” does what the user would expect.

    Walking the symbol table — once I implement that — might be your best bet. What do you think of that?

    Friday, December 5, 2008 at 7:51 pm | Permalink
  9. Alexander Larsson wrote:

    Well, right now i don’t pretty print the objects themselves, that would require object specific knowledge.

    All i do is tag gobject pointer with their actual runtime type, which is generally a subclass of the type the pointer is specified to. This is very useful, because in very many calls the arguments are just of type GtkWidget *, and knowing if the input is a button or a dialog helps understanding a lot.

    Friday, December 5, 2008 at 8:29 pm | Permalink
  10. tom wrote:

    I remembered how you can do this.

    Make a printer that matches any pointer type. Rather than pointing this entry at a constructor, though, point it at a function. This function should invoke the constructor the type is a GObject derivative. Otherwise, it should return None — this will let gdb skip to the next printer, if any.

    This function could records the types it checks so that it will do less work in future calls.

    BTW I “git pull”d from your repo but I don’t see any pretty-printers… could you check them in? I’d like to see the code more concretely.

    Saturday, December 6, 2008 at 12:53 am | Permalink
  11. Alexander Larsson wrote:

    tom: I didn’t write them as pretty printers (as i didn’t know it was possible to do it).

    But, in the gnome.gdbinit code, “describe_value() is pretty much it.

    Saturday, December 6, 2008 at 7:23 am | Permalink
  12. Axonian wrote:

    Trying to make pretty printing work on OS X 10.5 with python2.6 (from MacPorts). Finally came to the following problem:

    bash-3.2# ../build/gdb/gdb test
    GNU gdb (GDB) 6.8.50.20090106-cvs
    Copyright (C) 2009 Free Software Foundation, Inc.
    License GPLv3+: GNU GPL version 3 or later
    This is free software: you are free to change and redistribute it.
    There is NO WARRANTY, to the extent permitted by law. Type “show copying”
    and “show warranty” for details.
    This GDB was configured as “i386-apple-darwin9.6.0″.
    For bug reporting instructions, please see:

    Setting up the environment for debugging gdb.
    $1 = 111
    Traceback (most recent call last):
    File “…/sandbox/gcc/archer/archer/gdb/python/lib/gdb/libstdcxx/v6/printers.py”, line 474, in to_string
    print self.val['_M_dataplus']['_M_p'].string(encoding)
    RuntimeError: Error reading string from inferior: Input/output error

    Any idea about how to fix all that would be highly appreciated

    Saturday, April 25, 2009 at 9:55 am | Permalink
  13. Axonian wrote:

    Find out an interesting thing. The variables s1 and s are displayed properly after setting breakpoint on line 8 and run (in gdb: b 8 / r / p s1 / p s)
    —————-
    #include
    #include

    std::string s = “The long long string”;
    int main ()
    {
    std::string s1 = “The short and not long string”;
    std::cout << “Hello, world!\n”;
    std::cout << s1;
    return 0;
    }

    Saturday, April 25, 2009 at 10:29 am | Permalink
  14. Sreejith wrote:

    I am trying to use archer in Eclipe-Cygwin environment. Currently Eclipse can’t be used to debug STL as it gives row gdb output. I could enable the python scripting in GDB. But the STL containers are not printing data. It worked straight in debian. The following log (Cygwin) explains more:

    (gdb) python print 10
    10
    (gdb) print myList
    $1 = {<_List_base<int, std::allocator >> = {
    _M_impl = {<allocator<std::_List_node >> = {<new_allocator<std::_List_n
    ode >> = {}, }, _M_node = {
    _M_next = 0×681838, _M_prev = 0x6818c8}}}, }
    (gdb) python print myList
    Traceback (most recent call last):
    File “”, line 1, in
    NameError: name ‘myList’ is not defined
    Error while executing Python code.
    (gdb)

    I am clue-less about the error! what could go wrong?

    Wednesday, May 20, 2009 at 4:51 pm | Permalink
  15. Mari8i wrote:

    Hi all! I’m trying to use python GDB for pretty-printing of C structs using custom printing routines.

    More in detail, I have an AST and want to print it as a simple string.

    I have my printer routine in my C code, I would like that GDB always prints that type of structure pointer with my routine..

    is that possible? I’m not able to getting it working

    Tuesday, October 13, 2009 at 12:37 pm | Permalink
  16. tom wrote:

    You can try making an inferior function call from a pretty-printer.
    I have heard that people have done it, but it is not very safe, because
    you might hit a breakpoint during the call, and then weird things happen.
    Also, implementing a printer this way prevents people from using them
    when debugging core files.

    Anyway, if you really want to do it, try using gdb.parse_and_eval from
    your printer.
    From what you say it sounds like the problem you are having is that
    your printer is not being found at all, though. I would probably start
    by writing a printer to print anything at all (“Hello”) and then make that
    work, and then expand it to do what it really ought to do.

    Tuesday, October 13, 2009 at 4:08 pm | Permalink
  17. Sam wrote:

    class StdStringPrinter:
    def __init__(self, val):
    self.val = val
    def to_string(self):
    return None
    def display_hint(self):
    return ‘string’
    gdb.current_objfile().pretty_printers.append(StdStringPrinter)

    (gdb) print s
    $1 = TypeError: Could not convert Python object: None

    Is this expected behavior? I can’t do the “== None” comparison shown in the GDB reference example because I get this same error message.

    The reference says None cannot be converted to gdb.Type, however it also says pretty_printers should skip the entry if a pretty-printer object is not returned.

    Saturday, November 7, 2009 at 7:17 am | Permalink
  18. tom wrote:

    The API has changed a bit since this blog series.
    You’ll have to look at the manual to see what to do.
    I am not sure you can return None from to_string;
    but if you want to get that effect then your value-recognizer
    can return None.

    Saturday, November 7, 2009 at 4:18 pm | Permalink
  19. Sam wrote:

    Thanks for the answer and for all of your hard work on this Tom. The python API is great.

    For reference for anyone else working on this, this is what I have working so far from the latest git checkout, modified from the manual:

    class StdStringPrinter:
    def __init__ (self, val):
    self.val = val
    def to_string (self):
    return self.val['_M_p']
    def display_hint (self):
    return ‘string’

    def str_lookup_function (val):
    lookup_tag = val.type.tag
    regex = re.compile (“^std::basic_string”)
    if lookup_tag == None:
    return None
    if regex.match (lookup_tag):
    return StdStringPrinter (val)
    return None

    gdb.current_objfile().pretty_printers.append(str_lookup_function)

    This will print (better than default but still incomplete):

    (gdb) print s
    $1 = {static npos = 0xffffffffffffffff, _M_dataplus = 0×608238 “hello world”}

    Saturday, November 7, 2009 at 11:34 pm | Permalink
  20. Sam wrote:

    I found my mistake by looking at the libstdc++ svn:
    http://sourceware.org/gdb/wiki/STLSupport

    You need to get the unqualified type, stripped of typedefs, which is not shown in the manual. This can be done as:
    type = val.type.unqualified().strip_typedefs()

    After this:
    (gdb) print s
    $1 = “hello world” :)

    Sunday, November 8, 2009 at 7:03 am | Permalink
  21. gnub wrote:

    I amenthusiastic about this as I hope it will replace the ugly way of inspecting objects via the CLI-Interface and gdb-macros I used before. Nevertheless I have the problem of line breaks getting shown as simple “\n” inside the normal inspection environment of CDT.

    Printing containers in GDB/CLI inside CDT works as expected, the linebreaks produced by python are interpreted. This is not the case when using the graphical frontent of CDT.

    Is this a general problem or just mine?

    Monday, November 9, 2009 at 1:28 pm | Permalink
  22. tom wrote:

    Offhand, I don’t know where the bug is. It could be the CDT, or it could be GDB. You’d have to investigate deeper. At the very least, could you file a bug report in GDB’s bugzilla? That would help. Thanks.

    Monday, November 9, 2009 at 3:14 pm | Permalink
  23. gnub wrote:

    I investigated further and found something related:

    https://bugs.eclipse.org/bugs/show_bug.cgi?id=255946
    https://bugs.eclipse.org/bugs/show_bug.cgi?id=286785

    However, it is marked as fixed in 6.0.1 and is related to gdb server debugger and application output.

    I am not quite shure anymore if my “problem” is just a missing feature or a bug. Maybe someone with more Eclipse/CDT knowledge can help to clarify that. I will show an example.

    Printing a variable on GDB-Console or via the CLI-Console inside CDT gives this output:

    p initialLEDs

    $1 = std::map with 2 elements = {
    [1] = {
    x = 124,
    y = 99,
    led = 1 ’01′,
    val = 0
    },
    [2] = {
    x = 126,
    y = 102,
    led = 2 ’02′,
    val = 0
    }
    }

    Clicking on the “Variables” tab to this variable instead shows this:
    std::map with 2 elements = {\n [1] = {\n x = 124, \n y = 99, \n led = 1 ’01′, \n val = 0\n },\n [2] = {\n x = 126, \n y = 102, \n led = 2 ’02′, \n val = 0\n }\n}

    Deflating the triangle in front of the variable shows the STL internals as usual. Is this the “intended” or at least current behaviour of CDT? I don’t know as I just started using the python print facilities. If someone disagrees this might be a bug in CDT.

    Monday, November 9, 2009 at 4:41 pm | Permalink
  24. Mushi wrote:

    I agree with snub’s above observation. I also observe same behavior.

    I am also not sure whether it is a bug or intended behavior of CDT. Please throw some light on this.

    Thursday, December 10, 2009 at 7:17 am | Permalink
  25. tom wrote:

    I don’t know a whole lot about the CDT.

    MI users need some (small) modifications to work properly with pretty-printing. I assume those changes haven’t yet been made to the CDT.

    Friday, December 11, 2009 at 9:22 pm | Permalink
  26. gr3go wrote:

    Hey! Nice one, managed to configure gdb to use them. But when using with eclipse (cdt 7), my application hangs when started. If the pretty print is disabled, there is no start-up delay.
    Any hints, why could happen this?

    Gr3go

    Friday, July 15, 2011 at 7:22 am | Permalink
  27. Nick wrote:

    http://sourceware.org/gdb/onlinedocs/gdb/Values-From-Inferior.html#Values-From-Inferior

    has an example that shows if a gdb.Value object is of type TYPE_CODE_FUNC, we can make the function call from within python.

    If a gdb.Value object is of type TYPE_CODE_METHOD, how do we make the call from within python?

    Thanks.

    Sunday, October 16, 2011 at 6:27 am | Permalink

4 Trackbacks/Pingbacks

  1. Nibbles microblog » extensions de GDB en python on Friday, April 9, 2010 at 4:06 pm

    [...] nos objets complexes et les afficher d’une autre façon qui nous convient mieux. Il ya des articles et des bouts de code sur codesearch pour en savoir plus là [...]

  2. RethinkDB - The database for solid state drives. on Thursday, August 19, 2010 at 4:37 pm

    [...] we look at the code, I suggest reading Tromey’s blog post on pretty printing in Gdb if you don’t know the basic idea. To start off, here’s a basic pretty printer that [...]

  3. [...] STL support for gdb.. Python and gdb tutorial. And This blog entry. Also, this is my .gdbrc currently: set print pretty on set print object on set print [...]

  4. [libcxx]?gdb??libcxx?????? | ??·?? on Saturday, November 3, 2012 at 4:33 am

    [...] ??http://tromey.com/blog/?p=524 [...]

Post a Comment

Your email is never published nor shared. Required fields are marked *
*
*