LLVMコードのバイセクト¶
はじめに¶
git bisect
は、バグの原因となったリビジョンを見つけるのに便利なツールです。
このドキュメントでは、git bisect
の使い方について説明します。LLVMは主に線形な履歴を持っていますが、いくつかのプロジェクトを追加したマージコミットがあり、これらのプロジェクトの線形履歴をマージしています。その結果、LLVMリポジトリには複数のルートがあります。1つの「通常の」ルートと、ツリー外で開発されて後でマージされた各トップレベルプロジェクトのルートです。2020年初頭現在、このようなマージされたプロジェクトはMLIRのみですが、flangも同様の方法でまもなくマージされる可能性があります。
基本操作¶
良い概要については、https://git.dokyumento.jp/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
¶
1つのバイセクトステップでは、多くの場合、最初にclangをビルドし、次に新しくビルドされたclangで大きなコードベースをコンパイルする必要があります。これは時間がかかるため、完全に自動化できれば理想的です。問題を自動的に再現する実行スクリプトを作成すれば、git bisect run
がこれを実行できます。スクリプトの作成には10~20分かかりますが、ほとんどの場合、その価値があります。バイセクトの実行中は(このドキュメントの作成など)他の作業を行うことができます。
実行スクリプトの例を次に示します。これは、llvm-project
内にあり、CMakeがNinjaを使用するように構成された兄弟のllvm-build-project
ビルドディレクトリがあることを前提としています。現在のディレクトリには、trunkでclangをクラッシュさせるファイルrepro.c
がありますが、リビジョン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 good
またはgit bisect bad
を手動で1回実行し(スクリプトの実行後にecho $?
を確認)、その後にのみ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
は2回リストされています。gitの範囲は左側にリストされているリビジョンを除外するため、明示的に無視する必要があります。)
その他の情報源¶
https://git.dokyumento.jp/book/en/v2/Git-Tools-Revision-Selection