我正在使用 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
更新且不同的。这意味着无论何时或在另一个之后运行,它都会检测到文件不符合其预期(来自 gcno 的)并重置文件内容,从而丢弃任何累积的先前内容。lib.gcno
five
five
ten
lib.gcda
这可以通过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