0号帧是当前函数,1号帧是直接调用者;定位问题应聚焦#1帧,若为??需检查符号信息,多线程需thread apply all bt,崩溃后可用core文件还原调用链。

用 bt 看调用链,0号帧是当前函数,1号帧就是调用者
函数“是谁调用的”,本质就是看它的直接上层栈帧——也就是 bt 输出中编号为 1 的那一行。GDB 把正在执行的函数标为 #0,它上面那个函数(即调用者)就是 #1,再上面是 #2,依此类推。
常见错误现象:看到 bt 输出里 #0 是某个库函数(比如 malloc 或 strlen),就以为问题出在它身上;其实真正该盯的是 #1 —— 那才是你代码里触发它的位置。
-
bt默认显示全部帧,但实际只需关注#0和#1就能定位“谁调了它” - 如果
#1显示的是??或地址(如0x7f...),说明符号缺失,需确认程序带-g编译,且没 strip - 多线程下,
bt只显示当前线程;要查其他线程谁调了某函数,得先thread apply all bt找线索,再thread N切过去看
用 up 和 frame 1 查看调用者的上下文
光看 bt 行号不够?想确认调用者当时的参数、变量或源码行,就得切到 #1 帧。
两种等效方式:up(从 #0 往上跳一层)或直接 frame 1。之后用 info args 和 info locals 就能看清调用时传了什么、局部变量是什么值。
-
up后记得再执行一次frame或list,确认是否真跳到了调用者函数体内部 - 如果
up报错 “Not enough frames”,说明当前已在最外层(比如main或__libc_start_main),那它就没有调用者了 - 某些优化过的代码里,编译器可能内联函数,导致
bt里直接跳过中间层——这时#1看起来像“跳了一级”,需关掉优化(-O0)复现完整调用链
崩溃现场没断点?靠 core 文件还原调用者
程序已崩掉、没连着 GDB?只要生成了 core 文件,就能事后还原“谁调用了出问题的函数”。
命令是:gdb ./your_program core.xxx,进 GDB 后直接 bt —— 此时 #0 是崩溃点,#1 就是它的调用者,和运行时调试完全一致。
- 确保系统允许生成 core:
ulimit -c unlimited,否则可能根本没core文件 - 如果
bt里全是??,不是core文件损坏,而是可执行文件被移动/重命名过,或调试信息丢失;GDB 必须用**原编译产物**加载对应core -
core中的栈帧是快照,不能up/down修改状态,但info args和info registers依然可用
为什么有时 bt 看不到清晰的调用者?
这不是 GDB 失灵,而是底层事实被隐藏了:栈帧本身可能已被破坏,或编译器做了激进优化。
典型场景包括:
- 栈溢出后,
bt可能输出乱码或提前截断——此时#1不可信,优先检查info registers里的rbp/rsp是否异常 -
-O2及以上开启尾调用优化时,编译器可能把A → B → C合并成A → C,导致bt里#1直接是A,跳过了B - 信号处理函数(如
SIGSEGVhandler)会插入新栈帧,干扰原始调用链;用info threads确认是否在 signal-delivery 栈上
真正难的从来不是命令怎么输,而是当 bt 显示不全或矛盾时,得立刻意识到:栈本身可能已经不可信,这时候该转去看寄存器、内存布局,或者换 addr2line 配合汇编逆向。











