除錯與工具

除錯器(debugger,如 gdb / lldb)

/ gdb -> GEE-dee-bee; lldb -> ell-ell-DEE-bee /

想像你在看一部電影,只是遙控器握在你手上:你可以在任何一格畫面暫停、一格一格往前走、把注意力倒帶回較早的場景,還能把畫面凍住、去讀那些以正常速度永遠看不到的細節。除錯器就是執行中程式的那支遙控器。一般情況下程式每秒飛快跑過數百萬條指令,你只看得到最終結果;除錯器讓你在執行途中把它停下來、看清楚它正好在哪裡、檢查每一個變數,並讓它以受控的微小步伐往前走。

具體來說,除錯器是一個獨立的程式,會在你的程式執行時取得對它的控制權。它借助作業系統的協助(在 Linux 上是 ptrace 機制)來暫停目標、讀寫它的記憶體與 CPU 暫存器、再讓它繼續。你對除錯器下達像「跑到第 42 行時停下來」(中斷點)、「現在執行下一行」(單步執行)或「給我看 x 現在裝著什麼」(印出變數)這樣的命令。gdb 是 Linux 上行之有年、常見的 GNU 除錯器;lldb 是 LLVM 專案的除錯器,在 macOS 與搭配 Clang 時很常見。它們命令的拼法不同,但做的是同一件事:gdb 用 break、run、next、print 這類命令;lldb 用 breakpoint set、run、next、print。為了讓這一切可讀,你要在編譯程式時帶上除錯資訊,除錯器才能把原始的位址對應回你的原始碼行號與變數名稱。

為何重要:除錯器把「程式在某處當掉了」變成「程式停在這裡,帶著這些確切的值」,這正是用猜的與真的知道之間的差別。當一個臭蟲難以靠加印出來的敘述去逼近時——深藏在函式庫裡、出現在你無法預測的當機中、或在你不容易重新編譯的程式碼裡——除錯器特別好用。誠實的侷限:除錯器會把時間暫停,這可能掩蓋那些只在全速或特定時序下才出現的臭蟲(一個 heisenbug);而在重度最佳化的建構上,對應回原始碼會變得模糊——所以除錯版建構、或至少帶上除錯資訊,會讓除錯器有用得多。

$ gcc -g -O0 buggy.c -o buggy 接著 $ gdb ./buggy 然後在 gdb 裡: break main、run、next、print x。gdb 會停在 main,一次跑一行,並在每一步給你看 x 的值。

用 -g 編譯、在 gdb 下啟動、設一個中斷點、單步執行、檢查變數。

除錯器不會替你修臭蟲,也很少自己就指出根本成因——它給你的是精確的觀察,你仍得去推理這些觀察的意義。在除錯器下執行也可能稍微改變時序,所以它對一個只在全速時才出現的競爭未必忠實。

又稱
interactive debuggerGNU Debugger (gdb)LLDB互動式除錯器