Generating Code Coverage Reports for ZFS on Linux
Introduction
This is another post about collecting code coverage data for the ZFS on Linux project. We’ve recently added a new make target to the project, so I wanted to highlight how easy it is to use this to generate static HTML based code coverage reports, and/or to generate a report that can be used with other tools and services (e.g. codecov.io).
Examples
Before I get into the specifics for how to run the tests and generate
the coverage report, I want to show off the results. After the tests
have been run, one can use make code-coverage-capture to generate the
code coverage information.
This generates a directory full of static HTML pages, and a .info
file; both of which describe the lines executed for the set of tests
that were run. The HTML pages are to be consumed by humans, and the
.info file consumed by tools.
- See here to view an example of the static HTML pages.
- See here to view/download an example of
the
.infofile.
I’ll go into the details below, but that coverage was generated after
running zloop and ZTS, and only contains data for user execution.
Generating this data for kernel execution isn’t difficult, it just
requires a little more work, and is out of scope for this post (I have
some notes on this at the end of this post).
Details
With the end result in mind, lets now dive into the details of how I generated those reports.
As one might expect, we first need to compile the ZFS project, and
ensure it’s configured to correctly generate the needed code coverage
data. We do this via the new --enable-code-coverage flag to
configure:
$ ./autogen.sh && ./configure --enable-code-coverage && make
Now, before we jump into running the tests, we need to run the following commands to setup our environment:
$ ./scripts/zfs-tests.sh -c # create and populate constrained PATH
$ sudo ./scripts/zfs-helpers.sh -vi # create various symlinks in "/"
$ sudo ./scripts/zfs.sh -u # unload kernel modules
$ sudo ./scripts/zfs.sh # load kernel modules
We need to run the tests completely in-tree, so that the .gcda files
will be created within the ZFS repository tree. This enables the make
target to find these files, which it needs to generate the coverage
reports. Thus, the above commands are needed to configure the system to
enable running the tests in-tree vs. installing the various libraries,
commands, and kernel modules into the root filesystem.
With that done, we can go ahead and run our first test. This test will
run ztest in a loop for an hour:
$ sudo ./scripts/zloop.sh -t $((60*60))
After that completes, we can run ZTS (ZFS Test Suite):
$ ./scripts/zfs-tests.sh -vx
And finally, now we can generate the code coverage data for the tests that we ran above:
$ export CODE_COVERAGE_BRANCH_COVERAGE=1
$ make code-coverage-capture
LCOV --capture zfs-0.7.0-coverage.info
LCOV --remove /tmp/*
lcov: WARNING: negative counts found in tracefile zfs-0.7.0-coverage.info.tmp
GEN zfs-0.7.0-coverage
file:///export/home/delphix/zfs/zfs-0.7.0-coverage/index.html
That’s it!
After running that make command it should emit a .info file, and a
directory full of static HTML pages; much like the ones that I linked to
in the “Examples” section earlier in this post.
More Details
If you’ve read this far, you might be interested in the following details as well.
Branch Coverage
We set the CODE_COVERAGE_BRANCH_COVERAGE environment variable prior to
running make code-coverage-capture, so that we’ll get coverage data
for which branches of a conditional are executed, and which branches are
not. This allows us to see when a conditional is executed, but only a
subset of all possibilities of the branch are covered.
By default, lcov (which is used by the make target) has this feature
disabled, so branch coverage will not be collected without setting this
variable.
Kernel Coverage
I mentioned this before, but it’s worth pointing out again. The instructions in this post will only produce code coverage data for user execution. Generating data for kernel execution isn’t much more difficult than what’s described above, but it requires a couple of additional things.
The kernel must be compiled with
gcovsupport. If your kernel isn’t already compiled with this support, than you’ll have to build a new kernel that’s configured to expose the required code coverage data. I wrote some notes on this here. A more authoritative source can be found here and here.Once running a kernel that supports exporting gcov data, the coverage data for the kernel modules need to be copied into the zfs tree prior to generating the reports (i.e. prior to running
make code-coverage-capture). The following small script is one way this can be done:# Allow access to gcov files as a non-root user $ sudo chmod -R a+rx /sys/kernel/debug/gcov $ sudo chmod a+rx /sys/kernel/debug $ GCOV_KERNEL="/sys/kernel/debug/gcov" $ ZFS_BUILD="$(readlink -f ~/zfs)" $ pushd "$GCOV_KERNEL$ZFS_BUILD" >/dev/null $ find . -name "*.gcda" -exec sh -c 'cp -v $0 '$ZFS_BUILD'/$0' {} \; $ find . -name "*.gcno" -exec sh -c 'cp -vdn $0 '$ZFS_BUILD'/$0' {} \; $ popd >/dev/nullThe idea here, is to copy the kernel module’s
.gcdaand.gcnofiles out of/sys/kernel/debug/gcovand place them along side the.cfile they correspond to. This way, these files can be found bymake code-coverage-capturejust like the user execution files. See here for an example of how we’re doing this in the ZFS on Linux project’s automation.
Commit Adding New Make Target
The new code-coverage-capture make target was added in this commit to
the ZFS on Linux project:
commit acf044420b134b022da5c866b19df69934ad3778
Author: Prakash Surya <prakash.surya@delphix.com>
Date: Fri Sep 22 18:49:57 2017 -0700
Add support for "--enable-code-coverage" option
This change adds support for a new option that can be passed to the
configure script: "--enable-code-coverage". Further, the "--enable-gcov"
option has been removed, as this new option provides the same
functionality (plus more).
When using this new option the following make targets are available:
* check-code-coverage
* code-coverage-capture
* code-coverage-clean
Note: these make targets can only be run from the root of the project.
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Prakash Surya <prakash.surya@delphix.com>
Closes #6670
Thus, in order to use this new make target, one will need to use a version of the project that contains this commit.
ZFS Commit Tested
For the testing and coverage data I showed in the “Examples” section, I used the following commit from the ZFS on Linux’s “master” branch:
commit 269db7a4b3ef2bc14f3c2cf95f050479cbd69e72
Author: Simon Guest <simon.guest@tesujimath.org>
Date: Thu Sep 28 06:39:47 2017 +1300
vdev_id: extension for new scsi topology
On systems with SCSI rather than SAS disk topology, this change enables
the vdev_id script to match against the block device path, and therefore
create a vdev alias in /dev/disk/by-vdev.
Reviewed-by: Tony Hutter <hutter2@llnl.gov>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Simon Guest <simon.guest@tesujimath.org>
Closes #6592