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. Command
‘s 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.
The 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.
10 Comments
Mostly unrelated, but have we not posted the patch to add subcommands using define? If so, it’s not on purpose. It’s a handy and quite simple patch.
That doesn’t make this any less useful, mind!
Daniel — I don’t recall seeing the patch, though I could have missed it, particularly if it was sent before June or so.
It definitely is not in the tree:
(gdb) define delete foo
Junk in argument list: ” foo”
Thanks for getting this functionality into gdb. It works really great. You really need to make it easier to install/use though. I have been searching and downloading and building gdb and archer to no avail until I found a note for Chromium that explained how to get the python pretty printing to work when debugging Chromium. It was simple and to the point.
http://code.google.com/p/chromium/wiki/LinuxDebugging
I think the main issue here is that there is a lot that you take for granted that a Linux dev should just know before trying to delve into the deep innards of simply modifying the .gdbinit file. I think that having a short, simple example of how to get it working with gdb 7+ would be great. The website above did that. It may not be perfect, but it works.
Thanks
I am trying to extend gdb by creating custom commands using python. Can you please tell me where I can find the gdb class definition. I could not find all the methods available in gdb class.
@JK – I suggest the gdb documentation. Most of the gdb module is defined in C code.
At least with GDB 7.2 I’ve discovered that the source command shown above does not work. Instead you need to use the .py file extension and emit the -p, i.e.
(gdb) source /tmp/example.py
I guess things have moved on since this tutorial was written, but I still found it very helpful.
[…] Writing a new gdb command […]
I have a question regarding prefix commands. I know that the gdb “logging” command is a prefix command:
set logging file “/tmp/log”
set logging overwrite on
But at the same time, you can give it values independently like:
set logging on
set logging off
In python, if I need to get the value of a sub-parameter, I use:
gdb.parameter(“logging file”)
gdb.parameter(“logging overwrite”)
But how to get the value of the “logging” parameter itself? I tried:
gdb.parameter(“logging”)
but it didn’t work.
Thanks,
Haitham
@Haitham – I’m not actually sure. I suggest filing a bug report.
Nice tutorial, but several years have elapsed since it was published and the API seems to have changed a bit. Here’s the modified code that works for me on GDB 7.6.2:
(Figured it would save newcomers some frustration)
#######
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.breakpoints():
print >> f, “break”, bp.location,
if bp.thread is not None:
print >> f, ” thread”, bp.thread,
if bp.condition is not None:
print >> f, ” if”, bp.condition,
print >> f
if not bp.enabled:
print >> f, “disable $bpnum”
# Note: we don’t save the ignore count; there doesn’t
# seem to be much point.
commands = bp.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 ()
#######