LLVM 代码二分查找¶
简介¶
git bisect
是一个用于查找导致错误的修订版本的实用工具。
本文档介绍如何使用 git bisect
。特别是,虽然 LLVM 拥有一个主要线性的历史记录,但它也有一些合并提交添加了项目——这些合并提交合并了这些项目的线性历史记录。因此,LLVM 代码库具有多个根:一个“普通”根,以及每个在树外开发然后后来合并的顶级项目的根。截至 2020 年初,唯一合并的项目是 MLIR,但 flang 可能会很快以类似的方式合并。
基本操作¶
请参阅 https://git.js.cn/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
并调整脚本,直到它工作,然后根据脚本的结果手动运行一次 git bisect good
或 git bisect bad
(检查脚本运行后的 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
列出了两次,因为 git 范围会排除左侧列出的修订版本,因此需要显式忽略它。)