strace 偵錯:追蹤系統呼叫的技術
在 Linux 系統管理與開發過程中,我們經常會遇到程式「無緣無故」卡住、檔案讀取失敗或權限不足等神秘錯誤。當 grep、ls 或一般的日誌記錄無法提供足夠資訊時,strace 便成了我們手中最強大的偵錯利器。它允許我們追蹤程式與核心(Kernel)之間的互動,透過檢視底層的系統呼叫(System Calls),快速定位問題根源。
本文将深入解析 strace 的用法、常用選項以及實際應用場景,幫助你掌握這項必備技能。
什麼是 strace?
strace 是一個動態追蹤工具,它利用 Linux 的 ptrace 機制,攔截並記錄程式執行的所有系統呼叫。系統呼叫是程式請求核心服務的方式,例如開啟檔案 (openat)、讀寫資料 (read/write)、建立程序 (clone) 或處理網路連線 (socket)。透過分析這些呼叫的參數與回傳值,我們能清楚地看到程式到底在做什麼,以及在哪裡發生了錯誤。
基本安裝與啟動
在 Ubuntu 22.04 或 Debian 12 上,strace 通常預設未安裝。你可以透過以下指令輕鬆安裝:
sudo apt update
sudo apt install strace
安裝完成後,最基礎的用法是在指令前加上 strace。例如,我們想追蹤 ls 指令在列出目錄時執行了哪些系統呼叫:
strace ls /tmp
執行後,你會看到大量輸出資訊。每一行代表一個系統呼叫,格式大致為:
openat(AT_FDCWD, "/tmp", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
getdents64(3, /* 3 entries */, 32768) = 80
close(3) = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=12345, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
exit_group(0) = ?
+++ exited with 0 +++
這顯示 ls 首先呼叫 openat 開啟 /tmp 目錄,接著用 fstat 取得檔案狀態,然後用 getdents64 讀取目錄內容,最後關閉檔案描述符並結束。
實戰應用:過濾與聚焦
直接執行 strace 會產生海量輸出,通常我們需要過濾資訊。以下是幾個最實用的選項:
1. 追蹤特定錯誤:-e trace=error
當程式失敗時,我們通常只關心哪些呼叫失敗了。使用 -e trace=error 可以僅顯示回傳值為 -1(代表錯誤)的系統呼叫。
假設我們嘗試讀取一個不存在的檔案:
strace -e trace=error cat /nonexistent_file.txt
輸出結果會精簡許多,通常會看到類似這樣的錯誤:
openat(AT_FDCWD, "/nonexistent_file.txt", O_RDONLY) = -1 ENOENT (No such file or directory)
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=12346, si_uid=1000, si_status=1, si_utime=0, si_stime=0} ---
+++ exited with 1 +++
這明確指出 openat 呼叫失敗,錯誤原因是 ENOENT(檔案不存在)。
2. 追蹤特定系統呼叫:-e trace=open,close,read,write
如果你只關心檔案存取行為,可以指定追蹤的系統呼叫名稱。這對於診斷檔案權限或路徑問題非常有效。
strace -e trace=openat,read,write cat /etc/passwd
這只會顯示與開啟、讀取和寫入檔案相關的呼叫,忽略其他如記憶體配置或訊號處理的雜訊。
3. 輸出到檔案:-o
當追蹤長時間運作的程式或產生大量輸出時,將結果導向檔案是更好的選擇:
strace -o trace.log -e trace=openat sleep 10
執行後,所有 openat 相關的呼叫都會被記錄在 trace.log 中,方便後續分析。
進階技巧:追蹤子程序與時間戳記
追蹤子程序:-f
許多程式會產生子程序(child processes)。預設情況下,strace 只追蹤主程序。若需同時追蹤子程序,請使用 -f 選項。
例如,追蹤 find 指令在搜尋檔案時的行為:
strace -f -e trace=openat find /tmp -name "*.log" 2>&1 | grep "log"
這能幫助你確認 find 是否正確開啟了目標檔案。
顯示時間戳記:-T 和 t
使用 -T 可以顯示每個系統呼叫所花費的時間(以秒為單位),有助於診斷效能瓶頸。
strace -T -e trace=openat cat /etc/passwd
輸出範例:
openat(AT_FDCWD, "/etc/passwd", O_RDONLY) = 3 <0.000042>
這裡顯示該呼叫僅花費了 0.000042 秒。若某個呼叫花費過長時間(例如超過 1 秒),可能表示磁碟 I/O 或網路延遲問題。
常見問題
Q1: 為什麼 strace 的輸出看不懂?
A: strace 輸出的是底層 C 語言風格的系統呼叫。若不熟悉,可善用 -e trace=error 聚焦錯誤,或使用 -e abbrev=none 顯示完整參數。此外,許多錯誤碼(如 ENOENT, EACCES)可直接對應到常見問題。
Q2: strace 會顯著降低程式執行速度嗎?
A: 是的。由於每個系統呼叫都需要核心與使用者空間之間的切換,strace 會帶來顯著的效能開銷(通常慢 10-100 倍)。因此,僅在偵錯階段使用,並盡量縮小追蹤範圍(使用 -e 過濾)。
小結
strace 是 Linux 系統管理員與開發者不可或缺的工具。它像是一台 X 光機,讓我們能看透程式與核心互動的黑盒。透過掌握 -e 過濾選項、-f 追蹤子程序以及 -T 時間分析,你可以快速診斷檔案存取、權限、網路連線等常見問題。下次當程式出現神秘錯誤時,不妨試著用 strace 一探究竟,你可能會發現問題的真相就在那些系統呼叫的回傳值中。