-
Notifications
You must be signed in to change notification settings - Fork 520
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Update some outdated descriptions of coverage instrumentation #1809
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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,18 +121,18 @@ 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 | ||
[more detail below][instrument-coverage-pass-details]. | ||
|
||
[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()` | ||
Comment on lines
-346
to
-350
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The three |
||
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 |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The extra fields mentioned here were moved out into side-tables in
CoverageCounters
in rust-lang/rust#114354.