崩溃时应先执行info threads查看所有线程状态,再用thread apply all bt获取全部调用栈,重点排查栈顶含SIGSEGV/SIGABRT或异常地址的线程,忽略正常等待态线程,并通过frame、info registers和x命令定位非法指针或堆损坏源头。

崩溃时如何确认是哪个线程出的问题
多线程程序崩溃后,bt 默认只显示当前线程的栈,而真正出问题的线程可能不是 gdb 自动选中的那个。必须主动检查所有线程状态,否则容易误判。
进入 GDB 后第一步不是急着看栈,而是执行:
-
info threads—— 列出所有线程及其状态(Running / Idle / Blocked) -
thread apply all bt—— 对每个线程执行bt,一次性输出全部调用栈 - 重点关注栈顶含
SIGSEGV、SIGABRT或明显异常地址(如0x0、0xffffffffffff2e88)的线程
注意:有些线程栈会显示大量 pthread_cond_wait 或 nanosleep,这类是正常等待态,可直接跳过;真正可疑的是栈顶函数名缺失、地址乱码、或停在 memcpy/memset/strlen 等底层函数内部。
线程栈里看不到源码行号?检查调试符号和路径
即使有 core 文件,bt 也可能只显示地址(如 0x0000555555556789),没有文件名和行号——这通常不是 GDB 问题,而是二进制缺少调试信息或库路径不匹配。
验证方式:
-
file your_program—— 输出中应含with debug_info -
readelf -S your_program | grep debug—— 应看到多个.debug_*段 - 若使用了动态链接库(如自定义
.so),需提前告知 GDB 路径:set solib-search-path /path/to/libs
常见坑:生产环境二进制被 strip 过,或调试符号被分离但未用 objcopy --add-gnu-debuglink 关联。此时 bt 必然无源码上下文,只能靠寄存器和内存分析。
崩溃点在 libc 里?别急着怀疑自己代码
看到 bt 显示崩溃在 __memcpy_ssse3、__strcmp_sse42 或 malloc_consolidate 里,不代表 libc 有 bug——大概率是你传了非法指针给它。
关键操作:
-
frame 1(或更高层栈帧)切换到调用 libc 的上层业务函数 -
info registers查看rdi/rsi(memcpy 参数)或rdi(strcmp 第一参数)是否为0x0、极小值或极大值 -
x/10gx $rdi尝试读取疑似指针地址,若报Cannot access memory,基本坐实空指针或野指针
特别注意:SIGABRT 常由 glibc 检测到堆损坏触发,此时崩溃点只是“结果”,真正破坏可能发生在几分钟前——需结合 thread apply all bt 全局扫描,找最早出现异常分配/释放行为的线程。
附着运行中进程时线程状态不准?先 signal SIGSTOP
用 gdb -p PID 附着正在运行的服务时,有时 info threads 显示线程状态混乱(比如所有线程都标 Running),导致无法判断哪个线程卡死。
这是因为 GDB 附着瞬间未冻结线程调度。安全做法是:
- 附着后立即执行:
signal SIGSTOP(向目标进程发停止信号) - 再执行
info threads和thread apply all bt - 分析完用
signal SIGCONT恢复运行(不要用continue,它可能跳过信号处理逻辑)
这个细节极易被忽略:没 SIGSTOP 就直接查栈,看到的可能是线程刚切换走的“残影”,而非真实阻塞点。


















