Learner09
Learner09

Reputation: 11

.gcda file coverage info not getting updated when two executables run for same source file

There are three files u1.c , u2.c and common.c

content of ut1.c

#include<stdio.h>
void fun1();
int main(){
    fun1();
}

content of ut2.c

#include<stdio.h>
void fun2();
int main(){
    fun2();
}

content of common.c

void fun1(){
        printf("fun1\n");
}

void fun2(){
        printf("fun2\n");
}

Compilation steps:-

gcc -Wall -fprofile-arcs -ftest-coverage ut1.c common.c -o ut1
gcc -Wall -fprofile-arcs -ftest-coverage ut2.c common.c -o ut2

Execution steps:-

./ut1
./ut2

Now when running gcov common.c only fun2 coverage is coming.

    -:    0:Source:common.c
    -:    0:Graph:common.gcno
    -:    0:Data:common.gcda
    -:    0:Runs:1
    -:    0:Programs:1
    -:    1:void fun1(){
    -:    2:        printf("fun1\n");
    -:    3:}
    -:    4:
    1:    5:void fun2(){
    1:    6:        printf("fun2\n");
    1:    7:}

Upvotes: 0

Views: 8587

Answers (2)

Ilya Zlatkin
Ilya Zlatkin

Reputation: 86

You can run coverage runs in separate directories. Then run

lcov --capture --rc lcov_branch_coverage=1 --directory dir_1 --config-file ./lcovrc --output coverage_1.info
lcov --capture --rc lcov_branch_coverage=1 --directory dir_2 --config-file ./lcovrc --output coverage_2.info

then merge file (line coverage or branch coverage): coverage_1.info and coverage_2.info

If needed generate final htmp report

genhtml --branch-coverage --output ./generated-coverage/ merged_coverage.info

Upvotes: 1

amon
amon

Reputation: 57646

This is because you are compiling common.c two separate times.

When running your ut1 and ut2 programs, we can see the following warning (tested with GCC 10):

$ ./ut1
fun1
$ ./ut2
fun2
libgcov profiling error:/tmp/gcov-test/common.gcda:overwriting an existing profile data with a different timestamp

Each time when you compile with coverage enabled, GCC will assign a checksum to the coverage data. This checksum is primarily used by the gcov tool to ensure that the gcno file and gcda files match. When compiling ut1 and ut2, different checksums will be used. So instead of appending coverage data, ut2 sees the invalid checksum and will overwrite the data.

The solution is to treat common.c as a separate compilation unit and link it with ut1 and ut2. For example:

# compile common.c
gcc -Wall --coverage -c common.c -o common.o

# compile ut1 and ut2, and link with common.o
gcc -Wall --coverage ut1.c common.o -o ut1
gcc -Wall --coverage ut2.c common.o -o ut2

Then, the gcov output should be as expected:

        -:    0:Source:common.c
        -:    0:Graph:common.gcno
        -:    0:Data:common.gcda
        -:    0:Runs:2
        -:    1:#include<stdio.h>
        1:    2:void fun1(){
        1:    3:        printf("fun1\n");
        1:    4:}
        -:    5:
        1:    6:void fun2(){
        1:    7:        printf("fun2\n");
        1:    8:}

If you cannot change how your project is compiled, you could collect the coverage data with a tool such as lcov or gcovr, and then merge it. For example, the workflow with gcovr would be as follows:

  1. Compile ut1, execute it, and save the coverage data as a gcovr JSON report:

    gcc -Wall --coverage ut1.c common.c -o ut1
    ./ut1
    gcovr --json ut1.json
    rm *.gcda *.gcno
    
  2. Compile ut2, execute it, and save the coverage data as a gcovr JSON report:

    gcc -Wall --coverage ut2.c common.c -o ut2
    ./ut2
    gcovr --json ut2.json
    
  3. Create a combined report:

    gcovr -a ut1.json -a ut2.json --html-details coverage.html
    

While gcovr cannot output gcov-style textual reports, it can show the coverage as HTML:

The gcovr HTML report shows that both fun1 and fun2 are covered

Full code for this answer is at https://gist.github.com/latk/102b125dff160484f93d8997204fc201

Upvotes: 6

Related Questions