Skip to content

Commit

Permalink
coverage: Add debugging flag -Zcoverage-options=no-mir-spans
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Zalathar committed Jun 17, 2024
1 parent e23ae72 commit abc2c70
Show file tree
Hide file tree
Showing 10 changed files with 234 additions and 20 deletions.
2 changes: 1 addition & 1 deletion compiler/rustc_interface/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -761,7 +761,7 @@ fn test_unstable_options_tracking_hash() {
})
);
tracked!(codegen_backend, Some("abc".to_string()));
tracked!(coverage_options, CoverageOptions { level: CoverageLevel::Mcdc });
tracked!(coverage_options, CoverageOptions { level: CoverageLevel::Mcdc, no_mir_spans: true });
tracked!(crate_attr, vec!["abc".to_string()]);
tracked!(cross_crate_inline_threshold, InliningThreshold::Always);
tracked!(debug_info_for_profiling, true);
Expand Down
33 changes: 19 additions & 14 deletions compiler/rustc_mir_transform/src/coverage/mappings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use rustc_index::bit_set::BitSet;
use rustc_index::IndexVec;
use rustc_middle::mir::coverage::{BlockMarkerId, BranchSpan, ConditionInfo, CoverageKind};
use rustc_middle::mir::{self, BasicBlock, StatementKind};
use rustc_middle::ty::TyCtxt;
use rustc_span::Span;

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

