Skip to content

lcov 使用方式及参数说明

lcov 是 gcov 覆盖率数据的收集和汇总工具。它读取编译器生成的 .gcda、.gcno 数据,生成 .info 文件。.info 文件再交给 genhtml 生成 HTML 报告。

覆盖率数据链路

覆盖率数据链路

带覆盖率选项编译
  -> 运行 GoogleTest
  -> 生成 .gcda
  -> lcov 收集 .info
  -> lcov 过滤 .info
  -> genhtml 生成 HTML

编译选项

GCC 常用:

g++ --coverage -O0 -g src.cpp test.cpp -lgtest -lgtest_main -pthread -o unit_tests

CMake 中常用:

add_compile_options(--coverage -O0 -g)
add_link_options(--coverage)

--coverage 等价于编译和链接阶段添加 gcov 相关选项。覆盖率构建通常关闭优化或使用低优化等级,因为优化可能改变源码行与机器指令的对应关系。

基本命令

lcov --capture --directory build --output-file coverage.info
lcov --remove coverage.info '/usr/*' '*/tests/*' --output-file coverage.filtered.info
genhtml coverage.filtered.info --output-directory coverage-html

常用参数

参数 说明
--capture 收集覆盖率数据
--directory DIR 指定搜索 .gcda、.gcno 的目录
--output-file FILE 指定输出 .info 文件
--test-name NAME 为 tracefile 设置测试名称,合并多次测试数据时更容易区分
--base-directory DIR 指定源码基准目录,源码路径解析异常时常用
--build-directory DIR 指定构建目录,源码和构建目录分离时有用
--gcov-tool TOOL 指定 gcov 工具,例如 gcc-13-gcov、arm-none-eabi-gcov
--remove INFO PATTERN... 从覆盖率数据中删除匹配路径
--extract INFO PATTERN... 只保留匹配路径
--zerocounters 清零已有计数器
--initial 捕获初始覆盖率,用于包含未执行文件
--add-tracefile FILE 合并多个 .info 文件
--list INFO 在终端列出覆盖率摘要
--summary INFO 输出 tracefile 的摘要信息
--branch-coverage 捕获或处理分支覆盖率,新版本 lcov 常用
--no-external 捕获时排除外部源码文件
--external 捕获时包含外部源码文件
--no-recursion 不递归扫描子目录
--follow 扫描时跟随符号链接
--checksum tracefile 中记录源码校验信息
--no-checksum 不记录源码校验信息
--quiet 减少输出
--version 输出版本
--ignore-errors TYPE 忽略指定类型错误
--rc KEY=VALUE 临时覆盖 lcov 配置项,例如启用分支覆盖率

参数分类

目的 常用参数
收集数据 --capture、--directory、--output-file
处理路径 --base-directory、--build-directory、--no-external
指定工具链 --gcov-tool
清理计数 --zerocounters
包含未执行文件 --initial
合并数据 --add-tracefile
过滤数据 --remove、--extract
查看摘要 --list、--summary
分支覆盖率 --branch-coverage 或 --rc lcov_branch_coverage=1

推荐流程

lcov --directory build-coverage --zerocounters
ctest --test-dir build-coverage --output-on-failure
lcov --capture --directory build-coverage --output-file coverage.raw.info
lcov --remove coverage.raw.info '/usr/*' '*/_deps/*' '*/tests/*' --output-file coverage.info
lcov --list coverage.info

先清零计数器可以避免之前运行留下的数据污染结果。收集后过滤系统头文件、第三方依赖和测试代码,报告才更接近产品代码覆盖率。

指定 gcov 工具

当系统里存在多个 GCC 版本,或使用 MinGW、交叉编译工具链、Clang gcov 兼容模式时,应显式指定 --gcov-tool。

lcov --capture \
    --directory build-coverage \
    --gcov-tool gcc-13-gcov \
    --output-file coverage.info

交叉编译示例:

lcov --capture \
    --directory build-coverage \
    --gcov-tool /opt/toolchain/bin/arm-none-eabi-gcov \
    --output-file coverage.info

Clang 示例:

lcov --capture \
    --directory build-coverage \
    --gcov-tool "llvm-cov gcov" \
    --output-file coverage.info

gcov 版本应和生成 .gcno、.gcda 的编译器匹配,否则可能出现格式不兼容、覆盖率为空、行号异常等问题。

