核心記憶體對映I/O追蹤¶
主頁和可選使用者空間工具的連結
MMIO tracing 最初由 Intel 在 2003 年左右為他們的故障注入測試工具開發。在 2006 年 12 月 - 2007 年 1 月,Jeff Muizelaar 使用 Intel 的程式碼建立了一個工具,用於跟蹤以 Nouveau 專案為目標的 MMIO 訪問。從那時起,許多人都做出了貢獻。
Mmiotrace 是為逆向工程任何記憶體對映 IO 裝置而構建的,Nouveau 專案是第一個真正的使用者。僅支援 x86 和 x86_64 架構。
樹外 mmiotrace 最初由 Pekka Paalanen <pq@iki.fi> 修改,以便包含在主線和 ftrace 框架中。
準備工作¶
Mmiotrace 功能透過 CONFIG_MMIOTRACE 選項編譯。 預設情況下,跟蹤被停用,因此將其設定為 yes 是安全的。 支援 SMP 系統,但如果多個 CPU 線上,則跟蹤不可靠且可能錯過事件,因此 mmiotrace 在執行時啟用期間會使除一個 CPU 之外的所有 CPU 離線。 您可以手動重新啟用 CPU,但已被警告,無法自動檢測您是否由於 CPU 競爭而丟失事件。
使用快速參考¶
$ mount -t debugfs debugfs /sys/kernel/debug
$ echo mmiotrace > /sys/kernel/tracing/current_tracer
$ cat /sys/kernel/tracing/trace_pipe > mydump.txt &
Start X or whatever.
$ echo "X is up" > /sys/kernel/tracing/trace_marker
$ echo nop > /sys/kernel/tracing/current_tracer
Check for lost events.
使用¶
確保 debugfs 已掛載到 /sys/kernel/debug。 如果沒有(需要 root 許可權)
$ mount -t debugfs debugfs /sys/kernel/debug
檢查要跟蹤的驅動程式是否未載入。
啟用 mmiotrace(需要 root 許可權)
$ echo mmiotrace > /sys/kernel/tracing/current_tracer
開始儲存跟蹤
$ cat /sys/kernel/tracing/trace_pipe > mydump.txt &
“cat”程序應在後臺保持執行(睡眠)。
載入要跟蹤的驅動程式並使用它。 Mmiotrace 僅捕獲在 mmiotrace 啟用時 ioremapped 的區域的 MMIO 訪問。
在跟蹤期間,您可以透過 $ echo “X is up” > /sys/kernel/tracing/trace_marker 將註釋(標記)放入跟蹤中。這使得更容易檢視(巨大)跟蹤的哪個部分對應於哪個操作。建議放置描述性的標記來描述你所做的事情。
關閉 mmiotrace(需要 root 許可權)
$ echo nop > /sys/kernel/tracing/current_tracer
“cat”程序退出。 如果沒有,透過發出“fg”命令並按 ctrl+c 來殺死它。
檢查 mmiotrace 是否由於緩衝區已滿而丟失事件。 可以是
$ grep -i lost mydump.txt
告訴你準確地丟失了多少事件,或者使用
$ dmesg
檢視核心日誌並查詢“mmiotrace has lost events”警告。 如果事件丟失,則跟蹤不完整。 你應該擴大緩衝區並重試。 透過首先檢視當前緩衝區有多大來擴大緩衝區
$ cat /sys/kernel/tracing/buffer_size_kb
給你一個數字。 大約將此數字加倍並寫回,例如
$ echo 128000 > /sys/kernel/tracing/buffer_size_kb
然後從頭開始。
如果您正在為驅動程式專案進行跟蹤,例如 Nouveau,您還應該在傳送結果之前執行以下操作
$ lspci -vvv > lspci.txt
$ dmesg > dmesg.txt
$ tar zcf pciid-nick-mmiotrace.tar.gz mydump.txt lspci.txt dmesg.txt
然後傳送 .tar.gz 檔案。 跟蹤會大大壓縮。 將“pciid”和“nick”替換為您正在調查的硬體的 PCI ID 或型號名稱以及您的暱稱。
Mmiotrace 如何工作¶
透過呼叫 ioremap_*() 函式之一來對映 PCI 匯流排的地址來獲得對硬體 IO 記憶體的訪問。 Mmiotrace 被掛鉤到 __ioremap() 函式中,並在每次建立對映時被呼叫。 對映是一個記錄到跟蹤日誌中的事件。 請注意,ISA 範圍對映不會被捕獲,因為對映始終存在並直接返回。
MMIO 訪問透過頁面錯誤記錄。 就在 __ioremap() 返回之前,對映的頁面被標記為不存在。 對頁面的任何訪問都會導致錯誤。 頁面錯誤處理程式呼叫 mmiotrace 來處理錯誤。 Mmiotrace 將頁面標記為存在,設定 TF 標誌以實現單步執行並退出錯誤處理程式。 執行出錯的指令,並輸入除錯陷阱。 在這裡,mmiotrace 再次將頁面標記為不存在。 指令被解碼以獲取操作型別(讀/寫)、資料寬度和讀取或寫入的值。 這些儲存到跟蹤日誌中。
在頁面錯誤處理程式中設定頁面存在在 SMP 機器上存在競爭條件。 在單步執行期間,其他 CPU 可能會在該頁面上自由執行,並且可能會在沒有通知的情況下丟失事件。 不鼓勵在跟蹤期間重新啟用其他 CPU。
跟蹤日誌格式¶
原始日誌是文字,易於使用 grep 和 awk 等工具進行過濾。 一個記錄是日誌中的一行。 記錄以關鍵字開頭,後跟依賴於關鍵字的引數。 引數用空格分隔,或持續到行尾。 版本 20070824 的格式如下
解釋 關鍵字 空格分隔的引數¶
讀取事件 R 寬度、時間戳、對映 ID、物理地址、值、PC、PID 寫事件 W 寬度、時間戳、對映 ID、物理地址、值、PC、PID ioremap 事件 MAP 時間戳、對映 ID、物理地址、虛擬地址、長度、PC、PID iounmap 事件 UNMAP 時間戳、對映 ID、PC、PID 標記 MARK 時間戳、文字 版本 VERSION 字串“20070824” 讀取器資訊 LSPCI 來自 lspci -v 的一行 PCI 地址對映 PCIDEV 空格分隔的 /proc/bus/pci/devices 資料 未知 opcode UNKNOWN 時間戳、對映 ID、物理地址、資料、PC、PID
時間戳以秒為單位,帶小數。 物理地址是 PCI 匯流排地址,虛擬地址是核心虛擬地址。 寬度是以位元組為單位的資料寬度,值是資料值。 對映 ID 是一個任意 ID 號,用於標識在操作中使用的對映。 PC 是程式計數器,PID 是程序 ID。 如果未記錄,PC 為零。 PID 始終為零,因為尚不支援跟蹤源自使用者空間記憶體的 MMIO 訪問。
例如,以下 awk 過濾器將傳遞目標地址在 [0xfb73ce40, 0xfb800000] 範圍內的所有 32 位寫入
$ awk '/W 4 / { adr=strtonum($5); if (adr >= 0xfb73ce40 &&
adr < 0xfb800000) print; }'
開發人員工具¶
- 使用者空間工具包括以下實用程式
用硬體暫存器名稱替換數字地址和值
重放 MMIO 日誌,即重新執行記錄的寫入