代码覆盖工具gcov, lcov的一些使用经验

使用gcov和lcov做代码覆盖有一段时间了,期间走了一些弯路,算是一些经验教训。

  • gcov可以对shared object进行代码覆盖信息收集。

之前的一篇文章说过,gcov不能收集.so的代码覆盖率信息,其实这个是错的。

如果是C++程序,在CXXFLAGS中加入-fprofile-arcs -ftest-coverage 作为编译选项,并且在LDFLAGS中加入-lgcov作为链接选项。如果没有-lgcov选项编译出来的.so文件在动态加载的时候会提示类似 undefined reference to ‘__gcov_merge_add’ 或者 undefined reference to ‘__gcov_init’这样的错误。

如果是C程序,在CFLAGS中加入-fprofile-arcs -ftest-coverage 作为编译选项,也是在LDFLAGS中加入-lgcov作为链接选项,就OK了。跑测试的时候跟正常测试一样,最后把收集到的.gcda文件处理一下就能得到代码覆盖率报告

  • 用lcov处理.gcda文件的时候,在处理某些文件的时候hang了。

对于这个问题,不知道根本原因是什么,可能是那个文件的某行代码触发了bug,我对此是workaround了一下。lcov本身是一堆Perl脚本,打开它,通常在这里“/usr/bin/geninfo”,然后找到这行,注释掉

push(@gcov_options, “-a”) if ($gcov_caps->{‘all-blocks’});

  • genhtml的一个参数:-f, –frames

Use HTML frames for source code view。这样会在每个结果页的左边生成一个缩略图,review代码的时候很方便。但是,如果你的项目里面用到了一些第三方的库,而那些第三方的库没有提供完整的源代码,那么使用这个参数就会出现问题。

gd-png: fatal libpng error: Image width or height is zero in IHDR
gd-png error: setjmp returns error condition

解决方法可以是1. 尝试找一下源代码;2. 通常这些第三方库,我们都是放在一个单独的文件夹里面的,这样子用lcov处理.gcda文件的时候,可以指定目录,把这些第三方库的目录跳过去

  • genhtml的一个参数:–num-spaces NUM

默认值是8,出来的报告看着很费劲,建议换成4,如果代码有很多层,那设成2也是可以的

23 thoughts on “代码覆盖工具gcov, lcov的一些使用经验”

  1. 你好,我的系统使用的是动态库,在编译时没有报错,但启动系统时报了”undefined symbol: __bb_init_func“,我看到有资料说时“dynamic libraries and constructor function __bb_init_func and __bb_fork_func :
    If you want to avoid such an error: «Undefined symbol “__bb_init_func”», use static binaries.
    Explaination: when compiling using Gcov special flags “- fprofile- arcs” and “- ftest- coverage”, the linking of
    dynamic libraries may not perform as expected.” 我不太明白这里static binaries的意思,请问是说要使用静态库吗?

    1. 你好
      gcov做编译动态库的时候是不会报错的,只是在加载.so文件的时候才发现缺少gcov相关的symbol,我之前在编译动态库的时候,在LDFLAGS中加入-lgcov作为链接选项,能解决这个问题。
      你可以看一下你的configure文件里面LDFLAGS有没有包含 -lgcov 选项。如果没有就加上这个选项,然后再重新编译一下那些.so文件试试看

      1. 你好,如何使用gcov编译静态库呢,我使用ld链接的时候,总是提示undefined reference to ‘__gcov_init这样的错误

  2. 请好,请问有没有办法通过gcda或者gcno或者*.o文件对应的cpp文件?我尝试打开*.gcno文件,但从中得到的cpp文件不是完整的路径,是否有办法得到完整路径?
    谢谢

    1. 你好,你编译的程序是用debug版本的么?

      其实生成的.gcda文件都是相对路径,不是绝对路径。

      所以当你拿到.gcda文件以后,这些.gcda文件的结构应该跟你当初编译程序的时候那些.cpp文件的结构是一样的
      所以你可以把你编译路径的那些.cpp文件和.gcno文件,还有运行测试完成以后的.gcda文件都放在一起(通常都是一一对应的)。然后运行lcov提取结果。

  3. 如果有code是编译成la文件的,发现lcov后的info文件里就不会有这个la对应的cpp文件,这是合理的么,如果我想得到这个la对应的cpp文件的覆盖率,可以解决么

      1. 谢谢你的帮助,我的project结构类似于这样:
        Dir1: 1.cpp 2.cpp 3.cpp
        编译后(CFLAG参数添加了-fprofile-arcs -ftest-coverage), Dir1下文件生成如下:
        1.cpp 1.o 1.gcno
        2.cpp 2.o 2.lo
        3.cpp 3.o 3.lo
        .libc/***.a ***.la

        但是没有2.gcno 和3.gcno生成,导致无法统计2.cpp和3.cpp的覆盖率,貌似现在的情况是如果是生成lo文件的都没有生成gcno,我比较不明白的是2.o和3.o都生成了却没有对应的gcno文件生成。

        1. lo和la都是libtool的东西 你看看具体的make执行有没有加入-fprofile-arcs -ftest-coverage参数
          或者没啥需要的话 在构建里吧libtool去掉

  4. Hi magus,
    我有个问题,麻烦帮忙解答一下。我刚开始研究gcov,和.so相关。编译选项是要在编译.so时加入,还是在编译test code的时候加入啊?我测试的平台是android,那在android上跑test,是会在板子上生成.gcda文件吗?

  5. 请问,我在使用lcov的时候,遇到下面 问题,该如何解决呢?/Users/kerryzhao/Documents/cover/File.gcno:version ‘404*’, prefer ‘402*’
    /Users/kerryzhao/Documents/cover/File.gcda:unknown function ‘22167152’
    /Users/kerryzhao/Documents/cover/File.gcda:unknown function ‘22177440’
    Processing main.gcda
    /Users/kerryzhao/Documents/cover/main.gcno:version ‘404*’, prefer ‘402*’
    /Users/kerryzhao/Documents/cover/main.gcda:unknown function ‘22169120’
    Processing ViewController.gcda
    /Users/kerryzhao/Documents/cover/ViewController.gcno:version ‘404*’, prefer ‘402*’

  6. Hi magus,
    我想请教个问题,用gcov是可以查看覆盖率,就是如果合成测试的结果数。比如:第一次执行后生成数据A.cpp.gcov1,第二次生成A.cpp.gcov2。
    那么我们怎么把这个数据合并呢。然后知道所有分支都走了。

Leave a Reply

Your email address will not be published. Required fields are marked *