GDB函数调用栈管理

一、栈帧

当程序进行函数调用时,系统需要保存该函数调用过程中的上下文信息,例如:函数是从哪里被调用的、传入了哪些参数、局部变量有哪些、函数返回后应该回到哪里继续执行,以及部分寄存器状态等。这些与一次函数调用相关的上下文信息,就可以理解为一个栈帧

从调试角度看,一个栈帧对应一次尚未结束的函数调用。程序运行时,如果函数 A 调用了函数 B,那么在调用 B 的过程中,会产生 B 对应的栈帧;当 B 执行结束并返回后,B 的栈帧也会随之销毁,程序继续回到 A 中执行。

二、栈帧查看与切换


1、查看调用栈

在使用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函数中的参数和局部变量,而不是addmain中的变量。

这也是切换栈帧的重要作用:可以回到某一层函数调用现场,查看当时传入了什么参数、局部变量是什么值,从而分析程序为什么会执行到当前位置。

三、完整示例

下面通过一个包含 4 层函数调用的程序来理解调用栈。

cpp 复制代码
#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行

在当前的栈帧通过i localsi args等命令查看当前的上下文环境,然后再通过f 1切换到1号栈帧中,查看1号栈帧的上下文信息。结果如上图所示。

可以通过i f查看当前栈帧的具体信息

可以看见当前在1号栈帧中,还有其对应的起始地址,以及一些参数等信息

相关推荐
ttkwzyttk2 天前
GDB调试变量、内存与寄存器查看与修改
gdb
ttkwzyttk3 天前
GDB调试简介与调试配置
gdb
modelmd15 天前
GDB 摘要
gdb
源分享17 天前
GDB下载和安装保姆级教程
gdb
modelmd1 个月前
翻译 GDB 官方文档
gdb
kidwjb1 个月前
一次多进程信号量同步失效的排查实录
gdb·进程通信·信号量
炘爚1 个月前
C++11实现线程池:项目实现过程的报错与gdb调试
stl·gdb·shared_ptr
___波子 Pro Max.1 个月前
GDB 符号检视三件套:`ptype` / `info variables` / `info functions`
gdb
CC城子1 个月前
嵌入式Linux宕机问题GDB调试(一)
gdb·嵌入式软件