Getting useful GCov results for header-only libraries

I'm also using GCov to check test coverage (Tests written with Google Test framework), additionally I use the Eclipse GCov integration plugin or the LCov tool to generate easy to inspect views of the test coverage results. The raw GCov output is too hard to use :-(.

If you have header only template libraries, you also need to instrument (using G++ flag --coverage) your testing classes that instantiate the template classes and template member functions to see reasonable GCov outputs for these.

With the mentioned tools it's easy to spot template code that wasn't instantiated with the test cases at all, since it has NO annotations.

I have setup a sample and copied the LCov output to a DropBox link you can inspect.

Sample code (TemplateSampleTest.cpp is instrumented using the g++ --coverage option):

TemplateSample.hpp

template<typename T>
class TemplateSample
{

public:
    enum CodePath
    {
        Path1 ,
        Path2 ,
        Path3 ,
    };

    TemplateSample(const T& value)
    : data(value)
    {
    }

    int doSomething(CodePath path)
    {
        switch(path)
        {
        case Path1:
            return 1;
        case Path2:
            return 2;
        case Path3:
            return 3;
        default:
            return 0;
        }

        return -1;
    }

    template<typename U>
    U& returnRefParam(U& refParam)
    {
        instantiatedCode();
        return refParam;
    }

    template<typename U, typename R>
    R doSomethingElse(const U& param)
    {
        return static_cast<R>(data);
    }

private:
    void instantiatedCode()
    {
        int x = 5;
        x = x * 10;
    }

    void neverInstantiatedCode()
    {
        int x = 5;
        x = x * 10;
    }
    T data;
};

TemplateSampleTest.cpp

#include <string>
#include "gtest/gtest.h"
#include "TemplateSample.hpp"

class TemplateSampleTest : public ::testing::Test
{
public:

    TemplateSampleTest()
    : templateSample(5)
    {
    }

protected:
    TemplateSample<int> templateSample;

private:
};

TEST_F(TemplateSampleTest,doSomethingPath1)
{
    EXPECT_EQ(1,templateSample.doSomething(TemplateSample<int>::Path1));
}

TEST_F(TemplateSampleTest,doSomethingPath2)
{
    EXPECT_EQ(2,templateSample.doSomething(TemplateSample<int>::Path2));
}

TEST_F(TemplateSampleTest,returnRefParam)
{
    std::string stringValue = "Hello";
    EXPECT_EQ(stringValue,templateSample.returnRefParam(stringValue));
}

TEST_F(TemplateSampleTest,doSomethingElse)
{
    std::string stringValue = "Hello";
    long value = templateSample.doSomethingElse<std::string,long>(stringValue);
    EXPECT_EQ(5,value);
}

See the code coverage output generated from lcov here:

TemplateSample.hpp coverage

Caveat: 'Functions' statistics is reported as 100%, which is not really true regarding the not instantiated template functions.


I stumbled across this problem too and unfortunately didn't have much luck with the various flags mentioned, I did, however, discover two ways to generate more accurate coverage information when dealing with header-only functions.

The first is to add the flag -fkeep-inline-functions (https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#index-fkeep-inline-functions).

This did give me just the results I was after but came with some serious problems trying to integrate with other libraries (even the normal C++ standard library). I wound up getting link errors because certain functions that should have been removed by the linker were not (e.g. a function declaration with no definition).

The second approach (the one I opted for in the end) was to use __attribute(used)__ in GCC to annotate all my header API functions. The documentation (https://gcc.gnu.org/onlinedocs/gcc-4.3.0/gcc/Function-Attributes.html) states:

used

This attribute, attached to a function, means that code must be emitted for the function even if it appears that the function is not referenced.

I used a #define to wrap it so I only have it turned on when I'm using GCC and coverage is enabled:

#ifdef _MSC_VER
#define MY_API
#elif defined __GNUC__ && defined COVERAGE
#define MY_API __attribute__((__used__))
#endif // _MSC_VER ? __GNUC__ && COVERAGE

Usage then looks like this:

MY_API void some_inline_function() {}

I'm going to try and write up how I got everything working at some point which I'll link to from here in future if I ever get round to it 😁

(Note: I also used -coverage -g -O0 -fno-inline when compiling)


Apart from the usual flags to GCC controlling inlining;

--coverage -fno-inline -fno-inline-small-functions -fno-default-inline

You can instantiate your template classes at the top of your unit test files;

template class std::map<std::string, std::string>;

This will generate code for every method in that template class making the coverage tools work perfectly.

Also, make sure that you initialise your *.gcno files (so for lcov)

lcov -c -i -b ${ROOT} -d . -o Coverage.baseline
<run your tests here>
lcov -c -d . -b ${ROOT} -o Coverage.out
lcov -a Coverage.baseline -a Coverage.out -o Coverage.combined
genhtml Coverage.combined -o HTML

Tags:

C++

Gcc

Gcov