二分搜尋 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 將選擇附近的替代提交。

(要中止 bisect,請執行 git bisect reset,如果 git 抱怨無法重置,請執行通常的 git checkout -f main; git reset --hard origin/main 操作並重試)。

git bisect run

單個 bisect 步驟通常需要先建置 clang,然後使用剛建置的 clang 編譯大型程式碼庫。這可能需要很長時間,因此如果它可以完全自動發生,那就太好了。git bisect run 如果您編寫一個可以自動重現問題的執行腳本,它可以為您做到這一點。編寫腳本可能需要 10-20 分鐘,但幾乎總是值得的 – 您可以在 bisect 運行時做其他事情(例如編寫本文檔)。

這是一個範例執行腳本。它假設您在 llvm-project 中,並且您有一個同級的 llvm-build-project 建置目錄,您在其中配置 CMake 以使用 Ninja。您在目前目錄中建立了一個檔案 repro.c,它會使 clang 在 trunk 上崩潰,但在修訂版本 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 並調整腳本直到它工作,然後根據腳本的結果手動執行一次 git bisect goodgit bisect bad(檢查您的腳本運行後的 echo $?),然後才執行 git bisect run ./run.sh。不要忘記將您的執行腳本標記為可執行 – git bisect run 不會檢查這個,它只會假設執行腳本每次都失敗。

一旦您的執行腳本工作,請執行 git bisect run ./run.sh,幾個小時後您將知道哪個提交導致了回歸。

(這是一個非常簡單的執行腳本。通常,您希望使用剛建置的 clang 來建置不同的專案,然後在執行腳本中運行該專案的已建置可執行檔。)

跨多個根進行 Bisect

以下是 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