Skip to content

Commit bbec736

Browse files
Rollup merge of rust-lang#126587 - Zalathar:no-mir-spans, r=oli-obk
coverage: Add debugging flag `-Zcoverage-options=no-mir-spans` When set, this flag skips the code that normally extracts coverage spans from MIR statements and terminators. That sometimes makes it easier to debug branch coverage and MC/DC coverage instrumentation, because the coverage output is less noisy. For internal debugging only. If future code changes would make it hard to keep supporting this flag, it should be removed at that time. `@rustbot` label +A-code-coverage
2 parents ab2cada + abc2c70 commit bbec736

File tree

10 files changed

+234
-20
lines changed

10 files changed

+234
-20
lines changed

compiler/rustc_interface/src/tests.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -761,7 +761,7 @@ fn test_unstable_options_tracking_hash() {
761761
})
762762
);
763763
tracked!(codegen_backend, Some("abc".to_string()));
764-
tracked!(coverage_options, CoverageOptions { level: CoverageLevel::Mcdc });
764+
tracked!(coverage_options, CoverageOptions { level: CoverageLevel::Mcdc, no_mir_spans: true });
765765
tracked!(crate_attr, vec!["abc".to_string()]);
766766
tracked!(cross_crate_inline_threshold, InliningThreshold::Always);
767767
tracked!(debug_info_for_profiling, true);

compiler/rustc_mir_transform/src/coverage/mappings.rs

+19-14
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use rustc_index::bit_set::BitSet;
55
use rustc_index::IndexVec;
66
use rustc_middle::mir::coverage::{BlockMarkerId, BranchSpan, ConditionInfo, CoverageKind};
77
use rustc_middle::mir::{self, BasicBlock, StatementKind};
8+
use rustc_middle::ty::TyCtxt;
89
use rustc_span::Span;
910

