Skip to content

Commit aced4dc

Browse files
committed
coverage: Add a synthetic test for when all spans are discarded
1 parent 837a25d commit aced4dc

File tree

8 files changed

+67
-3
lines changed

8 files changed

+67
-3
lines changed

compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs

+11
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ fn fill_region_tables<'tcx>(
132132
let make_cov_span = |span: Span| {
133133
spans::make_coverage_span(local_file_id, source_map, fn_cov_info, &source_file, span)
134134
};
135+
let discard_all = tcx.sess.coverage_discard_all_spans_in_codegen();
135136

136137
// For each counter/region pair in this function+file, convert it to a
137138
// form suitable for FFI.
@@ -141,7 +142,17 @@ fn fill_region_tables<'tcx>(
141142
// MIR opts, replace those occurrences with zero.
142143
let kind = kind.map_terms(|term| if is_zero_term(term) { CovTerm::Zero } else { term });
143144

145+
// Convert the `Span` into coordinates that we can pass to LLVM, or
146+
// discard the span if conversion fails. In rare, cases _all_ of a
147+
// function's spans are discarded, and the rest of coverage codegen
148+
// needs to handle that gracefully to avoid a repeat of #133606.
149+
// We don't have a good test case for triggering that organically, so
150+
// instead we set `-Zcoverage-options=discard-all-spans-in-codegen`
151+
// to force it to occur.
144152
let Some(cov_span) = make_cov_span(span) else { continue };
153+
if discard_all {
154+
continue;
155+
}
145156

146157
match kind {
147158
MappingKind::Code(term) => {

compiler/rustc_interface/src/tests.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -766,7 +766,11 @@ fn test_unstable_options_tracking_hash() {
766766
})
767767
);
768768
tracked!(codegen_backend, Some("abc".to_string()));
769-
tracked!(coverage_options, CoverageOptions { level: CoverageLevel::Mcdc, no_mir_spans: true });
769+
tracked!(coverage_options, CoverageOptions {
770+
level: CoverageLevel::Mcdc,
771+
no_mir_spans: true,
772+
discard_all_spans_in_codegen: true
773+
});
770774
tracked!(crate_attr, vec!["abc".to_string()]);
771775
tracked!(cross_crate_inline_threshold, InliningThreshold::Always);
772776
tracked!(debug_info_for_profiling, true);

compiler/rustc_session/src/config.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -147,18 +147,24 @@ pub enum InstrumentCoverage {
147147
Yes,
148148
}
149149

150-
/// Individual flag values controlled by `-Z coverage-options`.
150+
/// Individual flag values controlled by `-Zcoverage-options`.
151151
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
152152
pub struct CoverageOptions {
153153
pub level: CoverageLevel,
154154

155-
/// `-Z coverage-options=no-mir-spans`: Don't extract block coverage spans
155+
/// `-Zcoverage-options=no-mir-spans`: Don't extract block coverage spans
156156
/// from MIR statements/terminators, making it easier to inspect/debug
157157
/// branch and MC/DC coverage mappings.
158158
///
159159
/// For internal debugging only. If other code changes would make it hard
160160
/// to keep supporting this flag, remove it.
161161
pub no_mir_spans: bool,
162+
163+
/// `-Zcoverage-options=discard-all-spans-in-codegen`: During codgen,
164+
/// discard all coverage spans as though they were invalid. Needed by
165+
/// regression tests for #133606, because we don't have an easy way to
166+
/// reproduce it from actual source code.
167+
pub discard_all_spans_in_codegen: bool,
162168
}
163169

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

compiler/rustc_session/src/options.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1037,6 +1037,7 @@ pub mod parse {
10371037
"condition" => slot.level = CoverageLevel::Condition,
10381038
"mcdc" => slot.level = CoverageLevel::Mcdc,
10391039
"no-mir-spans" => slot.no_mir_spans = true,
1040+
"discard-all-spans-in-codegen" => slot.discard_all_spans_in_codegen = true,
10401041
_ => return false,
10411042
}
10421043
}

compiler/rustc_session/src/session.rs

+5
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,11 @@ impl Session {
352352
self.opts.unstable_opts.coverage_options.no_mir_spans
353353
}
354354

355+
/// True if `-Zcoverage-options=discard-all-spans-in-codegen` was passed.
356+
pub fn coverage_discard_all_spans_in_codegen(&self) -> bool {
357+
self.opts.unstable_opts.coverage_options.discard_all_spans_in_codegen
358+
}
359+
355360
pub fn is_sanitizer_cfi_enabled(&self) -> bool {
356361
self.opts.unstable_opts.sanitizer.contains(SanitizerSet::CFI)
357362
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
//@ edition: 2021
2+
3+
// Force this function to be generated in its home crate, so that it ends up
4+
// with normal coverage metadata.
5+
#[inline(never)]
6+
pub fn external_function() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
LL| |//@ edition: 2021
2+
LL| |
3+
LL| |// Force this function to be generated in its home crate, so that it ends up
4+
LL| |// with normal coverage metadata.
5+
LL| |#[inline(never)]
6+
LL| 1|pub fn external_function() {}
7+
+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//! Regression test for <https://github.com/rust-lang/rust/issues/133606>.
2+
//!
3+
//! In rare cases, all of a function's coverage spans are discarded at a late
4+
//! stage during codegen. When that happens, the subsequent code needs to take
5+
//! special care to avoid emitting coverage metadata that would cause `llvm-cov`
6+
//! to fail with a fatal error.
7+
//!
8+
//! We currently don't know of a concise way to reproduce that scenario with
9+
//! ordinary Rust source code, so instead we set a special testing-only flag to
10+
//! force it to occur.
11+
12+
//@ edition: 2021
13+
//@ compile-flags: -Zcoverage-options=discard-all-spans-in-codegen
14+
15+
// The `llvm-cov` tool will complain if the test binary ends up having no
16+
// coverage metadata at all. To prevent that, we also link to instrumented
17+
// code in an auxiliary crate that doesn't have the special flag set.
18+
19+
//@ aux-build: discard_all_helper.rs
20+
extern crate discard_all_helper;
21+
22+
fn main() {
23+
discard_all_helper::external_function();
24+
}

0 commit comments

Comments
 (0)