二分查找 LLVM 代码

简介

git bisect 是一个有用的工具,用于查找哪个版本引入了错误。

本文档介绍了如何使用 git bisect。 特别是,虽然 LLVM 大部分历史是线性的,但它有一些合并提交添加了项目——这些项目合并了这些项目的线性历史。 因此,LLVM 仓库有多个根:一个“正常”根,然后每个顶层项目都有一个根,这些项目是树外开发的,然后稍后合并。 截至 2020 年初,唯一这样合并的项目是 MLIR,但 flang 很可能也会以类似的方式合并。

基本操作

有关详细概述,请参阅 https://git-scm.cn/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 并调整脚本直到它工作,然后根据脚本的结果手动运行一次 git bisect goodgit 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 范围排除左侧列出的修订版本,因此需要显式忽略它。)

更多资源

https://git-scm.cn/book/en/v2/Git-Tools-Revision-Selection