Run your instrumented eBPF programs and obtain coverage from them
Once you have built it, you can use the bpfcov CLI following the next steps.
First, run your eBPF application through it:
$ sudo ./bpfcov -v2 run ../examples/src/.output/cov/raw_enter
Notice that you must give to the run
subcommand the instrumented eBPF application (.output/
cov/raw_enter
).
To know how to instrument it please read this section. Or just take a look at the examples directory...
When the eBPF application exited, or when you stopped the run
subcommand, you may want to double-check
that bpfcov created its pinned eBPF maps in the BPF file system.
$ sudo tree /sys/fs/bpf/cov
/sys/fs/bpf/cov
└── raw_enter
├── covmap
├── profc
├── profd
└── profn
If so, then it is time to generate a .profraw
file by collecting info from those eBPF maps!
To do so, you need to use the gen
subcommand:
sudo ./bpfcov -v2 gen ../examples/src/.output/cov/raw_enter
This command will create a raw_enter.profraw
file sibling to the instrumented eBPF application binary (thus, in ../examples/src/.output/cov/raw_enter.profraw
).
By default, the gen
subcommand will not unpin the eBPF maps that the run
subcommand created.
But in case you want to unpin them, and you want to output the .profraw
file in a different location, you can do the following command:
sudo ./bpfcov -v2 gen --unpin -o hellow.profraw ../examples/src/.output/cov/raw_enter
Now that you have a fresh .profraw
file you can use the LLVM tools (llvm-profdata, and llvm-cov) as usual to get a nice source-based coverage report out of it.
Or you can use bpfcov out ...
!
It acts as an opinionated wrapper to the llvm-profdata
and llvm-cov
commands you'd need to execute manually otherwise. This sections shows how it works!
Anyways, here's how to output a source-based code coverage report to the standard output starting from the *.profraw
file we just generated.
First, generate a *.profdata
file:
$ llvm-profdata merge -sparse hellow.profraw -o hellow.profdata
With such a file, plus the *.bpf.obj
file created while instrumenting your eBPF program (read this if you haven't), you can now obtain various coverage reports!
$ llvm-cov show \
--instr-profile=hellow.profdata \
--show-region-summary --show-branch-summary --show-line-counts-or-regions \
../examples/src/.output/cov/raw_enter.bpf.obj
The previous command will output the annotated source code to stdout
:
1| |#include "vmlinux.h"
2| |#include <asm/unistd.h>
3| |#include <bpf/bpf_helpers.h>
4| |#include <bpf/bpf_core_read.h>
5| |#include <bpf/bpf_tracing.h>
6| |
7| |char LICENSE[] SEC("license") = "GPL";
8| |
9| |const volatile int count = 0;
10| |
11| |SEC("raw_tp/sys_enter")
12| |int BPF_PROG(hook_sys_enter)
13| 1|{
14| 1| bpf_printk("ciao0");
15| |
16| 1| struct trace_event_raw_sys_enter *x = (struct trace_event_raw_sys_enter *)ctx;
17| 1| if (x->id != __NR_connect)
18| 1| {
19| 0| return 0;
20| 0| }
21| |
22| 10| for (int i = 1; i < count; i++)
^1 ^9
23| 9| {
24| 9| bpf_printk("ciao%d", i);
25| 9| }
26| |
27| 1| return 0;
28| 1|}
You can also output a JSON report, a LCOV, or an HTML one (see the next section). It's your call!
Feel free to explore the different flags the bpfcov CLI and its subcommand supports by reading their manual (more in the help section).
This section shows how to generate code coverage reports for your eBPF programs, either via the bpfcov CLI (more straightforward) or manually.
It shows how to do it for a few eBPF applications in a single report, either HTML, json, or lcov.
But the same applies if you only have one eBPF application for which you want to generate a report.
Assuming we have gerenated with bpfcov gen ...
the *.profraw
files for 3 examples,
we can generate a *HTML report for all of them:
./bpfcov -v2 out -o awesome_html_cov_report \
../examples/src/.output/cov/lsm.profraw ../examples/src/.output/cov/fentry.profraw ../examples/src/.output/cov/fentry.profraw
Generating a JSON report it's just a matter of specifing the format:
./bpfcov -v2 out --format=json \
../examples/src/.output/cov/lsm.profraw ../examples/src/.output/cov/fentry.profraw ../examples/src/.output/cov/fentry.profraw
By default, the out
subcommand will output in out.json
when the --output
flag is not specified.
No need to repeat myself showing the lcov
format... Right?
Just in case you need to fine-tune the coverage report by passing different arguments to llvm-cov
,
here is how to manually do the same things the bpfcov out
command does.
- Generate the
*.profdata
files from your*.profraw
ones:
llvm-profdata merge -sparse ../examples/src/.output/cov/lsm.profraw -o lsm.profdata
llvm-profdata merge -sparse ../examples/src/.output/cov/fentry.profraw -o fentry.profdata
llvm-profdata merge -sparse ../examples/src/.output/cov/raw_enter.profraw -o raw_enter.profdata
- Merge all of them in a single
all.profdata
file:
llvm-profdata merge \
-sparse lsm.profdata fentry.profdata raw_enter.profdata \
-o all.profdata
- Play with
llvm-cov
to outpu your HTML coverage report, for example:
llvm-cov show --format=html \
--show-branches=count --show-line-counts-or-regions --show-region-summary \
-instr-profile=all.profdata \
-object ../examples/src/.output/cov/raw_enter.bpf.obj -object ../examples/src/.output/cov/fentry.bpf.obj -object ../examples/src/.output/cov/lsm.bpf.obj \
--output-dir=../yay
Notice that this is the step where you need the *.bpf.obj
archive files!
- Want to export an lcov representation of the coverage and generate a line coverage HTML report only?
llvm-cov export --format=lcov \
--show-region-summary --show-branch-summary \
-instr-profile=all.profdata \
-object ../examples/src/.output/cov/raw_enter.bpf.obj -object ../examples/src/.output/cov/fentry.bpf.obj -object ../examples/src/.output/cov/lsm.bpf.obj > all.info
genhtml all.info --legend --show-details --highlight --output-directory ../lcov_line_coverage
The bpfcov CLI provides a detailed --help
flag.
$ ./bpfcov --help
Usage: bpfcov [OPTION...] [run|gen|out] <arg(s)>
Obtain coverage from your instrumented eBPF applications.
OPTIONS:
--bpffs=path Set the BPF FS path (defaults to /sys/fs/bpf)
-v, --verbose[=level] Set the verbosity level when not built for release
(defaults to 0)
GLOBALS:
-?, --help Give this help list
--usage Give a short usage message
-V, --version Print program version
Mandatory or optional arguments to long options are also mandatory or optional
for any corresponding short options.
EXAMPLES:
bpfcov run <program>
bpfcov gen <program>
bpfcov out <program.profraw>+
...
It also provides a specific --help
flag for each subcommand.
For example, you can get to know more about the gen
subcommand by typing:
$ ./bpfcov gen --help
Usage: bpfcov gen [OPTION...] <program>
Generate the profraw file for the bpfcov instrumented eBPF applications.
OPTIONS:
-o, --output=path Set the output path
(defaults to <program>.profraw)
--unpin Unpin the maps
GLOBALS:
-?, --help Give this help list
--usage Give a short usage message
-V, --version Print program version
Feel free to explore the other subcommands and their flags.
I'm not sure this topic requires a whole section on its own:
make
🎈