Code Coverage in Chromium

Coverage Dashboard: link

Table of contents:

Chromium uses source-based code coverage for clang-compiled languages such as C++. This documentation explains how to use Clang’s source-based coverage features in general.

In this document, we first introduce the code coverage infrastructure that continuously generates code coverage information for the whole codebase and for specific CLs in Gerrit. For the latter, refer to code_coverage_in_gerrit.md. We then present a script that can be used to locally generate code coverage reports with one command, and finally we provide a description of the process of producing these reports.

Coverage Infrastructure

coverage infra diagram

There are 3 layers in the system:

Coverage Builders

The first layer is the LUCI builders that

  • build instrumented targets,
  • run the instrumented tests,
  • merge the results into single streams,
  • upload data to cloud storage.

There are two types of builder:

CI Builder

The Code-coverage CI Builders periodically build all the test targets and fuzzer targets for a given platform and instrument all available source files. Then save the coverage data to a dedicated storage bucket.

CQ Builder

The code coverage CQ builders instrument only the files changed for a given CL. more information about per-cl coverage info in this doc.

Coverage Service

The second layer in the system consists of an AppEngine application that consumes the coverage data from the builders above, structures it and stores it in cloud datastore. It then serves the information to the clients below.

Coverage Clients

In the last layer we currently have two clients that consume the service:

Coverage Dashboard

The coverage dashboard front end is hosted in the same application as the service above. It shows the full-code coverage reports with links to the builds that generated them, as well as per-directory and per-component aggregation, and can be drilled down to the single line of code level of detail.

Refer tho the following screenshots, or this 18-second video tutorial.

Project View

coverage dashboard main view

Directory View

coverage dashboard directory view

Component View

coverage dashboard component view

Source View

When you click on a particular source file in one of the views above, you can check per-line coverage information such as

  • Uncovered / Covered line fragments, lines and code blocks. This information can be useful to identify areas of code that lack test coverage.
  • Per-line hit counts indicating how many times this line was hit by all tested targets. This information can be useful to determine hot spots in your code.
  • Potentially dead code. See dead code example.

coverage dashboard file view

Gerrit Coverage View

The other client supported at the moment is the gerrit plugin for code coverage.

gerrit coverage view

See this doc for information about the feature that allows gerrit to display code coverage information generated for a given CL by CQ bot. Or see this 15-second video tutorial.

Local Coverage Script

The coverage script automates the process described below and provides a one-stop service to generate code coverage reports locally in just one command.

This script is currently supported on Linux, Mac, iOS and ChromeOS platforms.

Here is an example usage:

$ gn gen out/coverage \
    --args='use_clang_coverage=true is_component_build=false dcheck_always_on=true'
$ python tools/code_coverage/coverage.py \
    crypto_unittests url_unittests \
    -b out/coverage -o out/report \
    -c 'out/coverage/crypto_unittests' \
    -c 'out/coverage/url_unittests --gtest_filter=URLParser.PathURL' \
    -f url/ -f crypto/

The command above builds crypto_unittests and url_unittests targets and then runs them individually with their commands and arguments specified by the -c flag. For url_unittests, it only runs the test URLParser.PathURL. The coverage report is filtered to include only files and sub-directories under url/ and crypto/ directories.

Aside from automating the process, this script provides visualization features to view code coverage breakdown by directories and by components, similar to the views in the coverage dashboard above.

Workflow

This section presents the workflow of generating code coverage reports using two unit test targets in Chromium repo as an example: crypto_unittests and url_unittests, and the following diagram shows a step-by-step overview of the process.

code coverage generation workflow

Step 0 Download Tooling

Generating code coverage reports requires llvm-profdata and llvm-cov tools. Currently, these two tools are not part of Chromium’s Clang bundle, coverage script downloads and updates them automatically, you can also download the tools manually (tools link).

Step 1 Build

In Chromium, to compile code with coverage enabled, one needs to add use_clang_coverage=true and is_component_build=false GN flags to the args.gn file in the build output directory. Under the hood, they ensure -fprofile-instr-generate and -fcoverage-mapping flags are passed to the compiler.

$ gn gen out/coverage \
    --args='use_clang_coverage=true is_component_build=false'
$ gclient runhooks
$ autoninja -C out/coverage crypto_unittests url_unittests

Step 2 Create Raw Profiles

The next step is to run the instrumented binaries. When the program exits, it writes a raw profile for each process. Because Chromium runs tests in multiple processes, the number of processes spawned can be as many as a few hundred, resulting in the generation of a few hundred gigabytes’ raw profiles. To limit the number of raw profiles, %Nm pattern in LLVM_PROFILE_FILE environment variable is used to run tests in multi-process mode, where N is the number of raw profiles. With N = 4, the total size of the raw profiles are limited to a few gigabytes.

