基于源代码的代码覆盖率 (nightly 版)

2020 年 11 月 12 日 · Tyler Mandry 代表 The Compiler Team

nightly 编译器已支持基于源代码的代码覆盖率,我们希望你帮助测试它!

究竟什么是 基于源代码 的代码覆盖率?

你可能已经熟悉代码覆盖率,它显示了哪些代码行被执行。代码覆盖率通常应用于测试,以查明哪些代码实际得到了测试,哪些没有。

Nightly Rust 已经支持另一种源代码覆盖率,通常称为 gcov,它依赖于调试信息将 LLVM IR 映射到源代码行。然后在代码生成期间在 LLVM 后端添加插桩,以计算每行运行的次数。

然而,由于 LLVM 不完全知道 Rust 代码的结构,在 Rust 源代码和 LLVM IR 之间的转换中会丢失很多信息。行级粒度有时太粗糙,并且调试信息可能不可靠,尤其是在构建 release 模式时。结果是覆盖率报告只能近似显示实际执行的代码。

基于源代码的代码覆盖率插桩是由 Rust 编译器而非 LLVM 应用的。这种插桩更精确,因为它是在 MIR 中完成的,MIR 保存了原始 Rust 源代码与程序控制流图之间的映射。

这意味着像短路条件表达式、闭包和 match guard 这样的构造都能被精确计数。而且由于插桩计数器被注入为常规的 MIR 语句,编译器可以进一步优化程序而不会影响覆盖率结果。

Comparison of gcov and source-based coverage results

上图:gcov(左)和基于源代码的代码覆盖率(右)结果的比较。gcov 突出显示了用 ##### 标记的跳过行,而基于源代码的代码覆盖率突出显示了被跳过的精确代码区域。请注意,在第 30 行,一个布尔子表达式是短路的。这在基于源代码的代码覆盖率中得到了体现,但在 gcov 中没有。

这意味着基于源代码的代码覆盖率既高效又准确。LLVM 现有的覆盖率工具(llvm-profdatallvm-cov)可以生成覆盖率摘要和非常细粒度的代码区域,帮助你找到测试覆盖中的盲点。如何处理这些盲点则取决于你!

试用一下

这项实现的开发工作早在四月份就开始了,经过许多 PR 后,现在已经准备好让你试用了。你只需要一个最近的 nightly 版本以及一个读取覆盖率报告的工具。

请查阅此指南以开始。如果你发现了任何问题,请报告它们。这将是巨大的帮助!

最后,如果你试用后效果良好,我们也希望能听到你的反馈!请加入此更改的 Zulip stream 或在此 feature request 上评论。

致谢

所有实现工作均由 Rich Kadel 完成;感谢他所做的所有出色工作。还要感谢 Wesley Wiser 帮助审查,感谢 Bob Wilson 提供了他在 LLVM InstrProf 覆盖率 API 方面的经验,以及 eddyb 在选择基于 MIR 的方法方面提供的指导。