一起talk GDB吧(第四回:GDB呼叫棧除錯)
各位看官們,大家好,上一回中我們說的是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的內容,今天咱們就說到這裡。欲知後事如何,且聽下回分解!