Skip to content

Commit abc2c70

Browse files
committed
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, because the coverage output is less noisy. For internal debugging only. If other code changes would make it hard to keep supporting this flag, remove it.
1 parent e23ae72 commit abc2c70

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)