本文件說明:
- 如何在 Zircon 中使用 Clang Static Analyzer (CSA) 設定跨翻譯單位分析 (CTU);
- 由 Kareem Khazem 在實習期間完成的工作;以及
- 完成剩下的工作,才能讓 Zircon 完整支援 CTU。
在 Zircon 上設定並執行 CTU
摘要:下載 Clang 來源,並在編譯前套用數個非主要修補程式。在分析工具周圍執行我的包裝函式指令碼。下載 CodeChecker
工具;使用這項工具摘要分析結果,並啟動網路伺服器,以便使用網頁介面查看結果。
啟用 CTU 修補程式
您需要留意以下兩個修補程式集:
- Samsung 修補程式集,這是為 Clang 新增 AST 合併支援的龐大修補程式。主要由
lib/AST/ASTImporter.cpp
的新增內容組成。此外,tools/xtu-build/*
下也有一組 (原始,成效不彰) 的 CTU 分析工具。這個修補程式組以 Clang 舊有版本為基礎,而且這個大小和規模相當大,導致要把批發的批發到樹狀結構上 (ToT) 的 Clang 上非常困難。 - Ericsson 修補程式集內含一部分 Samsung 的 AST 合併工作,同時也新增了幾項可進行 CTU 分析的新工具 (
tools/xtu-build-new/*
和tools/scan-build-py/*
)。xtu-build-new 工具改善了 Samsung 的 xtu-build 工具,而且與上述工具略有不同。這個修補程式組比 Samsung One 還新,作者正在努力確保以 ToT 為基礎。
我們將與 Ericsson 的修補程式組修補 Clang,因為 AST 的合併工作基底已乾淨,我們也取得了新版分析工具。不過請注意,針對 Zircon 的 CTU 支援並不完整;在某些情況下,Samsung 修補程式組中的程式碼會提供必要功能 (詳情請見下文)。
建構支援 CTU 的 CSA 的步驟
- 照常下載及建構 Clang 和 LLVM。
- 在其他目錄中複製 Ericsson 的 Clang 分支,然後切換至 ctu-master 分支版本。
將這段指令碼下載至 Ericsson 的分支中並執行。程式應將一系列修補程式轉儲到修補程式目錄。我刻意只傾印 Ericsson 做出的變更到 1bb3636 (1bb3636) 後,在實習期間最新的修訂版本。
- 如果您想要查看 Ericsson 的最新變更內容,可以嘗試在指令碼中將 1bb3636 改為 HEAD。在指令碼中指定其他範圍,請務必略過將上游修訂版本合併至 ctu-master 分支版本的修訂版本。git log --graph 有助於判斷上游修訂版本與 Ericsson 的修訂版本。
git log --graph --decorate --date=relative --format=format:'%C(green)%h%C(yellow) %s%C(reset)%w(0,6,6)%C(bold green)\n%C(cyan)%G? %C(bold red)%aN%C(reset) %cr%C(reset)%w(0,0,0)\n%-D\n%C(reset)' --all
一次將產生的修補程式套用至上游 Clang (而非 Ericsson fork)。
for p in $(ls $PATCH_DIR/*.patch | sort -n); do git am < $p; done
套用下方列出的 Kareem Khazem 修補程式 (如果尚未送達)
重新建構上游 Clang 和 LLVM。
執行 CTU 分析
摘要:執行包裝函式指令碼。這會正常建構 Zircon,然後再次建構,而是傾印序列化的 AST (而非物件檔案),最後使用傾印的 AST 分析每個檔案,以達成 CTU。
CTU 的運作方式
由此向後述說故事:
非 CTU 靜態分析會分析每個 TU 的 AST;凡是對外部函式發出的函式呼叫均視為不透明。儘管如此,CTU 分析仍會設法將不透明函式呼叫節點「替換成」該函式實作的 AST。
因此,CTU 分析會照常開始分析 AST,但只要遇到函式呼叫節點,就會嘗試在 AST 中合併該函式的 AST。這需要事先將函式序列化到磁碟的 AST,以便分析器將 AST 重新載入記憶體。這個程式庫也仰賴支援 AST 合併,也就是針對 ASTImporter.cpp
的 Samsung 修補程式 (以及衍生自的 Ericsson 修補程式)。
為了將 AST 序列化到磁碟,我們需要模擬實際的建構程序。做法是在記錄編譯器叫用時實際執行 Zircon 的實際版本;這可讓我們「播放」叫用,但在編譯器旗標修改後會轉儲 AST 檔案,而不是物件檔案。
總結來說,這次轉寄:
- 使用 Clang 建構 Zircon,並且將建構程序納入 bear 等程式中,以記錄編譯器叫用內容,並產生 JSON 編譯資料庫。
- 重播相同的編譯步驟,但轉儲 AST 檔案而非物件檔案。這是 xtu-build.py 工具的功能。
- 照常執行靜態分析,但需要時可視需要為每個呼叫的函式反序列化 AST。這就是 xtu-analyze.py 工具在頂層執行的功能,也就是透過 Ericsson 團隊編寫的精簡掃描版本在 scan-build-py/libscanbuild 目錄叫用工具。
步驟請見下方所述的 Fuchsia 包裝函式。完成後,系統會以 Apple plist 格式列出完整報表目錄,其中包含回報錯誤的詳細資料。
Ericsson 的包裝函式指令碼
執行跨翻譯單元分析的工具組合有兩種:
tools/xtu-build-new
底下的工具是頂層指令碼。由於基礎分析工具會失敗 (例如 CSA 當機),我已修補 Ericsson 的分支版本中的xtu-analyze.py
,以便將分析器的輸出內容 (stdout/stderr,而非報告) 轉儲到檔案。輸出內容會根據分析器的傳回代碼出現在$OUT_DIR/{passes,fails}
,其中$OUT_DIR
是傳遞至xtu-analyze.py
中-o
引數的目錄。這些檔案特別實用的部分是開頭為analyze: DEBUG: exec command in
的第二行,由libscanbuild
工具 (下一個項目符號) 輸出。該指令在修改指令列經過長時間繁瑣的程序後,是實際叫用 CSA 的例子。因此,如果您要使用 gdb 對有問題的檔案執行 CSA,就必須編寫這個指令。tools/scan-build-py
底下的工具是鳥的巢狀工具,可納入 Clang 的實際叫用。他們必須負責修改指令列。我並不太熟悉,過去也不會幹擾他們。
Fuchsia 包裝函式指令碼
這個非常小的殼層指令碼會包裝 Ericsson xtu-build-new
包裝函式。如要完整分析 Zircon,請務必先清除,並指定正確的 Clang 版本路徑。然後在 zircon 目錄中:
ninja -t clean && ninja && ./run.sh
如要僅建構核心,請將 TARGET
指定為環境變數:
ninja -t clean && ninja clean && TARGET=./build-zircon-pc-x64/zircon.elf ./run.sh
此外,指令碼也需要將 clangify.py 放在已設定可執行位元的 zircon 目錄中。分析完成後會有 .result-xtu
目錄,其中包含:
- 一組 Apple plist 檔案,其中包含錯誤報告;
- 失敗目錄,其中包含傳回非零的分析工具叫用 std{out,err} ;
- 傳遞目錄,其中包含傳回 0 的分析工具叫用 std{out,err}。
查看分析結果
目前,剖析 plist 報表並透過網頁介面查看資料的唯一方法是使用在 Ericsson 開發的 CodeChecker 工具,用來理解程式碼及其他許多工作。CodeChecker 需要安裝大量依附元件。建議您使用 pip、npm 或其他任何依附元件來安裝,而不要使用 apt-get。簡單來說,在執行分析並將 plist 轉儲到 .result-xtu 後,您可以叫用 CodeChecker plist
來剖析 plist:
CodeChecker plist -d .result-xtu -n 2016-12-12T21:47_uniq_name -j 48
每次叫用 CodeChecker plist
時,-n
的引數都不得重複,因為代表單一剖析執行作業。此外,CodeChecker 也會抱怨。接著,執行 CodeChecker server
在 localhost:8001
上啟動網路伺服器,此伺服器會顯示先前所有剖析執行作業的報表。
取得協助
Samsung 修補程式組是由 Aleksei Sidorin 及其團隊所編寫。Aleksei 對 ASTImporter.cpp
和其他 AST 合併元素相當瞭解,而且很實用。他和 Sean Callanan 都很樂意檢查我的 AST 進口器修補程式。Aleksei 也在 2016 年的 LLVM 開發人員會議中,談論摘要型互通性分析相關講座。
Ericsson 修補程式組是由 Gábor Horváth 和他的團隊所撰寫。Gábor 提供了一些建議,協助您瞭解如何使用 xtu-build-new
工具執行 CTU 分析。
我 (Kareem Khazem) 也很樂意提供相關協助。
LLVM irc 管道也很實用。
Zircon 專屬分析
上游 Clang 都很願意收到 Zircon 專用 Clang 檢查工具的修補程式。MutexInInterruptContext 檢查工具是其中之一 (從 Farid Molazem Tabrizi 寫入的 LLVM 傳遞),以及 SpinLockChecker 和 MutexChecker。Clang 調查的潛在審查人員包括 Devin Coughlin (來自 Apple)、Artem Dergachev (位於 Samsung 的 Aleksei Sidorin 團隊) 和 Anna Zaks (同樣在 Apple 工作)。
這些檢查工具通常可以「選擇加入」,也就是說,您必須先將標記傳送至分析器,才能啟用這類檢查工具:例如 -analyzer-checker=optin.zircon.MutexInInterruptContext
。
如果這些修補程式未出現在 Clang 中,請自行套用。如要運用這些函式與 Ericsson 包裝函式指令碼分析 Zircon,您必須在檔案結尾的 xtu-analyze.py
叫用中新增 -e optin.zircon.MutexInInterruptContext
選項,修改 Fuchsia 包裝函式指令碼。MutexInInterruptContext
的修補程式包含測試套件,可做為分析功能的示例。
Zircon 中 CTU 支援的進度
AST 匯入工具中已修正的問題
絕大多數的 Zircon 檔案上,上游 CSA 都會當機。本節說明 Kareem Khazem 的一些問題及修正方法。
不支援的 AST 節點
Clang Static 分析工具無法匯入許多 Zircon 程式碼,因為不支援匯入特定種類的 AST 節點。以下列出支援這些節點的修補程式:
原子類型 | 已將修補程式合併至上游 |
---|---|
CXXDependentScopeMemberExpr |
https://reviews.llvm.org/D26904 |
UnresolvedLookupExpr |
https://reviews.llvm.org/D27033 |
DependentSizedArray |
|
CXXUnresolvedConstructExpr |
|
UsingDecl |
https://reviews.llvm.org/D27181 |
UsingShadowDecl |
https://reviews.llvm.org/D27181 |
FunctionTemplateDecl |
https://reviews.llvm.org/D26904 |
一般來說,實作新節點類型的支援時,必須在 ASTImporter.cpp
中實作 VisitNode
函式,以及單元測試和功能測試;上述的修補程式含有範例。目前仍有許多不支援的 AST 節點;請 g 代表 error: cannot import unsupported AST node
的分析工具輸出目錄。
Ericsson 修補程式集只包含 Samsung 修補程式集中的部分 ASTImporter
程式碼。在某些情況下,您可以直接從 Samsung 修補程式集取得不支援的節點的 Visit
函式。不過,Samsung 修補程式集不含任何測試,因此在對該節點的上游支援之前,仍需編寫測試。
Segfaults 全球集數
ASTImporter.cpp
中的許多程式碼容易發生錯誤。有時候,Aleksei 都有私人修補程式 (例如這個問題),因此值得 (a-sid) 快速在 IRC 上快速查看。我的偵錯策略是檢查 second 字串的包裝函式輸出內容 (開頭為 analyze: DEBUG: exec command in
,後方接上分析器的實際指令列),然後透過 gdb 執行該指令列。通常只需幾小時就能追蹤嚴重事件的來源。
CTU 前後發現的錯誤
VFS 中是否有可能的錯誤?
這是 oldparent
的雙重釋放,會在 system/ulib/fs/vfs.c:vfs_rename
上宣告為未初始化。後兩行之後,系統會使用 oldparent
呼叫 vfs_walk
(相同檔案),做為第二個引數。只要進入 for 迴圈,然後在第一個迴圈上按下 return r
陳述式,即可從 vfs_walk
傳回,而無須指派給 oldparent
。如果 r
的值大於零,我們就會前往 else if
陳述式,並在 oldparent
上呼叫 vn_release
(該陳述式尚未初始化)。
執行緒可能發生錯誤?
此為「釋放後使用」的叢集。路徑如下:
kernel/kernel/thread.c:thread_detach_and_resume
- 呼叫
thread_detach(t)
- 傳回
thread_join(t, NULL, 0)
- 免費
t
,並於NO_ERROR
退回
- 免費
- 回程時間:
NO_ERROR
- 傳回
- 檢查錯誤為 1false1
- 呼叫已釋出的
thread_resume(t)
。- 接著,
thread_resume
就會存取t
的欄位。
- 接著,
- 呼叫
CTU 偽陽性
- CSA 無法解析透過函式指標呼叫的函式實作。這表示它無法假設函式的傳回值可能,也無法假設函式可能會對輸出參數造成任何影響。
分析器無法存取多個函式類別的實作。再次提醒,分析器無法得知這類函式輕觸輸出引數,因此會明確回報下列程式碼讀取垃圾值:
struct timeval tv; gettimeofday(&tv, NULL); printf("%d\n", tv.tv_usec); // [SPURIOUS REPORT] Access to // uninitialized variable ‘tv’
以下列舉一些容易受到這種精確度類型的函式:
- 系統呼叫 (例如
gettimeofday
) - 編譯器內建項目 (例如
memcpy
)
- 系統呼叫 (例如