我最近升级到了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 7/8:https://godbolt.org/z/Te57s4WK8
发布于 2021-12-30 00:24:10
破坏者。通过更新到最近的GCC,或者使用更多的工具过滤掉覆盖范围。
这两个编译器生成的程序集几乎是相同的,但是这里的程序集并不是完整的。相关部分是gcov如何将程序集代码的部分与源代码关联。Gcov不为此使用调试信息。相反,无论何时输入基本块,gcov都会检测代码以增加计数器。块是没有内部控制流的程序集代码的一部分。Gcov使用其gcno文件将计数器ID与部分源代码相关联。
这两个编译器版本(GCC 7和8)都生成了三个与std::unique_ptr<S> s;声明相关的程序集片段:
初始化variable
现在发生的情况是,前两个片段不形成自己的块。它们是块的一部分,其中包括来自周围行的代码。特别是,在调试信息中,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_LINEGcovr还允许您定义自定义regex。例如,您可以使用:
gcovr --exclude-lines-by-pattern '(?x) ^ \s* unique_ptr<.*> \s* \w+; $'https://stackoverflow.com/questions/70514398
复制相似问题