gdb加载core文件后直接用bt查看调用栈,前提是core与带调试符号(-g编译)的可执行文件匹配;常见错误包括No stack(core损坏或不匹配)、#0 ??(缺失调试信息)、架构不兼容;bt full可显示局部变量,thread apply all bt用于多线程分析。

gdb加载core文件后直接用bt就能看调用栈
前提是core文件和可执行文件匹配、且后者带调试符号(编译时加了-g)。启动命令就是gdb ./your_program core-xxx,进GDB后第一件事就是输bt——它会从崩溃点(#0帧)开始,逐层往上列出函数调用链。
常见错误现象:
-
No stack.:core文件损坏,或不是该程序生成的(用readelf -n core-xxx | grep -A2 "NT_PRPSINFO"核对程序名和PID) -
#0 ?? in ??:可执行文件被strip过,或没编译-g,需重编译带调试信息的版本 -
Not compatible with target:core是64位,你拿32位gdb开;或程序是musl libc编译的,但系统gdb默认适配glibc
bt full比bt多显示局部变量,但依赖符号完整
bt full会在每个栈帧后附上该帧的局部变量值,对定位空指针、越界写、未初始化变量特别有用。但它不是万能的——如果某帧的变量被编译器优化掉了(比如gcc -O2下未加volatile),或者栈帧本身已破坏(如栈溢出),bt full可能只显示<optimized out></optimized>或报错。
使用场景:
- 段错误时想确认
ptr是否为NULL:bt full之后再frame 0,然后print ptr - 崩溃在第三方库内,但你想看上层业务代码传了什么参数:
bt full里找frame 2或frame 3对应的应用层帧 - 怀疑是递归过深:看
bt输出行数,若超过几百行且函数名高度重复,大概率是栈溢出
用frame和info registers定位真正崩溃指令
bt只告诉你“谁调用了谁”,但不告诉你“哪条指令崩的”。要查具体崩溃点,得切到#0帧并看寄存器:
- 先
frame 0确保在最顶层帧 - 再
info registers,重点关注rip(x86_64)或pc(ARM),它指向崩溃瞬间将要执行的下一条指令地址 - 用
x/5i $rip反汇编那几条指令,结合print/x $rdi(或$rsi等)看操作地址是否非法(如0x0、0xdeadbeef、超出info proc mappings里任一内存段)
性能影响几乎为零,但这是区分“空指针解引用”和“释放后使用”的关键动作——前者$rdi常为0,后者常为已unmap的地址。
多线程程序必须用thread apply all bt
单线程下bt够用,但多线程服务(如nginx、redis、自研worker池)崩溃时,主线程未必是肇事者。只看默认线程的栈,容易漏掉死锁源头或竞争条件。
正确做法:
-
info threads列出所有线程及其状态(running / stopped / idle) -
thread apply all bt一次性打印全部线程的调用栈 - 重点扫视哪些线程卡在
pthread_mutex_lock、__lll_lock_wait、epoll_wait或malloc内部——这些往往是阻塞点或资源争用点
容易被忽略的是:core文件默认只保存崩溃线程的完整上下文,其他线程寄存器可能不全,所以thread apply all bt里部分线程可能只显示#0 0x0000... in ?? (),这时得靠bt主栈 + 日志 + 代码逻辑交叉验证。


















