1
Fork 0

coverage: Simplify internal representation of debug types

This commit is contained in:
Zalathar 2023-09-15 16:24:52 +10:00
parent d0d1187ebb
commit 3b5b1aa2a5

View file

@ -258,36 +258,42 @@ impl Default for ExpressionFormat {
/// `DebugCounters` supports a recursive rendering of `Expression` counters, so they can be /// `DebugCounters` supports a recursive rendering of `Expression` counters, so they can be
/// presented as nested expressions such as `(bcb3 - (bcb0 + bcb1))`. /// presented as nested expressions such as `(bcb3 - (bcb0 + bcb1))`.
pub(super) struct DebugCounters { pub(super) struct DebugCounters {
some_counters: Option<FxHashMap<Operand, DebugCounter>>, state: Option<DebugCountersState>,
}
#[derive(Default)]
struct DebugCountersState {
counters: FxHashMap<Operand, DebugCounter>,
} }
impl DebugCounters { impl DebugCounters {
pub fn new() -> Self { pub fn new() -> Self {
Self { some_counters: None } Self { state: None }
} }
pub fn enable(&mut self) { pub fn enable(&mut self) {
debug_assert!(!self.is_enabled()); debug_assert!(!self.is_enabled());
self.some_counters.replace(FxHashMap::default()); self.state = Some(DebugCountersState::default());
} }
pub fn is_enabled(&self) -> bool { pub fn is_enabled(&self) -> bool {
self.some_counters.is_some() self.state.is_some()
} }
pub fn add_counter(&mut self, counter_kind: &BcbCounter, some_block_label: Option<String>) { pub fn add_counter(&mut self, counter_kind: &BcbCounter, some_block_label: Option<String>) {
if let Some(counters) = &mut self.some_counters { let Some(state) = &mut self.state else { return };
let id = counter_kind.as_operand();
counters let id = counter_kind.as_operand();
.try_insert(id, DebugCounter::new(counter_kind.clone(), some_block_label)) state
.expect("attempt to add the same counter_kind to DebugCounters more than once"); .counters
} .try_insert(id, DebugCounter::new(counter_kind.clone(), some_block_label))
.expect("attempt to add the same counter_kind to DebugCounters more than once");
} }
pub fn some_block_label(&self, operand: Operand) -> Option<&String> { pub fn some_block_label(&self, operand: Operand) -> Option<&String> {
self.some_counters.as_ref().and_then(|counters| { let Some(state) = &self.state else { return None };
counters.get(&operand).and_then(|debug_counter| debug_counter.some_block_label.as_ref())
}) state.counters.get(&operand)?.some_block_label.as_ref()
} }
pub fn format_counter(&self, counter_kind: &BcbCounter) -> String { pub fn format_counter(&self, counter_kind: &BcbCounter) -> String {
@ -307,7 +313,7 @@ impl DebugCounters {
if counter_format.operation { if counter_format.operation {
return format!( return format!(
"{}{} {} {}", "{}{} {} {}",
if counter_format.id || self.some_counters.is_none() { if counter_format.id || !self.is_enabled() {
format!("#{} = ", id.index()) format!("#{} = ", id.index())
} else { } else {
String::new() String::new()
@ -323,10 +329,9 @@ impl DebugCounters {
} }
let id = counter_kind.as_operand(); let id = counter_kind.as_operand();
if self.some_counters.is_some() && (counter_format.block || !counter_format.id) { if let Some(state) = &self.state && (counter_format.block || !counter_format.id) {
let counters = self.some_counters.as_ref().unwrap();
if let Some(DebugCounter { some_block_label: Some(block_label), .. }) = if let Some(DebugCounter { some_block_label: Some(block_label), .. }) =
counters.get(&id) state.counters.get(&id)
{ {
return if counter_format.id { return if counter_format.id {
format!("{}#{:?}", block_label, id) format!("{}#{:?}", block_label, id)
@ -342,8 +347,10 @@ impl DebugCounters {
if matches!(operand, Operand::Zero) { if matches!(operand, Operand::Zero) {
return String::from("0"); return String::from("0");
} }
if let Some(counters) = &self.some_counters { if let Some(state) = &self.state {
if let Some(DebugCounter { counter_kind, some_block_label }) = counters.get(&operand) { if let Some(DebugCounter { counter_kind, some_block_label }) =
state.counters.get(&operand)
{
if let BcbCounter::Expression { .. } = counter_kind { if let BcbCounter::Expression { .. } = counter_kind {
if let Some(label) = some_block_label && debug_options().counter_format.block { if let Some(label) = some_block_label && debug_options().counter_format.block {
return format!( return format!(
@ -377,30 +384,29 @@ impl DebugCounter {
/// If enabled, this data structure captures additional debugging information used when generating /// If enabled, this data structure captures additional debugging information used when generating
/// a Graphviz (.dot file) representation of the `CoverageGraph`, for debugging purposes. /// a Graphviz (.dot file) representation of the `CoverageGraph`, for debugging purposes.
pub(super) struct GraphvizData { pub(super) struct GraphvizData {
some_bcb_to_coverage_spans_with_counters: state: Option<GraphvizDataState>,
Option<FxHashMap<BasicCoverageBlock, Vec<(CoverageSpan, BcbCounter)>>>, }
some_bcb_to_dependency_counters: Option<FxHashMap<BasicCoverageBlock, Vec<BcbCounter>>>,
some_edge_to_counter: Option<FxHashMap<(BasicCoverageBlock, BasicBlock), BcbCounter>>, #[derive(Default)]
struct GraphvizDataState {
bcb_to_coverage_spans_with_counters:
FxHashMap<BasicCoverageBlock, Vec<(CoverageSpan, BcbCounter)>>,
bcb_to_dependency_counters: FxHashMap<BasicCoverageBlock, Vec<BcbCounter>>,
edge_to_counter: FxHashMap<(BasicCoverageBlock, BasicBlock), BcbCounter>,
} }
impl GraphvizData { impl GraphvizData {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self { state: None }
some_bcb_to_coverage_spans_with_counters: None,
some_bcb_to_dependency_counters: None,
some_edge_to_counter: None,
}
} }
pub fn enable(&mut self) { pub fn enable(&mut self) {
debug_assert!(!self.is_enabled()); debug_assert!(!self.is_enabled());
self.some_bcb_to_coverage_spans_with_counters = Some(FxHashMap::default()); self.state = Some(GraphvizDataState::default());
self.some_bcb_to_dependency_counters = Some(FxHashMap::default());
self.some_edge_to_counter = Some(FxHashMap::default());
} }
pub fn is_enabled(&self) -> bool { pub fn is_enabled(&self) -> bool {
self.some_bcb_to_coverage_spans_with_counters.is_some() self.state.is_some()
} }
pub fn add_bcb_coverage_span_with_counter( pub fn add_bcb_coverage_span_with_counter(
@ -409,27 +415,22 @@ impl GraphvizData {
coverage_span: &CoverageSpan, coverage_span: &CoverageSpan,
counter_kind: &BcbCounter, counter_kind: &BcbCounter,
) { ) {
if let Some(bcb_to_coverage_spans_with_counters) = let Some(state) = &mut self.state else { return };
self.some_bcb_to_coverage_spans_with_counters.as_mut()
{ state
bcb_to_coverage_spans_with_counters .bcb_to_coverage_spans_with_counters
.entry(bcb) .entry(bcb)
.or_insert_with(Vec::new) .or_insert_with(Vec::new)
.push((coverage_span.clone(), counter_kind.clone())); .push((coverage_span.clone(), counter_kind.clone()));
}
} }
pub fn get_bcb_coverage_spans_with_counters( pub fn get_bcb_coverage_spans_with_counters(
&self, &self,
bcb: BasicCoverageBlock, bcb: BasicCoverageBlock,
) -> Option<&[(CoverageSpan, BcbCounter)]> { ) -> Option<&[(CoverageSpan, BcbCounter)]> {
if let Some(bcb_to_coverage_spans_with_counters) = let Some(state) = &self.state else { return None };
self.some_bcb_to_coverage_spans_with_counters.as_ref()
{ state.bcb_to_coverage_spans_with_counters.get(&bcb).map(Deref::deref)
bcb_to_coverage_spans_with_counters.get(&bcb).map(Deref::deref)
} else {
None
}
} }
pub fn add_bcb_dependency_counter( pub fn add_bcb_dependency_counter(
@ -437,20 +438,19 @@ impl GraphvizData {
bcb: BasicCoverageBlock, bcb: BasicCoverageBlock,
counter_kind: &BcbCounter, counter_kind: &BcbCounter,
) { ) {
if let Some(bcb_to_dependency_counters) = self.some_bcb_to_dependency_counters.as_mut() { let Some(state) = &mut self.state else { return };
bcb_to_dependency_counters
.entry(bcb) state
.or_insert_with(Vec::new) .bcb_to_dependency_counters
.push(counter_kind.clone()); .entry(bcb)
} .or_insert_with(Vec::new)
.push(counter_kind.clone());
} }
pub fn get_bcb_dependency_counters(&self, bcb: BasicCoverageBlock) -> Option<&[BcbCounter]> { pub fn get_bcb_dependency_counters(&self, bcb: BasicCoverageBlock) -> Option<&[BcbCounter]> {
if let Some(bcb_to_dependency_counters) = self.some_bcb_to_dependency_counters.as_ref() { let Some(state) = &self.state else { return None };
bcb_to_dependency_counters.get(&bcb).map(Deref::deref)
} else { state.bcb_to_dependency_counters.get(&bcb).map(Deref::deref)
None
}
} }
pub fn set_edge_counter( pub fn set_edge_counter(
@ -459,11 +459,12 @@ impl GraphvizData {
to_bb: BasicBlock, to_bb: BasicBlock,
counter_kind: &BcbCounter, counter_kind: &BcbCounter,
) { ) {
if let Some(edge_to_counter) = self.some_edge_to_counter.as_mut() { let Some(state) = &mut self.state else { return };
edge_to_counter
.try_insert((from_bcb, to_bb), counter_kind.clone()) state
.expect("invalid attempt to insert more than one edge counter for the same edge"); .edge_to_counter
} .try_insert((from_bcb, to_bb), counter_kind.clone())
.expect("invalid attempt to insert more than one edge counter for the same edge");
} }
pub fn get_edge_counter( pub fn get_edge_counter(
@ -471,11 +472,9 @@ impl GraphvizData {
from_bcb: BasicCoverageBlock, from_bcb: BasicCoverageBlock,
to_bb: BasicBlock, to_bb: BasicBlock,
) -> Option<&BcbCounter> { ) -> Option<&BcbCounter> {
if let Some(edge_to_counter) = self.some_edge_to_counter.as_ref() { let Some(state) = &self.state else { return None };
edge_to_counter.get(&(from_bcb, to_bb))
} else { state.edge_to_counter.get(&(from_bcb, to_bb))
None
}
} }
} }
@ -484,41 +483,42 @@ impl GraphvizData {
/// _not_ used are retained in the `unused_expressions` Vec, to be included in debug output (logs /// _not_ used are retained in the `unused_expressions` Vec, to be included in debug output (logs
/// and/or a `CoverageGraph` graphviz output). /// and/or a `CoverageGraph` graphviz output).
pub(super) struct UsedExpressions { pub(super) struct UsedExpressions {
some_used_expression_operands: Option<FxHashMap<Operand, Vec<ExpressionId>>>, state: Option<UsedExpressionsState>,
some_unused_expressions: }
Option<Vec<(BcbCounter, Option<BasicCoverageBlock>, BasicCoverageBlock)>>,
#[derive(Default)]
struct UsedExpressionsState {
used_expression_operands: FxHashMap<Operand, Vec<ExpressionId>>,
unused_expressions: Vec<(BcbCounter, Option<BasicCoverageBlock>, BasicCoverageBlock)>,
} }
impl UsedExpressions { impl UsedExpressions {
pub fn new() -> Self { pub fn new() -> Self {
Self { some_used_expression_operands: None, some_unused_expressions: None } Self { state: None }
} }
pub fn enable(&mut self) { pub fn enable(&mut self) {
debug_assert!(!self.is_enabled()); debug_assert!(!self.is_enabled());
self.some_used_expression_operands = Some(FxHashMap::default()); self.state = Some(UsedExpressionsState::default())
self.some_unused_expressions = Some(Vec::new());
} }
pub fn is_enabled(&self) -> bool { pub fn is_enabled(&self) -> bool {
self.some_used_expression_operands.is_some() self.state.is_some()
} }
pub fn add_expression_operands(&mut self, expression: &BcbCounter) { pub fn add_expression_operands(&mut self, expression: &BcbCounter) {
if let Some(used_expression_operands) = self.some_used_expression_operands.as_mut() { let Some(state) = &mut self.state else { return };
if let BcbCounter::Expression { id, lhs, rhs, .. } = *expression {
used_expression_operands.entry(lhs).or_insert_with(Vec::new).push(id); if let BcbCounter::Expression { id, lhs, rhs, .. } = *expression {
used_expression_operands.entry(rhs).or_insert_with(Vec::new).push(id); state.used_expression_operands.entry(lhs).or_insert_with(Vec::new).push(id);
} state.used_expression_operands.entry(rhs).or_insert_with(Vec::new).push(id);
} }
} }
pub fn expression_is_used(&self, expression: &BcbCounter) -> bool { pub fn expression_is_used(&self, expression: &BcbCounter) -> bool {
if let Some(used_expression_operands) = self.some_used_expression_operands.as_ref() { let Some(state) = &self.state else { return false };
used_expression_operands.contains_key(&expression.as_operand())
} else { state.used_expression_operands.contains_key(&expression.as_operand())
false
}
} }
pub fn add_unused_expression_if_not_found( pub fn add_unused_expression_if_not_found(
@ -527,14 +527,10 @@ impl UsedExpressions {
edge_from_bcb: Option<BasicCoverageBlock>, edge_from_bcb: Option<BasicCoverageBlock>,
target_bcb: BasicCoverageBlock, target_bcb: BasicCoverageBlock,
) { ) {
if let Some(used_expression_operands) = self.some_used_expression_operands.as_ref() { let Some(state) = &mut self.state else { return };
if !used_expression_operands.contains_key(&expression.as_operand()) {
self.some_unused_expressions.as_mut().unwrap().push(( if !state.used_expression_operands.contains_key(&expression.as_operand()) {
expression.clone(), state.unused_expressions.push((expression.clone(), edge_from_bcb, target_bcb));
edge_from_bcb,
target_bcb,
));
}
} }
} }
@ -543,11 +539,9 @@ impl UsedExpressions {
pub fn get_unused_expressions( pub fn get_unused_expressions(
&self, &self,
) -> Vec<(BcbCounter, Option<BasicCoverageBlock>, BasicCoverageBlock)> { ) -> Vec<(BcbCounter, Option<BasicCoverageBlock>, BasicCoverageBlock)> {
if let Some(unused_expressions) = self.some_unused_expressions.as_ref() { let Some(state) = &self.state else { return Vec::new() };
unused_expressions.clone()
} else { state.unused_expressions.clone()
Vec::new()
}
} }
/// If enabled, validate that every BCB or edge counter not directly associated with a coverage /// If enabled, validate that every BCB or edge counter not directly associated with a coverage
@ -561,51 +555,53 @@ impl UsedExpressions {
BcbCounter, BcbCounter,
)], )],
) { ) {
if self.is_enabled() { if !self.is_enabled() {
let mut not_validated = bcb_counters_without_direct_coverage_spans return;
.iter() }
.map(|(_, _, counter_kind)| counter_kind)
.collect::<Vec<_>>(); let mut not_validated = bcb_counters_without_direct_coverage_spans
let mut validating_count = 0; .iter()
while not_validated.len() != validating_count { .map(|(_, _, counter_kind)| counter_kind)
let to_validate = not_validated.split_off(0); .collect::<Vec<_>>();
validating_count = to_validate.len(); let mut validating_count = 0;
for counter_kind in to_validate { while not_validated.len() != validating_count {
if self.expression_is_used(counter_kind) { let to_validate = not_validated.split_off(0);
self.add_expression_operands(counter_kind); validating_count = to_validate.len();
} else { for counter_kind in to_validate {
not_validated.push(counter_kind); if self.expression_is_used(counter_kind) {
} self.add_expression_operands(counter_kind);
} else {
not_validated.push(counter_kind);
} }
} }
} }
} }
pub fn alert_on_unused_expressions(&self, debug_counters: &DebugCounters) { pub fn alert_on_unused_expressions(&self, debug_counters: &DebugCounters) {
if let Some(unused_expressions) = self.some_unused_expressions.as_ref() { let Some(state) = &self.state else { return };
for (counter_kind, edge_from_bcb, target_bcb) in unused_expressions {
let unused_counter_message = if let Some(from_bcb) = edge_from_bcb.as_ref() {
format!(
"non-coverage edge counter found without a dependent expression, in \
{:?}->{:?}; counter={}",
from_bcb,
target_bcb,
debug_counters.format_counter(&counter_kind),
)
} else {
format!(
"non-coverage counter found without a dependent expression, in {:?}; \
counter={}",
target_bcb,
debug_counters.format_counter(&counter_kind),
)
};
if debug_options().allow_unused_expressions { for (counter_kind, edge_from_bcb, target_bcb) in &state.unused_expressions {
debug!("WARNING: {}", unused_counter_message); let unused_counter_message = if let Some(from_bcb) = edge_from_bcb.as_ref() {
} else { format!(
bug!("{}", unused_counter_message); "non-coverage edge counter found without a dependent expression, in \
} {:?}->{:?}; counter={}",
from_bcb,
target_bcb,
debug_counters.format_counter(&counter_kind),
)
} else {
format!(
"non-coverage counter found without a dependent expression, in {:?}; \
counter={}",
target_bcb,
debug_counters.format_counter(&counter_kind),
)
};
if debug_options().allow_unused_expressions {
debug!("WARNING: {}", unused_counter_message);
} else {
bug!("{}", unused_counter_message);
} }
} }
} }