各位看官们,大家好,上一回中我们说的是GDB的断点调试功能,并且说了如何使用GDB进行断点调试。
这一回中,我们继续介绍GDB的调试功能:调用栈调试。当然了,我们也会介绍如何使用GDB进行调用栈
调试。闲话休提,言归正转。让我们一起talk GDB吧!
看官们,我们先说一下什么是调用栈。大家都知道,程序中经常使用各种各样的函数,有的是语言提供的
库函数,比如printf(),有的是我们自己定义的函数。各种函数之间会相互调用,有时候函数多了,我们很难
找出函数之间的调用关系,函数栈就是用来显示函数之间调用关系的。这们说大家可能觉得有点抽象,不
容易理解,我们举个例子来说明,例如程序中有以下代码。
void funA()
{
printf("A function is called \n");
}
void funB()
{
printf("B function is called \n");
funA();
}
void funC()
{
printf("C function is called \n");
funB();
}
void funD()
{
printf("D function is called \n");
funC();
}
int main()
{
printf("show the call stack of functions \n");
funD();
return 0;
}
这些函数的功能比较简单,只有一个输出语句,显示函数被调用。大家可以看到,程序中自己定义了四个
函数,它们分别是:funA,funB,funC,funD。(以后不用全名,简称ABCD)各个函数之间的调用关
系为:D->C->B->A。编译并且运行该函数时,可以得到以下结果:
show the call stack of functions
D function is called
C function is called
B function is called
A function is called
从程序的运行结果中也可以看到,各个函数之间的调用关系。如果把D比作栈底,那么每次调用函数就是
在进行入栈操作,D调用C就是把C入栈,依此类推,直到最后一个函数A。这种函数调用关系符合栈”先进
后出“的特点,因此我们形象地叫它为调用栈。此外,从程序的运行结果中也可以看出来ABCD函数的调用
关系从栈顶到栈底依次排列。
看官们在实际的程序中,函数的功能不会这么简单,函数的调用关系也不会这么简单。如果我们想了解函
数之间的调用关系,怎么办?不用担心,GDB提供了显示函数调用栈的功能。和单步调用一样,调用栈调
试也有专门的命令:backtrace(缩写为bt)。使用该命令,可以通过GDB打印出函数调用栈,我们还是举个
例子来说明,为了方便,还使用上面提到过的代码。
1.首先编译程序,并且加入调试信息:gcc -g file.c -o file
2.启动GDB进行调试:gdb file
3.在函数D处打一个断点:b funD。运行结果如下:Breakpoint 3, funA () at file.c:5
4.运行程序,遇到断点停止运行:run
5.查看函数调用栈:bt.这时显示的结果如下:
(gdb) bt //查看函数调用栈
#0 funA () at file.c:5
#1 0x08048448 in funB () at file.c:10
#2 0x08048461 in funC () at file.c:15
#3 0x0804847a in funD () at file.c:20
#4 0x08048496 in main () at file.c:27
通过运行的结果,我们可以看到,函数A位于栈底,MAIN函数位于栈顶。而且在每行最前面有编号。当然
了,编号从0开始,有点类似数组中元素的位置。
看官们,关于GDB的内容,今天咱们就说到这里。欲知后事如何,且听下回分解!