diff options
| author | bors <bors@rust-lang.org> | 2023-09-20 16:16:01 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2023-09-20 16:16:01 +0000 |
| commit | 4f226925cefa01f41a425b277be466380ddf0b9e (patch) | |
| tree | 1a0e49db4d7e71cb32a08086039ba80916d0c442 /compiler/rustc_mir_transform/src/coverage/debug.rs | |
| parent | e3811a785e089294b58f6a22e447cc5c314d1070 (diff) | |
| parent | 70076c5fe8c9c46d5befcfa1d6f0c864332aa00f (diff) | |
| download | rust-4f226925cefa01f41a425b277be466380ddf0b9e.tar.gz rust-4f226925cefa01f41a425b277be466380ddf0b9e.zip | |
Auto merge of #116006 - GuillaumeGomez:rollup-elrbjd4, r=GuillaumeGomez
Rollup of 5 pull requests Successful merges: - #115566 (clean up unneeded `ToPredicate` impls) - #115962 (coverage: Remove debug code from the instrumentor) - #115988 (rustdoc: add test cases, and fix, search tabs layout jank) - #115991 (Ensure `build/tmp` exists in `rustdoc_themes::get_themes`) - #115997 (RELEASES.md: Add missing patch releases) r? `@ghost` `@rustbot` modify labels: rollup
Diffstat (limited to 'compiler/rustc_mir_transform/src/coverage/debug.rs')
| -rw-r--r-- | compiler/rustc_mir_transform/src/coverage/debug.rs | 797 |
1 files changed, 0 insertions, 797 deletions
diff --git a/compiler/rustc_mir_transform/src/coverage/debug.rs b/compiler/rustc_mir_transform/src/coverage/debug.rs deleted file mode 100644 index bb1f16aa8be..00000000000 --- a/compiler/rustc_mir_transform/src/coverage/debug.rs +++ /dev/null @@ -1,797 +0,0 @@ -//! The `InstrumentCoverage` MIR pass implementation includes debugging tools and options -//! to help developers understand and/or improve the analysis and instrumentation of a MIR. -//! -//! To enable coverage, include the rustc command line option: -//! -//! * `-C instrument-coverage` -//! -//! MIR Dump Files, with additional `CoverageGraph` graphviz and `CoverageSpan` spanview -//! ------------------------------------------------------------------------------------ -//! -//! Additional debugging options include: -//! -//! * `-Z dump-mir=InstrumentCoverage` - Generate `.mir` files showing the state of the MIR, -//! before and after the `InstrumentCoverage` pass, for each compiled function. -//! -//! * `-Z dump-mir-graphviz` - If `-Z dump-mir` is also enabled for the current MIR node path, -//! each MIR dump is accompanied by a before-and-after graphical view of the MIR, in Graphviz -//! `.dot` file format (which can be visually rendered as a graph using any of a number of free -//! Graphviz viewers and IDE extensions). -//! -//! For the `InstrumentCoverage` pass, this option also enables generation of an additional -//! Graphviz `.dot` file for each function, rendering the `CoverageGraph`: the control flow -//! graph (CFG) of `BasicCoverageBlocks` (BCBs), as nodes, internally labeled to show the -//! `CoverageSpan`-based MIR elements each BCB represents (`BasicBlock`s, `Statement`s and -//! `Terminator`s), assigned coverage counters and/or expressions, and edge counters, as needed. -//! -//! (Note the additional option, `-Z graphviz-dark-mode`, can be added, to change the rendered -//! output from its default black-on-white background to a dark color theme, if desired.) -//! -//! * `-Z dump-mir-spanview` - If `-Z dump-mir` is also enabled for the current MIR node path, -//! each MIR dump is accompanied by a before-and-after `.html` document showing the function's -//! original source code, highlighted by it's MIR spans, at the `statement`-level (by default), -//! `terminator` only, or encompassing span for the `Terminator` plus all `Statement`s, in each -//! `block` (`BasicBlock`). -//! -//! For the `InstrumentCoverage` pass, this option also enables generation of an additional -//! spanview `.html` file for each function, showing the aggregated `CoverageSpan`s that will -//! require counters (or counter expressions) for accurate coverage analysis. -//! -//! Debug Logging -//! ------------- -//! -//! The `InstrumentCoverage` pass includes debug logging messages at various phases and decision -//! points, which can be enabled via environment variable: -//! -//! ```shell -//! RUSTC_LOG=rustc_mir_transform::coverage=debug -//! ``` -//! -//! Other module paths with coverage-related debug logs may also be of interest, particularly for -//! debugging the coverage map data, injected as global variables in the LLVM IR (during rustc's -//! code generation pass). For example: -//! -//! ```shell -//! RUSTC_LOG=rustc_mir_transform::coverage,rustc_codegen_llvm::coverageinfo=debug -//! ``` -//! -//! Coverage Debug Options -//! --------------------------------- -//! -//! Additional debugging options can be enabled using the environment variable: -//! -//! ```shell -//! RUSTC_COVERAGE_DEBUG_OPTIONS=<options> -//! ``` -//! -//! These options are comma-separated, and specified in the format `option-name=value`. For example: -//! -//! ```shell -//! $ RUSTC_COVERAGE_DEBUG_OPTIONS=counter-format=id+operation,allow-unused-expressions=yes cargo build -//! ``` -//! -//! Coverage debug options include: -//! -//! * `allow-unused-expressions=yes` or `no` (default: `no`) -//! -//! The `InstrumentCoverage` algorithms _should_ only create and assign expressions to a -//! `BasicCoverageBlock`, or an incoming edge, if that expression is either (a) required to -//! count a `CoverageSpan`, or (b) a dependency of some other required counter expression. -//! -//! If an expression is generated that does not map to a `CoverageSpan` or dependency, this -//! probably indicates there was a bug in the algorithm that creates and assigns counters -//! and expressions. -//! -//! When this kind of bug is encountered, the rustc compiler will panic by default. Setting: -//! `allow-unused-expressions=yes` will log a warning message instead of panicking (effectively -//! ignoring the unused expressions), which may be helpful when debugging the root cause of -//! the problem. -//! -//! * `counter-format=<choices>`, where `<choices>` can be any plus-separated combination of `id`, -//! `block`, and/or `operation` (default: `block+operation`) -//! -//! This option effects both the `CoverageGraph` (graphviz `.dot` files) and debug logging, when -//! generating labels for counters and expressions. -//! -//! Depending on the values and combinations, counters can be labeled by: -//! -//! * `id` - counter or expression ID (ascending counter IDs, starting at 1, or descending -//! expression IDs, starting at `u32:MAX`) -//! * `block` - the `BasicCoverageBlock` label (for example, `bcb0`) or edge label (for -//! example `bcb0->bcb1`), for counters or expressions assigned to count a -//! `BasicCoverageBlock` or edge. Intermediate expressions (not directly associated with -//! a BCB or edge) will be labeled by their expression ID, unless `operation` is also -//! specified. -//! * `operation` - applied to expressions only, labels include the left-hand-side counter -//! or expression label (lhs operand), the operator (`+` or `-`), and the right-hand-side -//! counter or expression (rhs operand). Expression operand labels are generated -//! recursively, generating labels with nested operations, enclosed in parentheses -//! (for example: `bcb2 + (bcb0 - bcb1)`). - -use std::iter; -use std::ops::Deref; -use std::sync::OnceLock; - -use itertools::Itertools; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_middle::mir::coverage::*; -use rustc_middle::mir::create_dump_file; -use rustc_middle::mir::generic_graphviz::GraphvizWriter; -use rustc_middle::mir::spanview::{self, SpanViewable}; -use rustc_middle::mir::{self, BasicBlock}; -use rustc_middle::ty::TyCtxt; -use rustc_span::Span; - -use super::counters::{BcbCounter, CoverageCounters}; -use super::graph::{BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph}; -use super::spans::CoverageSpan; - -pub const NESTED_INDENT: &str = " "; - -const RUSTC_COVERAGE_DEBUG_OPTIONS: &str = "RUSTC_COVERAGE_DEBUG_OPTIONS"; - -pub(super) fn debug_options<'a>() -> &'a DebugOptions { - static DEBUG_OPTIONS: OnceLock<DebugOptions> = OnceLock::new(); - - &DEBUG_OPTIONS.get_or_init(DebugOptions::from_env) -} - -/// Parses and maintains coverage-specific debug options captured from the environment variable -/// "RUSTC_COVERAGE_DEBUG_OPTIONS", if set. -#[derive(Debug, Clone)] -pub(super) struct DebugOptions { - pub allow_unused_expressions: bool, - counter_format: ExpressionFormat, -} - -impl DebugOptions { - fn from_env() -> Self { - let mut allow_unused_expressions = true; - let mut counter_format = ExpressionFormat::default(); - - if let Ok(env_debug_options) = std::env::var(RUSTC_COVERAGE_DEBUG_OPTIONS) { - for setting_str in env_debug_options.replace(' ', "").replace('-', "_").split(',') { - let (option, value) = match setting_str.split_once('=') { - None => (setting_str, None), - Some((k, v)) => (k, Some(v)), - }; - match option { - "allow_unused_expressions" => { - allow_unused_expressions = bool_option_val(option, value); - debug!( - "{} env option `allow_unused_expressions` is set to {}", - RUSTC_COVERAGE_DEBUG_OPTIONS, allow_unused_expressions - ); - } - "counter_format" => { - match value { - None => { - bug!( - "`{}` option in environment variable {} requires one or more \ - plus-separated choices (a non-empty subset of \ - `id+block+operation`)", - option, - RUSTC_COVERAGE_DEBUG_OPTIONS - ); - } - Some(val) => { - counter_format = counter_format_option_val(val); - debug!( - "{} env option `counter_format` is set to {:?}", - RUSTC_COVERAGE_DEBUG_OPTIONS, counter_format - ); - } - }; - } - _ => bug!( - "Unsupported setting `{}` in environment variable {}", - option, - RUSTC_COVERAGE_DEBUG_OPTIONS - ), - }; - } - } - - Self { allow_unused_expressions, counter_format } - } -} - -fn bool_option_val(option: &str, some_strval: Option<&str>) -> bool { - if let Some(val) = some_strval { - if ["yes", "y", "on", "true"].contains(&val) { - true - } else if ["no", "n", "off", "false"].contains(&val) { - false - } else { - bug!( - "Unsupported value `{}` for option `{}` in environment variable {}", - option, - val, - RUSTC_COVERAGE_DEBUG_OPTIONS - ) - } - } else { - true - } -} - -fn counter_format_option_val(strval: &str) -> ExpressionFormat { - let mut counter_format = ExpressionFormat { id: false, block: false, operation: false }; - let components = strval.splitn(3, '+'); - for component in components { - match component { - "id" => counter_format.id = true, - "block" => counter_format.block = true, - "operation" => counter_format.operation = true, - _ => bug!( - "Unsupported counter_format choice `{}` in environment variable {}", - component, - RUSTC_COVERAGE_DEBUG_OPTIONS - ), - } - } - counter_format -} - -#[derive(Debug, Clone)] -struct ExpressionFormat { - id: bool, - block: bool, - operation: bool, -} - -impl Default for ExpressionFormat { - fn default() -> Self { - Self { id: false, block: true, operation: true } - } -} - -/// If enabled, this struct maintains a map from `BcbCounter` IDs (as `Operand`) to -/// the `BcbCounter` data and optional label (normally, the counter's associated -/// `BasicCoverageBlock` format string, if any). -/// -/// Use `format_counter` to convert one of these `BcbCounter` counters to a debug output string, -/// as directed by the `DebugOptions`. This allows the format of counter labels in logs and dump -/// files (including the `CoverageGraph` graphviz file) to be changed at runtime, via environment -/// variable. -/// -/// `DebugCounters` supports a recursive rendering of `Expression` counters, so they can be -/// presented as nested expressions such as `(bcb3 - (bcb0 + bcb1))`. -pub(super) struct DebugCounters { - state: Option<DebugCountersState>, -} - -#[derive(Default)] -struct DebugCountersState { - counters: FxHashMap<Operand, DebugCounter>, -} - -impl DebugCounters { - pub fn new() -> Self { - Self { state: None } - } - - pub fn enable(&mut self) { - debug_assert!(!self.is_enabled()); - self.state = Some(DebugCountersState::default()); - } - - pub fn is_enabled(&self) -> bool { - self.state.is_some() - } - - pub fn add_counter(&mut self, counter_kind: &BcbCounter, some_block_label: Option<String>) { - let Some(state) = &mut self.state else { return }; - - let id = counter_kind.as_operand(); - state - .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> { - let Some(state) = &self.state else { return None }; - - state.counters.get(&operand)?.some_block_label.as_ref() - } - - pub fn format_counter(&self, counter_kind: &BcbCounter) -> String { - match *counter_kind { - BcbCounter::Counter { .. } => { - format!("Counter({})", self.format_counter_kind(counter_kind)) - } - BcbCounter::Expression { .. } => { - format!("Expression({})", self.format_counter_kind(counter_kind)) - } - } - } - - fn format_counter_kind(&self, counter_kind: &BcbCounter) -> String { - let counter_format = &debug_options().counter_format; - if let BcbCounter::Expression { id, lhs, op, rhs } = *counter_kind { - if counter_format.operation { - return format!( - "{}{} {} {}", - if counter_format.id || !self.is_enabled() { - format!("#{} = ", id.index()) - } else { - String::new() - }, - self.format_operand(lhs), - match op { - Op::Add => "+", - Op::Subtract => "-", - }, - self.format_operand(rhs), - ); - } - } - - let id = counter_kind.as_operand(); - if let Some(state) = &self.state && (counter_format.block || !counter_format.id) { - if let Some(DebugCounter { some_block_label: Some(block_label), .. }) = - state.counters.get(&id) - { - return if counter_format.id { - format!("{}#{:?}", block_label, id) - } else { - block_label.to_string() - }; - } - } - format!("#{:?}", id) - } - - fn format_operand(&self, operand: Operand) -> String { - if matches!(operand, Operand::Zero) { - return String::from("0"); - } - if let Some(state) = &self.state { - if let Some(DebugCounter { counter_kind, some_block_label }) = - state.counters.get(&operand) - { - if let BcbCounter::Expression { .. } = counter_kind { - if let Some(label) = some_block_label && debug_options().counter_format.block { - return format!( - "{}:({})", - label, - self.format_counter_kind(counter_kind) - ); - } - return format!("({})", self.format_counter_kind(counter_kind)); - } - return self.format_counter_kind(counter_kind); - } - } - format!("#{:?}", operand) - } -} - -/// A non-public support class to `DebugCounters`. -#[derive(Debug)] -struct DebugCounter { - counter_kind: BcbCounter, - some_block_label: Option<String>, -} - -impl DebugCounter { - fn new(counter_kind: BcbCounter, some_block_label: Option<String>) -> Self { - Self { counter_kind, some_block_label } - } -} - -/// If enabled, this data structure captures additional debugging information used when generating -/// a Graphviz (.dot file) representation of the `CoverageGraph`, for debugging purposes. -pub(super) struct GraphvizData { - state: Option<GraphvizDataState>, -} - -#[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 { - pub fn new() -> Self { - Self { state: None } - } - - pub fn enable(&mut self) { - debug_assert!(!self.is_enabled()); - self.state = Some(GraphvizDataState::default()); - } - - pub fn is_enabled(&self) -> bool { - self.state.is_some() - } - - pub fn add_bcb_coverage_span_with_counter( - &mut self, - bcb: BasicCoverageBlock, - coverage_span: &CoverageSpan, - counter_kind: &BcbCounter, - ) { - let Some(state) = &mut self.state else { return }; - - state - .bcb_to_coverage_spans_with_counters - .entry(bcb) - .or_insert_with(Vec::new) - .push((coverage_span.clone(), counter_kind.clone())); - } - - pub fn get_bcb_coverage_spans_with_counters( - &self, - bcb: BasicCoverageBlock, - ) -> Option<&[(CoverageSpan, BcbCounter)]> { - let Some(state) = &self.state else { return None }; - - state.bcb_to_coverage_spans_with_counters.get(&bcb).map(Deref::deref) - } - - pub fn add_bcb_dependency_counter( - &mut self, - bcb: BasicCoverageBlock, - counter_kind: &BcbCounter, - ) { - let Some(state) = &mut self.state else { return }; - - state - .bcb_to_dependency_counters - .entry(bcb) - .or_insert_with(Vec::new) - .push(counter_kind.clone()); - } - - pub fn get_bcb_dependency_counters(&self, bcb: BasicCoverageBlock) -> Option<&[BcbCounter]> { - let Some(state) = &self.state else { return None }; - - state.bcb_to_dependency_counters.get(&bcb).map(Deref::deref) - } - - pub fn set_edge_counter( - &mut self, - from_bcb: BasicCoverageBlock, - to_bb: BasicBlock, - counter_kind: &BcbCounter, - ) { - let Some(state) = &mut self.state else { return }; - - state - .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( - &self, - from_bcb: BasicCoverageBlock, - to_bb: BasicBlock, - ) -> Option<&BcbCounter> { - let Some(state) = &self.state else { return None }; - - state.edge_to_counter.get(&(from_bcb, to_bb)) - } -} - -/// If enabled, this struct captures additional data used to track whether expressions were used, -/// directly or indirectly, to compute the coverage counts for all `CoverageSpan`s, and any that are -/// _not_ used are retained in the `unused_expressions` Vec, to be included in debug output (logs -/// and/or a `CoverageGraph` graphviz output). -pub(super) struct UsedExpressions { - state: Option<UsedExpressionsState>, -} - -#[derive(Default)] -struct UsedExpressionsState { - used_expression_operands: FxHashSet<Operand>, - unused_expressions: Vec<(BcbCounter, Option<BasicCoverageBlock>, BasicCoverageBlock)>, -} - -impl UsedExpressions { - pub fn new() -> Self { - Self { state: None } - } - - pub fn enable(&mut self) { - debug_assert!(!self.is_enabled()); - self.state = Some(UsedExpressionsState::default()) - } - - pub fn is_enabled(&self) -> bool { - self.state.is_some() - } - - pub fn add_expression_operands(&mut self, expression: &BcbCounter) { - let Some(state) = &mut self.state else { return }; - - if let BcbCounter::Expression { lhs, rhs, .. } = *expression { - state.used_expression_operands.insert(lhs); - state.used_expression_operands.insert(rhs); - } - } - - pub fn expression_is_used(&self, expression: &BcbCounter) -> bool { - let Some(state) = &self.state else { return false }; - - state.used_expression_operands.contains(&expression.as_operand()) - } - - pub fn add_unused_expression_if_not_found( - &mut self, - expression: &BcbCounter, - edge_from_bcb: Option<BasicCoverageBlock>, - target_bcb: BasicCoverageBlock, - ) { - let Some(state) = &mut self.state else { return }; - - if !state.used_expression_operands.contains(&expression.as_operand()) { - state.unused_expressions.push((expression.clone(), edge_from_bcb, target_bcb)); - } - } - - /// Return the list of unused counters (if any) as a tuple with the counter (`BcbCounter`), - /// optional `from_bcb` (if it was an edge counter), and `target_bcb`. - pub fn get_unused_expressions( - &self, - ) -> Vec<(BcbCounter, Option<BasicCoverageBlock>, BasicCoverageBlock)> { - let Some(state) = &self.state else { return Vec::new() }; - - state.unused_expressions.clone() - } - - /// If enabled, validate that every BCB or edge counter not directly associated with a coverage - /// span is at least indirectly associated (it is a dependency of a BCB counter that _is_ - /// associated with a coverage span). - pub fn validate( - &mut self, - bcb_counters_without_direct_coverage_spans: &[( - Option<BasicCoverageBlock>, - BasicCoverageBlock, - BcbCounter, - )], - ) { - if !self.is_enabled() { - return; - } - - let mut not_validated = bcb_counters_without_direct_coverage_spans - .iter() - .map(|(_, _, counter_kind)| counter_kind) - .collect::<Vec<_>>(); - let mut validating_count = 0; - while not_validated.len() != validating_count { - let to_validate = not_validated.split_off(0); - validating_count = to_validate.len(); - for counter_kind in to_validate { - 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) { - let Some(state) = &self.state else { return }; - - for (counter_kind, edge_from_bcb, target_bcb) in &state.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 { - debug!("WARNING: {}", unused_counter_message); - } else { - bug!("{}", unused_counter_message); - } - } - } -} - -/// Generates the MIR pass `CoverageSpan`-specific spanview dump file. -pub(super) fn dump_coverage_spanview<'tcx>( - tcx: TyCtxt<'tcx>, - mir_body: &mir::Body<'tcx>, - basic_coverage_blocks: &CoverageGraph, - pass_name: &str, - body_span: Span, - coverage_spans: &[CoverageSpan], -) { - let mir_source = mir_body.source; - let def_id = mir_source.def_id(); - - let span_viewables = span_viewables(tcx, mir_body, basic_coverage_blocks, &coverage_spans); - let mut file = create_dump_file(tcx, "html", false, pass_name, &0i32, mir_body) - .expect("Unexpected error creating MIR spanview HTML file"); - let crate_name = tcx.crate_name(def_id.krate); - let item_name = tcx.def_path(def_id).to_filename_friendly_no_crate(); - let title = format!("{crate_name}.{item_name} - Coverage Spans"); - spanview::write_document(tcx, body_span, span_viewables, &title, &mut file) - .expect("Unexpected IO error dumping coverage spans as HTML"); -} - -/// Converts the computed `BasicCoverageBlockData`s into `SpanViewable`s. -fn span_viewables<'tcx>( - tcx: TyCtxt<'tcx>, - mir_body: &mir::Body<'tcx>, - basic_coverage_blocks: &CoverageGraph, - coverage_spans: &[CoverageSpan], -) -> Vec<SpanViewable> { - let mut span_viewables = Vec::new(); - for coverage_span in coverage_spans { - let tooltip = coverage_span.format_coverage_statements(tcx, mir_body); - let CoverageSpan { span, bcb, .. } = coverage_span; - let bcb_data = &basic_coverage_blocks[*bcb]; - let id = bcb_data.id(); - let leader_bb = bcb_data.leader_bb(); - span_viewables.push(SpanViewable { bb: leader_bb, span: *span, id, tooltip }); - } - span_viewables -} - -/// Generates the MIR pass coverage-specific graphviz dump file. -pub(super) fn dump_coverage_graphviz<'tcx>( - tcx: TyCtxt<'tcx>, - mir_body: &mir::Body<'tcx>, - pass_name: &str, - basic_coverage_blocks: &CoverageGraph, - coverage_counters: &CoverageCounters, - graphviz_data: &GraphvizData, - intermediate_expressions: &[BcbCounter], - debug_used_expressions: &UsedExpressions, -) { - let debug_counters = &coverage_counters.debug_counters; - - let mir_source = mir_body.source; - let def_id = mir_source.def_id(); - let node_content = |bcb| { - bcb_to_string_sections( - tcx, - mir_body, - coverage_counters, - bcb, - &basic_coverage_blocks[bcb], - graphviz_data.get_bcb_coverage_spans_with_counters(bcb), - graphviz_data.get_bcb_dependency_counters(bcb), - // intermediate_expressions are injected into the mir::START_BLOCK, so - // include them in the first BCB. - if bcb.index() == 0 { Some(&intermediate_expressions) } else { None }, - ) - }; - let edge_labels = |from_bcb| { - let from_bcb_data = &basic_coverage_blocks[from_bcb]; - let from_terminator = from_bcb_data.terminator(mir_body); - let mut edge_labels = from_terminator.kind.fmt_successor_labels(); - edge_labels.retain(|label| label != "unreachable"); - let edge_counters = from_terminator - .successors() - .map(|successor_bb| graphviz_data.get_edge_counter(from_bcb, successor_bb)); - iter::zip(&edge_labels, edge_counters) - .map(|(label, some_counter)| { - if let Some(counter) = some_counter { - format!("{}\n{}", label, debug_counters.format_counter(counter)) - } else { - label.to_string() - } - }) - .collect::<Vec<_>>() - }; - let graphviz_name = format!("Cov_{}_{}", def_id.krate.index(), def_id.index.index()); - let mut graphviz_writer = - GraphvizWriter::new(basic_coverage_blocks, &graphviz_name, node_content, edge_labels); - let unused_expressions = debug_used_expressions.get_unused_expressions(); - if unused_expressions.len() > 0 { - graphviz_writer.set_graph_label(&format!( - "Unused expressions:\n {}", - unused_expressions - .as_slice() - .iter() - .map(|(counter_kind, edge_from_bcb, target_bcb)| { - if let Some(from_bcb) = edge_from_bcb.as_ref() { - format!( - "{:?}->{:?}: {}", - from_bcb, - target_bcb, - debug_counters.format_counter(&counter_kind), - ) - } else { - format!( - "{:?}: {}", - target_bcb, - debug_counters.format_counter(&counter_kind), - ) - } - }) - .join("\n ") - )); - } - let mut file = create_dump_file(tcx, "dot", false, pass_name, &0i32, mir_body) - .expect("Unexpected error creating BasicCoverageBlock graphviz DOT file"); - graphviz_writer - .write_graphviz(tcx, &mut file) - .expect("Unexpected error writing BasicCoverageBlock graphviz DOT file"); -} - -fn bcb_to_string_sections<'tcx>( - tcx: TyCtxt<'tcx>, - mir_body: &mir::Body<'tcx>, - coverage_counters: &CoverageCounters, - bcb: BasicCoverageBlock, - bcb_data: &BasicCoverageBlockData, - some_coverage_spans_with_counters: Option<&[(CoverageSpan, BcbCounter)]>, - some_dependency_counters: Option<&[BcbCounter]>, - some_intermediate_expressions: Option<&[BcbCounter]>, -) -> Vec<String> { - let debug_counters = &coverage_counters.debug_counters; - - let len = bcb_data.basic_blocks.len(); - let mut sections = Vec::new(); - if let Some(collect_intermediate_expressions) = some_intermediate_expressions { - sections.push( - collect_intermediate_expressions - .iter() - .map(|expression| { - format!("Intermediate {}", debug_counters.format_counter(expression)) - }) - .join("\n"), - ); - } - if let Some(coverage_spans_with_counters) = some_coverage_spans_with_counters { - sections.push( - coverage_spans_with_counters - .iter() - .map(|(covspan, counter)| { - format!( - "{} at {}", - debug_counters.format_counter(counter), - covspan.format(tcx, mir_body) - ) - }) - .join("\n"), - ); - } - if let Some(dependency_counters) = some_dependency_counters { - sections.push(format!( - "Non-coverage counters:\n {}", - dependency_counters - .iter() - .map(|counter| debug_counters.format_counter(counter)) - .join(" \n"), - )); - } - if let Some(counter_kind) = coverage_counters.bcb_counter(bcb) { - sections.push(format!("{counter_kind:?}")); - } - let non_term_blocks = bcb_data.basic_blocks[0..len - 1] - .iter() - .map(|&bb| format!("{:?}: {}", bb, mir_body[bb].terminator().kind.name())) - .collect::<Vec<_>>(); - if non_term_blocks.len() > 0 { - sections.push(non_term_blocks.join("\n")); - } - sections.push(format!( - "{:?}: {}", - bcb_data.basic_blocks.last().unwrap(), - bcb_data.terminator(mir_body).kind.name(), - )); - sections -} |
