Call Stack 的結構#

程式執行時,call stack(或簡稱 stack)是用來管理函式間呼叫關係的資料結構。當一個函式呼叫另一個函式時,以下資料會被 push 到 stack 上:

  • 傳給被呼叫函式的引數(通常由右到左)
  • 如果是 method,一個指向對應物件的指標
  • 呼叫函式的返回地址
  • 被呼叫函式的 local 變數

在某些 CPU 架構(如 ARM、PowerPC、SPARC)上,引數和返回地址可能存在 CPU register 中而非 stack 上,只有 register 不夠用時才會使用 stack。

查看 Stack#

所有 debugger 都提供查看 stack 的方式:

  • EclipseWindow - Show View - Debug
  • Visual StudioDebug - Windows - Call StackAlt-7
  • gdbwhere 指令

Stack 的顯示中,每一行代表一次函式呼叫,包含檔案/類別、函式名稱、引數、最後執行的行號。最上面是目前正在執行的函式,最下面是程式的入口點(通常是 main)。

從 Stack Trace 讀取線索#

不同的 stack trace 模式透露不同的問題:

Stack 頂端是不認識的函式#

程式正在執行第三方程式碼(如 GUI framework 的底層互動、嵌入式 SQL 資料庫的處理)。此時往下掃描 stack,找到你的程式呼叫第三方程式碼的位置

如果函式只顯示記憶體位址而非名稱,表示該程式碼沒有 debug 資訊(見 Item 28)。

Stack 中全是第三方函式#

程式可能透過 callback 與 framework 互動。你需要在自己的程式碼中加入 breakpoint,才有機會除錯自己的邏輯。

Stack 異常淺且函式不可辨識#

很可能 stack 已被 stray pointer 破壞。需要從更早的執行點開始 step through,找到 stack 被破壞的位置。

Stack 中同一個函式重複出現多次#

可能是 recursive code 中的 bug,導致無窮遞迴。

在 Stack Frames 之間移動#

在圖形化 debugger 中,點擊任一 stack line 就能切換到對應的 stack frame,查看該函式的 local 變數和引數。

在 gdb 中:

(gdb) frame n     # 切換到第 n 個 stack frame
(gdb) up          # 往呼叫者方向移動一層
(gdb) down        # 往被呼叫者方向移動一層

重點回顧#

  • 查看程式的 call stack 是理解程式狀態的核心手段
  • 異常的 stack trace(過淺、全是不認識的函式、重複函式)往往是程式碼問題的重要線索
  • 善用 stack frame 導覽來檢查各層函式的 local 變數與引數