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