The GNU debugger has several useful features you may not know. Jonathan Wakely shows us how to save time and pain with some simple tricks.
The GNU Debugger (GDB) is a powerful tool, but if you’re used to working in an IDE then using a command-line debugger can be daunting and may seem to be lacking features you take for granted in an IDE. This article has some simple tips that might help you have a more pleasant debugging experience, and might inspire you to read the documentation [ GDB ] to see what other tricks are waiting to be discovered.
The first tip, and maybe the most important, is to make sure you’re using a recent version. Support for debugging C++ code got much better with GDB 7.0 and has continued to improve since then. If you’re using anything older than GDB 7.0 you should upgrade right away, and it’s probably worth upgrading anything older than a couple of releases (GDB 7.8.2 and 7.9 were both released in early 2015). If you can’t get a pre-built version for your OS then compiling GDB from source is very easy, just download the tarball, unpack it, run configure (setting your preferred install directory with
--prefix=dir
) and run
make
.
One of the simplest GDB features, but one I always miss when using the venerable ‘dbx’ debugger on ‘proper’ UNIX machines, is the ability to use abbreviations for commands. Any unambiguous prefix for a command will run the full command, so instead of typing
print foo
you only need
p foo
and instead of
break source.cc:325
just
br source.cc:325
(and while not strictly a prefix of the full command,
bt
will print a stack trace just like
backtrace
).
You can also very easily create your own commands by defining them in your personal ~/.gdbinit file, which gets run by GDB on startup. I use the following to quit without being asked to confirm that I want to kill the process being debugged:
define qquit set confirm off quit end document qquit Quit without asking for confirmation. end
This allows me to use
qquit
(or just
qq
) to exit quickly. The
document
section provides the documentation that will be printed if you type
help qq
at the gdb prompt.
Sometimes stepping through C++ code can be very tedious if the program keeps stepping into tiny inline functions that don’t do anything interesting. This is very obvious in C++11 code that makes heavy use of
std::move
and
std::forward
, both functions that do nothing except cast a variable to the right kind of reference. The solution is to tell gdb not to bother stepping into uninteresting functions (or ones that get called a lot but which you know are not the source of the problem you’re debugging). Running the
skip
command with no arguments will cause the current function to be skipped over next time it is reached, instead of stepping into it. You can also use
skip FUNCTION
to cause a named function to be skipped instead of the current one, or
skip file FILENAME
to skip whole files (as with most commands, run
help skip
at the gdb prompt for more information). Unfortunately the
skip
command treats every specialization of a function template as a separate function and there’s no way to skip over all specializations of say,
std::move
, but if you skip each one as you step into it at least you know you won’t step into that particular specialization again. I define the following command in my
.gdbinit
to make this even easier:
define skipfin dont-repeat skip finish end document skipfin Return from the current function and skip over all future calls to it. end
This lets me use
skipfin
to mark the current function to be skipped in future and to finish running it and return to the caller. The
dont-repeat
line tells gdb that (unlike most built-in commands), hitting enter again after running
skipfin
should not run
skipfin
again, so that I don’t accidentally finish running the caller and mark that to be skipped as well!
Another useful entry in my
.gdbinit
is
set history save
, which causes gdb to save your command history when exiting, so you can use the cursor keys to scroll through your history and easily create the same breakpoints or watchpoints as you used in an earlier debugging session.
The GDB feature that I most wish I’d known about sooner is the ‘TUI’ mode, which is activated by the
-tui
command-line option, or can be turned on and off in an existing debugging session with Ctrl-X Ctrl-A [
TUI
]. This splits the terminal window horizontally, with the bottom pane showing the usual prompt where you type commands and get output, and the top pane showing the source code for the function being debugged, just like your IDE would. This gives you a much more immediate view of the code than using
list
to print out chunks of it. One thing to be aware of is that the TUI mode changes the behaviour of the cursor keys, so they scroll up and down in the source code rather than through your command history. If you’re familiar with them from the terminal, you can still use readline key bindings (Ctrl-P, Ctrl-N etc.) to scroll through the command history.
After
-tui
, the command-line option I most often use when starting gdb is
--args
. This can be used to start debugging a program with a specific set of arguments, so instead of running
gdb ./a.out
and then setting arguments for it with
set args a b c
then running it with
run
, you can start gdb as
gdb --args ./a.out a b c
and then just
run
. This is very useful when the program needs a long and complicated set of arguments, as you don’t need to find them and copy & paste them into gdb, just add
gdb --args
before the usual command to run the program.
One of the most useful features of modern version of GDB is the embedded Python interpreter. This allows you to write pretty printers for your own types (or use the ones that come with GCC for printing standard library types such as containers). Defining pretty printers for the types in your system can be very useful, and although it’s not too complicated there isn’t room to explain here, however the embedded Python interpreter is also very useful for running simple one-liners without leaving gdb. For example if you have a
time_t
variable containing a unix timestamp you can easily print it using Python’s
datetime
module:
(gdb) python import datetime (gdb) python print datetime.datetime.fromtimestamp(1425690208) 2015-03-07 01:03:28
If what you want to do isn’t suitable for a one-liner you can create multiple-line blocks of Python by entering just
python
on a line on its own, and then end the block with
end
. Many of gdb’s features are exposed via a python API (‘import gdb’) [
Python
] that lets you inspect variables, so you can examine their value, type, members etc.
None of these tips are groundbreaking, but I hope they give an idea of ways you can customise your debugging experience and define shortcuts to simplify the most repetitive tasks.
References
[GDB] https://sourceware.org/gdb/onlinedocs/gdb/index.html#Top
[Python] https://sourceware.org/gdb/onlinedocs/gdb/Python-API.html#Python-API