/// Extracts coverage-relevant spans from MIR, and associates them with
/// their corresponding BCBs.
pub(super) fn extract_all_mapping_info_from_mir(
mir_body: &mir::Body<'_>,
pub(super) fn extract_all_mapping_info_from_mir<'tcx>(
tcx: TyCtxt<'tcx>,
mir_body: &mir::Body<'tcx>,
hir_info: &ExtractedHirInfo,
basic_coverage_blocks: &CoverageGraph,
) -> ExtractedMappings {
if hir_info.is_async_fn {
let mut code_mappings = vec![];
let mut branch_pairs = vec![];
let mut mcdc_bitmap_bytes = 0;
let mut mcdc_branches = vec![];
let mut mcdc_decisions = vec![];

if hir_info.is_async_fn || tcx.sess.coverage_no_mir_spans() {
// An async function desugars into a function that returns a future,
// with the user code wrapped in a closure. Any spans in the desugared
// outer function will be unhelpful, so just keep the signature span
// and ignore all of the spans in the MIR body.
let mut mappings = ExtractedMappings::default();
//
// When debugging flag `-Zcoverage-options=no-mir-spans` is set, we need
// to give the same treatment to _all_ functions, because `llvm-cov`
// seems to ignore functions that don't have any ordinary code spans.
if let Some(span) = hir_info.fn_sig_span_extended {
mappings.code_mappings.push(CodeMapping { span, bcb: START_BCB });
code_mappings.push(CodeMapping { span, bcb: START_BCB });
}
return mappings;
} else {
// Extract coverage spans from MIR statements/terminators as normal.
extract_refined_covspans(mir_body, hir_info, basic_coverage_blocks, &mut code_mappings);
}

let mut code_mappings = vec![];
let mut branch_pairs = vec![];
let mut mcdc_bitmap_bytes = 0;
let mut mcdc_branches = vec![];
let mut mcdc_decisions = vec![];

extract_refined_covspans(mir_body, hir_info, basic_coverage_blocks, &mut code_mappings);

branch_pairs.extend(extract_branch_pairs(mir_body, hir_info, basic_coverage_blocks));

extract_mcdc_mappings(
Expand Down
8 changes: 6 additions & 2 deletions compiler/rustc_mir_transform/src/coverage/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,12 @@ fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir:

////////////////////////////////////////////////////
// Extract coverage spans and other mapping info from MIR.
let extracted_mappings =
mappings::extract_all_mapping_info_from_mir(mir_body, &hir_info, &basic_coverage_blocks);
let extracted_mappings = mappings::extract_all_mapping_info_from_mir(
tcx,
mir_body,
&hir_info,
&basic_coverage_blocks,
);

////////////////////////////////////////////////////
// Create an optimized mix of `Counter`s and `Expression`s for the `CoverageGraph`. Ensure
Expand Down
9 changes: 8 additions & 1 deletion compiler/rustc_session/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,14 @@ pub enum InstrumentCoverage {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
pub struct CoverageOptions {
pub level: CoverageLevel,
// Other boolean or enum-valued options might be added here.

/// `-Z coverage-options=no-mir-spans`: Don't extract block coverage spans
/// from MIR statements/terminators, making it easier to inspect/debug
/// branch and MC/DC coverage mappings.
///
/// For internal debugging only. If other code changes would make it hard
/// to keep supporting this flag, remove it.
pub no_mir_spans: bool,
}

/// Controls whether branch coverage or MC/DC coverage is enabled.
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_session/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,8 @@ mod desc {
pub const parse_optimization_fuel: &str = "crate=integer";
pub const parse_dump_mono_stats: &str = "`markdown` (default) or `json`";
pub const parse_instrument_coverage: &str = parse_bool;
pub const parse_coverage_options: &str = "`block` | `branch` | `condition` | `mcdc`";
pub const parse_coverage_options: &str =
"`block` | `branch` | `condition` | `mcdc` | `no-mir-spans`";
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`";
pub const parse_unpretty: &str = "`string` or `string=string`";
pub const parse_treat_err_as_bug: &str = "either no value or a non-negative number";
Expand Down Expand Up @@ -963,6 +964,7 @@ mod parse {
"branch" => slot.level = CoverageLevel::Branch,
"condition" => slot.level = CoverageLevel::Condition,
"mcdc" => slot.level = CoverageLevel::Mcdc,
"no-mir-spans" => slot.no_mir_spans = true,
_ => return false,
}
}
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_session/src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,11 @@ impl Session {
&& self.opts.unstable_opts.coverage_options.level >= CoverageLevel::Mcdc
}

/// True if `-Zcoverage-options=no-mir-spans` was passed.
pub fn coverage_no_mir_spans(&self) -> bool {
self.opts.unstable_opts.coverage_options.no_mir_spans
}

pub fn is_sanitizer_cfi_enabled(&self) -> bool {
self.opts.unstable_opts.sanitizer.contains(SanitizerSet::CFI)
}
Expand Down
52 changes: 52 additions & 0 deletions tests/coverage/branch/no-mir-spans.cov-map
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
Function name: no_mir_spans::while_cond
Raw bytes (16): 0x[01, 01, 00, 02, 01, 10, 01, 00, 11, 20, 05, 09, 04, 0b, 00, 10]
Number of files: 1
- file 0 => global file 1
Number of expressions: 0
Number of file 0 mappings: 2
- Code(Counter(0)) at (prev + 16, 1) to (start + 0, 17)
- Branch { true: Counter(1), false: Counter(2) } at (prev + 4, 11) to (start + 0, 16)
true = c1
false = c2

Function name: no_mir_spans::while_cond_not
Raw bytes (16): 0x[01, 01, 00, 02, 01, 19, 01, 00, 15, 20, 09, 05, 04, 0b, 00, 14]
Number of files: 1
- file 0 => global file 1
Number of expressions: 0
Number of file 0 mappings: 2
- Code(Counter(0)) at (prev + 25, 1) to (start + 0, 21)
- Branch { true: Counter(2), false: Counter(1) } at (prev + 4, 11) to (start + 0, 20)
true = c2
false = c1

Function name: no_mir_spans::while_op_and
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]
Number of files: 1
- file 0 => global file 1
Number of expressions: 1
- expression 0 operands: lhs = Counter(2), rhs = Counter(3)
Number of file 0 mappings: 3
- Code(Counter(0)) at (prev + 34, 1) to (start + 0, 19)
- Branch { true: Counter(2), false: Counter(1) } at (prev + 5, 11) to (start + 0, 16)
true = c2
false = c1
- Branch { true: Expression(0, Sub), false: Counter(3) } at (prev + 0, 20) to (start + 0, 25)
true = (c2 - c3)
false = c3

Function name: no_mir_spans::while_op_or
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]
Number of files: 1
- file 0 => global file 1
Number of expressions: 1
- expression 0 operands: lhs = Counter(2), rhs = Counter(3)
Number of file 0 mappings: 3
- Code(Counter(0)) at (prev + 45, 1) to (start + 0, 18)
- Branch { true: Counter(1), false: Counter(2) } at (prev + 5, 11) to (start + 0, 16)
true = c1
false = c2
- Branch { true: Counter(3), false: Expression(0, Sub) } at (prev + 0, 20) to (start + 0, 25)
true = c3
false = (c2 - c3)

77 changes: 77 additions & 0 deletions tests/coverage/branch/no-mir-spans.coverage
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
LL| |#![feature(coverage_attribute)]
LL| |//@ edition: 2021
LL| |//@ compile-flags: -Zcoverage-options=branch,no-mir-spans
LL| |//@ llvm-cov-flags: --show-branches=count
LL| |
LL| |// Tests the behaviour of the `-Zcoverage-options=no-mir-spans` debugging flag.
LL| |// The actual code below is just some non-trivial code copied from another test
LL| |// (`while.rs`), and has no particular significance.
LL| |
LL| |macro_rules! no_merge {
LL| | () => {
LL| | for _ in 0..1 {}
LL| | };
LL| |}
LL| |
LL| 1|fn while_cond() {
LL| | no_merge!();
LL| |
LL| | let mut a = 8;
LL| | while a > 0 {
------------------
| Branch (LL:11): [True: 8, False: 1]
------------------
LL| | a -= 1;
LL| | }
LL| |}
LL| |
LL| 1|fn while_cond_not() {
LL| | no_merge!();
LL| |
LL| | let mut a = 8;
LL| | while !(a == 0) {
------------------
| Branch (LL:11): [True: 8, False: 1]
------------------
LL| | a -= 1;
LL| | }
LL| |}
LL| |
LL| 1|fn while_op_and() {
LL| | no_merge!();
LL| |
LL| | let mut a = 8;
LL| | let mut b = 4;
LL| | while a > 0 && b > 0 {
------------------
| Branch (LL:11): [True: 5, False: 0]
| Branch (LL:20): [True: 4, False: 1]
------------------
LL| | a -= 1;
LL| | b -= 1;
LL| | }
LL| |}
LL| |
LL| 1|fn while_op_or() {
LL| | no_merge!();
LL| |
LL| | let mut a = 4;
LL| | let mut b = 8;
LL| | while a > 0 || b > 0 {
------------------
| Branch (LL:11): [True: 4, False: 5]
| Branch (LL:20): [True: 4, False: 1]
------------------
LL| | a -= 1;
LL| | b -= 1;
LL| | }
LL| |}
LL| |
LL| |#[coverage(off)]
LL| |fn main() {
LL| | while_cond();
LL| | while_cond_not();
LL| | while_op_and();
LL| | while_op_or();
LL| |}

62 changes: 62 additions & 0 deletions tests/coverage/branch/no-mir-spans.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#![feature(coverage_attribute)]
//@ edition: 2021
//@ compile-flags: -Zcoverage-options=branch,no-mir-spans
//@ llvm-cov-flags: --show-branches=count

// Tests the behaviour of the `-Zcoverage-options=no-mir-spans` debugging flag.
// The actual code below is just some non-trivial code copied from another test
// (`while.rs`), and has no particular significance.

macro_rules! no_merge {
() => {
for _ in 0..1 {}
};
}

fn while_cond() {
no_merge!();

let mut a = 8;
while a > 0 {
a -= 1;
}
}

fn while_cond_not() {
no_merge!();

let mut a = 8;
while !(a == 0) {
a -= 1;
}
}

fn while_op_and() {
no_merge!();

let mut a = 8;
let mut b = 4;
while a > 0 && b > 0 {
a -= 1;
b -= 1;
}
}

fn while_op_or() {
no_merge!();

let mut a = 4;
let mut b = 8;
while a > 0 || b > 0 {
a -= 1;
b -= 1;
}
}

#[coverage(off)]
fn main() {
while_cond();
while_cond_not();
while_op_and();
while_op_or();
}
2 changes: 1 addition & 1 deletion tests/ui/instrument-coverage/coverage-options.bad.stderr
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
error: incorrect value `bad` for unstable option `coverage-options` - `block` | `branch` | `condition` | `mcdc` was expected
error: incorrect value `bad` for unstable option `coverage-options` - `block` | `branch` | `condition` | `mcdc` | `no-mir-spans` was expected

0 comments on commit abc2c70

Please sign in to comment.