内核回归问题最怕描述成“新版本不行,旧版本可以”。这句话有方向,但还不是一个可执行的问题。真正能推动修复的是:哪个版本好,哪个版本坏,触发条件是什么,能否稳定复现,最终是哪一个提交引入。
git bisect 是定位回归的核心工具,但它不是魔法。bisect 结果是否可靠,取决于你给它的测试是否可靠。
先定义 good 和 bad
开始 bisect 前,必须明确两个点:
good: 已知没有问题的提交、tag 或版本
bad: 已知存在问题的提交、tag 或版本
例如:
git bisect start
git bisect bad v6.8
git bisect good v6.7
如果 good/bad 之间差异太大,bisect 时间会变长,干扰因素也会增加。能缩小范围就先缩小,例如先在厂商分支、stable tag 或合入窗口之间做粗定位。
现象必须可判定
bisect 需要一个二元判断:
- good
- bad
如果现象偶现,就要定义测试次数。例如:
每个提交启动 10 次,出现 1 次 panic 判定为 bad。
或者:
运行 stress 30 分钟,期间无 warning 判定为 good。
不要凭感觉判断。偶现问题如果测试不充分,bisect 很容易走错方向。
控制变量
bisect 期间要尽量保持不变:
- kernel config
- toolchain
- rootfs
- bootloader
- dtb
- firmware
- 测试脚本
- 硬件连接
如果每次测试都顺手改一点环境,最后得到的坏提交可能只是环境变化的产物。
设备树尤其要注意。有些提交需要配套 dts 变化,如果你在 bisect 过程中始终使用外部 dtb,可能会测出错误结果。要明确使用“内核构建出的 dtb”还是“固定 dtb”。
构建失败怎么处理
bisect 过程中某些提交可能编译不过。不要直接标 bad,而应该:
git bisect skip
skip 会降低定位精度,但比错误标记好。错误标记会把搜索路径带偏。
如果大范围编译失败,说明 good/bad 区间可能跨过了较大的构建系统或依赖变化。可以先缩小区间,或者临时应用最小构建修复补丁,但要记录清楚。
自动化测试脚本
如果现象能自动判断,最好写脚本:
#!/bin/sh
set -eu
make olddefconfig
make -j"$(nproc)"
./deploy-and-boot.sh
./run-test.sh
然后:
git bisect run ./bisect-test.sh
脚本返回值约定:
0: good1-127: bad125: skip
自动化的价值不只是省时间,更重要是减少人工判断波动。
bisect 出来的提交还不是结论
bisect 找到的 first bad commit 是强证据,但还不是完整结论。还需要验证:
- revert 这个提交是否修复问题
- 在 good 版本 cherry-pick 是否引入问题
- 这个提交的代码路径是否能解释现象
- 是否依赖后续提交才真正触发
- 是否是配置或设备树变化暴露了旧 bug
有时 bisect 指向的是“暴露问题的提交”,不是“根因提交”。例如某个提交打开了新功能,真正 bug 在旧驱动错误路径里。
合并提交和大补丁要小心
如果 first bad 是 merge commit,情况会复杂。需要判断问题来自哪个分支:
git show --summary <merge>
git log --oneline <parent1>..<merge>
git log --oneline <parent2>..<merge>
有时要在 merge 的两个父分支之间继续 bisect。
如果 first bad 是大补丁,也不要只把整块补丁当结论。要读 diff,找到真正改变行为的部分。一个补丁可能同时改了重构、配置、时序和错误路径。
回归报告要写清楚
给维护者的回归报告至少包括:
现象:
触发条件和错误日志。
good:
commit/tag
bad:
commit/tag
first bad:
commit id 和标题
验证:
revert 是否恢复,cherry-pick 是否复现
环境:
架构、板卡、config、dtb、toolchain
日志:
panic、warning、dmesg、测试脚本输出
这类报告比“这个提交有问题”更容易被维护者接受,也更容易进入修复流程。
回归修复要避免只修表象
回归修复常见两种:
- revert 或恢复旧行为
- 在新行为下修正根因
如果新提交本身方向正确,只是暴露了旧 bug,直接 revert 可能不是最佳方案。反过来,如果新提交违反了已有 ABI 或硬件约束,revert 可能是最稳妥的短期修复。
选择哪种方案,要看影响范围、stable 风险、维护者意图和是否有清晰根因。
一份 bisect 操作模板
git bisect start
git bisect bad <bad>
git bisect good <good>
# 手动测试每个提交
make -j"$(nproc)"
./deploy-and-test.sh
git bisect good # 或 git bisect bad
# 完成后
git bisect log > bisect.log
git bisect reset
保留 bisect.log 很重要。它能让别人复查你的路径,也能在误判后重新 replay。
结论
bisect 是工程流程,不只是 Git 命令。可靠的 bisect 需要稳定复现、明确判定、控制变量、记录日志,以及对 first bad commit 的二次验证。
真正好的回归定位,不只是找到一个提交,而是能解释为什么这个提交让问题出现,并给出可被维护者信任的证据。
评论