Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to suppress inlining of templates with gcov

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.

like image 450
neverlord Avatar asked Sep 21 '16 08:09

neverlord


People also ask

What is difference between gcov and LCOV?

Lcov is a graphical front-end for gcov. It collects gcov data for multiple source files and creates HTML pages containing the source code annotated with coverage information. It also adds overview pages for easy navigation within the file structure. Lcov supports statement, function, and branch coverage measurement.

What is the output of gcov?

gcov file is produced for each source (or header) file containing code, which was compiled to produce the data files. The mangledname part of the output file name is usually simply the source file name, but can be something more complicated if the ' -l ' or ' -p ' options are given.

What are branches in gcov?

The "Branch" column shows the branches that have been executed (✔) or skipped (x). This corresponds to the entries in the GCOV file. The "Exec" column shows how often a certain line has been executed. The "Source" column shows the actual source code.


1 Answers

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.

like image 136
neverlord Avatar answered Sep 29 '22 07:09

neverlord