info threads 必须手动执行才能查看所有线程,GDB attach 后默认仅显示当前调度线程(通常为主线程),执行后可列出全部线程ID、OS线程号及执行位置,带*号者为当前调试上下文所在线程。

info threads 必须手动执行,否则看不到其他线程
gdb attach 到运行中的多线程进程后,默认只显示当前被调度到的那个线程(通常是主线程),info threads 不会自动运行。你看到“只有 1 个线程”或“线程全消失了”,大概率只是没敲这行命令。
执行 info threads 后,输出类似:
Id Target Id Frame 1 Thread 0x7ffff7fc0700 (LWP 1234) "a.out" main () at main.cpp:25 2 Thread 0x7ffff77bf700 (LWP 1235) "a.out" business_1 () at main.cpp:12 * 3 Thread 0x7ffff6fbe700 (LWP 1236) "a.out" business_2 () at main.cpp:18
注意:* 表示当前调试上下文所在的线程;Id 是 gdb 内部编号(后面 thread 命令要用);Target Id 里的 LWP 1235 是真实 OS 线程 ID,可与 ps -T -p <pid> 对齐验证。
- 如果
info threads输出为空或仅一行,说明程序确实没活跃线程(比如刚启动还没pthread_create,或所有子线程已退出) - 某些旧版 GDB(如 CentOS 7 自带的 7.6)可能不显示状态字段,建议用
ps -T -p <pid>辅助确认真实线程数
thread apply all bt 批量看栈,但要注意 no stack 场景
想快速扫一遍所有线程在干什么,thread apply all bt 是最省事的方式。它等价于对每个线程依次执行 thread <id> + bt。
立即学习“C++免费学习笔记(深入)”;
但常见问题:某线程 bt 显示 No stack. 或一堆 ?? ():
-
No stack.:该线程刚创建、正卡在 libc 初始化(如__clone)、或陷入系统调用等待态(如sleep、read),用户栈尚未建立完整 —— 等几秒再试bt,或切过去后用step走几步再看 -
?? ():大概率是编译时没加-g,或二进制被strip过。用file ./your_binary检查输出是否含not stripped - 别在
pthread_create返回前设断点 —— 新线程栈帧未就绪,GDB 容易失联
thread 命令切换后,bt 显示的才是当前线程真实上下文
thread 2 不是“选中线程 2”,而是把整个调试上下文(寄存器、栈帧、当前源码位置)切换到线程 2。之后所有命令(bt、print、step)都作用于它。
关键点:
- 切换后
bt显示的是该线程当前执行点的完整调用链,包括函数参数、局部变量地址(若符号可用) - 用
frame N可跳转到任意栈帧,再用print查变量值 —— 注意:不同线程的同名局部变量互不干扰,但全局/静态变量是共享的 - 想确认当前在哪条线程?看
info threads输出里带*的那行,或者直接敲thread(不带参数)回显当前 ID
set scheduler-locking off 是并发调试的前提
默认 set scheduler-locking on,意味着你 continue 或 next 时,只有当前线程跑,其他线程被锁死。这不是“看不到上下文”,而是它们根本没机会执行到新位置 —— 你看到的永远是旧栈帧。
要观察真实并发行为,必须先执行:
set scheduler-locking off
此时:
- 所有线程自由运行,
continue后任何线程 hit 断点都会暂停(断点本身是全局的) - 用
thread apply all bt看到的才是各线程“此刻真实停驻点”,不是冻结态快照 - 若需单步分析某个线程而不受干扰,可临时
set scheduler-locking step,但记得完事后切回off
真正容易被忽略的点:attach 后默认是 on,很多人没改就直接 c,结果发现“怎么其他线程一直不动”,其实是被锁死了。


















