如何合并gcda文件的多个版本? [英] How to merge multiple versions of gcda files?

查看:751
本文介绍了如何合并gcda文件的多个版本?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

我只想将承保范围信息作为一个实例.

解决方案

我一直在研究相同的问题,这就是我所发现的. 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;
        }
}

然后我有两个程序,每个程序都调用其中一个功能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;
}

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;
}

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

捕获覆盖率

启用覆盖率捕获可以像

g++ -O0 --coverage -o five five.cpp lib.cpp

将创建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 

多次冲突

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

g++ -O0 --coverage -o ten ten.cpp lib.cpp

然后将创建一个lib.gcno,该lib.gcno会比five编译时所使用的lib.gcno更新并与之不同.这意味着每当fiveten在另一个之后运行时,它将检测到lib.gcda文件与它的预期值(gcno来源)不对应,并重置文件内容,从而丢弃任何累积的先前内容./p>

可以通过先分别编译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

现在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/.

注意,我说过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

I'm using gcov for get coverage information of my application. However, there are 3 instances of my applications are running concurrently creating 3 versions of "gcda" files. Is there a way to merge different versions of same "gcda" files in my coverage information file.

I want to take the coverage information as just one instance.

解决方案

I have just been looking into the same problem, and here is what I found. TL;DR yes it is possible, in best case by just being careful with compilation order, and in the most general (and complicated) way by using the gcov-tool merge command however it is not straight out of the box and it requires some dedicated setup in order to get it working.

Code

In my example I have one library file, lib.cpp, with two functions:

#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;
        }
}

and then I have two programs, each of them calling one of the functions, 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;
}

and 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;
}

Assume the code is located in a /tmp/merge-gcov directory.

Capturing coverage

Enabling coverage capturing can be done like

g++ -O0 --coverage -o five five.cpp lib.cpp

which will create lib.gcno and five.gcno files. When the five program is being run it will create /tmp/merge-gcov/lib.gcda and /tmp/merge-gcov/five.gcda. Notice that those gcda paths are hardcoded into the binary (but can be manipulated, more about that later).

./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 

Multiple conflicts

So far so good. But if we now also compile the ten program the same way

g++ -O0 --coverage -o ten ten.cpp lib.cpp

then it will create a lib.gcno which is then newer than and different from the lib.gcno that five was compiled with. This means that whenever five or ten is run after the other it will detect that the lib.gcda file is not corresponding to its expectations (of gcno origin) and reset the file contents, thereby discarding any accumulated previous content.

This can be avoided by compiling the lib.cpp file separately first, e.g.

g++ -O0 --coverage -c lib.cpp 
g++ -O0 --coverage -o five lib.o five.cpp
g++ -O0 --coverage -o ten lib.o ten.cpp

now both five and ten will share the same lib.gcno and they will both accumulate lib.gcda.

So if you are careful that all the shared code are compiled only once before linking the binaries you should be good to go for accumulating coverage results from multiple binaries.

Different compilation of shared code

But what if we want to compile the library differently? Perhaps we want to compile one version with debug code disabled and one with it enabled to verify that the code works in both cases. Then the previous solution does not work, and instead the strategy is to put files for each binary into its own directory and then merge those directories later.

g++ -O0 --coverage -c lib.cpp -DENABLE_DEBUG
g++ -O0 --coverage -o five lib.o five.cpp
mkdir gcov-five
mv *.gcno gcov-five/.

Notice I said that the gcda paths were hardcoded earlier? You can either just live with that, run each binary and then move the *.gcda files afterwords. Or you can set environmental variables to make the programs use different directories. GCOV_PREFIX_STRIP will chop of directories from the start of the full path, e.g. GCOV_PREFIX_STRIP=1 and /tmp/merge-gcov/lib.gcda becomes merge-gcov/lib.gcda. The GCOV_PREFIX variable will be put in front of the path.

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

这篇关于如何合并gcda文件的多个版本?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