核心測試指南¶
有許多不同的工具用於測試 Linux 核心,因此瞭解何時使用它們可能是一個挑戰。本文件概述了它們的區別以及它們如何協同工作。
編寫和執行測試¶
大部分核心測試是使用 kselftest 或 KUnit 框架編寫的。它們都提供了基礎設施來幫助簡化測試和測試組的執行,並提供了幫助編寫新測試的輔助工具。
如果您希望驗證核心的行為——特別是核心的特定部分——那麼您將需要使用 KUnit 或 kselftest。
KUnit 與 kselftest 的區別¶
KUnit (KUnit - Linux 核心單元測試) 是一個完全在核心內的系統,用於“白盒”測試:由於測試程式碼是核心的一部分,它可以訪問不向使用者空間公開的內部結構和函式。
因此,KUnit 測試最適合針對核心中小的、自包含的部分編寫,這些部分可以獨立測試。這與“單元”測試的概念非常吻合。
例如,KUnit 測試可能會測試單個核心函式(甚至是一個函式中的單個程式碼路徑,如錯誤處理情況),而不是整個功能。
這也使得 KUnit 測試構建和執行速度非常快,允許它們作為開發過程的一部分頻繁執行。
有一份 KUnit 測試風格指南,可能在 測試風格和命名法 中提供進一步的提示。
另一方面,kselftest (Linux 核心自測) 大部分在使用者空間中實現,測試是普通的使用者空間指令碼或程式。
這使得編寫更復雜的測試,或者需要更多地操作整個系統狀態(例如,生成程序等)的測試變得更容易。然而,無法直接從 kselftest 呼叫核心函式。這意味著只有透過某種方式(例如透過系統呼叫、裝置、檔案系統等)暴露給使用者空間的核心功能才能透過 kselftest 進行測試。為了解決這個問題,一些測試包含一個伴隨的核心模組,該模組暴露更多資訊或功能。但是,如果測試主要或完全在核心內部執行,KUnit 可能是更合適的工具。
因此,kselftest 非常適合對整個功能的測試,因為這些功能會向用戶空間公開一個介面,可以對其進行測試,但不會暴露實現細節。這與“系統”或“端到端”測試非常吻合。
例如,所有新的系統呼叫都應該附帶 kselftest 測試。
程式碼覆蓋率工具¶
Linux 核心支援兩種不同的程式碼覆蓋率測量工具。這些工具可以用來驗證測試是否正在執行特定的函式或程式碼行。這對於確定核心有多少部分正在被測試,以及查詢未被相應測試覆蓋的邊緣情況非常有用。
在 Linux 核心中使用 gcov 是 GCC 的覆蓋率測試工具,可以與核心一起使用以獲取全域性或每個模組的覆蓋率。與 KCOV 不同,它不記錄每個任務的覆蓋率。覆蓋率資料可以從 debugfs 讀取,並使用常用的 gcov 工具進行解釋。
KCOV:模糊測試的程式碼覆蓋率 是一種可以內建到核心中的功能,允許在每個任務級別捕獲覆蓋率。因此,它對於模糊測試和其他在例如單個系統呼叫期間執行的程式碼資訊有用的情況非常有用。
動態分析工具¶
核心還支援多種動態分析工具,這些工具試圖在執行中的核心中發生問題時檢測出特定類別的問題。這些工具通常各自查詢不同類別的 bug,例如無效的記憶體訪問、資料競爭等併發問題,或其他未定義行為如整數溢位。
下面列出了一些此類工具:
kmemleak 檢測可能的記憶體洩漏。參見 核心記憶體洩漏檢測器
KASAN 檢測無效記憶體訪問,例如越界和使用後釋放錯誤。參見 核心地址消毒器 (KASAN)
UBSAN 檢測 C 標準未定義的行為,如整數溢位。參見 未定義行為消毒器 - UBSAN
KCSAN 檢測資料競爭。參見 核心併發消毒器 (KCSAN)
KFENCE 是一個低開銷的記憶體問題檢測器,它比 KASAN 快得多,可以在生產環境中使用。參見 核心電圍欄 (KFENCE)
lockdep 是一個鎖正確性驗證器。參見 執行時鎖正確性驗證器
執行時驗證 (RV) 支援檢查給定子系統的特定行為。參見 執行時驗證
核心中還有其他幾個除錯檢測工具,其中許多可以在 lib/Kconfig.debug 中找到。
這些工具傾向於整體測試核心,並且不像 kselftest 或 KUnit 測試那樣“透過”。它們可以與 KUnit 或 kselftest 結合使用,方法是在啟用這些工具的核心上執行測試:這樣您就可以確保在測試期間不會發生這些錯誤。
其中一些工具與 KUnit 或 kselftest 整合,如果檢測到問題,將自動使測試失敗。
靜態分析工具¶
除了測試執行中的核心,還可以使用**靜態分析**工具直接分析核心原始碼(**在編譯時**)。核心中常用的工具允許檢查整個原始碼樹或其中特定檔案。它們使得在開發過程中更容易檢測和修復問題。
Sparse 可以透過執行型別檢查、鎖檢查、值範圍檢查,以及在檢查程式碼時報告各種錯誤和警告來幫助測試核心。有關如何使用它的詳細資訊,請參閱 Sparse 文件頁面。
Smatch 擴充套件了 Sparse,並提供了額外的檢查,用於檢測程式設計邏輯錯誤,例如 switch 語句中缺少 break、錯誤檢查中未使用的返回值、在錯誤路徑的返回中忘記設定錯誤程式碼等。Smatch 還針對更嚴重的問題進行測試,例如整數溢位、空指標解引用和記憶體洩漏。請參閱專案頁面:http://smatch.sourceforge.net/。
Coccinelle 是我們可用的另一個靜態分析器。Coccinelle 通常用於輔助原始碼的重構和附帶演進,但它也可以幫助避免常見程式碼模式中發生的某些錯誤。可用的測試型別包括 API 測試、核心迭代器正確使用測試、釋放操作健全性檢查、鎖定行為分析以及其他已知有助於保持核心使用一致性的測試。有關詳細資訊,請參閱 Coccinelle 文件頁面。
但請注意,靜態分析工具會產生**誤報**。在嘗試修復它們之前,需要仔細評估錯誤和警告。
何時使用 Sparse 和 Smatch¶
Sparse 進行型別檢查,例如驗證帶註釋的變數不會導致位元組序錯誤,檢測不正確使用 __user 指標的地方,以及分析符號初始化器的相容性。
Smatch 進行流分析,如果允許構建函式資料庫,它還會進行跨函式分析。Smatch 試圖回答諸如這個緩衝區在哪裡分配?它有多大?這個索引是否可以由使用者控制?這個變數是否比那個變數大?等問題。
在 Smatch 中編寫檢查通常比在 Sparse 中編寫檢查更容易。然而,Sparse 和 Smatch 的檢查之間存在一些重疊。
Smatch 和 Coccinelle 的強項¶
Coccinelle 可能是編寫檢查最簡單的工具。它在預處理器之前工作,因此使用 Coccinelle 檢查宏中的 bug 更容易。Coccinelle 還會為您建立補丁,這是其他工具所沒有的功能。
例如,使用 Coccinelle,您可以將 kmalloc(x * size, GFP_KERNEL) 大量轉換為 kmalloc_array(x, size, GFP_KERNEL),這非常有用。如果您只是建立了一個 Smatch 警告並試圖將轉換工作推給維護者,他們會感到惱火。您將不得不就每個警告是否真的會溢位進行爭論。
Coccinelle 不對變數值進行分析,這是 Smatch 的強項。另一方面,Coccinelle 允許您以簡單的方式做簡單的事情。