基于源代码的代码覆盖率在 nightly 版本中

2020 年 11 月 12 日 · Tyler Mandry 代表 编译器团队

基于源代码的代码覆盖率支持已在 nightly 编译器中落地,我们希望您能帮助测试它!

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

您可能已经熟悉代码覆盖率,它会显示哪些代码行被执行。代码覆盖率通常应用于测试,以找出哪些代码实际上正在被测试,而哪些代码没有。

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

然而,由于 LLVM 不确切知道 Rust 代码的结构方式,因此在 Rust 源代码和 LLVM IR 之间的转换过程中会丢失很多信息。行级别的粒度有时太粗,而且调试信息可能不可靠,尤其是在发布模式下构建时。结果是覆盖率报告仅显示实际执行的代码的近似值。

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

这意味着诸如短路条件、闭包和匹配守卫之类的东西都被精确计数。并且由于插桩计数器是作为常规 MIR 语句注入的,因此编译器可以进一步优化程序而不会影响覆盖率结果。

Comparison of gcov and source-based coverage results

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

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

尝试一下

该实现的开发工作早在 4 月就开始了,并且在许多 PR 之后,它已准备好供您尝试。您只需要一个最新的 nightly 版本和一个读取覆盖率报告的工具。

请查看此指南以开始使用。如果您发现任何问题,请报告它们。这将有很大帮助!

最后,如果您尝试使用它并且效果良好,我们也想听到您的反馈!请访问此更改的 Zulip 流或在 功能请求上发表评论。

致谢

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