1011
use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph, START_BCB};
@@ -63,31 +64,35 @@ pub(super) struct ExtractedMappings {
6364

6465
/// Extracts coverage-relevant spans from MIR, and associates them with
6566
/// their corresponding BCBs.
66-
pub(super) fn extract_all_mapping_info_from_mir(
67-
mir_body: &mir::Body<'_>,
67+
pub(super) fn extract_all_mapping_info_from_mir<'tcx>(
68+
tcx: TyCtxt<'tcx>,
69+
mir_body: &mir::Body<'tcx>,
6870
hir_info: &ExtractedHirInfo,
6971
basic_coverage_blocks: &CoverageGraph,
7072
) -> ExtractedMappings {
71-
if hir_info.is_async_fn {
73+
let mut code_mappings = vec![];
74+
let mut branch_pairs = vec![];
75+
let mut mcdc_bitmap_bytes = 0;
76+
let mut mcdc_branches = vec![];
77+
let mut mcdc_decisions = vec![];
78+
79+
if hir_info.is_async_fn || tcx.sess.coverage_no_mir_spans() {
7280
// An async function desugars into a function that returns a future,
7381
// with the user code wrapped in a closure. Any spans in the desugared
7482
// outer function will be unhelpful, so just keep the signature span
7583
// and ignore all of the spans in the MIR body.
76-
let mut mappings = ExtractedMappings::default();
84+
//
85+
// When debugging flag `-Zcoverage-options=no-mir-spans` is set, we need
86+
// to give the same treatment to _all_ functions, because `llvm-cov`
87+
// seems to ignore functions that don't have any ordinary code spans.
7788
if let Some(span) = hir_info.fn_sig_span_extended {
78-
mappings.code_mappings.push(CodeMapping { span, bcb: START_BCB });
89+
code_mappings.push(CodeMapping { span, bcb: START_BCB });
7990
}
80-
return mappings;
91+
} else {
92+
// Extract coverage spans from MIR statements/terminators as normal.
93+
extract_refined_covspans(mir_body, hir_info, basic_coverage_blocks, &mut code_mappings);
8194
}
8295

83-
let mut code_mappings = vec![];
84-
let mut branch_pairs = vec![];
85-
let mut mcdc_bitmap_bytes = 0;
86-
let mut mcdc_branches = vec![];
87-
let mut mcdc_decisions = vec![];
88-
89-
extract_refined_covspans(mir_body, hir_info, basic_coverage_blocks, &mut code_mappings);
90-
9196
branch_pairs.extend(extract_branch_pairs(mir_body, hir_info, basic_coverage_blocks));
9297

9398
extract_mcdc_mappings(

compiler/rustc_mir_transform/src/coverage/mod.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,12 @@ fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir:
7171

7272
////////////////////////////////////////////////////
7373
// Extract coverage spans and other mapping info from MIR.
74-
let extracted_mappings =
75-
mappings::extract_all_mapping_info_from_mir(mir_body, &hir_info, &basic_coverage_blocks);
74+
let extracted_mappings = mappings::extract_all_mapping_info_from_mir(
75+
tcx,
76+
mir_body,
77+
&hir_info,
78+
&basic_coverage_blocks,
79+
);
7680

7781
////////////////////////////////////////////////////
7882
// Create an optimized mix of `Counter`s and `Expression`s for the `CoverageGraph`. Ensure

compiler/rustc_session/src/config.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,14 @@ pub enum InstrumentCoverage {
149149
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
150150
pub struct CoverageOptions {
151151
pub level: CoverageLevel,
152-
// Other boolean or enum-valued options might be added here.
152+
153+
/// `-Z coverage-options=no-mir-spans`: Don't extract block coverage spans
154+
/// from MIR statements/terminators, making it easier to inspect/debug
155+
/// branch and MC/DC coverage mappings.
156+
///
157+
/// For internal debugging only. If other code changes would make it hard
158+
/// to keep supporting this flag, remove it.
159+
pub no_mir_spans: bool,
153160
}
154161

155162
/// Controls whether branch coverage or MC/DC coverage is enabled.

compiler/rustc_session/src/options.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -395,7 +395,8 @@ mod desc {
395395
pub const parse_optimization_fuel: &str = "crate=integer";
396396
pub const parse_dump_mono_stats: &str = "`markdown` (default) or `json`";
397397
pub const parse_instrument_coverage: &str = parse_bool;
398-
pub const parse_coverage_options: &str = "`block` | `branch` | `condition` | `mcdc`";
398+
pub const parse_coverage_options: &str =
399+
"`block` | `branch` | `condition` | `mcdc` | `no-mir-spans`";
399400
pub const parse_instrument_xray: &str = "either a boolean (`yes`, `no`, `on`, `off`, etc), or a comma separated list of settings: `always` or `never` (mutually exclusive), `ignore-loops`, `instruction-threshold=N`, `skip-entry`, `skip-exit`";
400401
pub const parse_unpretty: &str = "`string` or `string=string`";
401402
pub const parse_treat_err_as_bug: &str = "either no value or a non-negative number";
@@ -963,6 +964,7 @@ mod parse {
963964
"branch" => slot.level = CoverageLevel::Branch,
964965
"condition" => slot.level = CoverageLevel::Condition,
965966
"mcdc" => slot.level = CoverageLevel::Mcdc,
967+
"no-mir-spans" => slot.no_mir_spans = true,
966968
_ => return false,
967969
}
968970
}

compiler/rustc_session/src/session.rs

+5
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,11 @@ impl Session {
363363
&& self.opts.unstable_opts.coverage_options.level >= CoverageLevel::Mcdc
364364
}
365365

366+
/// True if `-Zcoverage-options=no-mir-spans` was passed.
367+
pub fn coverage_no_mir_spans(&self) -> bool {
368+
self.opts.unstable_opts.coverage_options.no_mir_spans
369+
}
370+
366371
pub fn is_sanitizer_cfi_enabled(&self) -> bool {
367372
self.opts.unstable_opts.sanitizer.contains(SanitizerSet::CFI)
368373
}
+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
Function name: no_mir_spans::while_cond
2+
Raw bytes (16): 0x[01, 01, 00, 02, 01, 10, 01, 00, 11, 20, 05, 09, 04, 0b, 00, 10]
3+
Number of files: 1
4+
- file 0 => global file 1
5+
Number of expressions: 0
6+
Number of file 0 mappings: 2
7+
- Code(Counter(0)) at (prev + 16, 1) to (start + 0, 17)
8+
- Branch { true: Counter(1), false: Counter(2) } at (prev + 4, 11) to (start + 0, 16)
9+
true = c1
10+
false = c2
11+
12+
Function name: no_mir_spans::while_cond_not
13+
Raw bytes (16): 0x[01, 01, 00, 02, 01, 19, 01, 00, 15, 20, 09, 05, 04, 0b, 00, 14]
14+
Number of files: 1
15+
- file 0 => global file 1
16+
Number of expressions: 0
17+
Number of file 0 mappings: 2
18+
- Code(Counter(0)) at (prev + 25, 1) to (start + 0, 21)
19+
- Branch { true: Counter(2), false: Counter(1) } at (prev + 4, 11) to (start + 0, 20)
20+
true = c2
21+
false = c1
22+
23+
Function name: no_mir_spans::while_op_and
24+
Raw bytes (25): 0x[01, 01, 01, 09, 0d, 03, 01, 22, 01, 00, 13, 20, 09, 05, 05, 0b, 00, 10, 20, 02, 0d, 00, 14, 00, 19]
25+
Number of files: 1
26+
- file 0 => global file 1
27+
Number of expressions: 1
28+
- expression 0 operands: lhs = Counter(2), rhs = Counter(3)
29+
Number of file 0 mappings: 3
30+
- Code(Counter(0)) at (prev + 34, 1) to (start + 0, 19)
31+
- Branch { true: Counter(2), false: Counter(1) } at (prev + 5, 11) to (start + 0, 16)
32+
true = c2
33+
false = c1
34+
- Branch { true: Expression(0, Sub), false: Counter(3) } at (prev + 0, 20) to (start + 0, 25)
35+
true = (c2 - c3)
36+
false = c3
37+
38+
Function name: no_mir_spans::while_op_or
39+
Raw bytes (25): 0x[01, 01, 01, 09, 0d, 03, 01, 2d, 01, 00, 12, 20, 05, 09, 05, 0b, 00, 10, 20, 0d, 02, 00, 14, 00, 19]
40+
Number of files: 1
41+
- file 0 => global file 1
42+
Number of expressions: 1
43+
- expression 0 operands: lhs = Counter(2), rhs = Counter(3)
44+
Number of file 0 mappings: 3
45+
- Code(Counter(0)) at (prev + 45, 1) to (start + 0, 18)
46+
- Branch { true: Counter(1), false: Counter(2) } at (prev + 5, 11) to (start + 0, 16)
47+
true = c1
48+
false = c2
49+
- Branch { true: Counter(3), false: Expression(0, Sub) } at (prev + 0, 20) to (start + 0, 25)
50+
true = c3
51+
false = (c2 - c3)
52+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
LL| |#![feature(coverage_attribute)]
2+
LL| |//@ edition: 2021
3+
LL| |//@ compile-flags: -Zcoverage-options=branch,no-mir-spans
4+
LL| |//@ llvm-cov-flags: --show-branches=count
5+
LL| |
6+
LL| |// Tests the behaviour of the `-Zcoverage-options=no-mir-spans` debugging flag.
7+
LL| |// The actual code below is just some non-trivial code copied from another test
8+
LL| |// (`while.rs`), and has no particular significance.
9+
LL| |
10+
LL| |macro_rules! no_merge {
11+
LL| | () => {
12+
LL| | for _ in 0..1 {}
13+
LL| | };
14+
LL| |}
15+
LL| |
16+
LL| 1|fn while_cond() {
17+
LL| | no_merge!();
18+
LL| |
19+
LL| | let mut a = 8;
20+
LL| | while a > 0 {
21+
------------------
22+
| Branch (LL:11): [True: 8, False: 1]
23+
------------------
24+
LL| | a -= 1;
25+
LL| | }
26+
LL| |}
27+
LL| |
28+
LL| 1|fn while_cond_not() {
29+
LL| | no_merge!();
30+
LL| |
31+
LL| | let mut a = 8;
32+
LL| | while !(a == 0) {
33+
------------------
34+
| Branch (LL:11): [True: 8, False: 1]
35+
------------------
36+
LL| | a -= 1;
37+
LL| | }
38+
LL| |}
39+
LL| |
40+
LL| 1|fn while_op_and() {
41+
LL| | no_merge!();
42+
LL| |
43+
LL| | let mut a = 8;
44+
LL| | let mut b = 4;
45+
LL| | while a > 0 && b > 0 {
46+
------------------
47+
| Branch (LL:11): [True: 5, False: 0]
48+
| Branch (LL:20): [True: 4, False: 1]
49+
------------------
50+
LL| | a -= 1;
51+
LL| | b -= 1;
52+
LL| | }
53+
LL| |}
54+
LL| |
55+
LL| 1|fn while_op_or() {
56+
LL| | no_merge!();
57+
LL| |
58+
LL| | let mut a = 4;
59+
LL| | let mut b = 8;
60+
LL| | while a > 0 || b > 0 {
61+
------------------
62+
| Branch (LL:11): [True: 4, False: 5]
63+
| Branch (LL:20): [True: 4, False: 1]
64+
------------------
65+
LL| | a -= 1;
66+
LL| | b -= 1;
67+
LL| | }
68+
LL| |}
69+
LL| |
70+
LL| |#[coverage(off)]
71+
LL| |fn main() {
72+
LL| | while_cond();
73+
LL| | while_cond_not();
74+
LL| | while_op_and();
75+
LL| | while_op_or();
76+
LL| |}
77+

tests/coverage/branch/no-mir-spans.rs

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#![feature(coverage_attribute)]
2+
//@ edition: 2021
3+
//@ compile-flags: -Zcoverage-options=branch,no-mir-spans
4+
//@ llvm-cov-flags: --show-branches=count
5+
6+
// Tests the behaviour of the `-Zcoverage-options=no-mir-spans` debugging flag.
7+
// The actual code below is just some non-trivial code copied from another test
8+
// (`while.rs`), and has no particular significance.
9+
10+
macro_rules! no_merge {
11+
() => {
12+
for _ in 0..1 {}
13+
};
14+
}
15+
16+
fn while_cond() {
17+
no_merge!();
18+
19+
let mut a = 8;
20+
while a > 0 {
21+
a -= 1;
22+
}
23+
}
24+
25+
fn while_cond_not() {
26+
no_merge!();
27+
28+
let mut a = 8;
29+
while !(a == 0) {
30+
a -= 1;
31+
}
32+
}
33+
34+
fn while_op_and() {
35+
no_merge!();
36+
37+
let mut a = 8;
38+
let mut b = 4;
39+
while a > 0 && b > 0 {
40+
a -= 1;
41+
b -= 1;
42+
}
43+
}
44+
45+
fn while_op_or() {
46+
no_merge!();
47+
48+
let mut a = 4;
49+
let mut b = 8;
50+
while a > 0 || b > 0 {
51+
a -= 1;
52+
b -= 1;
53+
}
54+
}
55+
56+
#[coverage(off)]
57+
fn main() {
58+
while_cond();
59+
while_cond_not();
60+
while_op_and();
61+
while_op_or();
62+
}
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
error: incorrect value `bad` for unstable option `coverage-options` - `block` | `branch` | `condition` | `mcdc` was expected
1+
error: incorrect value `bad` for unstable option `coverage-options` - `block` | `branch` | `condition` | `mcdc` | `no-mir-spans` was expected
22

0 commit comments

Comments
 (0)