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 goodgit 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 goodgit 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