Skip to content
C++

C++ Code Coverage

gcov/lcov (GCC/Clang), llvm-cov (Clang source-based), and OpenCppCoverage (MSVC/Windows) — how each works and how to integrate into CI.

Quick pick

gcov/lcov — default for GCC + Clang on Linux.
llvm-cov — more accurate, prefer for Clang-only projects.
OpenCppCoverage — MSVC on Windows, no recompile needed.
gcov / lcov
Compiler: GCC (also Clang with --coverage) · HTML: genhtml (via lcov) · Badges: coveralls, codecov

The GCC standard. gcov collects raw data; lcov aggregates + generates HTML reports; works with Clang via --coverage.

  • Comes with every GCC installation
  • Works with Clang (use --coverage flag)
  • lcov produces excellent HTML reports
  • Integrates with Codecov, Coveralls, SonarQube
  • Branch coverage in addition to line coverage

CMake configuration

target_compile_options(myapp PRIVATE --coverage)
target_link_options(myapp PRIVATE --coverage)

Collect & generate report

# After running tests:
lcov --capture --directory . --output-file coverage.info
lcov --remove coverage.info '*/test/*' '/usr/*' --output-file coverage.info
genhtml coverage.info --output-directory coverage_html
llvm-cov / llvm-profdata
Compiler: Clang · HTML: llvm-cov show --format=html · Badges: codecov (lcov export)

Clang's native coverage: source-based, more accurate than gcov, tracks regions not just lines.

  • Source-based coverage — more accurate than gcov
  • Region coverage (not just line/branch)
  • LLVM-integrated, optimized for Clang
  • Can export to lcov format for CI compatibility
  • JSON export for custom tooling

CMake configuration

target_compile_options(myapp PRIVATE
  -fprofile-instr-generate -fcoverage-mapping)
target_link_options(myapp PRIVATE
  -fprofile-instr-generate)

Collect & generate report

# Merge profile data
llvm-profdata merge -sparse default.profraw -o coverage.profdata

# Generate HTML report
llvm-cov show ./myapp -instr-profile=coverage.profdata \
  -format=html -output-dir=coverage_html

# Export to lcov for CI
llvm-cov export ./myapp -instr-profile=coverage.profdata \
  -format=lcov > coverage.info
OpenCppCoverage
Compiler: MSVC · HTML: Built-in HTML/Cobertura export · Badges: SonarQube, Codecov

Windows-native coverage for MSVC binaries. Works without recompiling (debug symbols only).

  • Works on Windows with MSVC — no special compile flags needed
  • Just needs PDB debug symbols
  • HTML and Cobertura XML export
  • Visual Studio / Azure DevOps integration
  • Supports child process coverage

CMake configuration

# No CMake flags needed — just need debug builds with PDB
cmake -DCMAKE_BUILD_TYPE=Debug ..

Collect & generate report

# Run with coverage (Windows CMD)
OpenCppCoverage.exe --sources src\** -- myapp_tests.exe
# Output: HTML report in CoverageReport\

GitHub Actions — coverage + Codecov upload

- name: Build with coverage
  run: |
    cmake -B build -DCMAKE_BUILD_TYPE=Debug \
          -DCMAKE_CXX_FLAGS="--coverage -fno-inline"
    cmake --build build

- name: Run tests
  run: cd build && ctest --output-on-failure

- name: Collect coverage
  run: |
    lcov --capture --directory build --output-file coverage.info
    lcov --remove coverage.info '*/test/*' '/usr/*' \
         --output-file coverage.info

- name: Upload to Codecov
  uses: codecov/codecov-action@v4
  with:
    files: coverage.info
    fail_ci_if_error: true

Coverage targets — a pragmatic guide

< 60%

Danger zone

Large untested areas. Critical bugs hiding in plain sight.

60–80%

Acceptable

Basic coverage. Good for mature libraries or integration-heavy code.

80–95%

Good

Most code paths tested. Some edge cases may be excluded intentionally.

100% coverage is not the goal — it is trivially achievable with meaningless tests. Aim for meaningful coverage of all behaviors, including error paths.

Deep-dive guides