【听歌】GDB入门教程之查看函数调用堆栈
寫在前面:又到周末啦~上上周忍痛買了個雅馬哈聲卡和 AKG 話筒,這周六才正式打開試用了下,效果還不錯,我自己還挺享受的。不過這玩意兒太高端,還不會用 AI 調音。小伙伴們感覺下這首加了一點點電音效果的歌曲如何呢等我慢慢摸熟了,后面更文的頻率可能會高些吧,哈哈哈。?
除了使用 GDB 啟動調試、暫停/恢復程序執行和查看變量外,另外一個重要的調試方法便是查看程序的函數調用堆棧情況。
調用堆棧是當前函數之前的所有已調用函數的列表,每個函數及其變量都被分配了一個 "棧幀",使用 GDB 查看函數調用堆棧可清晰地看到各個函數的調用順序以及各函數的輸入形參值,是分析程序的執行流程和輸入依賴的重要手段。
為了便于講解,本文基于下述通過遞歸算法計算斐波拉契數列的簡單 demo 進行舉例說明。
#include<stdio.h> #include<stdlib.h>int fibonacci(int n) {if (n == 1 || n == 2){return 1;}int i = n; // only for showing local variable in GDBreturn fibonacci(n - 1) + fibonacci(n - 2); }int main() {int n = 10;int ret = 0;ret = fibonacci(n);printf("fibonacci(%d)=%d\n", n, ret);return 0; }1. backtrace 命令
要查看當前的堆棧信息,可使用 backtrace 命令 (縮寫形式 bt)。堆棧中的每個函數都被分配了一個編號,最近被調用的函數在 0 號幀中 (棧頂)。
backtrace n 表示只打印棧頂上 n 層的棧信息 (n 表示一個正整數);相反地,backtrace -n 表示只打印棧底下 n 層的棧信息。
以本文使用的斐波拉契數列計算 demo 為例,假設通過 b fibonacci if n==5 設置完條件斷點后啟動程序,當程序被暫停時,使用 backtrace 相關命令查看到的函數調用堆棧信息如下圖所示。
可以看出,當程序被暫停時,棧中共有 7 個棧幀,從棧頂到棧底分別被編號為 0 ~ 6。
2. frame 命令
如果想查看棧中某一層的信息,首先要做的是切換當前棧。這時候需用用到 frame 命令 (縮寫形式為 f)。
frame n 命令表示在 GDB 下切換到編號為 n 的棧幀 (n 表示一個正整數)。例如,frame 4 將切換到棧的第 5 層。
切換完后,如果想查看當前棧幀的編號、函數名、函數參數值、函數所在文件及行號、函數執行到的語句等信息,可直接使用 frame 命令,如下圖所示。
注:使用 frame 命令切換棧幀時,會自動打印出切換后的棧幀信息,如果切換時不想打印出任何信息,可以使用 select-frame 命令替代 frame 命令。
3. up/down 命令
除了使用 frame 命令切換棧幀外,還可以使用 up 和 down 命令。
down n 命令表示往棧頂方向下移 n 層 (n 表示一個正整數,默認值為 1)。相反地,up n 命令表示往棧底方向上移 n 層 (類似地,up 表示往棧底方向上移 1 層)。
注:在虛擬內存地址空間中,棧從高地址向低地址延伸 (即棧頂在下),故往棧頂方向移動是 down。
同樣地,up 和 down 命令都會打印出移動到的棧層的信息。如果不想讓 GDB 打印出信息,可以分別使用 up-silently 和 down-silently 代替之。
4. info 命令
在《GDB入門教程之查看變量》一文已經介紹過,使用 info 命令可以查看各種變量的值。
如果希望看到詳細的當前棧幀的信息,如函數地址、調用函數的地址、被調用函數的地址、當前函數由哪種編程語言編寫、函數參數地址及形參值、局部變量的地址等,可以使用 info frame 命令(縮寫形式 i f)。
此外,info args 命令可打印出當前函數的參數名及其形參值;info locals 命令可打印出當前函數中所有局部變量及其值;info catch 命令可打印出當前函數中的異常處理信息。
5.? 棧和棧幀
內存棧區 (stack) 由編譯器自動分配和釋放,用于存放函數的形參值、局部變量的值、函數返回地址等數據,其操作方式與數據結構中的棧一致,都是后進先出的原則。在虛擬內存地址空間中,棧從高地址向低地址延伸。
棧幀 (stack frame) 是編譯器用來實現函數調用的一種數據結構,是內存棧區的基本單元。內存棧空間上保持了 N 個棧幀的實體。
所有函數調用均發生在棧上,每個函數的每次調用,都有它自己獨立的一個棧幀。寄存器 ebp 指向當前棧幀的底部 (高地址),寄存器 esp 指向當前棧幀的頂部 (低地址)。
以本文使用的遞歸函數 fibonacci 調用為例,如下圖所示,通過 info f 命令可清晰地看到各個棧幀的地址以及由遞歸調用導致的棧幀切換和依賴關系 (類似圖中的?called by frame at 0x7fffffffe4a0, caller of frame at 0x7fffffffe420)。
擴展閱讀:?
1.??GDB入門教程之如何使用GDB啟動調試
2.??GDB入門教程之暫停程序
3.??GDB入門教程之查看變量
4.??GDB入門教程之恢復程序執行
下期預告:《GDB入門教程之多線程調試》。
總結
以上是生活随笔為你收集整理的【听歌】GDB入门教程之查看函数调用堆栈的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: WTM系列视频教程:初体验
- 下一篇: 研发协同平台持续集成实践