5. The filtering backtrace

You’ll want to update and rebuild your python-gdb before trying this example — I found a bug today.

We’ve learned some of the basics of scripting gdb in Python.  Now let’s do something really useful.

Many projects provide a customized backtrace-like gdb command, implemented using “define“.  This is very common for interpreters — Python provides a command like this, and so does Emacs.  These commands let the user show the state of the interpreted code, not just the state of the interpreter.  This is ok if you are conversant with the new gdb commands provided by the program you are debugging, and — more importantly — your program only requires one such special command at a time.

It would be much nicer if the application-specific munging were done by the default backtrace command.  And, it would be great if multiple such filters could be used at once.

This, by the way, is a theme of the python-gdb work: gdb is more useful when its standard commands can act in application- or library-specific ways.  In the new model, gdb provides the core framework, and the application provides some Python code to make it work better.

I’m sure you know what is coming.  We’ve reimplemented backtrace, entirely in Python.  And, in so doing, we’ve added some functionality, namely filtering and reverse backtraces (I like gdb’s approach just fine, but, strangely to me, multiple people have wanted the display to come out in the opposite order from the default).

The new backtrace works by creating a frame iterator object — a typical Pythonic idea.  The frame iterator simply returns objects that act like gdb.Frame, but that also know how to print themselves.  (Hmm… we should probably just add this directly to Frame and avoid a wrapper.)  The backtrace command then simply iterates over frames, printing each one.  Simple stuff.

You can easily see how to implement a filter here: it is just a frame iterator, that accepts some other frame iterator as an argument.  When backtrace requests the next frame, the filter can do as it likes: stop iterating, request a frame from the underlying iterator and modify or drop it, synthesize a new frame, etc.  Because filters are required to return frame-like objects, these filters can easily be stacked.

Here’s a real-life example.  This filter is based on Alexander Larsson’s work, and removes glib signal-emission frames.  (This could easily be extended to do everything his does, namely emit information about the signal and instance.)

import gdb
class GFrameFilter:
    def __init__ (self, iter):
        self.iter = iter
        self.names = ['signal_emit_unlocked_R',
                      'g_signal_emit_valist',
                      'g_signal_emitv',
                      'g_signal_emit',
                      'g_signal_emit_by_name']

    def __iter__ (self):
        return self

    def wash (self, name):
        if not name:
            return "???"
        if name.startswith("IA__"):
            return name[4:]
        return name

    def next (self):
        while True:
            frame = self.iter.next ()
            name = self.wash (frame.get_name ())
            if name in self.names:
                continue
            if name == 'g_closure_invoke':
                frame = self.iter.next ()
                name = self.wash (frame.get_name ())
                if name.find ('_marshal_') != -1:
                    continue
            break
        return frame

gdb.backtrace.push_frame_filter (GFrameFilter)

This should be pretty clear.  The constructor takes a single argument — another frame iterator.  The new iterator’s next method simply discards frames that the user is unlikely to care about; it relies on the underlying iterator to terminate the iteration by raising an exception.

Simply source this into your gdb.  Then, load the new backtrace command:

(gdb) require command backtrace

For the time being, the new command is called “new-backtrace“; we’ll change this later once we are sure that the new command is working as desired.  If you decide you want an unfiltered backtrace, you can simply invoke “new-backtrace raw“.

You can also easily see how this is useful in modern, mixed-library programs.  For instance, the above signal emission filter would compose easily with a filter that nicely displayed Python frames; you could use this if you had a program that mixed Gtk and Python.  And, you wouldn’t have to learn any new application- or library-specific commands.

What if you want to configure the filters?  Say, drop one, or make one become more verbose?  We haven’t built anything like that into new-backtrace, but filters can easily provide their own configurability via the Parameter class we discussed earlier.

Now, wouldn’t it be cool if library-specific backtrace filters were automatically available whenever the inferior loaded the corresponding library?  That would make an awesome user experience… and that is what we’ll cover next time.

4 Comments

  • Trying to run your code in Ubuntu Maverick:

    Traceback (most recent call last):
    File “newbt.py”, line 35, in
    gdb.backtrace.push_frame_filter (GFrameFilter)
    AttributeError: ‘module’ object has no attribute ‘backtrace’

    ‘gdb.backtrace’ is nowhere to be found, neither in documentation on sourceware.org nor in packages. Has the API changed?

  • Maybe Ubuntu isn’t shipping everything from that branch, or maybe they just stuck with upstream — we didn’t upstream the new-backtrace stuff yet, since it still isn’t ready. We’re actively working on fixing that though.

  • Hello !

    Yes, no sign of “backtrace” in the python gdb…
    I’m using an up-to-date Debian Wheezy

    Any hint on how it should be done with the current API ?

  • The backtrace module was an experiment we did in Archer. After some experiences with it we replace it with frame filters. See

    https://sourceware.org/gdb/current/onlinedocs/gdb/Writing-a-Frame-Filter.html

    … for some information, but browse other nearby nodes for the full details.

Join the Discussion

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

This site uses Akismet to reduce spam. Learn how your comment data is processed.