- 上一篇文章学习了如何使用GDB数据断点进行内存监测:【软件开发底层知识修炼】十五 快速学习GDB调试三 使用GDB的数据断点监测变量是否改变
- 本篇文章继续上一篇文章的学习:如何使用GDB进行函数调用栈的查看
1 backtrace和frame
一般来说,查看函数调用栈,主要是为了研究函数的调用过程。
一般使用下面的命令进行查看:
backtrace
- 查看函数的调用顺序(函数调用栈的信息)
frame N
- 切换到栈编号为N的上下文中(具体栈编号是什么在下面的实际案例中会有)
info frame
- 查看当前函数调用栈帧的信息
至于什么是栈帧信息,大概就是下图的样子,这里不再多介绍,后面还会有文章学习函数栈帧的概念,或者推荐大家去阅读程序员的自我修养。
- 上面有一个info frame命令,我们在前几篇文章已经学习过info的几个命令。下面再介绍几个下图中的info命令:
2 使用GDB进行函数调用栈的查看的实际代码案例
我们还是给出以下代码,作为这次调试的代码:
frame.c
#include <stdio.h>
int sum(int n)
{
int ret = 0;
if( n > 0 )
{
ret = n + sum(n-1);
}
return ret;
}
int main()
{
int s = 0;
s = sum(10);
printf("sum = %d\n", s);
return 0;
}
上述代码很简单,sum函数是一个递归的求解过程,最终求得1+2+3+…+n
- 开始进行调试:
首先将程序编译,并打开gdb调试,这在前几篇文章已经做过很多次,大概如下图所示的步骤:
然后我们再sum函数处打一个断点,并给出条件,当n==0的时候断点成立
break sum if n==0
查看断点是否打上:info breakpoints
运行程序:continue
运行上述几个步骤后,程序运行到sum函数,并在sum函数递归调用到n==0的时候停止:
此时,函数调用被中断,我们现在来使用backtrace命令来查看之前sum函数的调用栈的顺序(左侧的#0 ,#1…就是栈的编号):
此时程序运行到n==0,本应该继续运行sum函数,但是却被我们的断点中断了。所以此时停在最后一层的sum函数递归调用上。且是停在sum函数中的第6行:
我们连续输入两次next,并且查看当前程序的栈信息:
程序运行到13行停下来了,这一行是本该return的。此时的函数栈中
n==0,ret==0
,这个ret就差返回给上一层函数调用了。现在我们来使用info registers查看当前的函数调用过程的各个寄存器的值,并使用info frame查看当前函数调用过程的函数栈帧的详细信息:
如上图,寄存器比较多,这里我们只关心一个寄存器,ebp,ebp寄存器保存的是调用这个函数的函数(也就是上一个函数,在这里是#1号栈对应的函数)栈帧基地址(old_ebp)。可以看到,此时的函数栈帧中的ebp地址为
0xbffff088
。注意你自己运行的话地址可能与我的不一样。这个地址中保存的是上一个函数,其实就是1号栈的基地址。我们使用以下命令来查看该地址处的内容:x /1bx 0xbffff088 //显示结果为:
如上图,红框内的内容,就是#1号栈的基地址。当然我们可以验证:连续输入两个next命令,程序就会把返回值返回给#1号栈的函数调用。那么此时再输入info args,n就等于1,因为此时位于#1号栈中。然后在输入info registers命令查看#1号栈的寄存器值信息,如下:
如上图,#1号栈中的ebp值为0xbffff0b8,与我们上面在#0号栈中查询的值是一样的。这与函数栈帧的理论也是完全相符的。
上面的调试内容,非常简单,我们并没有调试什么bug,而是通过上述内容,学习一些调试的技巧。
3 总结
- 本节内容学习如何使用GDB查看函数的调用栈信息。
本文章参考狄泰软件学院相关课程 想学习的可以加狄泰软件学院群, 群聊号码:199546072
学习探讨加个人(可以免费帮忙下载CSDN资源):
qq:1126137994
微信:liu1126137994
学习交流资源分享qq群:962535112