Phil-ZXX
Phil-ZXX

Reputation: 3275

Code Coverage (Lcov) incorrectly showing 100% coverage

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

Upvotes: 6

Views: 1814

Answers (1)

amon
amon

Reputation: 57646

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() { ... }

Upvotes: 3

Related Questions