Files
scylladb/docs/dev/code-coverage.md
TripleChecker e72e6fadeb Fix typos
2025-02-11 00:17:43 +02:00

8.5 KiB

Code Coverage Support

Scylla supports several code coverage workflows in order to analyze code coverage in testing and during runs this relies on llvm toolchain for instrumentation and conversion of the resulting profiles. The support for code coverage is provided through the following components:

  1. configure.py - build Scylla with code coverage instrumentation
  2. test.py - unit test runs with code coverage processing and reporting.
  3. test/pylib/coverage_utils.py - provides library utilities and cli for common code coverage processing operations.
  4. test/pylib/lcov_utils.py - provides library utilities for manipulation of lcov trace files.
  5. a rest api endpoint: /system/dump_llvm_profile for profile dumping during runtime or in cases where graceful shutdown of Scylla is not desired or possible.

General Workflow With Coverage

  1. Build Scylla with coverage instrumentation
  2. Run Scylla with some use case (or run test)
  3. Trigger coverage profile dump
  4. Process the profile into an lcov trace file
  5. (optional) post process the lcov trace
  6. Generate a textual or an HTML report to view the coverage report

Why Does The Profile Data Converted Into Lcov Format?

One can just use the coverage profiles produced by llvm with the native llvm tools, the method for converting the profile data into lcov format was chosen for the following reasons:

  1. Being source oriented, Lcov format allows for merging coverage data from different binaries easily even if they originated from different source trees (even though it is not very useful...). However combining different binaries from the same source file is useful since you can combine unit test runs with tests that used the actual Scylla binary.
  2. Lcov is easy to parse and manipulate (For example: we can transform it into a patch coverage report)
  3. The HTML reporting is nicer (genhtml) than the llvm one.

Building Scylla With Coverage Instrumentation

Building scylla with coverage instrumentation is done by adding the --coverage option to the configure.py command line. Example: ./configure.py --mode dev --coverage - will build Scylla in dev mode with coverage instrumentation.

NOTE: when adding the --coverage all build modes that are configured are going to be built with coverage instrumentation.

NOTE: Coverage instrumentation incurs some performance penalty but it is hard to determine exactly how much.

Running Unit Tests With Coverage Processing And Reporting

In order to get an lcov trace files for unit test runs, simply add --coverage or --coverage-mode to the test.py command line. Examples:

  1. ./test.py --coverage - will run all tests in all built modes and will produce a coverage lcov traces for all of them.

  2. ./test.py --coverage-mode dev - will run all tests in all built modes and will produce a coverage lcov traces only for the dev mode runs.

  3. ./test.py --mode dev --mode release --coverage-mode dev - will run all tests in the dev and release mode but will only produce an lcov traces for the dev mode tests.

    Where To Find The Lcov Traces After The test.py Run?

    test.py will produce a hierarchy of of lcov trace, all rooted in the <tmpdir> (default:./testlog).

    1. <suite_name>.info - will be produced for every suite under <tmpdir>/<mode>/coverage/<suite_name>/
    2. <mode>_coverage.info - will be produced for ever coverage enabled mode under <tmpdir>/<mode>/coverage/ and contains the aggregated coverage information from all suites.
    3. test_coverage.info - will be created under <tmpdir> and contains the aggregated coverage information for all coverage enabled modes.
    4. test_coverage_report.txt - will be produced under <tmpdir> a textual report of the coverage information in test_coverage.info.

Advanced And Manual Workflows

Once built with code coverage instrumentation, all test executables and scylla executable are going to dump a profile upon graceful shutdown / clean exit. However, in order to produce a coverage report some extra processing is needed. For reference of how to process the profile dumps using the llvm toolchain, refer to: https://clang.llvm.org/docs/SourceBasedCodeCoverage.html

