Skip to content

Commit b94950d

Browse files
committed
Update coverage docs
1 parent b12bee8 commit b94950d

File tree

1 file changed

+98
-94
lines changed

1 file changed

+98
-94
lines changed

src/llvm-coverage-instrumentation.md

+98-94
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,11 @@ them), and generate various reports for analysis, for example:
2828
<br/>
2929

3030
Detailed instructions and examples are documented in the
31-
[Rust Unstable Book (under _source-based-code-coverage_)][unstable-book-sbcc].
31+
[Rust Unstable Book (under _compiler-flags/instrument-coverage_)][unstable-book-instrument-coverage].
3232

3333
[llvm-instrprof-increment]: https://llvm.org/docs/LangRef.html#llvm-instrprof-increment-intrinsic
34-
[Coverage Map]: https://llvm.org/docs/CoverageMappingFormat.html
35-
[unstable-book-sbcc]: https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/source-based-code-coverage.html
34+
[coverage map]: https://llvm.org/docs/CoverageMappingFormat.html
35+
[unstable-book-instrument-coverage]: https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/instrument-coverage.html
3636

3737
## Rust symbol mangling
3838

@@ -82,7 +82,7 @@ a span of code ([`CodeRegion`][code-region]). It counts the number of times a
8282
branch is executed, and also specifies the exact location of that code span in
8383
the Rust source code.
8484

