如何合并gcda文件的多个版本? [英] How to merge multiple versions of gcda files?
问题描述
我正在使用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.gcno
和five.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
更新并与之不同.这意味着每当five
或ten
在另一个之后运行时,它将检测到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
现在five
和ten
将共享相同的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屋!