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 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.js.cn/book/en/v2/Git-Tools-Revision-Selection