85-
Note that many of these `Coverage` statements will *not* be converted into
85+
Note that many of these `Coverage` statements will _not_ be converted into
8686
physical counters (or any other executable instructions) in the final binary.
8787
Some of them will be (see `CoverageKind::`[`Counter`][counter-coverage-kind]),
8888
but other counters can be computed on the fly, when generating a coverage
@@ -111,7 +111,7 @@ fn some_func(flag: bool) {
111111
In this example, four contiguous code regions are counted while only
112112
incrementing two counters.
113113

114-
CFG analysis is used to not only determine *where* the branches are, for
114+
CFG analysis is used to not only determine _where_ the branches are, for
115115
conditional expressions like `if`, `else`, `match`, and `loop`, but also to
116116
determine where expressions can be used in place of physical counters.
117117

@@ -150,50 +150,53 @@ MIR `Statement` into some backend-specific action or instruction.
150150
match statement.kind {
151151
...
152152
mir::StatementKind::Coverage(box ref coverage) => {
153-
self.codegen_coverage(&mut bx, coverage.clone());
153+
self.codegen_coverage(&mut bx, coverage.clone(), statement.source_info.scope);
154154
bx
155155
}
156156
```
157157

158-
159158
`codegen_coverage()` handles each `CoverageKind` as follows:
160159

161-
* For all `CoverageKind`s, Coverage data (counter ID, expression equation
160+
- For all `CoverageKind`s, Coverage data (counter ID, expression equation
162161
and ID, and code regions) are passed to the backend's `Builder`, to
163162
populate data structures that will be used to generate the crate's
164163
"Coverage Map". (See the [`FunctionCoverage`][function-coverage] `struct`.)
165-
* For `CoverageKind::Counter`s, an instruction is injected in the backend
164+
- For `CoverageKind::Counter`s, an instruction is injected in the backend
166165
IR to increment the physical counter, by calling the `BuilderMethod`
167166
[`instrprof_increment()`][instrprof-increment].
168167

169168
```rust
170-
pub fn codegen_coverage(&self, bx: &mut Bx, coverage: Coverage) {
169+
pub fn codegen_coverage(&self, bx: &mut Bx, coverage: Coverage, scope: SourceScope) {
170+
...
171+
let instance = ... // the scoped instance (current or inlined function)
171172
let Coverage { kind, code_region } = coverage;
172173
match kind {
173174
CoverageKind::Counter { function_source_hash, id } => {
174-
if let Some(code_region) = code_region {
175-
bx.add_coverage_counter(self.instance, id, code_region);
176-
}
175+
...
176+
bx.add_coverage_counter(instance, id, code_region);
177177
...
178178
bx.instrprof_increment(fn_name, hash, num_counters, index);
179179
}
180180
CoverageKind::Expression { id, lhs, op, rhs } => {
181-
bx.add_coverage_counter_expression(self.instance, id, lhs, op, rhs, code_region);
181+
bx.add_coverage_counter_expression(instance, id, lhs, op, rhs, code_region);
182182
}
183183
CoverageKind::Unreachable => {
184-
...
184+
bx.add_coverage_unreachable(
185+
instance,
186+
code_region.expect(...
185187
```
186-
_code snippet trimmed for brevity_
188+
189+
_code snippet abbreviated for brevity_
187190

188191
> The function name `instrprof_increment()` is taken from the LLVM intrinsic
189-
call of the same name ([`llvm.instrprof.increment`][llvm-instrprof-increment]),
190-
and uses the same arguments and types; but note that, up to and through this
191-
stage (even though modeled after LLVM's implementation for code coverage
192-
instrumentation), the data and instructions are not strictly LLVM-specific.
192+
> call of the same name ([`llvm.instrprof.increment`][llvm-instrprof-increment]),
193+
> and uses the same arguments and types; but note that, up to and through this
194+
> stage (even though modeled after LLVM's implementation for code coverage
195+
> instrumentation), the data and instructions are not strictly LLVM-specific.
193196
>
194197
> But since LLVM is the only Rust-supported backend with the tooling to
195-
process this form of coverage instrumentation, the backend for `Coverage`
196-
statements is only implemented for LLVM, at this time.
198+
> process this form of coverage instrumentation, the backend for `Coverage`
199+
> statements is only implemented for LLVM, at this time.
197200

198201
[backend-lowering-mir]: backend/lowering-mir.md
199202
[codegen-statement]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/mir/struct.FunctionCx.html#method.codegen_statement
@@ -221,25 +224,28 @@ properly-configured variables in LLVM IR, according to very specific
221224
details of the [_LLVM Coverage Mapping Format_][coverage-mapping-format]
222225
(Version 4).[^llvm-and-covmap-versions]
223226

224-
[^llvm-and-covmap-versions]: The Rust compiler (as of <!-- date: 2021-01 -->
225-
January 2021) supports _LLVM Coverage Mapping Format_ Version 4 (the most
226-
up-to-date version of the format, at the time of this writing) for improved
227-
compatibility with other LLVM-based compilers (like _Clang_), and to take
228-
advantage of some format optimizations. Version 4 was introduced in _LLVM 11_,
229-
which is currently the default LLVM version for Rust. Note that the Rust
230-
compiler optionally supports some earlier LLVM versions, prior to _LLVM 11_. If
231-
`rustc` is configured to use an incompatible version of LLVM, compiling with `-Z
232-
instrument-coverage` will generate an error message.
227+
[^llvm-and-covmap-versions]:
228+
The Rust compiler (as of <!-- date: 2021-01 -->
229+
January 2021) supports _LLVM Coverage Mapping Format_ Version 4 (the most
230+
up-to-date version of the format, at the time of this writing) for improved
231+
compatibility with other LLVM-based compilers (like _Clang_), and to take
232+
advantage of some format optimizations. Version 4 was introduced in _LLVM 11_,
233+
which is currently the default LLVM version for Rust. Note that the Rust
234+
compiler optionally supports some earlier LLVM versions, prior to _LLVM 11_. If
235+
`rustc` is configured to use an incompatible version of LLVM, compiling with `-Z instrument-coverage` will generate an error message.
233236

234237
```rust
235238
pub fn finalize<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) {
239+
...
240+
if !tcx.sess.instrument_coverage_except_unused_functions() {
241+
add_unused_functions(cx);
242+
}
243+
236244
let mut function_coverage_map = match cx.coverage_context() {
237245
Some(ctx) => ctx.take_function_coverage_map(),
238246
None => return,
239247
};
240248
...
241-
add_unreachable_coverage(tcx, &mut function_coverage_map);
242-
243249
let mut mapgen = CoverageMapGenerator::new();
244250

245251
for (instance, function_coverage) in function_coverage_map {
@@ -248,66 +254,61 @@ pub fn finalize<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) {
248254
mapgen.write_coverage_mapping(expressions, counter_regions, coverage_mapping_buffer);
249255
});
250256
```
257+
251258
_code snippet trimmed for brevity_
252259

253-
One notable step, performed by `mapgen::finalize()` before processing the
254-
`Instance`s and their `FunctionCoverage`s, is the call to
255-
[`add_unreachable_functions()`][add-unreachable-coverage].
260+
One notable first step performed by `mapgen::finalize()` is the call to
261+
[`add_unused_functions()`][add-unused-functions].
256262

257263
When finalizing the coverage map, `FunctionCoverage` only has the `CodeRegion`s and counters for
258264
the functions that went through codegen; such as public functions and "used" functions
259-
(functions referenced by other "used" or public items). Any other functions (considered unused
260-
or "Unreachable") were still parsed and processed through the MIR stage.
265+
(functions referenced by other "used" or public items). Any other functions (considered unused)
266+
were still parsed and processed through the MIR stage.
261267

262-
The set of unreachable functions is computed via the set difference of all MIR
268+
The set of unused functions is computed via the set difference of all MIR
263269
`DefId`s (`tcx` query `mir_keys`) minus the codegenned `DefId`s
264-
(`tcx` query `collect_and_partition_mono_items`). `add_unreachable_functions()`
265-
computes the set of unreachable functions, queries the `tcx` for the
266-
previously-computed `CodeRegions`, for each unreachable MIR, and adds those code
267-
regions to one of the non-generic codegenned functions (non-generic avoids
268-
potentially injecting the unreachable coverage multiple times for multiple
269-
instantiations).
270+
(`tcx` query `codegened_and_inlined_items`). `add_unused_functions()`
271+
computes the set of unused functions, queries the `tcx` for the
272+
previously-computed `CodeRegions`, for each unused MIR, synthesizes an
273+
LLVM function (with no internal statements, since it will not be called),
274+
and adds a new `FunctionCoverage`, with `Unreachable` code regions.
270275

271276
[compile-codegen-unit]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_llvm/base/fn.compile_codegen_unit.html
272277
[coverageinfo-finalize]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_llvm/context/struct.CodegenCx.html#method.coverageinfo_finalize
273278
[mapgen-finalize]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_llvm/coverageinfo/mapgen/fn.finalize.html
274279
[coverage-mapping-format]: https://llvm.org/docs/CoverageMappingFormat.html
275-
[add-unreachable-coverage]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_llvm/coverageinfo/mapgen/fn.add_unreachable_coverage.html
280+
[add-unused-functions]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_llvm/coverageinfo/mapgen/fn.add_unused_functions.html
276281

277282
## Testing LLVM Coverage
278283

279284
Coverage instrumentation in the MIR is validated by a `mir-opt` test:
280285
[`instrument-coverage`][mir-opt-test].
281286

282287
More complete testing of end-to-end coverage instrumentation and reports are done
283-
in the `run-make-fulldeps` tests, with sample Rust programs (to be instrumented)
284-
in the [`coverage`][coverage-test-samples] directory, and the actual tests and expected
288+
in the `run-make` tests, with sample Rust programs (to be instrumented) in the
289+
[`coverage`][coverage-test-samples] directory, and the actual tests and expected
285290
results in [`coverage-reports`].
286291

287-
In addition to testing the final result, two intermediate results are also validated
288-
to catch potential regression errors early: Minimum `CoverageSpan`s computed during
289-
the `InstrumentCoverage` MIR pass are saved in `mir_dump` [Spanview][spanview-debugging]
290-
files and compared to expected results in [`coverage-spanview`].
291-
292292
Finally, the [`coverage-llvmir`] test compares compiles a simple Rust program with
293293
`-Z instrument-coverage` and compares the compiled program's LLVM IR to expected
294294
LLVM IR instructions and structured data for a coverage-enabled program, including
295295
various checks for Coverage Map-related metadata and the LLVM intrinsic calls to
296296
increment the runtime counters.
297297

298298
Expected results for both the `mir-opt` tests and the `coverage*` tests under
299-
`run-make-fulldeps` can be refreshed by running:
299+
`run-make` can be refreshed by running:
300300

301301
```shell
302-
$ ./x.py test src/test/<test-type> --blessed
302+
$ ./x.py test mir-opt --blessed
303+
$ ./x.py test src/test/run-make/coverage --blessed
303304
```
304305

305306
[mir-opt-test]: https://github.com/rust-lang/rust/blob/master/src/test/mir-opt/instrument_coverage.rs
306-
[coverage-test-samples]: https://github.com/rust-lang/rust/tree/master/src/test/run-make-fulldeps/coverage
307-
[`coverage-reports`]: https://github.com/rust-lang/rust/tree/master/src/test/run-make-fulldeps/coverage-reports
308-
[`coverage-spanview`]: https://github.com/rust-lang/rust/tree/master/src/test/run-make-fulldeps/coverage-spanview
307+
[coverage-test-samples]: https://github.com/rust-lang/rust/tree/master/src/test/run-make/coverage
308+
[`coverage-reports`]: https://github.com/rust-lang/rust/tree/master/src/test/run-make/coverage-reports
309+
[`coverage-spanview`]: https://github.com/rust-lang/rust/tree/master/src/test/run-make/coverage-spanview
309310
[spanview-debugging]: compiler-debugging.md#viewing-spanview-output
310-
[`coverage-llvmir`]: https://github.com/rust-lang/rust/tree/master/src/test/run-make-fulldeps/coverage-llvmir
311+
[`coverage-llvmir`]: https://github.com/rust-lang/rust/tree/master/src/test/run-make/coverage-llvmir
311312

312313
## Implementation Details of the `InstrumentCoverage` MIR Pass
313314

@@ -352,11 +353,12 @@ with the following steps:
352353
- `inject_intermediate_expression()`, called for each intermediate expression
353354
returned from `make_bcb_counters()`
354355

355-
[^intermediate-expressions]: Intermediate expressions are sometimes required
356-
because `Expression`s are limited to binary additions or subtractions. For
357-
example, `A + (B - C)` might represent an `Expression` count computed from three
358-
other counters, `A`, `B`, and `C`, but computing that value requires an
359-
intermediate expression for `B - C`.
356+
[^intermediate-expressions]:
357+
Intermediate expressions are sometimes required
358+
because `Expression`s are limited to binary additions or subtractions. For
359+
example, `A + (B - C)` might represent an `Expression` count computed from three
360+
other counters, `A`, `B`, and `C`, but computing that value requires an
361+
intermediate expression for `B - C`.
360362

361363
[instrumentor]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir/transform/coverage/struct.Instrumentor.html
362364
[coverage-graph]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir/transform/coverage/graph/struct.CoverageGraph.html
@@ -396,20 +398,21 @@ contrast with the [`SimplifyCfg`][simplify-cfg] MIR pass, this step does
396398
not alter the MIR itself, because the `CoverageGraph` aggressively simplifies
397399
the CFG, and ignores nodes that are not relevant to coverage. For example:
398400

399-
* The BCB CFG ignores (excludes) branches considered not relevant
400-
to the current coverage solution. It excludes unwind-related code[^78544]
401-
that is injected by the Rust compiler but has no physical source
402-
code to count, which allows a `Call`-terminated BasicBlock
403-
to be merged with its successor, within a single BCB.
404-
* A `Goto`-terminated `BasicBlock` can be merged with its successor
405-
***as long as*** it has the only incoming edge to the successor `BasicBlock`.
406-
* Some BasicBlock terminators support Rust-specific concerns--like borrow-checking--that are
407-
not relevant to coverage analysis. `FalseUnwind`, for example, can be treated the same as
408-
a `Goto` (potentially merged with its successor into the same BCB).
409-
410-
[^78544]: (Note, however, that Issue [#78544][rust-lang/rust#78544] considers
411-
providing future support for coverage of programs that intentionally
412-
`panic`, as an option, with some non-trivial cost.)
401+
- The BCB CFG ignores (excludes) branches considered not relevant
402+
to the current coverage solution. It excludes unwind-related code[^78544]
403+
that is injected by the Rust compiler but has no physical source
404+
code to count, which allows a `Call`-terminated BasicBlock
405+
to be merged with its successor, within a single BCB.
406+
- A `Goto`-terminated `BasicBlock` can be merged with its successor
407+
**_as long as_** it has the only incoming edge to the successor `BasicBlock`.
408+
- Some BasicBlock terminators support Rust-specific concerns--like borrow-checking--that are
409+
not relevant to coverage analysis. `FalseUnwind`, for example, can be treated the same as
410+
a `Goto` (potentially merged with its successor into the same BCB).
411+
412+
[^78544]:
413+
(Note, however, that Issue [#78544][rust-lang/rust#78544] considers
414+
providing future support for coverage of programs that intentionally
415+
`panic`, as an option, with some non-trivial cost.)
413416

414417
The BCB CFG is critical to simplifying the coverage analysis by ensuring graph path-based
415418
queries (`is_dominated_by()`, `predecessors`, `successors`, etc.) have branch (control flow)
@@ -418,10 +421,11 @@ significance.
418421
To visualize the `CoverageGraph`, you can generate a _graphviz_ `*.dot`
419422
file with the following `rustc` flags:[^graphviz-dark-mode]
420423

421-
[^graphviz-dark-mode]: This image also applies `-Z graphviz-dark-mode`, to
422-
produce a Graphviz document with "dark mode" styling. If you use a dark mode or
423-
theme in your development environment, you will probably want to use this
424-
option so you can review the graphviz output without straining your vision.
424+
[^graphviz-dark-mode]:
425+
This image also applies `-Z graphviz-dark-mode`, to
426+
produce a Graphviz document with "dark mode" styling. If you use a dark mode or
427+
theme in your development environment, you will probably want to use this
428+
option so you can review the graphviz output without straining your vision.
425429

426430
```shell
427431
$ rustc -Z instrument-coverage -Z dump-mir=InstrumentCoverage \
@@ -448,19 +452,19 @@ directional edges (the arrows) leading from each node to its `successors()`.
448452
The nodes contain information in sections:
449453

450454
1. The gray header has a label showing the BCB ID (or _index_ for looking up
451-
its `BasicCoverageBlockData`).
455+
its `BasicCoverageBlockData`).
452456
2. The first content section shows the assigned `Counter` or `Expression` for
453-
each contiguous section of code. (There may be more than one `Expression`
454-
incremented by the same `Counter` for discontiguous sections of code representing
455-
the same sequential actions.) Note the code is represented by the line and
456-
column ranges (for example: `52:28-52:33`, representing the original source
457-
line 52, for columns 28-33). These are followed by the MIR `Statement` or
458-
`Terminator` represented by that source range. (How these coverage regions
459-
are determined is discussed in the following section.)
457+
each contiguous section of code. (There may be more than one `Expression`
458+
incremented by the same `Counter` for discontiguous sections of code representing
459+
the same sequential actions.) Note the code is represented by the line and
460+
column ranges (for example: `52:28-52:33`, representing the original source
461+
line 52, for columns 28-33). These are followed by the MIR `Statement` or
462+
`Terminator` represented by that source range. (How these coverage regions
463+
are determined is discussed in the following section.)
460464
3. The final section(s) show the MIR `BasicBlock`s (by ID/index and its
461-
`TerminatorKind`) contained in this BCB. The last BCB is separated out because
462-
its `successors()` determine the edges leading out of the BCB, and into
463-
the `leading_bb()` (first `BasicBlock`) of each successor BCB.
465+
`TerminatorKind`) contained in this BCB. The last BCB is separated out because
466+
its `successors()` determine the edges leading out of the BCB, and into
467+
the `leading_bb()` (first `BasicBlock`) of each successor BCB.
464468

465469
Note, to find the `BasicCoverageBlock` from a final BCB `Terminator`'s
466470
successor `BasicBlock`, there is an index and helper
@@ -572,7 +576,7 @@ incoming edges. Given the following graph, for example, the count for
572576

573577
In this situation, BCB node `B` may require an edge counter for its
574578
"edge from A", and that edge might be computed from an `Expression`,
575-
`Counter(A) - Counter(C)`. But an expression for the BCB _node_ `B`
579+
`Counter(A) - Counter(C)`. But an expression for the BCB _node_ `B`
576580
would be the sum of all incoming edges:
577581

578582
```text

0 commit comments

Comments
 (0)