分支覆盖率

不同 lcov 版本启用分支覆盖率的写法略有差异。常见写法如下:

lcov --capture \
    --directory build-coverage \
    --output-file coverage.info \
    --branch-coverage

兼容写法:

lcov --capture \
    --directory build-coverage \
    --output-file coverage.info \
    --rc lcov_branch_coverage=1

生成 HTML 时,genhtml 也要启用分支覆盖率显示:

genhtml coverage.info --branch-coverage --output-directory coverage-html

分支覆盖率比行覆盖率更严格,适合检查 if、switch、三目运算、短路逻辑等条件是否两侧都被测试。

初始覆盖率

如果只捕获运行后的数据,未被执行且没有产生 .gcda 的文件可能不出现在报告中。可以先捕获初始覆盖率:

lcov --capture --initial --directory build-coverage --output-file coverage.base.info
ctest --test-dir build-coverage --output-on-failure
lcov --capture --directory build-coverage --output-file coverage.test.info
lcov --add-tracefile coverage.base.info --add-tracefile coverage.test.info --output-file coverage.total.info

这种方式能更准确反映“完全没有被测试触达”的文件。

多测试目标合并

大型工程中可能有多个测试目标,例如 core_tests、io_tests、net_tests。可以分别运行并收集,再合并 tracefile。

lcov --directory build-coverage --zerocounters

ctest --test-dir build-coverage -R core_tests --output-on-failure
lcov --capture --directory build-coverage --test-name core --output-file core.info

lcov --directory build-coverage --zerocounters

ctest --test-dir build-coverage -R io_tests --output-on-failure
lcov --capture --directory build-coverage --test-name io --output-file io.info

lcov --add-tracefile core.info \
     --add-tracefile io.info \
     --output-file coverage.total.info

如果不清零计数器,第二次收集可能包含第一次测试留下的数据。是否清零取决于你希望得到“每组测试单独覆盖率”还是“累计覆盖率”。

路径过滤示例

目标 示例
删除系统头文件 '/usr/*'
删除第三方依赖 '*/third_party/*'、'*/_deps/*'
删除测试代码 '*/tests/*'
只保留源码目录 lcov --extract coverage.info '*/src/*'

保留产品代码的常见写法

先删除明显无关路径:

lcov --remove coverage.raw.info \
    '/usr/*' \
    '*/_deps/*' \
    '*/third_party/*' \
    '*/tests/*' \
    --output-file coverage.no_external.info

再只保留产品源码:

lcov --extract coverage.no_external.info \
    '*/src/*' \
    '*/include/*' \
    --output-file coverage.info

这种“两步过滤”比只用 --remove 更稳,因为项目结构变化时,最终报告仍只会保留明确指定的源码目录。

源码路径异常处理

如果报告中出现源码找不到、路径过长、路径前缀不对,可以尝试:

lcov --capture \
    --directory build-coverage \
    --base-directory . \
    --output-file coverage.info

构建目录和源码目录完全分离时,可以加上构建目录:

lcov --capture \
    --directory build-coverage \
    --base-directory . \
    --build-directory build-coverage \
    --output-file coverage.info

--base-directory 影响源码路径解析,--directory 影响 .gcda、.gcno 搜索位置。二者不是同一个概念。

忽略错误

有些工程在迁移阶段会遇到部分源码缺失、第三方文件路径异常、旧 .gcda 数据残留等问题。可以临时使用:

lcov --capture \
    --directory build-coverage \
    --ignore-errors source \
    --output-file coverage.info

--ignore-errors 应谨慎使用。它适合让 CI 临时继续运行,但长期看应修复根因,否则覆盖率报告可能掩盖真实问题。

常见问题

问题 可能原因 处理
没有 .gcda 测试没有运行,或程序异常退出 先确认测试可执行文件正常结束
覆盖率包含系统头 没有过滤路径 使用 --remove
行号对应异常 编译优化影响 使用 -O0 -g
报告缺少未执行文件 没有初始捕获 使用 --initial
多次运行数据叠加 计数器未清零 使用 --zerocounters
覆盖率为空 gcov 工具链不匹配 使用 --gcov-tool 指定匹配版本
分支覆盖率缺失 lcov 或 genhtml 未启用分支参数 添加 --branch-coverage 或对应 --rc
路径不对 源码目录和构建目录分离 尝试 --base-directory、--build-directory