LLVM 程式碼二分搜尋法¶
簡介¶
git bisect
是一個用於查找導致錯誤的修訂版本的實用工具。
本文件描述如何使用 git bisect
。特別是,雖然 LLVM 有一個幾乎線性的歷史記錄,但它有一些合併提交添加了專案——這些合併提交合併了這些專案的線性歷史記錄。因此,LLVM 儲存庫有多個根目錄:一個“普通”根目錄,然後是每個在樹外開發並稍後合併的頂級專案的一個根目錄。截至 2020 年初,唯一這樣合併的專案是 MLIR,但 flang 可能很快會以類似的方式合併。
基本操作¶
有關詳細概述,請參閱 https://git.dev.org.tw/docs/git-bisect。總之
git bisect start git bisect bad main git bisect good f00ba
git 將簽出介於兩者之間的修訂版本。嘗試在該修訂版本中重現您的問題,並執行 git bisect good
或 git bisect bad
。
如果您無法在當前提交中重現(可能是建置損壞),請執行 git bisect skip
,git 將選擇附近的替代提交。
(要中止二分搜尋,請執行 git bisect reset
,如果 git 抱怨無法重置,請執行通常的 git checkout -f main; git reset --hard origin/main
操作,然後重試)。
git bisect run
¶
單個二分搜尋步驟通常需要先建置 clang,然後使用剛建置的 clang 編譯大型程式碼庫。這可能需要很長時間,所以如果它可以完全自動完成,那就太好了。如果您編寫一個自動重現問題的執行腳本,git bisect run
可以為您完成此操作。編寫腳本可能需要 10-20 分鐘,但幾乎總是值得的——您可以在二分搜尋運行時執行其他操作(例如編寫本文檔)。
這是一個執行腳本示例。它假設您在 llvm-project
中,並且您有一個同級目錄 llvm-build-project
建置目錄,您在其中配置 CMake 以使用 Ninja。您在當前目錄中有一個文件 repro.c
,它會使 clang 在主幹崩潰,但在修訂版本 f00ba
中可以正常工作。
# Build clang. If the build fails, `exit 125` causes this # revision to be skipped ninja -C ../llvm-build-project clang || exit 125 ../llvm-build-project/bin/clang repro.c
為了確保你的執行腳本能正常運作,建議你先手動執行 ./run.sh
,並調整腳本直到它能正常運作,然後根據腳本的結果(在腳本執行後檢查 echo $?
)手動執行一次 git bisect good
或 git bisect bad
,最後才執行 git bisect run ./run.sh
。別忘了將你的執行腳本標記為可執行檔 – git bisect run
不會檢查這個,它只會假設執行腳本每次都失敗。
一旦你的執行腳本能正常運作,執行 git bisect run ./run.sh
,幾個小時後你就會知道是哪個提交導致了回歸。
(這是一個非常簡單的執行腳本。通常,你會希望在執行腳本中使用剛建置好的 clang 來建置另一個專案,然後執行該專案的建置後可執行檔。)
跨多個根目錄進行二分搜尋¶
以下是 LLVM 歷史記錄目前的樣子
A-o-o-......-o-D-o-o-HEAD / B-o-...-o-C-
A
是 LLVM 中的第一個提交,97724f18c79c
。
B
是 MLIR 中的第一個提交,aed0d21a62db
。
D
是將 MLIR 合併到主要 LLVM 儲存庫的合併提交,0f0d0ed1c78f
。
C
是 MLIR 在被合併前的最後一個提交,0f0d0ed1c78f^2
。(^n
修飾詞會選取合併提交的第 n 個父提交。)
git bisect
會檢查所有父版本。由於 MLIR 的合併方式,在 C
或更早版本的每個版本中,*只有* mlir/
目錄存在,其他什麼都沒有。
截至 2020 年初,git bisect
沒有任何標記可以告訴它不要遞迴到所有可到達的提交。理想情況下,我們希望告訴它只追蹤 D
的第一個父提交。
最好的解決方法是將目錄列表傳遞給 git bisect
:如果你知道錯誤是由於 llvm、clang 或 compiler-rt 中的更改所致,請使用
git bisect start -- clang llvm compiler-rt
這樣一來,mlir
中的提交就不會被評估。
或者,git bisect skip aed0d21a6 aed0d21a6..0f0d0ed1c78f
會明確跳過該分支上的所有提交。它需要在快速的機器上執行 1.5 分鐘,並且會使 git bisect log
輸出無法閱讀。(aed0d21a6
被列出兩次是因為 git 範圍不包括左側列出的版本,因此需要明確忽略它。)
更多資源¶
https://git.dev.org.tw/book/en/v2/Git-Tools-Revision-Selection