I'll be using the Linux kernel for examples throughout this post, not because these
examples are necessarily realistic, but because it's a large C codebase that I know and
that anyone can download and take a look at. Don't worry if you aren't familiar with
Linux's source in particular -- the details of the examples won't matter too much.
But did you know that you can set conditional breakpoints? If you add if
CONDITIONto a breakpoint command, you can include an expression to be
evaluated whenever the program reaches that point, and the program will only
be stopped if the condition is fulfilled. Suppose I was debugging the Linux
kernel and wanted to stop whenever init got scheduled. I could do:
(gdb) break context_switch if next == init_task
Note that the condition is evaluated by gdb, not by the debugged program, so
you still pay the cost of the target stopping and switching to gdb every time the
breakpoint is hit. As such, they still slow the target down in relation to to how
often the target location is hit, not how often the condition is met.
2. command
In addition to conditional breakpoints, the command command lets you specify
commands to be run every time you hit a breakpoint. This can be used for a
number of things, but one of the most basic is to augment points in a program
to include debug output, without having to recompile and restart the program. I
could get a minimal log of every mmap() operation performed on a system
using:
(gdb) b do_mmap_pgoff
Breakpoint 1 at 0xffffffff8111a441: file mm/mmap.c, line 940.
(gdb) command 1
Type commands for when breakpoint 1 is hit, one per line.
End with a line saying just "end".
>print addr
>print len
>print prot
>end
(gdb)
3. gdb --args
This one is simple, but a huge timesaver if you didn't know it. If you just want to
start a program under gdb, passing some arguments on the command line,
you can just build your command-line like usual, and then put "gdb --args" in
front to launch gdb with the target program and the argument list both set:
(gdb) p GFP_ATOMIC
No symbol "GFP_ATOMIC" in current context.
(gdb) p task_is_stopped(&init_task)
No symbol "task_is_stopped" in current context.
However, if you're willing to tell GCC to generate debug symbols specifically
optimized for gdb, using -ggdb3, it can preserve this information:
$ make KCFLAGS=-ggdb3
...
(gdb) break schedule
(gdb) continue
(gdb) p/x GFP_ATOMIC
$1 = 0x20
(gdb) p task_is_stopped_or_traced(init_task)
$2 = 0
You can also use the macro and info macro commands to work with macros
from inside your gdb session:
(gdb) macro expand task_is_stopped_or_traced(init_task)
expands to: ((init_task->state & (4 | 8)) != 0)
(gdb) info macro task_is_stopped_or_traced
Defined at include/linux/sched.h:218
included at include/linux/nmi.h:7
included at kernel/sched.c:31
#define task_is_stopped_or_traced(task) ((task->state &
(__TASK_STOPPED | __TASK_TRACED)) != 0)
Note that gdb actually knows which contexts macros are and aren't visible, so
when you have the program stopped inside some function, you can only
access macros visible at that point. (You can see that the "included at" lines
above show you through exactly what path the macro is visible).
6. gdb variables
Whenever you print a variable in gdb, it prints this weird $NN = before it in the
output:
(gdb) p 5+5
$1 = 10
This is actually a gdb variable, that you can use to reference that same
variable any time later in your session:
(gdb) p $1
$2 = 10
You can also assign your own variables for convenience, using set:
(gdb) set $foo = 4
(gdb) p $foo
$3 = 4
This can be useful to grab a reference to some complex expression or similar
that you'll be referencing many times, or, for example, for simplicity in writing a
conditional breakpoint (see tip 1).
7. Register variables
In addition to the numeric variables, and any variables you define, gdb
exposes your machine's registers as pseudo-variables, including some cross-
architecture aliases for common ones, like $sp for the the stack pointer,
or $pc for the program counter or instruction pointer.
These are most useful when debugging assembly code or code without
debugging symbols. Combined with a knowledge of your machine's calling
convention, for example, you can use these to inspect function parameters:
Ksplice is hiring!
Do you love tinkering with, exploring, and debugging Linux systems? Does writing
Python clones of your favorite childhood computer games sound like a fun weekend
project? Have you ever told a joke whose punch line was a git command?
Join Ksplice and work on technology that most people will tell you is impossible:
updating the Linux kernel while it is running.
Help us develop the software and infrastructure to bring rebootless kernel updates to
Linux, as well as new operating system kernels and other parts of the software stack.
We're hiring backend, frontend, and kernel engineers. Say hello at jobs@ksplice.com!