I'm using GCC 4.9 with GCOV to get code and branch coverage. However, the results for branch coverage are utterly useless for my C++ code. It seems GCC inlines templates despite using all -fno-*-inline
flags I know of.
Here is a small example application that illustrates the problem:
#include <string>
#include <iostream>
using namespace std;
int main() {
string foo;
foo = "abc";
cout << foo << endl;
}
I compile the program with g++ -O0 -fno-inline -fno-inline-small-functions -fno-default-inline --coverage -fprofile-arcs test.cpp -o test
After running test
, gcovr -r . -b
prints:
------------------------------------------------------------------------------
GCC Code Coverage Report
Directory: .
------------------------------------------------------------------------------
File Branches Taken Cover Missing
------------------------------------------------------------------------------
test.cpp 14 7 50% 7,8,9,10
------------------------------------------------------------------------------
TOTAL 14 7 50%
------------------------------------------------------------------------------
There is not a single branch in our main
function. For example, line 7 contains string foo;
. It seems the constructor of std::basic_string<...>
has some if statement in it, but that's not useful information when looking at the coverage of main
.
The problem is that all these inlined branches sum up and the branch coverage calculated for my actual unit tests are about 40% as a result. I'm interested in the branch coverage of my code, as opposed to how much branches I hit in the C++ standard library.
Is there any way to completely shut down inlining in the compiler or to tell GCOV to not consider inlined branches? I couldn't find any guide on the GCOV homepage or someplace else regarding that topic.
Any help is much appreciated.
Well, you should always double-check your expectations. Thanks a lot @Useless for pointing me to the gcov
output itself. You weren't quite right, though: the branches are not attributed to the test.cpp
file. Running gcovr
with -k
and looking at all the intermediate files shows that gcov
correctly produces files such as #usr#include#c++#4.9#bits#basic_string.h.gcov
that show coverage for the C++ standard library side of things.
However, the reason for all the branches in test.cpp
is not inlining. It's exceptions. Each call into the standard library is a branch because of potential exceptions (e.g. std::bad_alloc
). Adding -fno-exceptions
to the compiler flags gives the following output:
------------------------------------------------------------------------------
GCC Code Coverage Report
Directory: .
------------------------------------------------------------------------------
File Branches Taken Cover Missing
------------------------------------------------------------------------------
test.cpp 4 2 50% 10
------------------------------------------------------------------------------
TOTAL 4 2 50%
------------------------------------------------------------------------------
Digging deeper into the gcov
output via cat foo.cpp.gcov
prints:
-: 0:Source:test.cpp
-: 0:Graph:/home/neverlord/gcov/test.gcno
-: 0:Data:/home/neverlord/gcov/test.gcda
-: 0:Runs:1
-: 0:Programs:1
-: 1:#include <string>
-: 2:#include <iostream>
-: 3:
-: 4:using namespace std;
-: 5:
function main called 1 returned 100% blocks executed 100%
1: 6:int main() {
1: 7: string foo;
call 0 returned 1
1: 8: foo = "abc";
call 0 returned 1
1: 9: cout << foo << endl;
call 0 returned 1
call 1 returned 1
call 2 returned 1
function _GLOBAL__sub_I_main called 1 returned 100% blocks executed 100%
function _Z41__static_initialization_and_destruction_0ii called 1 returned 100% blocks executed 100%
4: 10:}
call 0 returned 1
branch 1 taken 1 (fallthrough)
branch 2 taken 0
branch 3 taken 1 (fallthrough)
branch 4 taken 0
Sorry for the noise.