Frequently Asked Questions

What is the difference between lcov and gcovr?

Both lcov and gcovr are tools to create coverage reports.

Gcovr was originally created as a simple script to provide a convenient command line interface to gcov that produced more easily digestible output similar to Python’s coverage utilities.

Later, we added XML output that could be used with the Cobertura plugin of the Jenkins continuous integration server. This gave us nice coverage reports for C/C++ code in Jenkins.

HTML output was added much later. If all you need is HTML, pick whichever one produces the output you like better or integrates easier with your existing workflow.

Lcov is a far older project that is part of the Linux Test Project. It provides some features that gcovr does not have: For example, lcov has explicit support for capturing Linux kernel coverage. Lcov also supports various trace file manipulation functions such as merging trace files from different test runs. You can learn more at the lcov website or the lcov GitHub repository.

Why does C++ code have so many uncovered branches?

Gcovr’s branch coverage reports are based on GCC’s -profile-arcs feature, which uses the compiler’s control flow graph (CFG) of each function to determine branches. This is a very low-level view: to understand the branches in a given function, it can help to view the function’s assembly, e.g. via the Godbolt Compiler Explorer.

What gcovr calls a branch is in fact an arc between basic blocks in the CFG. This means gcovr’s reports have many branches that are not caused by if statements! For example:

  • Arcs are caused by C/C++ branching operators: for, if, while, switch/case, &&, ||, ? :. Note that switches are often compiled as a decision tree which introduces extra arcs, not just one per case.
  • (Arcs into another function are not shown.)
  • Arcs are caused when a function that may throw returns: one arc to the next block or statement for normal returns, and one arc to an exception handler for exceptions, if this function contains an exception handler. Every local variable with a destructor is an exception handler as well.
  • Compiler-generated code that deals with exceptions often needs extra branches: throw statements, catch clauses, and destructors.
  • Extra arcs are created for static initialization and destruction.
  • Arcs may be added or removed by compiler optimizations. If you compile without optimizations, some arcs may even be unreachable!

Gcovr is not able to remove any “unwanted” branches because GCC’s gcov tool does not make the necessary information available, and because different projects are interested in different kinds of branches. However, gcovr has the following options to reduce unwanted branches:

With the gcovr --exclude-unreachable-branches option, gcovr parses the source code to see whether that line even contains any code. If the line is empty or only contains curly braces, this could be an indication of compiler-generated code that was mis-attributed to that line (such as that for static destruction) and branch coverage will be ignored on that line.

With the gcovr --exclude-throw-branches option, exception-only branches will be ignored. These are typically arcs from a function call into an exception handler.

With the :option`gcovr --decisions` option, gcovr parses the source code to extract a ISO 26262 compliant metric for decision coverage. This metric can be interpreted as the branch coverage on C/C++-Level. While the feature is not always able to detect the decisions reliabily when the code is written very compact (uncheckable decisions will be marked), it provides a reliable tool for (i.e. MISRA-compliant) code in security-relevant situations.

Compiling with optimizations will typically remove unreachable branches and remove superfluous branches, but makes the coverage report less exact. For example, branching operators might be optimized away. Decision coverage analysis will be very buggy when compiling with optimizations. See also: Gcov and Optimization in the GCC documentation.

Despite these approaches, 100% branch coverage will be impossible for most programs.

Why are uncovered files not reported?

Gcovr does report files that have zero coverage, even when no .gcda file is available for that compilation unit.

However, the gcov tool in some versions of GCC refuses to generate output for uncovered files.

To fix this, upgrade GCC to:

  • version 5.5 or later,
  • version 6.2 or later, or
  • any version since 7.

Note that the compiler may ignore inline functions that are never used.

Which options are used for calling gcov?

The options used for calling gcov depends on the version of gcov.

Following options are always used:

  • --branch-counts
  • --branch-probabilities
  • --object-directory

Following options are only used if available:

  • --demangled-names: Not available for LLVM based gcov.
  • --hash-filenames: Available since GCC 7, as fallback the option --preserve-paths is used.