【发布时间】:2014-09-23 10:18:46
【问题描述】:
我正在使用 gcov 获取我的应用程序的覆盖率信息。但是,我的应用程序有 3 个实例同时运行,创建了 3 个版本的“gcda”文件。有没有办法在我的覆盖信息文件中合并相同“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.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,它比five 编译时使用的lib.gcno 更新和不同。这意味着每当five 或ten 在另一个之后运行时,它会检测到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
现在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
【讨论】: