【问题标题】:.gcda file coverage info not getting updated when two executables run for same source file当两个可执行文件为同一个源文件运行时,.gcda 文件覆盖率信息没有得到更新
【发布时间】:2021-09-09 03:46:21
【问题描述】:

共有三个文件u1.c、u2.c和common.c

ut1.c 的内容

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

ut2.c 的内容

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

common.c 的内容

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

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

编译步骤:-

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

执行步骤:-

./ut1
./ut2

现在在运行 gcov common.c 时,只会出现 fun2 覆盖范围。

    -:    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:}

【问题讨论】:

    标签: c unit-testing gcov lcov gcovr


    【解决方案1】:

    这是因为您正在编译 common.c 两次。

    在运行 ut1 和 ut2 程序时,我们可以看到以下警告(使用 GCC 10 测试):

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

    每次编译时启用覆盖,GCC 都会为覆盖数据分配一个校验和。此校验和主要由 gcov 工具使用,以确保 gcno 文件和 gcda 文件匹配。在编译 ut1 和 ut2 时,会使用不同的校验和。因此,ut2 不会追加覆盖数据,而是看到无效的校验和并将覆盖数据。

    解决办法是把common.c当作一个单独的编译单元,与ut1和ut2链接。例如:

    # 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
    

    那么,gcov 的输出应该和预期的一样:

            -:    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:}
    

    如果您无法更改项目的编译方式,您可以使用 lcov 或 gcovr 等工具收集覆盖率数据,然后将其合并。例如,使用 gcovr 的工作流程如下:

    1. 编译 ut1,执行它,并将覆盖数据保存为 gcovr JSON 报告:

      gcc -Wall --coverage ut1.c common.c -o ut1
      ./ut1
      gcovr --json ut1.json
      rm *.gcda *.gcno
      
    2. 编译 ut2,执行它,并将覆盖数据保存为 gcovr JSON 报告:

      gcc -Wall --coverage ut2.c common.c -o ut2
      ./ut2
      gcovr --json ut2.json
      
    3. 创建合并报告:

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

    虽然 gcovr 无法输出 gcov 样式的文本报告,但它可以将覆盖范围显示为 HTML:

    此答案的完整代码位于https://gist.github.com/latk/102b125dff160484f93d8997204fc201

    【讨论】:

    • 是否有任何命令可以删除之前生成的校验和、gcda 和 gcno 文件并保留最新的。我的意思是在一个通用文件的目录中,多个可执行文件正在使用并且出了点问题,所以我只想要最后一个可执行文件覆盖范围
    • @Learner09 我不确定我明白你在问什么。您似乎在描述 gcov 的默认行为。但是在您的问题中,这种默认行为似乎对您来说是个问题。
    • 在另一种情况下,正在创建 gcda 和 gcno 文件,但是当我运行 gov 文件时。 C,然后我得到时间戳不匹配 gcda ......所以我在上面讨论了这个场景如何绕过时间戳不匹配
    • @Learner09 时间戳是我在回答中描述的校验和机制。在这种情况下,如果您 (1) 编译创建 .gcno 的 ut1,(2) 运行创建 gcda 的 ut1,(3) 编译创建不同 .gcno 的 ut2,(4) 尝试运行 gcov 或 gcovr,则可能会发生这种情况.然后你将有一个来自 ut1 的 gcda 但来自 ut2 的 gcno 不能工作。解决方案是从头开始重新编译,然后在不兼容的配置中重新编译之前处理覆盖数据。
    • 如果我创建另一个文件夹以将 gcda +gcno 与 case ut1 和 ut2 @amon 分开会怎样?
    【解决方案2】:

    您可以在单独的目录中运行覆盖运行。然后运行

    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
    

    然后合并文件(行覆盖或分支覆盖): coverage_1.infocoverage_2.info

    如果需要,生成最终的 htmp 报告

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

    【讨论】:

      猜你喜欢
      • 2021-06-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-02-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-03
      相关资源
      最近更新 更多