Fil*_*ipp 2 c++ code-coverage g++ gcov gcovr
我最近升级到了新版本的 gcc/g++/gcov,现在 gcov 的行为很奇怪。新版本声称未涵盖旧版本中涵盖的某些代码行。我设法将我的代码减少到这个最小的示例。
#include <memory>
using namespace std;
struct S {};
int main() {
unique_ptr<S> s;
s = make_unique<S>();
}
Run Code Online (Sandbox Code Playgroud)
然后我使用 编译此文件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:}
Run Code Online (Sandbox Code Playgroud)
这与 gcov 的旧版本不同,旧版本声称第 6 行被命中了 2 次。
为什么 gcov 认为第 6 行没有被覆盖?难道我做错了什么?
我能够使用 gcc8、gcc9 和 gcc10 重现此行为。gcc7 的行为符合预期。
比较 gcc7/8 的编译器资源管理器:https ://godbolt.org/z/Te57s4WK8
析构函数。通过更新到最新的 GCC 或使用其他工具来过滤覆盖范围来修复。
\n两个编译器生成的程序集都差不多相同,但此处的程序集并不是完整的情况。相关部分是 gcov 如何将部分汇编代码与源代码关联起来。Gcov 不为此使用调试信息。相反,gcov 会在输入基本块时使用代码来增加计数器。块是没有内部控制流的汇编代码的一部分。Gcov 使用其 gcno 文件将计数器 ID 与部分源代码相关联。
\n两个编译器版本(GCC 7 和 8)都会生成与以下内容相关的三个汇编片段:std::unique_ptr<S> s;:
现在,前两个片段没有形成自己的块。它们是包含周围行代码的块的一部分。特别是,gcov 计数器的代码在调试信息中归因于其他行。只有异常处理程序行的块包含一个明确归属于相关行的计数器。因此,不同的 GCC 版本表现出不同程度的混乱似乎是可以理解的。由于 gcov 没有将未覆盖的代码标记为仅异常,因此混乱似乎更大。
\n最终,实际上不可能弄清楚 gcov 是什么 \xe2\x80\x9cthinking\xe2\x80\x9d。根据我的经验,异常是 GCC 良好代码覆盖率的祸根,因为异常需要额外的代码路径。在某些情况下,可以使用以下命令进行编译-fno-exceptions. 这将使您获得完美的代码覆盖率,但它会显着改变语言。我不喜欢这个。
使用 GCC 11 编译似乎可以解决您的问题。与 GCC 8、9 或 10 相比,它生成不同的程序集。覆盖率报告将显示所有被覆盖的行,并正确地注意到一个块未被覆盖(如果您使用gcov -a)。
如果您无法切换到更新的 GCC,您可以考虑使用第三方工具将此类行排除在覆盖范围之外。例如 gcovr 和 lcov 让您可以使用排除标记来注释这些行,例如
\nstd::unique_ptr<S> s; // LCOV_EXCL_LINE\nRun Code Online (Sandbox Code Playgroud)\nGcovr 还允许您定义自定义正则表达式。例如,您可以使用:
\ngcovr --exclude-lines-by-pattern \'(?x) ^ \\s* unique_ptr<.*> \\s* \\w+; $\'\nRun Code Online (Sandbox Code Playgroud)\n