$ export LLVM_PROFILE_FILE=”out/report/crypto_unittests.%4m.profraw”
$ ./out/coverage/crypto_unittests
$ ls out/report/
crypto_unittests.3657994905831792357_0.profraw
...
crypto_unittests.3657994905831792357_3.profraw

Step 3 Create Indexed Profile

Raw profiles must be indexed before generating code coverage reports, and this is done using the merge command of llvm-profdata tool, which merges multiple raw profiles (.profraw) and indexes them to create a single profile (.profdata).

At this point, all the raw profiles can be thrown away because their information is already contained in the indexed profile.

$ llvm-profdata merge -o out/report/coverage.profdata \
    out/report/crypto_unittests.3657994905831792357_0.profraw
...
out/report/crypto_unittests.3657994905831792357_3.profraw
out/report/url_unittests.714228855822523802_0.profraw
...
out/report/url_unittests.714228855822523802_3.profraw
$ ls out/report/coverage.profdata
out/report/coverage.profdata

Step 4 Create Coverage Reports

Finally, llvm-cov is used to render code coverage reports. There are different report generation modes, and all of them require the following as input:

  • Indexed profile
  • All built target binaries
  • All exercised source files

For example, the following command can be used to generate per-file line-by-line code coverage report:

$ llvm-cov show -output-dir=out/report -format=html \
    -instr-profile=out/report/coverage.profdata \
    -object=out/coverage/url_unittests \
    out/coverage/crypto_unittests

For more information on how to use llvm-cov, please refer to the guide.

Contacts

Reporting problems

For any breakage report and feature requests, please file a bug.

Mailing list

For questions and general discussions, please join code-coverage group.

FAQ

Can I use is_component_build=true for code coverage build?

Yes, code coverage instrumentation works with both component and non-component builds. Component build is usually faster to compile, but can be up to several times slower to run with code coverage instrumentation. For more information, see crbug.com/831939.

I am getting some warnings while using the script, is that fine?

Usually this is not a critical issue, but in general we tend not to have any warnings. Please check the list of known issues, and if there is a similar bug, leave a comment with the command you run, the output you get, and Chromium revision you use. Otherwise, please file a new issue providing the same information.

How do crashes affect code coverage?

If a crash of any type occurs (e.g. Segmentation Fault or ASan error), the crashing process might not dump coverage information necessary to generate code coverage report. For single-process applications (e.g. fuzz targets), that means no coverage might be reported at all. For multi-process applications, the report might be incomplete. It is important to fix the crash first. If this is happening only in the coverage instrumented build, please file a bug.

How do assertions affect code coverage?

If a crash is caused by CHECK or DCHECK, the coverage dump will still be written on the disk (crrev.com/c/1172932). However, if a crashing process calls the standard assert directly or through a custom wrapper, the dump will not be written (see How do crashes affect code coverage?).

Is it possible to obtain code coverage from a full Chromium build?

Yes, with some important caveats. It is possible to build chrome target with code coverage instrumentation enabled. However, there are some inconveniences involved:

  • Linking may take a while
  • The binary is huge (~4GB)
  • The browser “works”, but is noticeably slow and laggy
  • The sandbox needs to be disabled (--no-sandbox)

For more information, please see crbug.com/834781.

Why do we see significantly different coverage reported on different revisions?

There can be two possible scenarios:

  • It can be a one time flakiness due to a broken build or failing tests.
  • It can be caused by extension of the test suite used for generating code coverage reports. When we add new tests to the suite, the aggregate coverage reported usually grows after that.

How can I improve coverage dashboard?

Source code of the dashboard is not open sourced at the moment, but if you are a Googler, you should have access to the code-coverage repository. There is a documentation and scripts for running it locally. To get access and report issues, ping the code-coverage group.

The code for the service and dashboard currently lives along with findit at this location because of significant shared logic.

The code used by the bots that generate the coverage data lives (among other places) in the clang coverage recipe module.

Why is coverage for X not reported or unreasonably low, even though there is a test for X?

There are several reasons why coverage reports can be incomplete or incorrect:

  • A particular test is not used for code coverage report generation. Please file a bug.
  • A test may have a build failure or a runtime crash. Please check the build for that particular report (rightmost column on the coverage dashboard). If there is any failure, please upload a CL with the fix. If you can't fix it, feel free to file a bug.
  • A particular test may not be available on a particular platform. As of now, only reports generated on Linux are available on the coverage dashboard.

Is coverage reported for the code executed inside the sandbox?

Not at the moment until crbug.com/842424 is resolved. We do not disable the sandbox when running the tests. However, if there are any other non-sandbox'ed tests for the same code, the coverage should be reported from those. For more information, see crbug.com/842424.