Today I used the Python support in gdb to do real work. Until now I’ve just been playing around, adding functionality that looked fun.
My application was something I’ve wanted to be able to do in gdb for a long, long time: set a breakpoint in one function, but have the breakpoint be conditional on the caller. In my case, I’m debugging the compile server, and I want to examine calls to c_parser_lookup_callback
which pass a complicated test (that is, the breakpoint is in the true branch of an if
statement); but only calls originating in declspecs_add_type
are interesting.
Roland taught me that you can fake this in plain gdb by using convenience variables and breakpoint commands. However, it is rather painful to get this right, as you have to remember to reset the convenience variable in some cases (in my case, if we reach the outer breakpoint but not the inner one).
With the Python support, this kind of thing is easy. First I define a Python function to do a little work:
(gdb) python Type python script End with a line saying just "end". >def check(): > frame = gdb.current_frame().get_prev() > return frame.get_name() == "declspecs_add_type" >end
As you can see this just checks the parent frame’s function name.
Now, I make the breakpoint conditional on this function’s value:
(gdb) cond 1 $(check())
That’s all there is to it!
I suppose I could have written this as a one-liner, like:
(gdb) cond 1 $(gdb.current_frame().get_prev().get_name() == "declspecs_add_type")
Both of the above formulations still seem a little clunky to me. But, it is still early days for the Python integration; I think we’ll find some nice ways to simplify common tasks.
What’s your missing feature?
7 Comments
One thing I’ve sometimes wanted is to stop at a breakpoint only if one or two other breakpoints have been hit first. Maybe it’s possible to do already today (is it?), but with the Python support I’m sure it will be much easier.
However, what I’d really, really like is for the gdb python support to evolve into a library of useful functionality that can be reused. For example your break-if-came-from could probably be quite useful to a lot of people.
Thanks for implementing this, I believe it will really be a huge boost for GDB.
// Simon
Simon — yeah, you can do that with gdb convenience variables. I think the Python solution might not look much different, fwiw.
The library thing is definitely the direction I want to go. I’d like to even migrate some gdb commands away from their C implementation to a Python implementation.
Ultimately I would like to be able to completely control gdb from python, e.g., even changing gdb’s startup sequence and the like.
I’d like Emacs to automatically load .gdbinit.py along with .gdbinit.
Here are some crazy random ideas that the Python support will make a lot easier to implement.
– when I’m debugging code generated by Ur-Scheme, it would be nice to be able to print out the in-memory structures I’m looking at as Scheme forms instead of blocks of memory with hexadecimal in them.
– if I set a breakpoint in malloc and realloc before they return, I can remember (in the debugger) the size of each piece of memory that gets allocated. This could be useful information when I suspect that an array index is out of bounds.
– when I’m debugging, say, a Python interpreter, I can see a sort of approximation to the Python stack already by typing bt in gdb. But there are something like five C stack frames per Python stack frame, and it requires a bit of hassling to see what the actual arguments of each of those are. A little scripting of the debugger (in whatever — elisp would be fine, Tcl would be OK, Python should be fantastic but that’s a confusing coincidence in this context) should make it easy to display that.
– logging a bunch of events to a data structure in the memory of the debugger, and then querying that data structure to filter out the events that I decide of are interest, will be a heck of a lot easier with Python in the debugger than it is with DDD (I think, it’s been a decade since I last used DDD) or than it would be with Tcl. [x.size for x in log if x.type == malloc], here I come.
– a lot of the stuff people do with DTrace on Solaris could be done (less efficiently, of course) with the above approach.
– suppose I want to know whether some arbitrary process on my system is appropriately ulimited. Maybe there’s a way to get this information through /proc, I don’t know. If you were a sane person, the way you would solve this would normally be to start a shell, set the ulimit, kill the old process, and start a new one. What I did instead was to attach to the process with gdb, allocate myself a stack buffer (saving its old contents by means of the p command, so that I could later refer to $4 and $6 when I was restoring them), call getrlimit with the buffer, inspect its contents, restore its contents, and detach the debugger: http://irc.canonical.org:8080/01/417 — all of which would have been easier if I could have scripted it in Python instead of gdb’s built-in scripting language. (Or, as it was, not scripted it at all.)
– suppose I want to steal a file descriptor from a process (maybe you want to move a process you started in gnome-terminal into your screen). In theory you ought to be able to open /proc/20844/fd/4, right? That doesn’t seem to work; maybe I’m doing something wrong. What does work is passing the file descriptor over a Unix-domain socket. But what do you do if the program doesn’t already have code to send you the descriptor on request? Well, you can always inject it via gdb. But I’d really rather script that in Python rather than gdb’s current scripting language. And, as with the getrlimit example, I’d love to be able to invoke the whole script from the bash command line, although that’s maybe somewhat orthogonal to the ability to hang Python scripts on run-time events in gdb.
– presumably the embedded Python interpreter can import all of the usual things Python can import, right? So you can easily start a web server or a GUI from inside gdb, with control over the process gdb is attached to? You can do the above-mentioned logging to SQLite or MySQL or Postgres instead of an in-memory data structure?
– How about if you want to add a really advanced feature to gdb. Say, you want to be able to debug optimized code as fully as nonoptimized code, except for information that’s actually been erased — so you hack up your copy of GCC so it writes down in painful detail which registers have the values of which variables at what time, and which instruction ranges are inlined from which other functions, and so on. Now to take advantage of that, you have to teach GDB how to interpret all that crap, so it can tell you what the value of “i” really is. (“Except in the last two instructions of the loop, it’s %edi – %edx; in the last two instructions, it’s %edi – %edx – 1.”) I don’t know about you, but I’d rather do that kind of stuff in Python than in C. Doing it seamlessly I think requires deeper integration than I think you’re contemplating at the moment, so that when I say p i, my Python code runs instead of the normal GDB p command.
The basics would be nice, such a quickstart doc link at http://sourceware.org/gdb/wiki/PythonGdb
My work is mostly coredump analysis and I already heavy use python based automation (external to gdb) so I’m welcome embedded python.
My typical task is: if (%rax & 1) print $(some_address) as StructA so I would like to have raw access to addresses and registers from within python.
Also the future that I believe could be implemented easy – make gdb.execute returning python file object or list of strings rather then print the output to stdout.
To be able to do:
for nn in gdb.execute(“thread apply all bt”) :
do something
Hi Tromey,
I tried gdb.execute(‘run’) , but it hangs there. Do you know why?