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).
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.)
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
(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 “
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.