除錯與工具

單步執行程式(stepping)

一旦除錯器在某個中斷點把你的程式暫停下來,你就需要一種方式,能一點一點把它往前推、看看會發生什麼——就像把一份食譜一個指令一個指令地往前走、每走一步都檢查一下碗裡的東西。單步執行正是這種受控的前進。你不再讓程式自由地跑,而是一次移動一個原始碼行(或一個函式呼叫),在每次移動之間檢查變數,這樣你就能以慢動作看著臭蟲一步步成形。

有四個動作你會不斷用到,而其中兩個之間的差別是經典的初學者陷阱。「step」(gdb:step)執行下一個原始碼行,如果那一行呼叫了一個函式,它會走進那個函式,讓你看著它執行。「next」(gdb:next)也執行下一行,但如果那一行呼叫了一個函式,它會把整個呼叫跑到結束、停在接下來那一行——它跨過了那個呼叫。所以 step 是潛入;next 是跳過。「finish」(gdb:finish)說的是「我人在一個函式裡面,把剩下的部分跑完、在它返回時停下來」,當你不小心走進了某個無聊的東西時很方便。「continue」(gdb:continue)完全放棄單步、讓程式以全速跑到下一個中斷點或結束為止。lldb 用同樣的字:step、next、finish、continue。

為何重要:單步執行是你把一個靜態的猜想轉成對因果的即時觀察的方式——你真的看著哪個分支被走、哪個值改變、執行實際去了哪裡、相對於你以為它去了哪裡。要有的紀律是:對你信任的函式跨過去(next),只對你懷疑的函式潛進去(step),否則你會把一輩子花在逐指令走過函式庫的程式碼。也記住那個誠實的侷限:單步執行以人類的速度移動,所以它無法重現像競爭這種依賴時序的臭蟲;而在最佳化的建構上,原始碼行可能被重排或合併,使得單步執行令人困惑地到處跳。

停在一個呼叫 result = compute(data); 上,在 gdb 裡: next 把整個 compute() 呼叫跑完、停在它之後那一行,把它當成黑盒子。 step 則進入 compute(),讓你一行一行地看它。 finish (進去之後)把 compute() 跑到它返回、並顯示回傳的值。

next 跨過一個呼叫;step 走進它;finish 把目前的函式跑到它返回。

經典的混淆是 step 與 next:step 會降進每一個被呼叫的函式(包括你根本不在乎的函式庫呼叫),而 next 一步就把它們跑完。預設用 next,只對你真正懷疑的程式碼用 step 潛進去。

又称
single-steppingstep / next / continue / finish逐步執行逐行執行