HINT: It is recommended to run with LLVM_PROFILE_FILE set in the environment so the profiles will be dumped to a known location (https://clang.llvm.org/docs/SourceBasedCodeCoverage.html#running-the-instrumented-program)

Working with the cli

The cli test/pylib/coverage_utils.py contains some commands to help with the processing of profile dumps. It is recommended to run the tool from the main source directory

For extensive cli help run: test/pylib/coverage_utils.py or give the -h or --help to one of the subcommands.

Given a profiles/someprofile.profraw (or several files) the most common operations are:

NOTE: In most of the commands a list of files can be given in order to batch the operation on multiple profiles. But we concentrate on single file here for simplicity.

1. Converting Raw Profiles Into Indexed Profile:

Run: test/pylib/coverage_utils.py llvm-profiles merge profiles/someprofile.profraw profiles- This will create an indexed profile in the ./profiles directory. This file is named after the build ID of the executable that created this file. For example: 85e5e08c67bd9bd74c2caeb98aca2a45360cf25a.profdata On this file you can use the llvm tools to produce reports from. However files that originated in different binaries are hard to merge and will yield incomplete or incorrect results.

2. Converting Indexed Profiles Into Lcov files:

Run: test/pylib/coverage_utils.py llvm-profiles to-lcov --binary-search-path build --excludes-file coverage_excludes.txt profiles/85e5e08c67bd9bd74c2caeb98aca2a45360cf25a.profdata - This will create profiles/85e5e08c67bd9bd74c2caeb98aca2a45360cf25a.info which can be manipulated and be used to produce a report.

3. Producing A Report From Lcov trace files:

For textual reports:

  1. lcov --summary profiles/85e5e08c67bd9bd74c2caeb98aca2a45360cf25a.info for a summary of the coverage rates:
Reading tracefile profiles/85e5e08c67bd9bd74c2caeb98aca2a45360cf25a.info
Summary coverage rate:
lines......: 14.5% (11 of 76 lines)
functions..: 11.8% (2 of 17 functions)
branches...: no data found

  1. lcov --list profiles/85e5e08c67bd9bd74c2caeb98aca2a45360cf25a.info for a per file report:
Reading tracefile profiles/85e5e08c67bd9bd74c2caeb98aca2a45360cf25a.info
                     |Lines       |Functions  |Branches
Filename             |Rate     Num|Rate    Num|Rate     Num
===========================================================
[utils/]
abi/eh_ia64.hh       | 100%      3| 100%     1|    -      0
exceptions.cc        |15.1%     53|16.7%     6|    -      0
exceptions.hh        | 0.0%     20| 0.0%    10|    -      0
===========================================================
               Total:|14.5%     76|11.8%    17|    -      0

For an HTML report: test/pylib/coverage_utils.py lcov-tools genhtml --output-dir profiles/html profiles/85e5e08c67bd9bd74c2caeb98aca2a45360cf25a.info - This will create an HTML report rooted at profiles/html in order to view it, open profiles/html/index.html in your browser.

Other Advanced Operations With Lcov Files:

Once you have a coverage data you can manipulate it in the following ways:

  1. "Set Like" operations -
union               Merges several (or single) lcov file into another trace file. If testname is given, the resulting lcov file will be tagged with this name, else if will just merge the files similarly to 'lcov -a...' command.
                        Files can also be filtered (see 'man lcovrc')
    diff                computes the diff between two or more coverage files (lines that are covered by first but not others)
    intersection        computes the intersection between two or more coverage files (lines that are covered by all trace files)
    symmetric-dff       computes the symmetric difference between two traces (line covered by either trace but not both)
  1. Produce a "patch coverage" report which transform the report into a patch centric report of the coverage of lines introduced by some git history fragment (i.e HEAD~10..HEAD) there is also an option for patch coverage report for a merge commit in which case only the patches on the merge will be considered.

For further information you can refer to the cli help: test/pylib/coverage_utils.py help or view the tools code: test/pylib/coverage_utils.py and test/pylib/lcov_utils.py