GDB函数调用栈管理 _ 、栈帧当程序进行函数调用时系统需要保存该函数调用过程中的上下文信息例如函数是从哪里被调用的、传入了哪些参数、局部变量有哪些、函数返回后应该回到哪里继续执行以及部分寄存器状态等。这些与一次函数调用相关的上下文信息就可以理解为一个栈帧。从调试角度看一个栈帧对应一次尚未结束的函数调用。程序运行时如果函数 A 调用了函数 B那么在调用 B 的过程中会产生 B 对应的栈帧当 B 执行结束并返回后B 的栈帧也会随之销毁程序继续回到 A 中执行。二、栈帧查看与切换1、查看调用栈在使用GDB调试时可以使用以下命令对调用栈进行查看backtrace或者简写为btbt 命令用于查看当前线程的调用栈信息。也可以指定只显示前几个栈帧bt 3表示只显示最靠近当前执行位置的第3个栈帧。如果想在查看调用栈时同时显示每个栈帧中的局部变量可以使用bt fullbt 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 2i f也可以在支持的 GDB 版本中直接使用i f 2小技巧bt命令还能查看当前程序执行到哪里了查看最顶层的栈帧后面就是他当前执行所在行4、查看当前栈帧的参数与局部变量查看当前栈帧中的函数参数info args查看当前栈帧中的局部变量info locals例如当前选中的是 calc 函数的栈帧那么info argsinfo locals看到的就是calc函数中的参数和局部变量而不是add或main中的变量。这也是切换栈帧的重要作用可以回到某一层函数调用现场查看当时传入了什么参数、局部变量是什么值从而分析程序为什么会执行到当前位置。三、完整示例下面通过一个包含 4 层函数调用的程序来理解调用栈。#includeint 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(a8, b10) 当前程序停在add函数中在test.cpp源文件的第5行#1 calc(x4) add是由calc调用的在test.cpp源文件的第12行#2 work(n3) calc是由work调用的在test.cpp源文件的第18行#3 main() work是由main调用的在test.cpp源文件的第24行在当前的栈帧通过i locals、i args等命令查看当前的上下文环境然后再通过f 1切换到1号栈帧中查看1号栈帧的上下文信息。结果如上图所示。可以通过i f查看当前栈帧的具体信息