If you’re like me, you’ve probably wished you could write new commands in gdb. Sure, there is the
define command — which I’ve used heavily — but it has some annoying limitations. For example, you can’t make new sub-commands using
define. Also, arguments to
define are parsed oddly; you can’t have an argument that has a space in it. Instead, the user has to do the quoting, unlike other gdb commands.
Naturally, the Python extensions to gdb make these problems trivial to solve. In this post, I’ll show you how to implement a new command to save breakpoints to a file. This is a feature I’ve often wanted, and which, strangely, has never been written.
I think we’ll call our new command “
save breakpoints“. Given the existence of “
save-tracepoints” you might think that “
save-breakpoints” is a better choice; but I never liked the former, and plus this gives me a chance to show you how to make a new prefix command. Let’s start there.
A command written in Python is implemented by subclassing gdb.Command:
import gdb class SavePrefixCommand (gdb.Command): "Prefix command for saving things." def __init__ (self): super (SavePrefixCommand, self).__init__ ("save", gdb.COMMAND_SUPPORT, gdb.COMPLETE_NONE, True) SavePrefixCommand()
That is simple enough.
init method takes the name of the command, a command class (the choices are listed in the manual), the completion method (optional; and again, documented), and an optional final argument which is
True for prefix commands.
You can try the above very easily. Save the above to a file and then source it into gdb, telling gdb that it is Python code:
(gdb) source -p /tmp/example
Now take a look:
(gdb) help save Prefix command for saving things. List of save subcommands: Type "help save" followed by save subcommand name for full documentation. Type "apropos word" to search for commands related to "word". Command name abbreviations are allowed if unambiguous.
Note how the class documentation string turned into the gdb help.
Now we’ll write the subcommand which does the actual work. It will take a filename as an argument, and will write a series of gdb commands to the file. This will make it simple to restore breakpoints — just source the file.
As you might expect, many of gdb’s internal data structures have Python analogs. We’ve seen commands, above. Our new command will examine
Breakpoint objects. The gdb manual has documentation for most of these new classes (but not all, yet. In the early days of this work we were quite lax … we’re still catching up).
from __future__ import with_statement import gdb class SaveBreakpointsCommand (gdb.Command): """Save the current breakpoints to a file. This command takes a single argument, a file name. The breakpoints can be restored using the 'source' command.""" def __init__ (self): super (SaveBreakpointsCommand, self).__init__ ("save breakpoints", gdb.COMMAND_SUPPORT, gdb.COMPLETE_FILENAME) def invoke (self, arg, from_tty): with open (arg, 'w') as f: for bp in gdb.get_breakpoints (): print >> f, "break", bp.get_location (), if bp.get_thread () is not None: print >> f, " thread", bp.get_thread (), if bp.get_condition () is not None: print >> f, " if", bp.get_condition (), print >> f if not bp.is_enabled (): print >> f, "disable $bpnum" # Note: we don't save the ignore count; there doesn't # seem to be much point. commands = bp.get_commands () if commands is not None: print >> f, "commands" # Note that COMMANDS has a trailing newline. print >> f, commands, print >> f, "end" print >> f SaveBreakpointsCommand ()
I think the
init method is fairly straightforward, given our earlier class definition. The name of the command is “
save breakpoints” — the
Command initializer will parse this properly for us. The command takes a filename as an argument, and because there is a built-in filename completer, we simply ask gdb to use that for us. This means that completion on the command-line will automatically do the right thing.
invoke method is where we do the real work — this method is called by gdb when the the command is executed from the CLI. There are two arguments passed by gdb. The first is the argument to the command. This is either a string, if there is an argument, or
None. The second argument is a boolean, and is
True if the user typed this command at the CLI; it is
False if the command was run as part of a script of some kind, for instance a
.gdbinit or breakpoint commands.
The body of
invoke is quite simple: we loop over all existing breakpoints, and write a representation of each one to the output file. This is simple, because the gdb command language is easy to emit, and because the
Breakpoint class provides simple access to all the attributes we care about.
Note that the precise syntax of some things here is already slated to change. We’re going to drop the “
get_” prefixes at least; and some class attributes will move from method form to “field” form. We’ll be making these changes soon, before there is too much code out there relying on the current API.
Adding new commands is easy! A few Python-based commands come with your gdb. You can use the new “require” command to load new commands dynamically. To see what is available, try:
(gdb) require command <TAB>
We’re also currently shipping an “alias” command, a simple version of “pahole” (find holes in structures), a replacement for “backtrace” which we’ll explore in a later post, and maybe more by the time you read this (I’m considering committing the “save breakpoints” command).
A new command isn’t always the best way to extend the debugger. For example, it is fairly common for an application to have a user-defined command to pretty-print a data structure; but wouldn’t it be nicer for the user if this was automatically integrated into the print command?
It would also sometimes be more convenient to hook into expression evaluation — that would make breakpoint conditions more powerful, among other things. This is what we’ll explore in the next post.