6

I have the following setup (using g++ 10 and lcov 1.14):

g++ SampleScript.cpp -g -O0 -fprofile-arcs -ftest-coverage -o MyScript
./MyScript

lcov -d . -c -o coverage.info --rc lcov_branch_coverage=1
genhtml coverage.info -o cov_out --legend --branch-coverage

with

/* SampleScript.cpp */

class Container
{
public:
    Container()
        : m_value(0) { }

    Container(int value)
        : m_value(value) { }

    int& value()
    {
        return m_value;
    }

    const int& value() const
    {
        return m_value;
    }

private:
    int m_value;
};

int main()
{
    const Container c;
    return c.value();
}

But the resulting output incorrectly shows a 100% coverage even though my code skips 2 of the functions (1 constructor & 1 value() function). Are there any settings that I am missing?

lcov screenshot

Phil-ZXX
  • 2,359
  • 2
  • 28
  • 40

1 Answers1

2

Because the functions are inline. When a member function is defined within the class/struct definition, it is an implicitly inline function. The compiler only generates code for inline functions when they are called. And lcov uses GCC's built-in gcov coverage mechanism, which is based on inserting counters into the generated machine code. So:

  • the compiler doesn't generate code for these functions
  • thus gcov doesn't know these functions exist
  • thus lcov doesn't know these functions exist

This is a systematic limitation of all gcov/lcov/gcovr style coverage tools.

If you want to make sure these tools recognize when a function is uncovered, either make sure that it's not inline (and has external linkage), or ensure that your tests contain a call to that function (even if the call is never executed). Or use different coverage tools that parse the source code themselves.

The inline concept in C++ does not refer to inlining optimization, but is more related to linkage and the one-definition-rule (ODR). The definition of functions/objects must be visible in all compilation units where they are used, and the linker can merge otherwise conflicting definitions (an exception to the ODR). The flip side is that the compiler will typically not emit code for an inline function if it's not used. A function can be inline when marked with the inline keyword, or when it's defined in the body of a class/struct:

struct Example {
  void inline_function() { ... }
  void also_inline();
  void not_inline();
};

inline void Example::also_inline() { ... }

void Example::not_inline() { ... }
amon
  • 57,091
  • 2
  • 89
  • 149
  • 1
    Thank you. How would I (a) make sure a function is not inlined, or (b) ensure that my test contains "a call to that function even if the call is never executed"? – Phil-ZXX Jul 12 '20 at 18:35
  • @Phil-ZXX I edited the answer with a short discussion of C++'s “inline” concept. (a) you could just declare the function signature in the class, and define `int& Container::value() { ... }` externally. (b) Sorry I probably made that more complicated than necessary. If you call the function somewhere, then the code for the function will be generated. Of course, then you could also just write a test case for it, so this advice isn't that helpful in practice … – amon Jul 12 '20 at 21:15