Skip to content

Commit

Permalink
coverage. Add limit options for mcdc
Browse files Browse the repository at this point in the history
  • Loading branch information
zhuyunxing committed Jun 20, 2024
1 parent 458c1fc commit b268271
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 29 deletions.
59 changes: 32 additions & 27 deletions compiler/rustc_mir_build/src/build/coverageinfo/mcdc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,6 @@ use rustc_span::Span;
use crate::build::Builder;
use crate::errors::{MCDCExceedsConditionLimit, MCDCExceedsDecisionDepth};

/// The MCDC bitmap scales exponentially (2^n) based on the number of conditions seen,
/// So llvm sets a maximum value prevents the bitmap footprint from growing too large without the user's knowledge.
/// This limit may be relaxed if the [upstream change](https://github.com/llvm/llvm-project/pull/82448) is merged.
const MAX_CONDITIONS_IN_DECISION: usize = 6;

/// MCDC allocates an i32 variable on stack for each depth. Ignore decisions nested too much to prevent it
/// consuming excessive memory.
const MAX_DECISION_DEPTH: u16 = 0x3FFF;
Expand Down Expand Up @@ -356,6 +351,7 @@ pub(crate) struct MCDCInfoBuilder {
mcdc_targets: FxIndexMap<DecisionId, MCDCTargetInfo>,
state: MCDCState,
decision_id_gen: DecisionIdGen,
required_num_test_vectors: usize,
}

impl MCDCInfoBuilder {
Expand All @@ -365,6 +361,7 @@ impl MCDCInfoBuilder {
mcdc_targets: FxIndexMap::default(),
state: MCDCState::new(),
decision_id_gen: DecisionIdGen::default(),
required_num_test_vectors: 0,
}
}

Expand Down Expand Up @@ -396,27 +393,30 @@ impl MCDCInfoBuilder {
conditions: Vec<MCDCBranchSpan>,
) -> Option<&mut MCDCTargetInfo> {
let num_conditions = conditions.len();
match num_conditions {
0 => {
unreachable!("Decision with no condition is not expected");
}
// Ignore decisions with only one condition given that mcdc for them is completely equivalent to branch coverage.
2..=MAX_CONDITIONS_IN_DECISION => {
let info = MCDCTargetInfo::new(decision, conditions);
Some(self.mcdc_targets.entry(id).or_insert(info))
}
_ => {
self.append_normal_branches(conditions);
if num_conditions > MAX_CONDITIONS_IN_DECISION {
tcx.dcx().emit_warn(MCDCExceedsConditionLimit {
span: decision.span,
num_conditions,
max_conditions: MAX_CONDITIONS_IN_DECISION,
});
}
None
}
let max_conditions = tcx.sess.coverage_mcdc_max_conditions_per_decision();
let max_test_vectors = tcx.sess.coverage_mcdc_max_test_vectors();
// Ignore decisions with only one condition given that mcdc for them is completely equivalent to branch coverage.
if num_conditions <= 1 {
return None;
}
if num_conditions > max_conditions {
self.append_normal_branches(conditions);
tcx.dcx().emit_warn(MCDCExceedsConditionLimit {
span: decision.span,
num_conditions,
max_conditions,
});
return None;
}

let info = MCDCTargetInfo::new(decision, conditions);
let expected_num_tv = info.decision.num_test_vectors + self.required_num_test_vectors;
if expected_num_tv > max_test_vectors {
self.append_normal_branches(info.conditions);
return None;
}

Some(self.mcdc_targets.entry(id).or_insert(info))
}

fn normalize_depth_from(&mut self, tcx: TyCtxt<'_>, id: DecisionId) {
Expand Down Expand Up @@ -499,8 +499,13 @@ impl MCDCInfoBuilder {
pub(crate) fn into_done(
self,
) -> (Vec<MCDCBranchSpan>, Vec<(MCDCDecisionSpan, Vec<MCDCBranchSpan>)>) {
let MCDCInfoBuilder { normal_branch_spans, mcdc_targets, state: _, decision_id_gen: _ } =
self;
let MCDCInfoBuilder {
normal_branch_spans,
mcdc_targets,
state: _,
decision_id_gen: _,
required_num_test_vectors: _,
} = self;

let mcdc_spans = mcdc_targets
.into_values()
Expand Down
29 changes: 28 additions & 1 deletion compiler/rustc_session/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ pub enum InstrumentCoverage {
}

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

Expand All @@ -157,6 +157,33 @@ pub struct CoverageOptions {
/// For internal debugging only. If other code changes would make it hard
/// to keep supporting this flag, remove it.
pub no_mir_spans: bool,

/// Limit for number of conditions in a decision. By default the limit is
/// `32767` and user defined limit shall not be larger than it.
pub mcdc_max_conditions: usize,

/// Limit for number of test vectors of all decisions in a function.
/// Each test vectors takes up 1 bit memory. By default the limit is
/// `2,147,483,646` and user defined limit shall not be larger than it.
pub mcdc_max_test_vectors: usize,
}

impl Default for CoverageOptions {
fn default() -> Self {
Self {
level: CoverageLevel::default(),
no_mir_spans: false,
mcdc_max_conditions: Self::MCDC_MAX_CONDITIONS_IN_DECISION,
mcdc_max_test_vectors: Self::MCDC_MAX_TEST_VECTORS,
}
}
}

impl CoverageOptions {
// Maxium number of conditions in a decision specified by LLVM.
pub const MCDC_MAX_CONDITIONS_IN_DECISION: usize = 0x7fff;
// Maxium number of test vectors in a function specified by LLVM.
pub const MCDC_MAX_TEST_VECTORS: usize = 0x7fffffff;
}

/// Controls whether branch coverage or MC/DC coverage is enabled.
Expand Down
18 changes: 17 additions & 1 deletion compiler/rustc_session/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -965,7 +965,23 @@ mod parse {
"condition" => slot.level = CoverageLevel::Condition,
"mcdc" => slot.level = CoverageLevel::Mcdc,
"no-mir-spans" => slot.no_mir_spans = true,
_ => return false,
_ => {
if let Some(max_conditions) = option
.strip_prefix("mcdc-max-conditions=")
.and_then(|num| num.parse::<usize>().ok())
{
slot.mcdc_max_conditions =
max_conditions.min(CoverageOptions::MCDC_MAX_CONDITIONS_IN_DECISION);
} else if let Some(max_test_vectors) = option
.strip_prefix("mcdc-test-vectors=")
.and_then(|num| num.parse::<usize>().ok())
{
slot.mcdc_max_test_vectors =
max_test_vectors.min(CoverageOptions::MCDC_MAX_TEST_VECTORS);
} else {
return false;
}
}
}
}
true
Expand Down
8 changes: 8 additions & 0 deletions compiler/rustc_session/src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,14 @@ impl Session {
self.opts.unstable_opts.coverage_options.no_mir_spans
}

pub fn coverage_mcdc_max_conditions_per_decision(&self) -> usize {
self.opts.unstable_opts.coverage_options.mcdc_max_conditions
}

pub fn coverage_mcdc_max_test_vectors(&self) -> usize {
self.opts.unstable_opts.coverage_options.mcdc_max_test_vectors
}

pub fn is_sanitizer_cfi_enabled(&self) -> bool {
self.opts.unstable_opts.sanitizer.contains(SanitizerSet::CFI)
}
Expand Down

0 comments on commit b268271

Please sign in to comment.