崩溃后第一件事是用bt查看调用栈:gdb ./myapp core后执行bt,显示崩溃时函数调用路径,#0为崩溃点,需关注帧编号、函数名及参数、源码位置;若显示??,说明缺少调试信息或core与二进制不匹配。

崩溃后第一件事:用 bt 看调用栈
程序崩了,core 文件生成后,别急着翻源码。直接进 GDB:gdb ./myapp core,然后敲 bt(backtrace 缩写)——这是唯一必须立刻执行的命令。它会从当前崩溃点开始,逐层往上列出函数调用路径,最顶上是出问题的那一帧,最底下通常是 main 或线程入口。
常见错误现象:不加 -g 编译时,bt 只显示地址和未知符号,比如 #0 0x00007f8e5a1f4a25 in ?? ();这时你得重编带调试信息的版本再试。
-
bt显示的是栈帧快照,不是执行日志——它只反映崩溃瞬间的调用状态,不包含中间跳转或已返回的函数 - 如果看到
??或大量in ?? (),说明可执行文件缺少调试信息,或core和二进制不匹配(比如改过代码但没重编) - 线上环境若无法保留
-g版本,至少保留一份带-g的符号文件(如用objcopy --strip-debug分离),后续可配合add-symbol-file加载
看懂 bt 输出里的关键字段
bt 每行类似:#1 0x00010628 in process_data (buf=0x7efff5c0 "invalid_input") at main.c:89。重点盯三个部分:
-
#1:帧编号,数字越小越靠近崩溃点(#0是当前崩溃函数) -
in process_data (buf=0x7efff5c0 "invalid_input"):函数名 + 实际传入参数值(注意:这里显示的是调用时压栈的值,不是当前栈帧里变量的最新值) -
at main.c:89:源码位置,但仅当该行有调试信息且未被优化掉才准确;-O2以上可能让行号错位甚至消失
特别注意:如果参数显示为 0x0、0x1 或乱码字符串,大概率是野指针或内存越界破坏了栈,此时要结合 info registers 和 x/10xw $sp 查看栈底原始数据。
切换栈帧查局部变量,别只信 bt 表面信息
bt 告诉你“谁调了谁”,但具体哪一层传了坏数据,得进对应帧看真实变量值。用 frame N(或简写 f N)切到第 N 帧,再用 print(p)查变量:
- 切到
#1帧:f 1,然后p buf—— 如果显示0x0,说明上层传了空指针;如果显示有效地址但内容异常(如"invalid_input"实为超长截断),问题可能在更上层 - 某些变量可能被优化掉(
optimized out),尤其开了-O2;这时要么关优化重编,要么用info registers结合汇编指令反推寄存器中的值 - 多线程场景下,
bt默认只显示当前线程。用info threads列出所有线程,再用thread N切换后执行bt,否则容易漏掉死锁或竞态源头
为什么 bt 有时只显示一层?
最典型原因是栈被严重破坏:比如缓冲区溢出覆盖了返回地址,或 free() 了栈上变量导致帧指针错乱。此时 bt 可能只输出 #0 0x... 一行,甚至报错 Cannot access memory at address 0x...。
这种情况下,不要反复敲 bt:
- 先用
info registers看$rsp、$rbp(x86_64)是否为非法地址(如全 0、接近 0 或远超栈范围) - 用
x/20xg $rsp手动 dump 栈顶 20 个 8 字节单元,找疑似返回地址的规律值(通常相邻地址差在几 KB 内) - 配合
disassemble看崩溃地址附近的汇编,确认是不是在ret指令上出的问题
栈破坏类问题往往需要结合 AddressSanitizer 预先检测,GDB 的 bt 在这种情况下只是最后验证手段,不是首要依赖项。


















