gdb中backtrace(bt)可直接显示断点处调用栈,需-g编译;bt full显示局部变量,info frame查看单帧细节如返回地址和栈布局,程序内可用backtrace()函数配合-rdynamic打印栈信息。

gdb 里用 backtrace 看调用栈最直接
只要程序在断点处暂停,backtrace(或简写为 bt)就能立刻列出当前执行路径上所有未返回的函数。它不依赖源码是否带符号,但必须用 -g 编译,否则只显示地址,看不到函数名。
常见错误现象:运行 bt 后输出全是 ?? 或十六进制地址 —— 这基本说明没加 -g,或者链接了 stripped 的库。
- 编译时务必加
-g:gcc -g -O0 test.c -o test - 如果用了
-fomit-frame-pointer(某些优化级别默认开启),部分栈帧可能丢失,bt会不完整;加-fno-omit-frame-pointer可修复 -
bt full会额外显示每个栈帧的局部变量和参数,但前提是这些变量没被优化掉(-O0最保险)
info frame 查看单个栈帧的细节
当你需要确认某一层调用的参数值、返回地址或栈布局时,info frame 比 backtrace 更细粒度。它告诉你当前帧的 saved rip(返回地址)、caller's stack frame 位置、以及该帧在内存中的起始/结束地址。
使用场景:怀疑某次函数调用传参错误,或想验证栈帧是否被破坏(比如 buffer overflow 后 bt 显示异常,这时 info frame 能看出 rbp 是否对齐、rip 是否指向合理代码段)。
- 先用
bt确认目标帧编号(比如#2),再执行frame 2切过去,然后info frame -
info args和info locals是它的轻量替代,适合快速检查参数和变量,但不显示内存布局 - 注意:在内联函数或尾调用优化后,某些帧可能被合并或省略,
info frame也会反映这种“缺失”
程序运行时主动打印调用栈用 backtrace()
不是所有问题都能靠 gdb 复现(比如线上偶发 crash、无 core dump 的 exit)。这时可以在代码里插桩,用 execinfo.h 提供的 backtrace() + backtrace_symbols() 把栈信息写到日志。
容易踩的坑:符号解析依赖可执行文件未 strip,且 backtrace_symbols() 返回的字符串需手动 free();若只想要函数名(不带偏移),得配合 addr2line 或 dladdr() 做二次解析。
- 编译时加
-rdynamic(等价于--export-dynamic),否则动态链接的符号无法被backtrace_symbols()解析 - 数组大小要足够:
void *buffer[64]比[16]更稳妥,深层递归或复杂调用链容易溢出 - 不要在信号处理函数(如
SIGSEGVhandler)里直接调用printf或malloc—— 它们不是 async-signal-safe;改用write(2)输出原始字符串
-finstrument-functions 不适合常规调试
这个 GCC 选项会在每个函数入口/出口插入回调,理论上能记录完整调用流,但它生成的 trace 量极大,且必须自己实现 __cyg_profile_func_enter/exit,还要处理多线程竞争、性能毛刺等问题。日常调试远不如 gdb 的 bt 直观可靠。
真正适合它的场景极少:比如做静态调用图生成、或嵌入式环境无法跑 gdb 时做粗粒度路径审计。普通开发中,它带来的编译慢、二进制膨胀、难以过滤噪音等问题,远超收益。
- 启用后必须链接
-lc,否则undefined reference错误 - 回调函数里不能调用任何非 async-signal-safe 函数(包括
printf、malloc),否则可能死锁 - 它不记录栈帧内容(参数/变量),只记录函数地址和调用点,无法替代
info locals类功能
-rdynamic 却指望 backtrace_symbols() 输出函数名),结果就不可信。











