about summary refs log tree commit diff
diff options
context:
space:
mode:
authorGuillaume Gomez <guillaume1.gomez@gmail.com>2024-06-18 15:30:46 +0200
committerGitHub <noreply@github.com>2024-06-18 15:30:46 +0200
commitbbec736f2d40855f2bc0f5e8dca1a05c19861bb1 (patch)
tree72c668aa192beed5d8960bd0cfcdaa0459b75714
parentab2cadabe17936fcc8df6f7a6a0aef723d762009 (diff)
parentabc2c702af0b7b2eb36fb35839a322f5789af141 (diff)
downloadrust-bbec736f2d40855f2bc0f5e8dca1a05c19861bb1.tar.gz
rust-bbec736f2d40855f2bc0f5e8dca1a05c19861bb1.zip
Rollup merge of #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
-rw-r--r--compiler/rustc_interface/src/tests.rs2
-rw-r--r--compiler/rustc_mir_transform/src/coverage/mappings.rs33
-rw-r--r--compiler/rustc_mir_transform/src/coverage/mod.rs8
-rw-r--r--compiler/rustc_session/src/config.rs9
-rw-r--r--compiler/rustc_session/src/options.rs4
-rw-r--r--compiler/rustc_session/src/session.rs5
-rw-r--r--tests/coverage/branch/no-mir-spans.cov-map52
-rw-r--r--tests/coverage/branch/no-mir-spans.coverage77
-rw-r--r--tests/coverage/branch/no-mir-spans.rs62
-rw-r--r--tests/ui/instrument-coverage/coverage-options.bad.stderr2
10 files changed, 234 insertions, 20 deletions
diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs
index 6538995926a..fa3e44e7744 100644
--- a/compiler/rustc_interface/src/tests.rs
+++ b/compiler/rustc_interface/src/tests.rs
@@ -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);
diff --git a/compiler/rustc_mir_transform/src/coverage/mappings.rs b/compiler/rustc_mir_transform/src/coverage/mappings.rs
index 0e209757100..759bb7c1f9d 100644
--- a/compiler/rustc_mir_transform/src/coverage/mappings.rs
+++ b/compiler/rustc_mir_transform/src/coverage/mappings.rs
@@ -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};
@@ -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(
diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs
index 419e39bc386..4a64d21f3d1 100644
--- a/compiler/rustc_mir_transform/src/coverage/mod.rs
+++ b/compiler/rustc_mir_transform/src/coverage/mod.rs
@@ -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
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index a622f1b577d..5f9c3a14d60 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -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.
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index fd4a3a9e6ce..145af50117c 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -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";
@@ -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,
             }
         }
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index 87bbfcf07c8..20e50ef1b4a 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -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)
     }
diff --git a/tests/coverage/branch/no-mir-spans.cov-map b/tests/coverage/branch/no-mir-spans.cov-map
new file mode 100644
index 00000000000..cb19211913f
--- /dev/null
+++ b/tests/coverage/branch/no-mir-spans.cov-map
@@ -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)
+
diff --git a/tests/coverage/branch/no-mir-spans.coverage b/tests/coverage/branch/no-mir-spans.coverage
new file mode 100644
index 00000000000..2cae98ed3ff
--- /dev/null
+++ b/tests/coverage/branch/no-mir-spans.coverage
@@ -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|       |}
+
diff --git a/tests/coverage/branch/no-mir-spans.rs b/tests/coverage/branch/no-mir-spans.rs
new file mode 100644
index 00000000000..acb268f2d45
--- /dev/null
+++ b/tests/coverage/branch/no-mir-spans.rs
@@ -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();
+}
diff --git a/tests/ui/instrument-coverage/coverage-options.bad.stderr b/tests/ui/instrument-coverage/coverage-options.bad.stderr
index 4a272cf97fb..1a6b30dc832 100644
--- a/tests/ui/instrument-coverage/coverage-options.bad.stderr
+++ b/tests/ui/instrument-coverage/coverage-options.bad.stderr
@@ -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