Home / Alt manpages / llvm-cov-20(1)

  • llvm-cov-20(1)
  • User command
  • linux

Measure C and C++ Coverage with llvm-cov 20

You will finish with a repeatable LLVM coverage run: compile a small program with coverage mapping, execute it, merge the raw profile, and inspect the result as text, HTML or lcov data. The examples match the installed LLVM package version 20.1.8 and use llvm-cov-20, clang-20 and llvm-profdata-20.

Allow about fifteen minutes. You need a writable build directory, a C or C++ compiler from LLVM 20, and tests that can run without damaging data. These commands are ordinary user commands. No sudo is needed, and the guide does not install packages or alter a system service.

1. Check the installed tools

Confirm the binaries before building. This avoids silently mixing a compiler from one LLVM release with reporting tools from another:

$ llvm-cov-20 --version
Ubuntu LLVM version 20.1.8
  Optimized build.
$ clang-20 --version
Ubuntu clang version 20.1.8
$ llvm-profdata-20 --version
Ubuntu LLVM version 20.1.8

The exact packaging suffix and revision may differ on another distribution. The useful checkpoint is that all three tools report the same LLVM major version.

2. Compile with coverage mapping

Use both -fprofile-instr-generate and -fcoverage-mapping. The first adds the runtime that records execution counts; the second puts the source mapping in the executable. Pass the generation flag at link time too, which the clang driver does here when it builds the final program:

$ mkdir -p "$HOME/llvm-cov-demo"
$ cd "$HOME/llvm-cov-demo"
$ clang-20 -fprofile-instr-generate -fcoverage-mapping demo.c -o demo

A minimal demo.c can be used for a smoke test:

#include <stdio.h>

static int classify(int value) {
    if (value > 0)
        return 1;
    return 0;
}

int main(void) {
    puts(classify(1) ? "positive" : "not positive");
    return 0;
}

Checkpoint: confirm that the executable contains coverage mapping before collecting a profile:

$ test -x demo && echo "instrumented executable built"
instrumented executable built

3. Run the instrumented program and locate the raw profile

Run the program as part of the test command. The runtime normally writes default.profraw in the current directory, but an explicit LLVM_PROFILE_FILE is clearer and prevents an unrelated working directory from receiving the file:

$ LLVM_PROFILE_FILE="$PWD/default.profraw" ./demo
positive
$ test -s default.profraw && echo "raw profile ready"
raw profile ready

Each execution adds to profile data when it writes to the same destination. That is useful for a test suite, but old runs can make coverage look better than the current tests deserve. Start a fresh run by choosing a new profile path, or remove an old raw profile only after checking that it is disposable. Removing a profile is irreversible; it does not change the executable or source.

If a program starts several processes, use a pattern such as LLVM_PROFILE_FILE="$PWD/profiles/%p.profraw" so each process gets a separate file. Merge all resulting raw files in the next step.

4. Merge raw data into a report profile

llvm-cov consumes the indexed profile produced by llvm-profdata, not the raw file directly. Merge one or more raw files into a named output:

$ llvm-profdata-20 merge -sparse default.profraw -o demo.profdata
$ test -s demo.profdata && echo "indexed profile ready"
indexed profile ready

For several process files, list them together, for example llvm-profdata-20 merge -sparse profiles/*.profraw -o demo.profdata. Check the glob first if it is generated by a script. An empty or unmatched input set is a coverage collection failure, not a zero-coverage result.

5. Read line coverage with show

Give show the executable, the indexed profile and, optionally, the source file to display. The executable is the important argument: it contains the coverage mapping:

$ llvm-cov-20 show ./demo \
    -instr-profile=./demo.profdata ./demo.c
    1|       |#include <stdio.h>
    3|      1|static int classify(int value) {
    4|      1|    if (value > 0)
    5|      1|        return 1;
    6|      0|    return 0;
    9|      1|int main(void) {

The exact line selection depends on the source and compiler. A count of zero identifies an uncovered executable region. Lines containing no executable code have a blank count. To inspect branches as percentages, add -show-branches=percent; use -show-branches=count when raw branch counts are more useful. The supported views in LLVM 20 are count and percent.

Checkpoint: if the command reports that the profile cannot be read, check both paths and merge the raw file again. If source paths changed between build and reporting, use -path-equivalence=<built-path>,<local-path>. Mappings are applied in the order given, so put a more specific mapping first.

6. Get a compact summary with report

Use report when you need totals rather than annotated source:

$ llvm-cov-20 report ./demo -instr-profile=./demo.profdata
Filename              Regions    Missed Regions     Cover
---------------------------------------------------------
...                   ...        ...               ...%

LLVM 20 also reports functions, lines and branches in the wider table. Values will change with the source and tests. The table distinguishes region coverage from line coverage, so do not treat one percentage as a universal quality score. Add -show-functions for per-function summaries, or -ignore-filename-regex='...' to exclude generated or third-party paths deliberately.

7. Produce HTML or machine-readable data

For a browsable report, select HTML and an output directory. The directory is created if it does not exist:

$ llvm-cov-20 show ./demo \
    -instr-profile=./demo.profdata \
    -format=html \
    -output-dir=coverage-html \
    ./demo.c
$ test -f coverage-html/index.html && echo "HTML report ready"
HTML report ready

Open coverage-html/index.html with a browser or serve the directory through your normal local tooling. Do not publish the directory without checking whether source code and file paths are acceptable to disclose. The HTML output can contain source text and build-path information.

For a CI consumer, export JSON or lcov data. In LLVM 20, text means JSON for export:

$ llvm-cov-20 export ./demo \
    -instr-profile=./demo.profdata \
    -format=lcov > coverage.info
$ head -n 4 coverage.info
SF:.../demo.c
FN:...
FNDA:...
FNF:...

Use -summary-only when downstream tooling needs file totals without individual regions and functions. Use -skip-expansions or -skip-functions to reduce exported detail. Treat generated coverage files as build artefacts and regenerate them rather than hand-editing them.

8. Diagnose the failures that waste most time

An absent default.profraw usually means the instrumented program was not run, it could not write its working directory, or LLVM_PROFILE_FILE pointed somewhere unexpected. Set the path explicitly and check it with test -s.

A profile that reads successfully but shows no useful source normally indicates a binary/profile mismatch, a different build than the one that produced the raw data, or source paths that moved. Rebuild and rerun the complete sequence, then use -path-equivalence only when the source really is the same. Do not combine profiles from unrelated builds.

If you need a hard failure when profile data refers to a binary that cannot be found, add -check-binary-ids to show, report or export. This is useful in CI because automatic binary lookup, including debuginfod where configured, can otherwise hide a missing local artefact.

Done means

  • The compiler and LLVM coverage tools report the intended LLVM 20 release.
  • The executable was built with profile generation and coverage mapping.
  • A fresh program run produced a non-empty raw profile at a known path.
  • llvm-profdata-20 merge produced the indexed profile used by every report.
  • llvm-cov-20 show and report display results for the matching binary.
  • HTML or lcov output is treated as generated data and checked for source disclosure before sharing.