除錯與工具

strace 與 ltrace 追蹤工具

/ strace -> ESS-trace; ltrace -> ELL-trace /

有時候程式在它與外部世界的邊界上出問題:它找不到一個檔案、卡在等網路、帶著一個難解的錯誤結束,而你自己的程式碼看起來沒問題。你會想偷聽程式與作業系統的每一段對話——它開的每個檔、它讀的每個位元組、它發的每個請求——而不必碰它的原始碼。strace 正是做這件事:它坐在你的程式與核心之間,把你程式發出的每一個系統呼叫連同它的引數與結果都印出來。

具體來說,你執行「strace ./program」,strace 在核心的追蹤機制(ptrace)下啟動你的程式,然後每個系統呼叫印一行:呼叫名稱、它的引數,以及回傳值或錯誤。你會看到像「openat(AT_FDCWD, "config.txt", O_RDONLY) = -1 ENOENT (No such file or directory)」這樣的東西——立刻揭露程式到錯誤的地方找 config.txt、而開檔失敗了。因為程式與作業系統的每一次互動——讀檔、透過 mmap 配置記憶體、網路、衍生行程——都經過系統呼叫,strace 顯示的是程式對這個世界的真實作用。它的姐妹工具 ltrace 在上一層做類比的事:它追蹤對共享函式庫的呼叫(像 libc 的 malloc()、strlen() 或 printf())而非對核心的呼叫,所以你看到的是程式與它函式庫的對話。

為何重要:對邊界臭蟲而言 strace 無可匹敵——「為什麼它找不到那個檔?」、「是哪個系統呼叫回傳了那個錯誤?」、「它是不是真的卡在 read() 永遠等下去?」——因為它不必重新編譯、不需要原始碼、也不需要除錯資訊;它對任何執行檔都管用。它對理解一個你不熟悉的程式究竟做了什麼也很棒。誠實的提醒:它顯示的是系統呼叫介面、不是你程式的內部邏輯,所以它告訴你程式向作業系統要了什麼、而不是為什麼;追蹤會加上明顯的額外負擔、並可能擾動時序;在一個繁忙的程式上輸出像消防水管,所以你要過濾(strace -e trace=open,read)。ltrace 在現代執行檔上比較不穩健(某些連結方式會讓它失效),所以 strace 是比較可靠的主力。

$ strace -e trace=openat ./app 對一個「莫名其妙讀不到它設定檔」的程式,印出 openat(AT_FDCWD, "config.txt", O_RDONLY) = -1 ENOENT (No such file or directory) ——顯示它在目前目錄找、而不是在 /etc,所以臭蟲是路徑錯了、不是解析錯誤。

strace 揭露了失敗的 openat() 與它的 ENOENT 錯誤——檔案路徑錯了。

strace 顯示的是核心邊界(系統呼叫)、不是你的內部邏輯,而且它加上的額外負擔會擾動時序——所以它告訴你程式要作業系統做什麼、而不是你的程式碼為什麼這麼要求。ltrace 改為追蹤函式庫呼叫,但在現代、完全重新連結過的執行檔上比較不可靠。

又稱
system call tracerlibrary call tracer系統呼叫追蹤器函式庫呼叫追蹤器