0

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:}
Learner09
  • 9
  • 1
  • 5

2 Answers2

4

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

amon
  • 57,091
  • 2
  • 89
  • 149
  • is there any command to delete checksum, gcda and gcno files previously generated and keep the latest one. I mean in one directory for common file multiple executable is using and something is going wrong so i want the last executable coverage only – Learner09 Jul 01 '21 at 01:12
  • @Learner09 I'm not sure I understand what you are asking. It seems you are describing the default behaviour of gcov. But in your question, it seemed this default behaviour was a problem for you. – amon Jul 01 '21 at 08:27
  • in one of a other case, gcda and gcno file is getting created but when I run gov file. C, then I am getting timestamp mismatch gcda... . So i taking about this scenario above how to bypass timestamp mismatch – Learner09 Jul 01 '21 at 20:11
  • @Learner09 The timestamp is the checksum mechanism I described in my answer. In this scenario this could happen if you (1) compile ut1 which creates a .gcno, (2) run ut1 which creates a gcda, (3) compile ut2 which creates a different .gcno, (4) try to run gcov or gcovr. Then you will have a gcda from ut1 but the gcno from ut2 which can't work. The solution would be to recompile from scratch, and then process the coverage data before recompiling in an incompatible configuration. – amon Jul 01 '21 at 21:24
  • What if I create another folder to keep both gcda +gcno separate for both case ut1 and ut2 @amon – Learner09 Jul 01 '21 at 23:39
  • @Learner09 To some degree that is possible, but getting it right is extremely tricky. The relative paths between gcda/gcno and your source code files must stay exactly the same. You will likely also have to use [gcov's cross-profiling feature](https://gcc.gnu.org/onlinedocs/gcc/Cross-profiling.html). Personally, I don't recommend going this route unless you really have to. Instead, fix how you compile and run your tests. – amon Jul 02 '21 at 09:09
  • can you please answer this , https://stackoverflow.com/questions/68377864/gcov-getting-stamp-mismatch-with-note-file @amon – Learner09 Jul 14 '21 at 12:39
1

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