Update some outdated descriptions of coverage instrumentation
This commit is contained in:
parent
1f674ff57b
commit
c6f1dc58ba
|
|
@ -73,21 +73,21 @@ When compiling with `-C instrument-coverage`,
|
|||
Coverage instrumentation is performed on the MIR with a [MIR pass][mir-passes]
|
||||
called [`InstrumentCoverage`][mir-instrument-coverage]. This MIR pass analyzes
|
||||
the control flow graph (CFG)--represented by MIR `BasicBlock`s--to identify
|
||||
code branches, and injects additional [`Coverage`][coverage-statement]
|
||||
statements into the `BasicBlock`s.
|
||||
code branches, attaches [`FunctionCoverageInfo`] to the function's body,
|
||||
and injects additional [`Coverage`][coverage-statement] statements into the
|
||||
`BasicBlock`s.
|
||||
|
||||
A MIR `Coverage` statement is a virtual instruction that indicates a counter
|
||||
should be incremented when its adjacent statements are executed, to count
|
||||
a span of code ([`CodeRegion`][code-region]). It counts the number of times a
|
||||
branch is executed, and also specifies the exact location of that code span in
|
||||
the Rust source code.
|
||||
branch is executed, and is referred to by coverage mappings in the function's
|
||||
coverage-info struct.
|
||||
|
||||
Note that many of these `Coverage` statements will _not_ be converted into
|
||||
Note that many coverage counters will _not_ be converted into
|
||||
physical counters (or any other executable instructions) in the final binary.
|
||||
Some of them will be (see [`CoverageKind::Counter`]),
|
||||
Some of them will be (see [`CoverageKind::CounterIncrement`]),
|
||||
but other counters can be computed on the fly, when generating a coverage
|
||||
report, by mapping a `CodeRegion` to a
|
||||
[`CoverageKind::Expression`].
|
||||
report, by mapping a `CodeRegion` to a coverage-counter _expression_.
|
||||
|
||||
As an example:
|
||||
|
||||
|
|
@ -121,8 +121,8 @@ determines when to break out of a loop (a `while` condition, or an `if` or
|
|||
`match` with a `break`). In MIR, this is typically lowered to a `SwitchInt`,
|
||||
with one branch to stay in the loop, and another branch to break out of the
|
||||
loop. The branch that breaks out will almost always execute less often,
|
||||
so `InstrumentCoverage` chooses to add a `Counter` to that branch, and an
|
||||
`Expression(continue) = Counter(loop) - Counter(break)` to the branch that
|
||||
so `InstrumentCoverage` chooses to add a `CounterIncrement` to that branch, and
|
||||
uses an expression (`Counter(loop) - Counter(break)`) for the branch that
|
||||
continues.
|
||||
|
||||
The `InstrumentCoverage` MIR pass is documented in
|
||||
|
|
@ -130,9 +130,9 @@ The `InstrumentCoverage` MIR pass is documented in
|
|||
|
||||
[mir-passes]: mir/passes.md
|
||||
[mir-instrument-coverage]: https://github.com/rust-lang/rust/tree/master/compiler/rustc_mir_transform/src/coverage
|
||||
[`FunctionCoverageInfo`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/coverage/struct.FunctionCoverageInfo.html
|
||||
[code-region]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/coverage/struct.CodeRegion.html
|
||||
[`CoverageKind::Counter`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/coverage/enum.CoverageKind.html#variant.Counter
|
||||
[`CoverageKind::Expression`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/coverage/enum.CoverageKind.html#variant.Expression
|
||||
[`CoverageKind::CounterIncrement`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/coverage/enum.CoverageKind.html#variant.CounterIncrement
|
||||
[coverage-statement]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/enum.StatementKind.html#variant.Coverage
|
||||
[instrument-coverage-pass-details]: #implementation-details-of-the-instrumentcoverage-mir-pass
|
||||
|
||||
|
|
@ -150,40 +150,38 @@ MIR `Statement` into some backend-specific action or instruction.
|
|||
match statement.kind {
|
||||
...
|
||||
mir::StatementKind::Coverage(box ref coverage) => {
|
||||
self.codegen_coverage(&mut bx, coverage.clone(), statement.source_info.scope);
|
||||
bx
|
||||
self.codegen_coverage(bx, coverage, statement.source_info.scope);
|
||||
}
|
||||
```
|
||||
|
||||
`codegen_coverage()` handles each `CoverageKind` as follows:
|
||||
`codegen_coverage()` handles inlined statements and then forwards the coverage
|
||||
statement to [`Builder::add_coverage`], which handles each `CoverageKind` as
|
||||
follows:
|
||||
|
||||
- For all `CoverageKind`s, Coverage data (counter ID, expression equation
|
||||
and ID, and code regions) are passed to the backend's `Builder`, to
|
||||
populate data structures that will be used to generate the crate's
|
||||
"Coverage Map". (See the [`FunctionCoverage`][function-coverage] `struct`.)
|
||||
- For `CoverageKind::Counter`s, an instruction is injected in the backend
|
||||
|
||||
- For both `CounterIncrement` and `ExpressionUsed`, the underlying counter or
|
||||
expression ID is passed through to the corresponding [`FunctionCoverage`]
|
||||
struct to indicate that the corresponding regions of code were not removed
|
||||
by MIR optimizations.
|
||||
- For `CoverageKind::CounterIncrement`s, an instruction is injected in the backend
|
||||
IR to increment the physical counter, by calling the `BuilderMethod`
|
||||
[`instrprof_increment()`][instrprof-increment].
|
||||
|
||||
```rust
|
||||
pub fn codegen_coverage(&self, bx: &mut Bx, coverage: Coverage, scope: SourceScope) {
|
||||
fn add_coverage(&mut self, instance: Instance<'tcx>, coverage: &Coverage) {
|
||||
...
|
||||
let instance = ... // the scoped instance (current or inlined function)
|
||||
let Coverage { kind, code_region } = coverage;
|
||||
match kind {
|
||||
CoverageKind::Counter { function_source_hash, id } => {
|
||||
...
|
||||
bx.add_coverage_counter(instance, id, code_region);
|
||||
let Coverage { kind } = coverage;
|
||||
match *kind {
|
||||
CoverageKind::CounterIncrement { id } => {
|
||||
func_coverage.mark_counter_id_seen(id);
|
||||
...
|
||||
bx.instrprof_increment(fn_name, hash, num_counters, index);
|
||||
}
|
||||
CoverageKind::Expression { id, lhs, op, rhs } => {
|
||||
bx.add_coverage_counter_expression(instance, id, lhs, op, rhs, code_region);
|
||||
CoverageKind::ExpressionUsed { id } => {
|
||||
func_coverage.mark_expression_id_seen(id);
|
||||
}
|
||||
CoverageKind::Unreachable => {
|
||||
bx.add_coverage_unreachable(
|
||||
instance,
|
||||
code_region.expect(...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> The function name `instrprof_increment()` is taken from the LLVM intrinsic
|
||||
|
|
@ -199,7 +197,8 @@ statements is only implemented for LLVM, at this time.
|
|||
[backend-lowering-mir]: backend/lowering-mir.md
|
||||
[codegen-statement]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/mir/struct.FunctionCx.html#method.codegen_statement
|
||||
[codegen-coverage]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/mir/struct.FunctionCx.html#method.codegen_coverage
|
||||
[function-coverage]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_llvm/coverageinfo/map_data/struct.FunctionCoverage.html
|
||||
[`Builder::add_coverage`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_llvm/builder/struct.Builder.html#method.add_coverage
|
||||
[`FunctionCoverage`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_llvm/coverageinfo/map_data/struct.FunctionCoverage.html
|
||||
[instrprof-increment]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/traits/trait.BuilderMethods.html#tymethod.instrprof_increment
|
||||
|
||||
### Coverage Map Generation
|
||||
|
|
@ -327,9 +326,10 @@ Instrumentor::new(&self.name(), tcx, mir_body).inject_counters();
|
|||
The `CoverageGraph` is a coverage-specific simplification of the MIR control
|
||||
flow graph (CFG). Its nodes are [`BasicCoverageBlock`s][bcb], which
|
||||
encompass one or more sequentially-executed MIR `BasicBlock`s
|
||||
(with no internal branching), plus a `CoverageKind` counter (to
|
||||
be added, via coverage analysis), and an optional set of additional counters
|
||||
to count incoming edges (if there are more than one).
|
||||
(with no internal branching).
|
||||
|
||||
Nodes and edges in the graph can have associated [`BcbCounter`]s, which are
|
||||
stored in [`CoverageCounters`].
|
||||
|
||||
The `Instrumentor`'s `inject_counters()` uses the `CoverageGraph` to
|
||||
compute the best places to inject coverage counters, as MIR `Statement`s,
|
||||
|
|
@ -338,16 +338,15 @@ with the following steps:
|
|||
1. [`generate_coverage_spans()`][generate-coverage-spans] computes the minimum set of distinct,
|
||||
non-branching code regions, from the MIR. These `CoverageSpan`s
|
||||
represent a span of code that must be counted.
|
||||
2. [`make_bcb_counters()`][make-bcb-counters] generates `CoverageKind::Counter`s and
|
||||
`CoverageKind::Expression`s for each `CoverageSpan`, plus additional
|
||||
`intermediate_expressions`[^intermediate-expressions], not associated with any `CodeRegion`, but
|
||||
2. [`make_bcb_counters()`][make-bcb-counters] generates `BcbCounter::Counter`s and
|
||||
`BcbCounter::Expression`s for each `CoverageSpan`, plus additional
|
||||
_intermediate expressions_[^intermediate-expressions] that are not associated
|
||||
with any `CodeRegion`, but
|
||||
are required to compute a final `Expression` value for a `CodeRegion`.
|
||||
3. Inject the new counters into the MIR, as new `StatementKind::Coverage`
|
||||
statements. This is done by three distinct functions:
|
||||
- `inject_coverage_span_counters()`
|
||||
- `inject_indirect_counters()`
|
||||
- `inject_intermediate_expression()`, called for each intermediate expression
|
||||
returned from `make_bcb_counters()`
|
||||
statements.
|
||||
4. Attach all other necessary coverage information to the function's body as
|
||||
[`FunctionCoverageInfo`].
|
||||
|
||||
[^intermediate-expressions]: Intermediate expressions are sometimes required
|
||||
because `Expression`s are limited to binary additions or subtractions. For
|
||||
|
|
@ -359,7 +358,8 @@ intermediate expression for `B - C`.
|
|||
[coverage-graph]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/coverage/graph/struct.CoverageGraph.html
|
||||
[inject-counters]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/coverage/struct.Instrumentor.html#method.inject_counters
|
||||
[bcb]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/coverage/graph/struct.BasicCoverageBlock.html
|
||||
[debug]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/coverage/debug
|
||||
[`BcbCounter`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/coverage/counters/enum.BcbCounter.html
|
||||
[`CoverageCounters`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/coverage/counters/struct.CoverageCounters.html
|
||||
[generate-coverage-spans]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/coverage/spans/struct.CoverageSpans.html#method.generate_coverage_spans
|
||||
[make-bcb-counters]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/coverage/counters/struct.BcbCounters.html#method.make_bcb_counters
|
||||
|
||||
|
|
@ -505,34 +505,3 @@ its `Counter` or `Expression`.
|
|||
|
||||
[bcb-counters]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/coverage/counters/struct.BcbCounters.html
|
||||
[traverse-coverage-graph-with-loops]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/coverage/graph/struct.TraverseCoverageGraphWithLoops.html
|
||||
|
||||
### Injecting counters into a MIR `BasicBlock`
|
||||
|
||||
With the refined `CoverageSpan`s, and after all `Counter`s and `Expression`s are
|
||||
created, the final step is to inject the `StatementKind::Coverage` statements
|
||||
into the MIR. There are three distinct sources, handled by the following
|
||||
functions:
|
||||
|
||||
- [`inject_coverage_span_counters()`][inject-coverage-span-counters] injects the
|
||||
counter from each `CoverageSpan`'s BCB.
|
||||
- [`inject_indirect_counters()`][inject-indirect-counters] injects counters
|
||||
for any BCB not assigned to a `CoverageSpan`, and for all edge counters.
|
||||
These counters don't have `CoverageSpan`s.
|
||||
- [`inject_intermediate_expression()`][inject-intermediate-expression] injects
|
||||
the intermediate expressions returned from `make_bcb_counters()`. These
|
||||
counters aren't associated with any BCB, edge, or `CoverageSpan`.
|
||||
|
||||
These three functions inject the `Coverage` statements into the MIR.
|
||||
`Counter`s and `Expression`s with `CoverageSpan`s add `Coverage` statements
|
||||
to a corresponding `BasicBlock`, with a `CodeRegion` computed from the
|
||||
refined `Span` and current `SourceMap`.
|
||||
|
||||
All other `Coverage` statements have a `CodeRegion` of `None`, but they
|
||||
still must be injected because they contribute to other `Expression`s.
|
||||
|
||||
Finally, edge's with a `CoverageKind::Counter` require a new `BasicBlock`,
|
||||
so the counter is only incremented when traversing the branch edge.
|
||||
|
||||
[inject-coverage-span-counters]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/coverage/struct.Instrumentor.html#method.inject_coverage_span_counters
|
||||
[inject-indirect-counters]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/coverage/struct.Instrumentor.html#method.inject_indirect_counters
|
||||
[inject-intermediate-expression]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/coverage/fn.inject_intermediate_expression.html
|
||||
|
|
|
|||
Loading…
Reference in New Issue