info threads 可查看所有线程ID、状态、栈顶函数及源码行,带*号者为当前活跃线程;线程ID是GDB内部编号,切换需用thread N,非LWP号。

如何查看当前所有线程及其状态
调试多线程程序时,第一件事是确认线程是否按预期创建和运行。info threads 是最直接的命令,它会列出所有线程 ID、状态(如 running、stopped)、函数调用栈顶部和所在源码行。
注意:GDB 默认只显示当前线程的栈帧,info threads 的输出中带 * 号的是当前活跃线程。线程 ID(如 Thread 2 (LWP 12345))里的数字不是系统 PID,而是 GDB 内部编号,后续切换要用这个编号,不是 LWP 号。
- 用
thread 2切换到线程 2(编号必须是info threads显示的左侧数字) - 用
thread apply all bt对所有线程一次性打印调用栈,适合快速定位死锁或卡住的线程 - 如果线程刚创建就退出,
info threads可能看不到它——需在pthread_create后加断点,或启用set follow-fork-mode child配合set detach-on-fork off
如何在线程特定位置打断点并控制执行
普通 break main 会在所有线程到达该位置时触发,但多数时候你只想监控某个线程的行为。GDB 支持线程限定断点:
-
break func_name thread 2:仅在线程 2 进入func_name时停住 -
break file.cpp:42 thread 3 if i == 5:线程 3 在第 42 行且变量i等于 5 时才中断 - 已设断点后可用
info breakpoints查看是否带thread X限制;没限制的断点对所有线程生效 - 误删了线程专属断点?别用
clear——它清所有同位置断点;改用delete N(N 是info b显示的编号)
为什么 step/next 会跳进其他线程?怎么避免
GDB 的 step 和 next 默认不绑定当前线程,遇到系统调用(如 pthread_mutex_lock)、条件变量等待或调度切换时,可能切到别的线程继续执行,导致“单步像乱跳”。这不是 bug,是内核调度的真实反映。
立即学习“C++免费学习笔记(深入)”;
- 用
set scheduler-locking on锁定当前线程:之后step/next/continue都只在当前线程内执行,其他线程挂起 - 但注意:锁住后若当前线程在等另一个线程释放锁(比如 mutex),程序会死锁——此时需临时
set scheduler-locking off让对方线程跑起来 -
set scheduler-locking step更温和:只在step/next期间锁定,continue仍放开调度
如何捕获线程创建/退出事件
靠手动加断点太被动。GDB 提供原生跟踪点:catch syscall clone 可捕获 pthread_create 底层调用,catch syscall exit_group 捕获进程级退出,但线程退出更常用:
-
catch pthread_create—— 在libpthread的封装入口停住(需有 debuginfo) -
catch throw+catch catch对 C++ 异常跨线程传播特别有用,尤其当异常未被某线程捕获导致std::terminate - 若想观察线程退出前最后一刻,可在
pthread_exit或线程函数 return 前设断点;但要注意:detached 线程退出后资源立即回收,GDB 可能来不及响应
catch pthread_create commands printf "New thread created\n" info registers rax # 查看新线程 tid(x86_64) continue end
线程调试真正的复杂点不在命令本身,而在于调度不可控性与状态竞态——哪怕你锁住了 scheduler,内存可见性、指令重排、缓存一致性这些底层问题依然存在。GDB 给你看的是某一瞬间的快照,不是确定性回放。










