[clang][coverage] Add a compile-time SPI/mapping sidecar option for source coverage
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
I would like LLVM source coverage to have a way for Clang to write the coverage
mapping into a sidecar file while compiling, something along these lines:
```console
clang -fprofile-instr-generate -fcoverage-mapping \
-mllvm -spi-file=/path/to/coverage.spi \
-c foo.c -o foo.o
```
The sidecar would not replace profile data. The instrumented program would still
write `.profraw`, and users would still merge that into `.profdata`. The SPI
file would only carry the source coverage mapping that `llvm-cov` normally has
to read from the final binary or shared library.
The report step would then be able to use the sidecar plus the profile:
```console
llvm-cov show coverage.spi --instr-profile=merged.profdata
```
## Why this would help
For small programs it is fine to run `llvm-cov` on the executable:
```console
llvm-cov show ./a.out --instr-profile=merged.profdata
```
For a large product build, that gets awkward. A test run can involve many
executables and shared libraries, and the report machine may not have the same
build tree layout. In that situation it is much easier if the build can produce
a coverage mapping sidecar during compilation and carry that to the reporting
step with the `.profdata`.
This is roughly the workflow people expect from an SPI-style file: counts come
from the runtime profile, and source mapping comes from a compact build-time
artifact.
## Shape of the option
There are two useful forms.
One is per-output emission:
```console
clang ... -mllvm -spi-file=foo.spi -c foo.c -o foo.o
clang ... -mllvm -spi-file=bar.spi -c bar.c -o bar.o
```
The build system can later decide which sidecars belong in the final coverage
set.
The other is append-to-one-file emission:
```console
clang ... -mllvm -spi-file=$BUILD/coverage/all.spi -c foo.c -o foo.o
clang ... -mllvm -spi-file=$BUILD/coverage/all.spi -c bar.c -o bar.o
```
That is more convenient for large parallel builds, but it needs to be safe under
`make -j`. In particular, the compiler should append complete records under a
lock and flush them before releasing the lock. Otherwise buffered writes from
parallel compiler processes can corrupt the file.
The build wrapper should remove the old SPI file once before starting a fresh
coverage build. Clang should not truncate the file on every invocation, because
that is unsafe when many compile jobs are running in parallel.
## What the sidecar needs to contain
The sidecar only needs the same coverage mapping data that `llvm-cov` already
reads from instrumented objects today, such as the coverage names, function
records, and coverage mapping data. It should not contain counter values.
For an aggregate file, a simple record format should be enough:
```text
magic
repeated:
record_size
coverage_mapping_record
```
`llvm-cov` could then read each record as another coverage mapping input.
## Important tradeoff
Compile-time emission can include code that was compiled but never linked into
the final executable or shared library. That is a real tradeoff, but for some
large builds it is still useful because it gives the build system a compact
coverage mapping artifact without having to rediscover mapping sections from
final binaries later.
If a project needs exact final-image coverage, the build system can use the
per-output sidecar mode and only include sidecars for objects that made it into
the final product. The important part is that Clang has a supported way to emit
the mapping sidecar during compilation.
## Existing LLVM evidence
`llvm-cov convert-for-testing` already shows that `llvm-cov` can report from an
extracted coverage mapping file instead of the original executable:
```console
llvm-cov convert-for-testing a.out -o coverage.testing
llvm-cov show coverage.testing --instr-profile=default.profdata
```
That command is test support, not a user-facing build feature. The request here
is for the compile-time half of that workflow: let Clang write the mapping
sidecar directly when it is already producing coverage mapping.
Contributor guide
Assessment
This issue has not been assessed yet.