栈帧查看与切换
在使用GDB调试时,可以使用以下命令对调用栈进行查看
backtrace
或者简写为:
bt
bt命令用于查看当前线程的调用栈信息。
也可以指定只显示前几个栈帧:
bt 3
表示只显示最靠近当前执行位置的第3个栈帧。
如果想在查看调用栈时,同时显示每个栈帧中的局部变量,可以使用:
bt full
bt full比普通的bt输出更详细,适合排查程序崩溃时每一层函数中的变量值。
2、切换栈帧
使用如下命令可以对栈帧进行切换
frame n
或者简写为:
f n
其中n表示栈帧编号。
在bt输出中,每个栈帧前面都会有编号,例如:
#0 add(int, int) at main.cpp:6 #1 calc(int) at main.cpp:13 #2 work(int) at main.cpp:19 #3 main() at main.cpp:25
其中:
| 栈帧编号 | 含义 |
|---|---|
#0 | 当前正在执行的函数,也就是最顶层栈帧 |
#1 | 调用当前函数的上一层函数 |
#2 | 再上一层调用者 |
#3 | 更早的调用者 |
如果想切换到work函数对应的栈帧,可以执行:
f 2
切换栈帧后,GDB 的上下文也会切换到对应函数中。此时查看局部变量、函数参数、源码位置,都是针对当前选中的栈帧而言。
3、查看当前栈帧信息
查看当前栈帧的详细信息:
info frame
或者简写为:
i f
该命令可以查看当前栈帧的地址、调用关系、保存的寄存器信息等。
如果想查看某个栈帧的信息,常见做法是先切换到对应栈帧:
f 2 i f
也可以在支持的 GDB 版本中直接使用:
i f 2
小技巧:bt命令还能查看当前程序执行到哪里了,查看最顶层的栈帧,后面就是他当前执行所在行
4、查看当前栈帧的参数与局部变量
查看当前栈帧中的函数参数:
info args
查看当前栈帧中的局部变量:
info locals
例如,当前选中的是calc函数的栈帧,那么:
info args info locals
看到的就是calc函数中的参数和局部变量,而不是add或main中的变量。
这也是切换栈帧的重要作用:可以回到某一层函数调用现场,查看当时传入了什么参数、局部变量是什么值,从而分析程序为什么会执行到当前位置。
三、完整示例
下面通过一个包含 4 层函数调用的程序来理解调用栈。
#include <iostream> int add(int a, int b) { int sum = a + b; return sum; } int calc(int x) { int y = x * 2; return add(y, 10); } int work(int n) { int value = n + 1; return calc(value); } int main() { int data = 3; int ret = work(data); std::cout << "ret = " << ret << std::endl; return 0; }这个程序的调用关系是:
main -> work -> calc -> add
也就是 4 层函数调用。
将程序编译完成后,通过gdb启动,在add函数处设置断点,并通过bt命令查看当前的函数栈调用。如下图所示
这个输出表示:
| 栈帧 | 函数 | 含义 |
|---|---|---|
#0 | add(a=8, b=10) | 当前程序停在add函数中,在test.cpp源文件的第5行 |
#1 | calc(x=4) | add是由calc调用的,在test.cpp源文件的第12行 |
#2 | work(n=3) | calc是由work调用的,在test.cpp源文件的第18行 |
#3 | main() | work是由main调用的,在test.cpp源文件的第24行 |
