【问题标题】:Why does GCOV claim this line is not covered?为什么 GCOV 声称这条线不包括在内?
【发布时间】:2021-12-29 01:58:09
【问题描述】:

我最近升级到了新版本的 gcc/g++/gcov,现在 gcov 的行为很奇怪。新版本声称某些代码行没有被旧版本覆盖。我设法将我的代码简化为这个最小的示例。

#include <memory>
using namespace std;
struct S {};

int main() {
    unique_ptr<S> s;
    s = make_unique<S>();
}

然后我使用g++ -O0 -Wall -Wextra -Werror --std=c++17 --coverage 编译此文件,运行生成的a.out,然后运行gcov

生成的.gcov 文件包含:

    -:    0:Runs:1
    -:    1:#include <memory>
    -:    2:using namespace std;
    -:    3:struct S {};
    -:    4:
    1:    5:int main() {
#####:    6:    unique_ptr<S> s;
    1:    7:    s = make_unique<S>();
    1:    8:}

这与旧版本的 gcov 不同,后者声称第 6 行被命中 2 次。

为什么 gcov 认为第 6 行没有被覆盖?我做错了吗?

我能够使用 gcc8、gcc9 和 gcc10 重现此行为。 gcc7 的行为符合预期。

比较 gcc7/8 的编译器资源管理器:https://godbolt.org/z/Te57s4WK8

【问题讨论】:

  • 代码和机器指令不存在 1 比 1 的相关性。如果你真的想在这个问题上挖洞,请将两者都加载到 Godbolt 中并查看程序集,看看到底有什么,或者没有,视情况而定可能是。
  • 纯粹出于好奇,我继续这样做了。在 Godbolt GCC 7 中,额外的 3 行汇编代码归属于第 6 行,而在 GCC8+ 中,它归属于第 7 行。这 3 行中的每一行都是相同的。
  • 我知道源代码行和机器代码之间没有直接关联。但这个结果似乎不一致。第 6 行没有关联的机器代码并且 gcov 应该报告 -,或者它确实有关联的机器代码并且 gcov 应该报告它已覆盖。 ##### 应该是不可能的。 @Taekahn
  • 在 Godbolt 中只需右键单击并选择显示代码。第 6 行的部分有 2 个分段,所以向下滚动。如果你愿意,我会在我回家时连接我的神栓
  • @Taekahn 我刚试过,代码属性的方式看起来是一样的。

标签: c++ g++ code-coverage gcov gcovr


【解决方案1】:

析构函数。通过更新到最新的 GCC 或使用其他工具过滤掉覆盖范围来修复。

两个编译器生成的程序集几乎完全相同,但这里的程序集并不是完整的图片。相关部分是 gcov 如何将部分汇编代码与源代码相关联。 Gcov 不为此使用调试信息。相反,gcov 检测代码以在输入基本块时递增计数器。块是没有内部控制流的汇编代码的一部分。 Gcov 使用其 gcno 文件将计数器 ID 与部分源代码相关联。

两个编译器版本(GCC 7 和 8)都会生成三个与 std::unique_ptr&lt;S&gt; s; 声明相关的程序集 sn-ps:

  • 初始化变量
  • 析构函数调用 1(正常退出)
  • 析构函数调用 2(用于因异常而展开)

现在恰好前两个 sn-ps 没有形成自己的块。它们是包含周围行代码的块的一部分。特别是,gcov 计数器的代码在调试信息中归因于其他行。只有异常处理程序行的块包含一个明确归属于相关行的计数器。因此,不同的 GCC 版本表现出不同程度的混淆似乎是可以理解的。混乱似乎更大,因为 gcov 没有将未发现的代码标记为仅异常。

最终,要弄清楚 gcov 在“思考”什么是不可能的。根据我的经验,异常是 GCC 良好代码覆盖率的祸根,因为异常需要额外的代码路径。在某些情况下,可以使用-fno-exceptions 进行编译。这将使您获得完美的代码覆盖率,但它会显着改变语言。我不喜欢这个。

使用 GCC 11 编译似乎可以解决您的问题。与 GCC 8、9 或 10 相比,它产生不同的程序集。覆盖率报告将显示所有行都被覆盖,并正确地指出一个块未被覆盖(如果您使用 gcov -a)。

如果您无法切换到更新的 GCC,您可以考虑使用第三方工具将此类行排除在覆盖范围之外。例如。 gcovr 和 lcov 允许您使用排除标记来注释这些行,例如

std::unique_ptr<S> s;  // LCOV_EXCL_LINE

Gcovr 还允许您定义自定义正则表达式。例如,您可以使用:

gcovr --exclude-lines-by-pattern '(?x) ^ \s* unique_ptr<.*> \s* \w+; $'

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-08
    • 1970-01-01
    • 2011-12-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多