strace 偵錯:追蹤系統呼叫的技術 文章首圖

strace 偵錯:追蹤系統呼叫的技術

strace 偵錯:追蹤系統呼叫的技術

在 Linux 系統管理與開發過程中,我們經常會遇到程式「無緣無故」卡住、檔案讀取失敗或權限不足等神秘錯誤。當 grepls 或一般的日誌記錄無法提供足夠資訊時,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 是否正確開啟了目標檔案。

顯示時間戳記:-Tt

使用 -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 一探究竟,你可能會發現問題的真相就在那些系統呼叫的回傳值中。