如何合并多个版本的gcda文件?

Suj*_*ane 6 c++ gcov lcov

我正在使用 gcov 来获取我的应用程序的覆盖范围信息。但是,我的应用程序有 3 个实例同时运行,创建了 3 个版本的“gcda”文件。有没有办法在我的覆盖信息文件中合并相同“gcda”文件的不同版本。

我只想将覆盖范围信息作为一个实例。

hlo*_*dal 6

我刚刚研究了同样的问题,这就是我发现的。TL;DR 是的,在最好的情况下,只要小心编译顺序,并以最通用(和复杂)的方式使用该命令,gcov-tool merge但是它不是直接开箱即用的,它需要一些专门的设置才能完成让它发挥作用。

代码

在我的示例中,我有一个库文件 ,它lib.cpp具有两个函数:

#include <iostream>

void five(int n) {
        if (n > 5) {
                std::cout << n << " is greater than five" << std::endl;
        } else {
                std::cout << n << " is not greater than five" << std::endl;
        }
}

void ten(int n) {
        if (n > 10) {
                std::cout << n << " is greater than ten" << std::endl;
        } else {
                std::cout << n << " is not greater than ten" << std::endl;
        }
}
Run Code Online (Sandbox Code Playgroud)

然后我有两个程序,每个程序都调用其中一个函数,five.cpp

#include <iostream>
#include "lib.hpp"

int main(int argc, char *argv[]) {
        if (argc != 2) {
                std::cerr << "usage: " << argv[0] << " <n>" << std::endl;
                return 2;
        }

        int n = std::stoi(argv[1]);
        five(n);
        return 0;
}
Run Code Online (Sandbox Code Playgroud)

ten.cpp

#include <iostream>
#include "lib.hpp"

int main(int argc, char *argv[]) {
        if (argc != 2) {
                std::cerr << "usage: " << argv[0] << " <n>" << std::endl;
                return 2;
        }

        int n = std::stoi(argv[1]);
        ten(n);
        return 0;
}
Run Code Online (Sandbox Code Playgroud)

假设代码位于/tmp/merge-gcov目录中。

捕获覆盖范围

启用覆盖捕获可以像这样完成

g++ -O0 --coverage -o five five.cpp lib.cpp
Run Code Online (Sandbox Code Playgroud)

这将创建lib.gcnofive.gcno文件。当five程序运行时,它将创建/tmp/merge-gcov/lib.gcda/tmp/merge-gcov/five.gcda。请注意,这些 gcda 路径被硬编码到二进制文件中(但可以进行操作,稍后会详细介绍)。

./five 1
./five 12       # Results from multiple runs will accumulate in gcda files
mkdir report
gcovr -r . --html --html-details --output report/report.html
firefox report/report.html 
Run Code Online (Sandbox Code Playgroud)

多重冲突

到目前为止,一切都很好。ten但如果我们现在也以同样的方式编译程序

g++ -O0 --coverage -o ten ten.cpp lib.cpp
Run Code Online (Sandbox Code Playgroud)

然后它将创建一个lib.gcno比编译时更新且不同的lib.gcno文件five。这意味着每当fiveten在另一个之后运行时,它都会检测到该lib.gcda文件与其期望(gcno 来源)不相符,并重置文件内容,从而丢弃任何累积的先前内容。

lib.cpp可以通过先单独编译文件来避免这种情况,例如

g++ -O0 --coverage -c lib.cpp 
g++ -O0 --coverage -o five lib.o five.cpp
g++ -O0 --coverage -o ten lib.o ten.cpp
Run Code Online (Sandbox Code Playgroud)

现在,fiveten将共享相同的内容lib.gcno,并且它们都会积累lib.gcda

因此,如果您小心地确保所有共享代码在链接二进制文件之前仅编译一次,那么您应该很好地积累多个二进制文件的覆盖率结果。

共享代码的不同编译

但是如果我们想以不同的方式编译库怎么办?也许我们想要编译一个禁用调试代码的版本和一个启用调试代码的版本,以验证代码在这两种情况下是否有效。那么之前的解决方案就不起作用了,取而代之的策略是将每个二进制文件的文件放入其自己的目录中,然后稍后合并这些目录。

g++ -O0 --coverage -c lib.cpp -DENABLE_DEBUG
g++ -O0 --coverage -o five lib.o five.cpp
mkdir gcov-five
mv *.gcno gcov-five/.
Run Code Online (Sandbox Code Playgroud)

注意到我说过 gcda 路径之前是硬编码的吗?您可以接受这一点,运行每个二进制文件,然后将*.gcda文件移到后面。或者您可以设置环境变量,使程序使用不同的目录。GCOV_PREFIX_STRIP 将从完整路径的开头开始截取目录,例如GCOV_PREFIX_STRIP=1/tmp/merge-gcov/lib.gcda变为merge-gcov/lib.gcda. GCOV_PREFIX 变量将被放置在路径前面。

export GCOV_PREFIX=/tmp/merge-gcov/gcov-five
export GCOV_PREFIX_STRIP=2
# Gives /tmp/merge-gcov/gcov-five/lib.gcda
./five 1
./five 12

# Repeat for ten
g++ -O0 --coverage -c lib.cpp -DDISABLE_DEBUG
g++ -O0 --coverage -o ten lib.o ten.cpp
mkdir gcov-ten
mv *.gcno gcov-ten/.
export GCOV_PREFIX=/tmp/merge-gcov/gcov-ten
./ten 1
./ten 12

# Combine 
gcov-tool merge --outdir merged gcov-five gcov-ten
Run Code Online (Sandbox Code Playgroud)

  • `gcov-tool merge` 一次只会处理两个覆盖率报告。对于任意数量的报告,您可以使用 [gcov-tool-many](https://github.com/yugr/maintainer-scripts/blob/master/gcov-tool-many)。 (2认同)