GDB Cheatsheet
Threads
Use this GDB reference while you build software engineering projects, review code, or refresh the syntax you reach for most.
Listing and Switching
gdb gives every thread a number in the current inferior and a global id.num
identifier. The * marks the one your commands apply to.
(gdb) info threads Id Target Id Frame * 1 Thread 0x7ffff7d8a740 (LWP 4711) "prog" main () at prog.c:61 2 Thread 0x7ffff7589640 (LWP 4712) "worker" __futex_abstimed_wait () 3 Thread 0x7ffff6d88640 (LWP 4713) "worker" poll () from /lib/libc.so.6 (gdb) thread 2 [Switching to thread 2 (Thread 0x7ffff7589640 (LWP 4712))] #0 __futex_abstimed_wait_common64 () at futex-internal.c:57 (gdb) bt
| Command | Effect |
|---|---|
info threads | list every thread with its current frame |
info threads 2 4-6 | only those thread numbers |
thread N | switch to thread N |
thread 1.3 | thread 3 of inferior 1, the global form |
thread name reader | name the current thread for readability |
thread find worker | find threads whose name or target id matches a regex |
p $_thread | the current thread number |
p $_gthread | its global number |
p $_inferior_thread_count | how many threads exist |
info inferiors | list processes, not threads |
inferior 2 | switch process, for a forked child |
Selecting a thread changes what bt, info locals, p, and the registers refer
to. Breakpoints stay global unless you scope them with thread N.
Running a Command on Every Thread
This is how you triage a hang or a deadlock. One command, every stack.
(gdb) thread apply all bt (gdb) thread apply all -s bt # skip threads where it errors (gdb) thread apply all bt 6 # only 6 frames each, much shorter (gdb) thread apply 2-5 p errno (gdb) taas bt # short for thread apply all -s (gdb) tfaas info locals # thread apply all -s frame apply all -s
| Command | Expands to |
|---|---|
thread apply all CMD | run CMD in every thread |
thread apply all -s CMD | same, silently skipping threads where it fails |
taas CMD | thread apply all -s CMD |
tfaas CMD | thread apply all -s frame apply all -s CMD |
frame apply all CMD | run CMD in every frame of the current thread |
# the one-liner that diagnoses most hangs gdb -q -p 4711 -batch -ex "thread apply all bt full" > hang.txt
Read the output looking for two things: several threads blocked in
pthread_mutex_lock, and which thread holds a lock while waiting on something
else. taas bt 8 keeps the output short enough to actually read.
Freezing the Scheduler
By default a step in one thread lets the others run, so the program can stop
somewhere else entirely. set scheduler-locking fixes the focus.
| Mode | Behavior |
|---|---|
off | any thread may run at any time |
on | only the current thread runs when you resume, new threads are held at entry |
step | acts like on while stepping, off for continue, until, finish |
replay | acts like on in replay mode, off otherwise, the default |
(gdb) set scheduler-locking on # nobody else moves while I step (gdb) show scheduler-locking (gdb) set schedule-multiple on # resume all inferiors, not just this one (gdb) set print thread-events off # stop the create and exit chatter
scheduler-locking on makes stepping deterministic, and it also deadlocks you if
the thread you are stepping waits on a lock another thread must release. The symptom
is a next that never returns. Set it back to off and continue.
(gdb) b worker.c:40 thread 3 # only when thread 3 gets there (gdb) watch counter thread 3 # hardware watchpoints only (gdb) info breakpoints # the thread scope shows in the What column
Non-Stop Mode
In the default all-stop mode every thread freezes at every stop. Non-stop mode stops only the thread that hit the event and leaves the rest running, which matters for a server you cannot afford to pause.
# in .gdbinit, this must be set before the program runs set non-stop on
(gdb) continue& # resume in the background, prompt comes back (gdb) interrupt # stop the current thread only (gdb) interrupt -a # stop every thread (gdb) info threads # running threads show "(running)" (gdb) thread 4 (gdb) bt
Non-stop mode has to be enabled before running or attaching. In non-stop mode
continue resumes only the current thread, and every execution command takes the
& suffix to run in the background so you keep the prompt.
Fork and Exec
| Setting | Values | Effect |
|---|---|---|
set follow-fork-mode | parent (default), child | which side gdb follows |
set detach-on-fork | on (default), off | off keeps both under gdb |
set follow-exec-mode | same (default), new | reuse the inferior or make a new one |
catch fork / catch vfork / catch exec | — | stop at the event itself |
(gdb) set follow-fork-mode child (gdb) set detach-on-fork off (gdb) run (gdb) info inferiors Num Description Connection Executable * 1 process 4711 1 (native) /home/me/prog 2 process 4718 1 (native) /home/me/prog (gdb) inferior 1 # go back to the parent (gdb) detach inferior 2
With detach-on-fork off both processes stay stopped under gdb, so a parent that
would normally wait on its child now sits still while you inspect the child. That
is the setup for debugging a fork plus exec server, and it is also how you
accidentally hang a program that expects its child to finish quickly.
For a daemon that double-forks past you, it is usually easier to let it start and attach to the final pid than to follow it through two forks.