gcovr missing all branches in report

167 views Asked by At

I'm trying to use gcovr (version 5.0) to collect branch coverage metrics, but I see this error in many places in my code. I am certain the code it running over both branches in many places, but instead of 2/2, I get a 0/2. I notice this is usually in template code, but can't find reference to any known issues with templates in gcovr, and it works for a lot of other template code too. I am not sure how to make a "smallest example which shows the problem" as it's only become visible when working on a larger project.

My question is really: how do I debug my gcovr usage and resolve the issue? I need 100% coverage for my project, and just having 100% without metrics proving it isn't enough. We need proof.

image showing a clearly taken branch, but showing 0% or 0/2 branch coverage

for the snippet above, the xml output seems to indicate the gathering step is going wrong, but I don't know where else to look.

<line number="194" hits="655482" branch="true" condition-coverage="0% (0/2)">
<conditions>
<condition number="0" type="jump" coverage="0%"/>
</conditions>
</line>

We're using the flags --exclude-unreachable-branches --exclude-throw-branches

UPDATE:

It seems this only happens if the template code is the last instantiated template in a file and the template has tests which cover different template specifications. I have an example which shows that merely adding a procedure which refers to a later template suddenly makes the prior template show branches, and if I remove tests such that only one type is tested, then the branches reappear too.

Tested with g++ (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0 with the following command line: g++ test.cpp -o test -ftest-coverage -fprofile-arcs -O0 then running gcovr with gcovr --gcov-executable=/usr/bin/gcov --print-summary --exclude-unreachable-branches --exclude-throw-branches --h and it seems somehow I am running gcovr 4.2 for this example, not 5.0, which might be part of why the small example issue does not reproduce 100% the same as it does in our main build pipeline which is definitely using gcovr 5.0 as we are using the exclude filter which was only introduced in 5.0. I will try to get 5.0 working with this example and see what the difference is there.

example.hpp

#pragma once

#include <cstdint>
#include <cstdio>

class Example
{
  public:
    template <typename T>
    static T* firstProcedure(int32_t n)
    {
        if (n <= 0)
            return nullptr;

        if (n < sizeof(T))
        {
            printf("uh oh\n");
        }
        else
        {
            printf("ok\n");
        }

        return nullptr;
    }

    template <typename T>
    static T* secondProcedure(int32_t n)
    {
        if (n <= 0)
            return nullptr;

        if (n < sizeof(T))
        {
            printf("uh oh\n");
        }
        else
        {
            printf("ok\n");
        }

        return nullptr;
    }

    template <typename T>
    static T* lastProcedure(int32_t n)
    {
        if (n <= 0)
            return nullptr;

        if (n < sizeof(T))
        {
            printf("uh oh\n");
        }
        else
        {
            printf("ok\n");
        }

        return nullptr;
    }
};

test.cpp

#include "example.hpp"
#include <cstdio>

void Test1()
{
    uint32_t* a = Example::firstProcedure<uint32_t>(1);
    uint8_t* b = Example::firstProcedure<uint8_t>(1);
}

void Test2()
{
    uint32_t* a = Example::secondProcedure<uint32_t>(1);
#if 1 // disable this and branch coverage can be seen.
    uint8_t* b = Example::secondProcedure<uint8_t>(1);
#endif
}

void LastTest()
{
#if 0 // enable this to see branch coverage on secondProcedure.
    uint32_t* a = Example::lastProcedure<uint32_t>(1);
#endif
}


int main() noexcept
{
    Test1();
    Test2();
    //LastTest(); // don't need to run this to get the benefit
    return 0;
}
0

There are 0 answers