GDB Cheatsheet
Watchpoints and Catchpoints
Use this GDB reference while you build software engineering projects, review code, or refresh the syntax you reach for most.
Watching a Value Change
A watchpoint stops the program when an expression's value changes, wherever that happens. Use it when you know what got corrupted but not where.
| Command | Stops when |
|---|---|
watch expr | the program writes expr and the value actually changes |
rwatch expr | the program reads expr |
awatch expr | the program reads or writes expr |
watch -l expr | watch the memory expr resolves to, ignoring scope |
watch expr thread 3 | only when thread 3 changes it, hardware only |
info watchpoints | list them, same format as info breakpoints |
(gdb) watch total Hardware watchpoint 2: total (gdb) continue Hardware watchpoint 2: total Old value = 0 New value = 42 add_row (n=42) at prog.c:31
Delete and disable use the same numbering space as breakpoints, so delete 2 and
info b cover watchpoints too.
Hardware Versus Software Cost
gdb uses a hardware watchpoint when it can. x86 has four debug registers, each covering at most 8 bytes, so you get about four small watchpoints at once. Anything larger or more numerous falls back to software.
| Hardware | Software | |
|---|---|---|
| Mechanism | CPU debug register | single-step and re-test after every instruction |
| Speed | full program speed | hundreds of times slower |
| Reported at | the exact instruction | the next statement after the change |
| Limit | ~4 regions of ≤8 bytes on x86-64 | unlimited |
(gdb) watch big_struct Hardware watchpoint 2: big_struct (gdb) c Continuing. Could not insert hardware watchpoint 2. Could not insert hardware breakpoints: You may have requested too many.
That message means you ran out of debug registers. Delete other watchpoints, or watch one field rather than the whole struct.
(gdb) watch cfg.timeout # one field, fits a register (gdb) set can-use-hw-watchpoints 0 # force software, slow but unlimited (gdb) show can-use-hw-watchpoints
Addresses, Scope, and the Constant Trap
Watching a raw number does not work, because the number never changes. Cast and dereference it.
(gdb) watch 0x600850 Cannot watch constant value 0x600850. (gdb) watch *(int *) 0x600850 Hardware watchpoint 1: *(int *) 6293584 (gdb) watch *(char (*)[16]) buf # 16 bytes at buf
A watchpoint on a local variable is deleted automatically when its frame exits.
Watchpoint 2 deleted because the program has left the block in which its expression is valid.
watch -l (long form -location) sidesteps that. It evaluates the expression
once, takes the address, and watches that memory for the rest of the run. That is
what you want for tracking a heap buffer that outlives the function that made it.
(gdb) watch -l node->key (gdb) watch *global_ptr # allowed before global_ptr is even set
Watching *p where p later changes watches the old object, since gdb
re-evaluates the expression rather than the address. Use -l if you meant the
original memory.
Catchpoints
A catchpoint stops on an event rather than a location. tcatch is the one-shot
version.
| Command | Event |
|---|---|
catch throw | a C++ exception is thrown |
catch throw std::bad_alloc | only throws whose type matches the regex |
catch rethrow | an exception is re-thrown |
catch catch | an exception reaches a handler |
catch exec | the program calls exec |
catch fork / catch vfork | the program forks |
catch syscall | any syscall, on entry and on return |
catch syscall write | one syscall by name |
catch syscall 257 | one syscall by number |
catch syscall group:network | a whole syscall group |
catch load / catch unload | a shared library loads or unloads, regex optional |
catch signal | any signal except SIGTRAP and SIGINT |
catch signal SIGSEGV SIGBUS | only these signals |
catch signal all | including the ones gdb uses internally |
(gdb) catch throw Catchpoint 1 (throw) (gdb) r Catchpoint 1 (exception thrown), 0x00007ffff7e9c0e2 in __cxa_throw () from /lib/x86_64-linux-gnu/libstdc++.so.6 (gdb) bt (gdb) up 4 # climb out of libstdc++ into your own code
catch throw stops inside libstdc++, not in your code, so the first frames are
runtime internals. Walk up until you reach a frame you recognize. The
$_exception convenience variable holds the thrown object where the libstdc++
probes are present.
(gdb) catch catch std::runtime_error (gdb) p $_exception (gdb) catch syscall group:process (gdb) catch load libplug
Signals
Catchpoints are one way to see a signal. handle is the other, and it controls
whether the signal is passed on to the program at all.
(gdb) info signals (gdb) handle SIGPIPE nostop noprint pass (gdb) handle SIGUSR1 stop print (gdb) signal SIGUSR1 # deliver one by hand (gdb) signal 0 # resume without delivering the pending signal
| Keyword | Meaning |
|---|---|
stop / nostop | stop the program when the signal arrives |
print / noprint | announce it at the prompt |
pass / noignore | deliver it to the program |
nopass / ignore | swallow it, the program never sees it |
The usual case is a server using SIGPIPE or a timer using SIGALRM as normal
flow control, which makes gdb stop constantly. handle SIGPIPE nostop noprint
pass makes the session usable again. Unlike handle, a catchpoint can carry a
condition and a commands block.