diff --git a/Cargo.lock b/Cargo.lock index 47d6b3c44a8d7..9834f401365fb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -703,6 +703,18 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +[[package]] +name = "coverage-dump" +version = "0.1.0" +dependencies = [ + "anyhow", + "leb128", + "md-5", + "miniz_oxide", + "regex", + "rustc-demangle", +] + [[package]] name = "coverage_test_macros" version = "0.0.0" @@ -2009,6 +2021,12 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" +[[package]] +name = "leb128" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" + [[package]] name = "libc" version = "0.2.147" diff --git a/Cargo.toml b/Cargo.toml index d2e84d5426f9f..9b11ae8744b4f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,6 +43,7 @@ members = [ "src/tools/generate-windows-sys", "src/tools/rustdoc-gui-test", "src/tools/opt-dist", + "src/tools/coverage-dump", ] exclude = [ diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs index 7a82d05ce9ea2..020d4461e9b16 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs @@ -1,4 +1,4 @@ -use rustc_middle::mir::coverage::{CounterId, MappedExpressionIndex}; +use rustc_middle::mir::coverage::{CounterId, ExpressionId, Operand}; /// Must match the layout of `LLVMRustCounterKind`. #[derive(Copy, Clone, Debug)] @@ -30,11 +30,8 @@ pub struct Counter { } impl Counter { - /// Constructs a new `Counter` of kind `Zero`. For this `CounterKind`, the - /// `id` is not used. - pub fn zero() -> Self { - Self { kind: CounterKind::Zero, id: 0 } - } + /// A `Counter` of kind `Zero`. For this counter kind, the `id` is not used. + pub(crate) const ZERO: Self = Self { kind: CounterKind::Zero, id: 0 }; /// Constructs a new `Counter` of kind `CounterValueReference`. pub fn counter_value_reference(counter_id: CounterId) -> Self { @@ -42,20 +39,16 @@ impl Counter { } /// Constructs a new `Counter` of kind `Expression`. - pub fn expression(mapped_expression_index: MappedExpressionIndex) -> Self { - Self { kind: CounterKind::Expression, id: mapped_expression_index.into() } - } - - /// Returns true if the `Counter` kind is `Zero`. - pub fn is_zero(&self) -> bool { - matches!(self.kind, CounterKind::Zero) + pub(crate) fn expression(expression_id: ExpressionId) -> Self { + Self { kind: CounterKind::Expression, id: expression_id.as_u32() } } - /// An explicitly-named function to get the ID value, making it more obvious - /// that the stored value is now 0-based. - pub fn zero_based_id(&self) -> u32 { - debug_assert!(!self.is_zero(), "`id` is undefined for CounterKind::Zero"); - self.id + pub(crate) fn from_operand(operand: Operand) -> Self { + match operand { + Operand::Zero => Self::ZERO, + Operand::Counter(id) => Self::counter_value_reference(id), + Operand::Expression(id) => Self::expression(id), + } } } @@ -81,6 +74,11 @@ pub struct CounterExpression { } impl CounterExpression { + /// The dummy expression `(0 - 0)` has a representation of all zeroes, + /// making it marginally more efficient to initialize than `(0 + 0)`. + pub(crate) const DUMMY: Self = + Self { lhs: Counter::ZERO, kind: ExprKind::Subtract, rhs: Counter::ZERO }; + pub fn new(lhs: Counter, kind: ExprKind, rhs: Counter) -> Self { Self { kind, lhs, rhs } } @@ -172,7 +170,7 @@ impl CounterMappingRegion { ) -> Self { Self { counter, - false_counter: Counter::zero(), + false_counter: Counter::ZERO, file_id, expanded_file_id: 0, start_line, @@ -183,9 +181,6 @@ impl CounterMappingRegion { } } - // This function might be used in the future; the LLVM API is still evolving, as is coverage - // support. - #[allow(dead_code)] pub(crate) fn branch_region( counter: Counter, false_counter: Counter, @@ -220,8 +215,8 @@ impl CounterMappingRegion { end_col: u32, ) -> Self { Self { - counter: Counter::zero(), - false_counter: Counter::zero(), + counter: Counter::ZERO, + false_counter: Counter::ZERO, file_id, expanded_file_id, start_line, @@ -243,8 +238,8 @@ impl CounterMappingRegion { end_col: u32, ) -> Self { Self { - counter: Counter::zero(), - false_counter: Counter::zero(), + counter: Counter::ZERO, + false_counter: Counter::ZERO, file_id, expanded_file_id: 0, start_line, @@ -268,7 +263,7 @@ impl CounterMappingRegion { ) -> Self { Self { counter, - false_counter: Counter::zero(), + false_counter: Counter::ZERO, file_id, expanded_file_id: 0, start_line, diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs index f1e68af25d406..048459ee8cf0f 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs @@ -1,10 +1,8 @@ use crate::coverageinfo::ffi::{Counter, CounterExpression, ExprKind}; -use rustc_index::{IndexSlice, IndexVec}; -use rustc_middle::bug; -use rustc_middle::mir::coverage::{ - CodeRegion, CounterId, ExpressionId, MappedExpressionIndex, Op, Operand, -}; +use rustc_data_structures::fx::FxHashMap; +use rustc_index::IndexVec; +use rustc_middle::mir::coverage::{CodeRegion, CounterId, ExpressionId, Op, Operand}; use rustc_middle::ty::Instance; use rustc_middle::ty::TyCtxt; @@ -16,6 +14,23 @@ pub struct Expression { region: Option, } +pub struct CoverageCounterAndRegion<'a> { + pub(crate) kind: CoverageCounterKind, + pub(crate) region: &'a CodeRegion, +} + +pub enum CoverageCounterKind { + Counter(Counter), + Branch { true_counter: Counter, false_counter: Counter }, +} + +#[derive(Debug)] +struct CoverageBranch { + true_: Operand, + false_: Operand, + region: CodeRegion, +} + /// Collects all of the coverage regions associated with (a) injected counters, (b) counter /// expressions (additions or subtraction), and (c) unreachable regions (always counted as zero), /// for a given Function. This struct also stores the `function_source_hash`, @@ -34,6 +49,7 @@ pub struct FunctionCoverage<'tcx> { is_used: bool, counters: IndexVec>, expressions: IndexVec>, + branches: Vec, unreachable_regions: Vec, } @@ -60,6 +76,7 @@ impl<'tcx> FunctionCoverage<'tcx> { is_used, counters: IndexVec::from_elem_n(None, coverageinfo.num_counters as usize), expressions: IndexVec::from_elem_n(None, coverageinfo.num_expressions as usize), + branches: Vec::new(), unreachable_regions: Vec::new(), } } @@ -86,6 +103,11 @@ impl<'tcx> FunctionCoverage<'tcx> { } } + /// Adds a branch region using the two provided true/false operands + pub fn add_branch_counter(&mut self, true_: Operand, false_: Operand, region: CodeRegion) { + self.branches.push(CoverageBranch { true_, false_, region }) + } + /// Both counters and "counter expressions" (or simply, "expressions") can be operands in other /// expressions. These are tracked as separate variants of `Operand`, so there is no ambiguity /// between operands that are counter IDs and operands that are expression IDs. @@ -128,184 +150,169 @@ impl<'tcx> FunctionCoverage<'tcx> { self.unreachable_regions.push(region) } + /// Perform some simplifications to make the final coverage mappings + /// slightly smaller. + pub(crate) fn simplify_expressions(&mut self) { + // The set of expressions that were simplified to either `Zero` or a + // `Counter`. Other expressions that refer to these as operands + // can then also be simplified. + let mut simplified_expressions = FxHashMap::default(); + type SimplifiedExpressions = FxHashMap; + + // If an operand refers to an expression that has been simplified, then + // replace that operand with the simplified version. + let maybe_simplify_operand = |simplified_expressions: &SimplifiedExpressions, + operand: &mut Operand| { + if let Operand::Expression(id) = &*operand { + if let Some(simplified) = simplified_expressions.get(id) { + *operand = *simplified; + } + } + }; + + // For each expression, perform simplifications based on lower-numbered + // expressions, and then update the map of simplified expressions if + // necessary. + // (By construction, expressions can only refer to other expressions + // that have lower IDs, so one simplification pass is sufficient.) + for (id, maybe_expression) in self.expressions.iter_enumerated_mut() { + let Some(expression) = maybe_expression else { + // If an expression is missing, it must have been optimized away, + // so any operand that refers to it can be replaced with zero. + simplified_expressions.insert(id, Operand::Zero); + continue; + }; + + maybe_simplify_operand(&simplified_expressions, &mut expression.lhs); + maybe_simplify_operand(&simplified_expressions, &mut expression.rhs); + + // Coverage counter values cannot be negative, so if an expression + // involves subtraction from zero, assume that its RHS must also be zero. + // (Do this after simplifications that could set the LHS to zero.) + if let Expression { lhs: Operand::Zero, op: Op::Subtract, .. } = expression { + expression.rhs = Operand::Zero; + } + + // After the above simplifications, if the right hand operand is zero, + // we can replace the expression by its left hand side. + if let Expression { lhs, rhs: Operand::Zero, .. } = expression { + simplified_expressions.insert(id, *lhs); + } + } + + // Do the same simplification for each side of a branch region. + for branch in &mut self.branches { + maybe_simplify_operand(&simplified_expressions, &mut branch.true_); + maybe_simplify_operand(&simplified_expressions, &mut branch.false_); + } + } + + /// This will further simplify any `Operand::Expression`, + /// "inlining" the left hand side operand if the right hand side is `Zero`. + fn simplified_expression(&self, id: ExpressionId) -> Counter { + if let Some(expr) = &self.expressions[id] { + if expr.rhs == Operand::Zero { + return Counter::from_operand(expr.lhs); + } + } + Counter::expression(id) + } + /// Return the source hash, generated from the HIR node structure, and used to indicate whether /// or not the source code structure changed between different compilations. pub fn source_hash(&self) -> u64 { self.source_hash } - /// Generate an array of CounterExpressions, and an iterator over all `Counter`s and their - /// associated `Regions` (from which the LLVM-specific `CoverageMapGenerator` will create - /// `CounterMappingRegion`s. + /// Generate an array of CounterExpressions, and an array of all `Counter`s and their + /// associated `Regions` sorted by `Region`, from which the LLVM-specific + /// `CoverageMapGenerator` will create `CounterMappingRegion`s. pub fn get_expressions_and_counter_regions( &self, - ) -> (Vec, impl Iterator) { + ) -> (Vec, Vec>) { assert!( self.source_hash != 0 || !self.is_used, "No counters provided the source_hash for used function: {:?}", self.instance ); - let counter_regions = self.counter_regions(); - let (counter_expressions, expression_regions) = self.expressions_with_regions(); - let unreachable_regions = self.unreachable_regions(); + let counter_expressions = self.counter_expressions(); + // Expression IDs are indices into `self.expressions`, and on the LLVM + // side they will be treated as indices into `counter_expressions`, so + // the two vectors should correspond 1:1. + assert_eq!(self.expressions.len(), counter_expressions.len()); - let counter_regions = - counter_regions.chain(expression_regions.into_iter().chain(unreachable_regions)); - (counter_expressions, counter_regions) - } - - fn counter_regions(&self) -> impl Iterator { - self.counters.iter_enumerated().filter_map(|(index, entry)| { + let counter_regions = self.counters.iter_enumerated().filter_map(|(index, entry)| { // Option::map() will return None to filter out missing counters. This may happen // if, for example, a MIR-instrumented counter is removed during an optimization. - entry.as_ref().map(|region| (Counter::counter_value_reference(index), region)) - }) - } - - fn expressions_with_regions( - &self, - ) -> (Vec, impl Iterator) { - let mut counter_expressions = Vec::with_capacity(self.expressions.len()); - let mut expression_regions = Vec::with_capacity(self.expressions.len()); - let mut new_indexes = IndexVec::from_elem_n(None, self.expressions.len()); - - // This closure converts any `Expression` operand (`lhs` or `rhs` of the `Op::Add` or - // `Op::Subtract` operation) into its native `llvm::coverage::Counter::CounterKind` type - // and value. - // - // Expressions will be returned from this function in a sequential vector (array) of - // `CounterExpression`, so the expression IDs must be mapped from their original, - // potentially sparse set of indexes. - // - // An `Expression` as an operand will have already been encountered as an `Expression` with - // operands, so its new_index will already have been generated (as a 1-up index value). - // (If an `Expression` as an operand does not have a corresponding new_index, it was - // probably optimized out, after the expression was injected into the MIR, so it will - // get a `CounterKind::Zero` instead.) - // - // In other words, an `Expression`s at any given index can include other expressions as - // operands, but expression operands can only come from the subset of expressions having - // `expression_index`s lower than the referencing `Expression`. Therefore, it is - // reasonable to look up the new index of an expression operand while the `new_indexes` - // vector is only complete up to the current `ExpressionIndex`. - type NewIndexes = IndexSlice>; - let id_to_counter = |new_indexes: &NewIndexes, operand: Operand| match operand { - Operand::Zero => Some(Counter::zero()), - Operand::Counter(id) => Some(Counter::counter_value_reference(id)), - Operand::Expression(id) => { - self.expressions - .get(id) - .expect("expression id is out of range") - .as_ref() - // If an expression was optimized out, assume it would have produced a count - // of zero. This ensures that expressions dependent on optimized-out - // expressions are still valid. - .map_or(Some(Counter::zero()), |_| new_indexes[id].map(Counter::expression)) - } - }; - - for (original_index, expression) in - self.expressions.iter_enumerated().filter_map(|(original_index, entry)| { - // Option::map() will return None to filter out missing expressions. This may happen - // if, for example, a MIR-instrumented expression is removed during an optimization. - entry.as_ref().map(|expression| (original_index, expression)) + entry.as_ref().map(|region| CoverageCounterAndRegion { + kind: CoverageCounterKind::Counter(Counter::counter_value_reference(index)), + region, }) - { - let optional_region = &expression.region; - let Expression { lhs, op, rhs, .. } = *expression; + }); - if let Some(Some((lhs_counter, mut rhs_counter))) = id_to_counter(&new_indexes, lhs) - .map(|lhs_counter| { - id_to_counter(&new_indexes, rhs).map(|rhs_counter| (lhs_counter, rhs_counter)) + // Find all of the expression IDs that weren't optimized out AND have + // an attached code region, and return the corresponding mapping as a + // counter/region pair. + let expression_regions = + self.expressions.iter_enumerated().filter_map(|(id, expression)| { + let region = expression.as_ref()?.region.as_ref()?; + Some(CoverageCounterAndRegion { + kind: CoverageCounterKind::Counter(self.simplified_expression(id)), + region, }) - { - if lhs_counter.is_zero() && op.is_subtract() { - // The left side of a subtraction was probably optimized out. As an example, - // a branch condition might be evaluated as a constant expression, and the - // branch could be removed, dropping unused counters in the process. - // - // Since counters are unsigned, we must assume the result of the expression - // can be no more and no less than zero. An expression known to evaluate to zero - // does not need to be added to the coverage map. - // - // Coverage test `loops_branches.rs` includes multiple variations of branches - // based on constant conditional (literal `true` or `false`), and demonstrates - // that the expected counts are still correct. - debug!( - "Expression subtracts from zero (assume unreachable): \ - original_index={:?}, lhs={:?}, op={:?}, rhs={:?}, region={:?}", - original_index, lhs, op, rhs, optional_region, - ); - rhs_counter = Counter::zero(); + }); + + let unreachable_regions = self.unreachable_regions.iter().map(|region| { + CoverageCounterAndRegion { kind: CoverageCounterKind::Counter(Counter::ZERO), region } + }); + + let branch_regions = self.branches.iter().map(|branch| CoverageCounterAndRegion { + kind: CoverageCounterKind::Branch { + true_counter: Counter::from_operand(branch.true_), + false_counter: Counter::from_operand(branch.false_), + }, + region: &branch.region, + }); + + let mut all_regions: Vec<_> = expression_regions.collect(); + all_regions.extend(counter_regions); + all_regions.extend(unreachable_regions); + all_regions.extend(branch_regions); + + // make sure all the regions are sorted + all_regions.sort_unstable_by_key(|coverage_region| coverage_region.region); + + (counter_expressions, all_regions) + } + + /// Convert this function's coverage expression data into a form that can be + /// passed through FFI to LLVM. + fn counter_expressions(&self) -> Vec { + // We know that LLVM will optimize out any unused expressions before + // producing the final coverage map, so there's no need to do the same + // thing on the Rust side unless we're confident we can do much better. + // (See `CounterExpressionsMinimizer` in `CoverageMappingWriter.cpp`.) + + self.expressions + .iter() + .map(|expression| match expression { + None => { + // This expression ID was allocated, but we never saw the + // actual expression, so it must have been optimized out. + // Replace it with a dummy expression, and let LLVM take + // care of omitting it from the expression list. + CounterExpression::DUMMY } - debug_assert!( - lhs_counter.is_zero() - // Note: with `as usize` the ID _could_ overflow/wrap if `usize = u16` - || ((lhs_counter.zero_based_id() as usize) - <= usize::max(self.counters.len(), self.expressions.len())), - "lhs id={} > both counters.len()={} and expressions.len()={} - ({:?} {:?} {:?})", - lhs_counter.zero_based_id(), - self.counters.len(), - self.expressions.len(), - lhs_counter, - op, - rhs_counter, - ); - - debug_assert!( - rhs_counter.is_zero() - // Note: with `as usize` the ID _could_ overflow/wrap if `usize = u16` - || ((rhs_counter.zero_based_id() as usize) - <= usize::max(self.counters.len(), self.expressions.len())), - "rhs id={} > both counters.len()={} and expressions.len()={} - ({:?} {:?} {:?})", - rhs_counter.zero_based_id(), - self.counters.len(), - self.expressions.len(), - lhs_counter, - op, - rhs_counter, - ); - - // Both operands exist. `Expression` operands exist in `self.expressions` and have - // been assigned a `new_index`. - let mapped_expression_index = - MappedExpressionIndex::from(counter_expressions.len()); - let expression = CounterExpression::new( - lhs_counter, + &Some(Expression { lhs, op, rhs, .. }) => CounterExpression::new( + Counter::from_operand(lhs), match op { Op::Add => ExprKind::Add, Op::Subtract => ExprKind::Subtract, }, - rhs_counter, - ); - debug!( - "Adding expression {:?} = {:?}, region: {:?}", - mapped_expression_index, expression, optional_region - ); - counter_expressions.push(expression); - new_indexes[original_index] = Some(mapped_expression_index); - if let Some(region) = optional_region { - expression_regions.push((Counter::expression(mapped_expression_index), region)); - } - } else { - bug!( - "expression has one or more missing operands \ - original_index={:?}, lhs={:?}, op={:?}, rhs={:?}, region={:?}", - original_index, - lhs, - op, - rhs, - optional_region, - ); - } - } - (counter_expressions, expression_regions.into_iter()) - } - - fn unreachable_regions(&self) -> impl Iterator { - self.unreachable_regions.iter().map(|region| (Counter::zero(), region)) + Counter::from_operand(rhs), + ), + }) + .collect::>() } } diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs index 97a99e5105675..d40aba53527c1 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs @@ -1,6 +1,6 @@ use crate::common::CodegenCx; use crate::coverageinfo; -use crate::coverageinfo::ffi::{Counter, CounterExpression, CounterMappingRegion}; +use crate::coverageinfo::ffi::{CounterExpression, CounterMappingRegion}; use crate::llvm; use rustc_codegen_ssa::traits::ConstMethods; @@ -14,6 +14,8 @@ use rustc_middle::mir::coverage::CodeRegion; use rustc_middle::ty::TyCtxt; use rustc_span::Symbol; +use super::map_data::{CoverageCounterAndRegion, CoverageCounterKind}; + /// Generates and exports the Coverage Map. /// /// Rust Coverage Map generation supports LLVM Coverage Mapping Format version @@ -59,8 +61,10 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) { // Encode coverage mappings and generate function records let mut function_data = Vec::new(); - for (instance, function_coverage) in function_coverage_map { + for (instance, mut function_coverage) in function_coverage_map { debug!("Generate function coverage for {}, {:?}", cx.codegen_unit.name(), instance); + function_coverage.simplify_expressions(); + let mangled_function_name = tcx.symbol_name(instance).name; let source_hash = function_coverage.source_hash(); let is_used = function_coverage.is_used(); @@ -140,14 +144,13 @@ impl CoverageMapGenerator { /// Using the `expressions` and `counter_regions` collected for the current function, generate /// the `mapping_regions` and `virtual_file_mapping`, and capture any new filenames. Then use /// LLVM APIs to encode the `virtual_file_mapping`, `expressions`, and `mapping_regions` into - /// the given `coverage_mapping` byte buffer, compliant with the LLVM Coverage Mapping format. - fn write_coverage_mapping<'a>( + /// the given `coverage_mapping_buffer` byte buffer, compliant with the LLVM Coverage Mapping format. + fn write_coverage_mapping( &mut self, expressions: Vec, - counter_regions: impl Iterator, + counter_regions: Vec>, coverage_mapping_buffer: &RustString, ) { - let mut counter_regions = counter_regions.collect::>(); if counter_regions.is_empty() { return; } @@ -157,13 +160,13 @@ impl CoverageMapGenerator { let mut current_file_name = None; let mut current_file_id = 0; - // Convert the list of (Counter, CodeRegion) pairs to an array of `CounterMappingRegion`, sorted + // Convert the list of `CoverageCounterAndRegion` to an array of `CounterMappingRegion`, sorted // by filename and position. Capture any new files to compute the `CounterMappingRegion`s // `file_id` (indexing files referenced by the current function), and construct the // function-specific `virtual_file_mapping` from `file_id` to its index in the module's // `filenames` array. - counter_regions.sort_unstable_by_key(|(_counter, region)| *region); - for (counter, region) in counter_regions { + for counter_region in counter_regions { + let region = counter_region.region; let CodeRegion { file_name, start_line, start_col, end_line, end_col } = *region; let same_file = current_file_name.is_some_and(|p| p == file_name); if !same_file { @@ -175,15 +178,34 @@ impl CoverageMapGenerator { let (filenames_index, _) = self.filenames.insert_full(file_name); virtual_file_mapping.push(filenames_index as u32); } - debug!("Adding counter {:?} to map for {:?}", counter, region); - mapping_regions.push(CounterMappingRegion::code_region( - counter, - current_file_id, - start_line, - start_col, - end_line, - end_col, - )); + match counter_region.kind { + CoverageCounterKind::Counter(counter) => { + debug!("Adding counter {:?} to map for {:?}", counter, region); + mapping_regions.push(CounterMappingRegion::code_region( + counter, + current_file_id, + start_line, + start_col, + end_line, + end_col, + )); + } + CoverageCounterKind::Branch { true_counter, false_counter } => { + debug!( + "Adding branch ({:?} / {:?}) to map for {:?}", + true_counter, false_counter, region + ); + mapping_regions.push(CounterMappingRegion::branch_region( + true_counter, + false_counter, + current_file_id, + start_line, + start_col, + end_line, + end_col, + )); + } + } } // Encode and append the current function's coverage mapping data diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs index 621fd36b2a323..d0cd35f4e2bf5 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs @@ -16,7 +16,7 @@ use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_llvm::RustString; use rustc_middle::bug; -use rustc_middle::mir::coverage::{CodeRegion, CounterId, CoverageKind, ExpressionId, Op, Operand}; +use rustc_middle::mir::coverage::{CounterId, CoverageKind}; use rustc_middle::mir::Coverage; use rustc_middle::ty; use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt}; @@ -104,144 +104,75 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> { fn add_coverage(&mut self, instance: Instance<'tcx>, coverage: &Coverage) { let bx = self; + let Some(coverage_context) = bx.coverage_context() else { return }; + let mut coverage_map = coverage_context.function_coverage_map.borrow_mut(); + let func_coverage = coverage_map + .entry(instance) + .or_insert_with(|| FunctionCoverage::new(bx.tcx(), instance)); + let Coverage { kind, code_region } = coverage.clone(); match kind { CoverageKind::Counter { function_source_hash, id } => { - if bx.set_function_source_hash(instance, function_source_hash) { - // If `set_function_source_hash()` returned true, the coverage map is enabled, - // so continue adding the counter. - if let Some(code_region) = code_region { - // Note: Some counters do not have code regions, but may still be referenced - // from expressions. In that case, don't add the counter to the coverage map, - // but do inject the counter intrinsic. - bx.add_coverage_counter(instance, id, code_region); - } - - let coverageinfo = bx.tcx().coverageinfo(instance.def); - - let fn_name = bx.get_pgo_func_name_var(instance); - let hash = bx.const_u64(function_source_hash); - let num_counters = bx.const_u32(coverageinfo.num_counters); - let index = bx.const_u32(id.as_u32()); + debug!( + "ensuring function source hash is set for instance={:?}; function_source_hash={}", + instance, function_source_hash, + ); + func_coverage.set_function_source_hash(function_source_hash); + + if let Some(code_region) = code_region { + // Note: Some counters do not have code regions, but may still be referenced + // from expressions. In that case, don't add the counter to the coverage map, + // but do inject the counter intrinsic. debug!( - "codegen intrinsic instrprof.increment(fn_name={:?}, hash={:?}, num_counters={:?}, index={:?})", - fn_name, hash, num_counters, index, + "adding counter to coverage_map: instance={:?}, id={:?}, region={:?}", + instance, id, code_region, ); - bx.instrprof_increment(fn_name, hash, num_counters, index); + func_coverage.add_counter(id, code_region); } + // We need to explicitly drop the `RefMut` before calling into `instrprof_increment`, + // as that needs an exclusive borrow. + drop(coverage_map); + + let coverageinfo = bx.tcx().coverageinfo(instance.def); + + let fn_name = bx.get_pgo_func_name_var(instance); + let hash = bx.const_u64(function_source_hash); + let num_counters = bx.const_u32(coverageinfo.num_counters); + let index = bx.const_u32(id.as_u32()); + debug!( + "codegen intrinsic instrprof.increment(fn_name={:?}, hash={:?}, num_counters={:?}, index={:?})", + fn_name, hash, num_counters, index, + ); + bx.instrprof_increment(fn_name, hash, num_counters, index); } CoverageKind::Expression { id, lhs, op, rhs } => { - bx.add_coverage_counter_expression(instance, id, lhs, op, rhs, code_region); + debug!( + "adding counter expression to coverage_map: instance={:?}, id={:?}, {:?} {:?} {:?}; region: {:?}", + instance, id, lhs, op, rhs, code_region, + ); + func_coverage.add_counter_expression(id, lhs, op, rhs, code_region); + } + CoverageKind::Branch { true_, false_ } => { + let code_region = code_region.expect("branch regions always have code regions"); + debug!( + "adding branch to coverage_map: instance={:?}, {:?} / {:?}; region: {:?}", + instance, true_, false_, code_region, + ); + func_coverage.add_branch_counter(true_, false_, code_region); } CoverageKind::Unreachable => { - bx.add_coverage_unreachable( - instance, - code_region.expect("unreachable regions always have code regions"), + let code_region = + code_region.expect("unreachable regions always have code regions"); + debug!( + "adding unreachable code to coverage_map: instance={:?}, at {:?}", + instance, code_region, ); + func_coverage.add_unreachable_region(code_region); } } } } -// These methods used to be part of trait `CoverageInfoBuilderMethods`, but -// after moving most coverage code out of SSA they are now just ordinary methods. -impl<'tcx> Builder<'_, '_, 'tcx> { - /// Returns true if the function source hash was added to the coverage map (even if it had - /// already been added, for this instance). Returns false *only* if `-C instrument-coverage` is - /// not enabled (a coverage map is not being generated). - fn set_function_source_hash( - &mut self, - instance: Instance<'tcx>, - function_source_hash: u64, - ) -> bool { - if let Some(coverage_context) = self.coverage_context() { - debug!( - "ensuring function source hash is set for instance={:?}; function_source_hash={}", - instance, function_source_hash, - ); - let mut coverage_map = coverage_context.function_coverage_map.borrow_mut(); - coverage_map - .entry(instance) - .or_insert_with(|| FunctionCoverage::new(self.tcx, instance)) - .set_function_source_hash(function_source_hash); - true - } else { - false - } - } - - /// Returns true if the counter was added to the coverage map; false if `-C instrument-coverage` - /// is not enabled (a coverage map is not being generated). - fn add_coverage_counter( - &mut self, - instance: Instance<'tcx>, - id: CounterId, - region: CodeRegion, - ) -> bool { - if let Some(coverage_context) = self.coverage_context() { - debug!( - "adding counter to coverage_map: instance={:?}, id={:?}, region={:?}", - instance, id, region, - ); - let mut coverage_map = coverage_context.function_coverage_map.borrow_mut(); - coverage_map - .entry(instance) - .or_insert_with(|| FunctionCoverage::new(self.tcx, instance)) - .add_counter(id, region); - true - } else { - false - } - } - - /// Returns true if the expression was added to the coverage map; false if - /// `-C instrument-coverage` is not enabled (a coverage map is not being generated). - fn add_coverage_counter_expression( - &mut self, - instance: Instance<'tcx>, - id: ExpressionId, - lhs: Operand, - op: Op, - rhs: Operand, - region: Option, - ) -> bool { - if let Some(coverage_context) = self.coverage_context() { - debug!( - "adding counter expression to coverage_map: instance={:?}, id={:?}, {:?} {:?} {:?}; \ - region: {:?}", - instance, id, lhs, op, rhs, region, - ); - let mut coverage_map = coverage_context.function_coverage_map.borrow_mut(); - coverage_map - .entry(instance) - .or_insert_with(|| FunctionCoverage::new(self.tcx, instance)) - .add_counter_expression(id, lhs, op, rhs, region); - true - } else { - false - } - } - - /// Returns true if the region was added to the coverage map; false if `-C instrument-coverage` - /// is not enabled (a coverage map is not being generated). - fn add_coverage_unreachable(&mut self, instance: Instance<'tcx>, region: CodeRegion) -> bool { - if let Some(coverage_context) = self.coverage_context() { - debug!( - "adding unreachable code to coverage_map: instance={:?}, at {:?}", - instance, region, - ); - let mut coverage_map = coverage_context.function_coverage_map.borrow_mut(); - coverage_map - .entry(instance) - .or_insert_with(|| FunctionCoverage::new(self.tcx, instance)) - .add_unreachable_region(region); - true - } else { - false - } - } -} - fn declare_unused_fn<'tcx>(cx: &CodegenCx<'_, 'tcx>, def_id: DefId) -> Instance<'tcx> { let tcx = cx.tcx; diff --git a/compiler/rustc_middle/src/mir/coverage.rs b/compiler/rustc_middle/src/mir/coverage.rs index d7d6e3a0086cd..6afd556563044 100644 --- a/compiler/rustc_middle/src/mir/coverage.rs +++ b/compiler/rustc_middle/src/mir/coverage.rs @@ -45,16 +45,6 @@ impl ExpressionId { } } -rustc_index::newtype_index! { - /// MappedExpressionIndex values ascend from zero, and are recalculated indexes based on their - /// array position in the LLVM coverage map "Expressions" array, which is assembled during the - /// "mapgen" process. They cannot be computed algorithmically, from the other `newtype_index`s. - #[derive(HashStable)] - #[max = 0xFFFF_FFFF] - #[debug_format = "MappedExpressionIndex({})"] - pub struct MappedExpressionIndex {} -} - /// Operand of a coverage-counter expression. /// /// Operands can be a constant zero value, an actual coverage counter, or another @@ -93,6 +83,10 @@ pub enum CoverageKind { op: Op, rhs: Operand, }, + Branch { + true_: Operand, + false_: Operand, + }, Unreachable, } @@ -102,6 +96,7 @@ impl CoverageKind { match *self { Counter { id, .. } => Operand::Counter(id), Expression { id, .. } => Operand::Expression(id), + Branch { .. } => bug!("Branch coverage cannot be part of an expression"), Unreachable => bug!("Unreachable coverage cannot be part of an expression"), } } @@ -127,6 +122,9 @@ impl Debug for CoverageKind { }, rhs, ), + Branch { true_, false_ } => { + write!(fmt, "Branch: {true_:?} / {false_:?}") + } Unreachable => write!(fmt, "Unreachable"), } } diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index f979ddd00fa01..7e93a236308b8 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -469,7 +469,6 @@ TrivialTypeTraversalAndLiftImpls! { ::rustc_target::spec::abi::Abi, crate::mir::coverage::CounterId, crate::mir::coverage::ExpressionId, - crate::mir::coverage::MappedExpressionIndex, crate::mir::Local, crate::mir::Promoted, crate::traits::Reveal, diff --git a/compiler/rustc_mir_transform/src/coverage/counters.rs b/compiler/rustc_mir_transform/src/coverage/counters.rs index d1f2f0c76c839..08d06845f7af4 100644 --- a/compiler/rustc_mir_transform/src/coverage/counters.rs +++ b/compiler/rustc_mir_transform/src/coverage/counters.rs @@ -69,8 +69,31 @@ impl CoverageCounters { &mut self, basic_coverage_blocks: &CoverageGraph, coverage_spans: &[CoverageSpan], - ) -> Result<(), Error> { - MakeBcbCounters::new(self, basic_coverage_blocks).make_bcb_counters(coverage_spans) + ) -> Result, Error> { + let mut make_counters = MakeBcbCounters::new(self, basic_coverage_blocks); + make_counters.make_bcb_counters(coverage_spans)?; + + let branches: Vec<_> = basic_coverage_blocks + .iter_enumerated() + .filter_map(|(bcb, _data)| { + let branches = make_counters.bcb_branches(bcb); + if branches.len() != 2 { + return None; + } + let mut branch_counters = + branches.iter().filter_map(|branch| make_counters.branch_counter(branch)); + + // FIXME(swatinem): figure out what the correct ordering here is? + // In simple testing, the `false` (aka `0`) branch of `SwitchInt` seems to be first. + let false_ = branch_counters.next()?.as_operand(); + let true_ = branch_counters.next()?.as_operand(); + + let branch = CoverageKind::Branch { true_, false_ }; + Some((bcb, branch)) + }) + .collect(); + + Ok(branches) } fn make_counter(&mut self, debug_block_label_fn: F) -> CoverageKind diff --git a/compiler/rustc_mir_transform/src/coverage/debug.rs b/compiler/rustc_mir_transform/src/coverage/debug.rs index 083f1f8aef692..3830fe77cf4ca 100644 --- a/compiler/rustc_mir_transform/src/coverage/debug.rs +++ b/compiler/rustc_mir_transform/src/coverage/debug.rs @@ -44,7 +44,7 @@ //! points, which can be enabled via environment variable: //! //! ```shell -//! RUSTC_LOG=rustc_mir_transform::transform::coverage=debug +//! RUSTC_LOG=rustc_mir_transform::coverage=debug //! ``` //! //! Other module paths with coverage-related debug logs may also be of interest, particularly for @@ -52,7 +52,7 @@ //! code generation pass). For example: //! //! ```shell -//! RUSTC_LOG=rustc_mir_transform::transform::coverage,rustc_codegen_ssa::coverageinfo,rustc_codegen_llvm::coverageinfo=debug +//! RUSTC_LOG=rustc_mir_transform::coverage,rustc_codegen_ssa::coverageinfo,rustc_codegen_llvm::coverageinfo=debug //! ``` //! //! Coverage Debug Options @@ -299,6 +299,9 @@ impl DebugCounters { CoverageKind::Expression { .. } => { format!("Expression({})", self.format_counter_kind(counter_kind)) } + CoverageKind::Branch { true_, false_ } => { + format!("Branch({} / {})", self.format_operand(true_), self.format_operand(false_)) + } CoverageKind::Unreachable { .. } => "Unreachable".to_owned(), } } diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index e08b6d6f6e8d8..e03836acd1a83 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -202,11 +202,11 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { // // Intermediate expressions (used to compute other `Expression` values), which have no // direct association with any `BasicCoverageBlock`, are accumulated inside `coverage_counters`. - let result = self + let mut result = self .coverage_counters .make_bcb_counters(&mut self.basic_coverage_blocks, &coverage_spans); - if let Ok(()) = result { + if let Ok(branches) = result.as_mut() { // If debugging, add any intermediate expressions (which are not associated with any // BCB) to the `debug_used_expressions` map. if debug_used_expressions.is_enabled() { @@ -216,7 +216,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { } //////////////////////////////////////////////////// - // Remove the counter or edge counter from of each `CoverageSpan`s associated + // Remove the counter or edge counter from each `CoverageSpan`s associated // `BasicCoverageBlock`, and inject a `Coverage` statement into the MIR. // // `Coverage` statements injected from `CoverageSpan`s will include the code regions @@ -231,6 +231,10 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { &mut debug_used_expressions, ); + // TODO: if self.tcx.sess.instrument_coverage_branch() { + self.inject_branch_counters(std::mem::take(branches)); + // } + //////////////////////////////////////////////////// // For any remaining `BasicCoverageBlock` counters (that were not associated with // any `CoverageSpan`), inject `Coverage` statements (_without_ code region `Span`s) @@ -275,6 +279,27 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { } } + fn inject_branch_counters(&mut self, branches: Vec<(BasicCoverageBlock, CoverageKind)>) { + let tcx = self.tcx; + let source_map = tcx.sess.source_map(); + let body_span = self.body_span; + let file_name = Symbol::intern(&self.source_file.name.prefer_remapped().to_string_lossy()); + + for branch in branches { + let bcb_data = self.bcb_data(branch.0); + let terminator = bcb_data.terminator(self.mir_body); + + let span = terminator.source_info.span; + + // FIXME(swatinem): figure out what a good span for the conditional is. + // For trivial code examples, the `terminator` seems to be sufficient. + let code_region = + Some(make_code_region(source_map, file_name, &self.source_file, span, body_span)); + + inject_statement(self.mir_body, branch.1, bcb_data.last_bb(), code_region); + } + } + /// Inject a counter for each `CoverageSpan`. There can be multiple `CoverageSpan`s for a given /// BCB, but only one actual counter needs to be incremented per BCB. `bb_counters` maps each /// `bcb` to its `Counter`, when injected. Subsequent `CoverageSpan`s for a BCB that already has diff --git a/compiler/rustc_mir_transform/src/coverage/tests.rs b/compiler/rustc_mir_transform/src/coverage/tests.rs index d797a6057a7f5..0c2910148b020 100644 --- a/compiler/rustc_mir_transform/src/coverage/tests.rs +++ b/compiler/rustc_mir_transform/src/coverage/tests.rs @@ -2,7 +2,7 @@ //! pass. //! //! ```shell -//! ./x.py test --keep-stage 1 compiler/rustc_mir --test-args '--show-output coverage' +//! ./x.py test --keep-stage 1 compiler/rustc_mir_transform --test-args '--show-output coverage' //! ``` //! //! The tests construct a few "mock" objects, as needed, to support the `InstrumentCoverage` @@ -676,7 +676,7 @@ fn test_make_bcb_counters() { } } let mut coverage_counters = counters::CoverageCounters::new(0, &basic_coverage_blocks); - let () = coverage_counters + let _ = coverage_counters .make_bcb_counters(&mut basic_coverage_blocks, &coverage_spans) .expect("should be Ok"); assert_eq!(coverage_counters.intermediate_expressions.len(), 0); diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index f00472f181d8e..9e84ae2c0b0c0 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -168,6 +168,8 @@ pub enum MirSpanview { pub enum InstrumentCoverage { /// Default `-C instrument-coverage` or `-C instrument-coverage=statement` All, + /// Additionally, instrument branches and output branch overage + Branch, /// `-Zunstable-options -C instrument-coverage=except-unused-generics` ExceptUnusedGenerics, /// `-Zunstable-options -C instrument-coverage=except-unused-functions` diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 055ab2d9c1583..23d6491010cf1 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -382,7 +382,7 @@ mod desc { pub const parse_mir_spanview: &str = "`statement` (default), `terminator`, or `block`"; pub const parse_dump_mono_stats: &str = "`markdown` (default) or `json`"; pub const parse_instrument_coverage: &str = - "`all` (default), `except-unused-generics`, `except-unused-functions`, or `off`"; + "`all` (default), `branch`, `except-unused-generics`, `except-unused-functions`, or `off`"; 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 number bigger than 0"; @@ -895,6 +895,7 @@ mod parse { *slot = Some(match v { "all" => InstrumentCoverage::All, + "branch" => InstrumentCoverage::Branch, "except-unused-generics" | "except_unused_generics" => { InstrumentCoverage::ExceptUnusedGenerics } @@ -1299,6 +1300,7 @@ options! { reports (note, the compiler build config must include `profiler = true`); \ implies `-C symbol-mangling-version=v0`. Optional values are: `=all` (implicit value) + `=branch` `=except-unused-generics` `=except-unused-functions` `=off` (default)"), @@ -1547,6 +1549,7 @@ options! { reports (note, the compiler build config must include `profiler = true`); \ implies `-C symbol-mangling-version=v0`. Optional values are: `=all` (implicit value) + `=branch` `=except-unused-generics` `=except-unused-functions` `=off` (default)"), diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 086ce4e69646a..227deae3bb4c3 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -695,6 +695,10 @@ impl Session { self.opts.cg.instrument_coverage() != InstrumentCoverage::Off } + pub fn instrument_coverage_branch(&self) -> bool { + self.opts.cg.instrument_coverage() == InstrumentCoverage::Branch + } + pub fn instrument_coverage_except_unused_generics(&self) -> bool { self.opts.cg.instrument_coverage() == InstrumentCoverage::ExceptUnusedGenerics } diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs index 06b37008ebed3..8b2d16b3570af 100644 --- a/compiler/rustc_smir/src/rustc_smir/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/mod.rs @@ -491,6 +491,10 @@ impl<'tcx> Stable<'tcx> for mir::coverage::CoverageKind { rhs: opaque(rhs), } } + CoverageKind::Branch { true_, false_ } => stable_mir::mir::CoverageKind::Branch { + true_: opaque(true_), + false_: opaque(false_), + }, CoverageKind::Unreachable => stable_mir::mir::CoverageKind::Unreachable, } } diff --git a/compiler/rustc_smir/src/stable_mir/mir/body.rs b/compiler/rustc_smir/src/stable_mir/mir/body.rs index c16bd6cbd70e2..838f0dde185db 100644 --- a/compiler/rustc_smir/src/stable_mir/mir/body.rs +++ b/compiler/rustc_smir/src/stable_mir/mir/body.rs @@ -184,6 +184,10 @@ pub enum CoverageKind { op: Op, rhs: ExpressionOperandId, }, + Branch { + true_: ExpressionOperandId, + false_: ExpressionOperandId, + }, Unreachable, } diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index b366619285338..a24a6a4636d48 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -703,7 +703,8 @@ impl<'a> Builder<'a> { llvm::Lld, llvm::CrtBeginEnd, tool::RustdocGUITest, - tool::OptimizedDist + tool::OptimizedDist, + tool::CoverageDump, ), Kind::Check | Kind::Clippy | Kind::Fix => describe!( check::Std, @@ -725,6 +726,7 @@ impl<'a> Builder<'a> { test::Tidy, test::Ui, test::RunPassValgrind, + test::CoverageMap, test::RunCoverage, test::MirOpt, test::Codegen, diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index d0d62db08071a..b43ca581051cb 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -1328,6 +1328,12 @@ host_test!(RunMakeFullDeps { default_test!(Assembly { path: "tests/assembly", mode: "assembly", suite: "assembly" }); +default_test!(CoverageMap { + path: "tests/coverage-map", + mode: "coverage-map", + suite: "coverage-map" +}); + host_test!(RunCoverage { path: "tests/run-coverage", mode: "run-coverage", suite: "run-coverage" }); host_test!(RunCoverageRustdoc { path: "tests/run-coverage-rustdoc", @@ -1533,6 +1539,14 @@ note: if you're sure you want to do this, please open an issue as to why. In the .arg(builder.ensure(tool::JsonDocLint { compiler: json_compiler, target })); } + if mode == "coverage-map" { + let coverage_dump = builder.ensure(tool::CoverageDump { + compiler: compiler.with_stage(0), + target: compiler.host, + }); + cmd.arg("--coverage-dump-path").arg(coverage_dump); + } + if mode == "run-make" || mode == "run-coverage" { let rust_demangler = builder .ensure(tool::RustDemangler { diff --git a/src/bootstrap/tool.rs b/src/bootstrap/tool.rs index e6d27757ac661..d76bfc4b13125 100644 --- a/src/bootstrap/tool.rs +++ b/src/bootstrap/tool.rs @@ -306,6 +306,7 @@ bootstrap_tool!( GenerateWindowsSys, "src/tools/generate-windows-sys", "generate-windows-sys"; RustdocGUITest, "src/tools/rustdoc-gui-test", "rustdoc-gui-test", is_unstable_tool = true, allow_features = "test"; OptimizedDist, "src/tools/opt-dist", "opt-dist"; + CoverageDump, "src/tools/coverage-dump", "coverage-dump"; ); #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)] diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index 7c17e92d0dfe2..b91d5a958bb62 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -66,6 +66,7 @@ string_enum! { JsDocTest => "js-doc-test", MirOpt => "mir-opt", Assembly => "assembly", + CoverageMap => "coverage-map", RunCoverage => "run-coverage", } } @@ -161,6 +162,9 @@ pub struct Config { /// The rust-demangler executable. pub rust_demangler_path: Option, + /// The coverage-dump executable. + pub coverage_dump_path: Option, + /// The Python executable to use for LLDB and htmldocck. pub python: String, @@ -639,6 +643,7 @@ pub const UI_EXTENSIONS: &[&str] = &[ UI_STDERR_32, UI_STDERR_16, UI_COVERAGE, + UI_COVERAGE_MAP, ]; pub const UI_STDERR: &str = "stderr"; pub const UI_STDOUT: &str = "stdout"; @@ -649,6 +654,7 @@ pub const UI_STDERR_64: &str = "64bit.stderr"; pub const UI_STDERR_32: &str = "32bit.stderr"; pub const UI_STDERR_16: &str = "16bit.stderr"; pub const UI_COVERAGE: &str = "coverage"; +pub const UI_COVERAGE_MAP: &str = "cov-map"; /// Absolute path to the directory where all output for all tests in the given /// `relative_dir` group should reside. Example: diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs index 1a765477fe501..619ff9b322114 100644 --- a/src/tools/compiletest/src/lib.rs +++ b/src/tools/compiletest/src/lib.rs @@ -48,6 +48,7 @@ pub fn parse_config(args: Vec) -> Config { .reqopt("", "rustc-path", "path to rustc to use for compiling", "PATH") .optopt("", "rustdoc-path", "path to rustdoc to use for compiling", "PATH") .optopt("", "rust-demangler-path", "path to rust-demangler to use in tests", "PATH") + .optopt("", "coverage-dump-path", "path to coverage-dump to use in tests", "PATH") .reqopt("", "python", "path to python to use for doc tests", "PATH") .optopt("", "jsondocck-path", "path to jsondocck to use for doc tests", "PATH") .optopt("", "jsondoclint-path", "path to jsondoclint to use for doc tests", "PATH") @@ -218,6 +219,7 @@ pub fn parse_config(args: Vec) -> Config { rustc_path: opt_path(matches, "rustc-path"), rustdoc_path: matches.opt_str("rustdoc-path").map(PathBuf::from), rust_demangler_path: matches.opt_str("rust-demangler-path").map(PathBuf::from), + coverage_dump_path: matches.opt_str("coverage-dump-path").map(PathBuf::from), python: matches.opt_str("python").unwrap(), jsondocck_path: matches.opt_str("jsondocck-path"), jsondoclint_path: matches.opt_str("jsondoclint-path"), diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 4ef79af3124a8..dd6f5d155c181 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -6,8 +6,8 @@ use crate::common::{Assembly, Incremental, JsDocTest, MirOpt, RunMake, RustdocJs use crate::common::{Codegen, CodegenUnits, DebugInfo, Debugger, Rustdoc}; use crate::common::{CompareMode, FailMode, PassMode}; use crate::common::{Config, TestPaths}; -use crate::common::{Pretty, RunCoverage, RunPassValgrind}; -use crate::common::{UI_COVERAGE, UI_RUN_STDERR, UI_RUN_STDOUT}; +use crate::common::{CoverageMap, Pretty, RunCoverage, RunPassValgrind}; +use crate::common::{UI_COVERAGE, UI_COVERAGE_MAP, UI_RUN_STDERR, UI_RUN_STDOUT}; use crate::compute_diff::{write_diff, write_filtered_diff}; use crate::errors::{self, Error, ErrorKind}; use crate::header::TestProps; @@ -18,7 +18,6 @@ use crate::ColorConfig; use regex::{Captures, Regex}; use rustfix::{apply_suggestions, get_suggestions_from_json, Filter}; -use std::borrow::Cow; use std::collections::hash_map::DefaultHasher; use std::collections::{HashMap, HashSet}; use std::env; @@ -254,6 +253,7 @@ impl<'test> TestCx<'test> { MirOpt => self.run_mir_opt_test(), Assembly => self.run_assembly_test(), JsDocTest => self.run_js_doc_test(), + CoverageMap => self.run_coverage_map_test(), RunCoverage => self.run_coverage_test(), } } @@ -467,6 +467,46 @@ impl<'test> TestCx<'test> { } } + fn run_coverage_map_test(&self) { + let Some(coverage_dump_path) = &self.config.coverage_dump_path else { + self.fatal("missing --coverage-dump"); + }; + + let proc_res = self.compile_test_and_save_ir(); + if !proc_res.status.success() { + self.fatal_proc_rec("compilation failed!", &proc_res); + } + drop(proc_res); + + let llvm_ir_path = self.output_base_name().with_extension("ll"); + + let mut dump_command = Command::new(coverage_dump_path); + dump_command.arg(llvm_ir_path); + let proc_res = self.run_command_to_procres(&mut dump_command); + if !proc_res.status.success() { + self.fatal_proc_rec("coverage-dump failed!", &proc_res); + } + + let kind = UI_COVERAGE_MAP; + + let expected_coverage_dump = self.load_expected_output(kind); + let actual_coverage_dump = self.normalize_output(&proc_res.stdout, &[]); + + let coverage_dump_errors = self.compare_output( + kind, + &actual_coverage_dump, + &expected_coverage_dump, + self.props.compare_output_lines_by_subset, + ); + + if coverage_dump_errors > 0 { + self.fatal_proc_rec( + &format!("{coverage_dump_errors} errors occurred comparing coverage output."), + &proc_res, + ); + } + } + fn run_coverage_test(&self) { let should_run = self.run_if_enabled(); let proc_res = self.compile_test(should_run, Emit::None); @@ -523,7 +563,12 @@ impl<'test> TestCx<'test> { // Run `llvm-cov show` to produce a coverage report in text format. let proc_res = self.run_llvm_tool("llvm-cov", |cmd| { - cmd.args(["show", "--format=text", "--show-line-counts-or-regions"]); + cmd.args([ + "show", + "--format=text", + "--show-line-counts-or-regions", + "--show-branches=count", + ]); cmd.arg("--Xdemangler"); cmd.arg(self.config.rust_demangler_path.as_ref().unwrap()); @@ -650,6 +695,10 @@ impl<'test> TestCx<'test> { let mut cmd = Command::new(tool_path); configure_cmd_fn(&mut cmd); + self.run_command_to_procres(&mut cmd) + } + + fn run_command_to_procres(&self, cmd: &mut Command) -> ProcRes { let output = cmd.output().unwrap_or_else(|_| panic!("failed to exec `{cmd:?}`")); let proc_res = ProcRes { @@ -678,7 +727,7 @@ impl<'test> TestCx<'test> { /// Replace line numbers in coverage reports with the placeholder `LL`, /// so that the tests are less sensitive to lines being added/removed. - fn anonymize_coverage_line_numbers(coverage: &str) -> Cow<'_, str> { + fn anonymize_coverage_line_numbers(coverage: &str) -> String { // The coverage reporter prints line numbers at the start of a line. // They are truncated or left-padded to occupy exactly 5 columns. // (`LineNumberColumnWidth` in `SourceCoverageViewText.cpp`.) @@ -688,7 +737,10 @@ impl<'test> TestCx<'test> { // have an additional prefix of ` |` for each nesting level. static LINE_NUMBER_RE: Lazy = Lazy::new(|| Regex::new(r"(?m:^)(?(?: \|)*) *[0-9]+\|").unwrap()); - LINE_NUMBER_RE.replace_all(coverage, "$prefix LL|") + static BRANCH_LINE_RE: Lazy = + Lazy::new(|| Regex::new(r"(?m:^)(?(?: \|)*) Branch \([0-9]+:").unwrap()); + let coverage = LINE_NUMBER_RE.replace_all(coverage, "$prefix LL|"); + BRANCH_LINE_RE.replace_all(&coverage, "$prefix Branch (LL:").into_owned() } /// Coverage reports can describe multiple source files, separated by @@ -2399,6 +2451,12 @@ impl<'test> TestCx<'test> { rustc.arg(dir_opt); } + CoverageMap => { + rustc.arg("-Cinstrument-coverage"); + // These tests only compile to MIR, so they don't need the + // profiler runtime to be present. + rustc.arg("-Zno-profiler-runtime"); + } RunCoverage => { rustc.arg("-Cinstrument-coverage"); } diff --git a/src/tools/coverage-dump/Cargo.toml b/src/tools/coverage-dump/Cargo.toml new file mode 100644 index 0000000000000..7f14286b5d0c4 --- /dev/null +++ b/src/tools/coverage-dump/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "coverage-dump" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow = "1.0.71" +leb128 = "0.2.5" +md5 = { package = "md-5" , version = "0.10.5" } +miniz_oxide = "0.7.1" +regex = "1.8.4" +rustc-demangle = "0.1.23" diff --git a/src/tools/coverage-dump/README.md b/src/tools/coverage-dump/README.md new file mode 100644 index 0000000000000..e2625d5adf27e --- /dev/null +++ b/src/tools/coverage-dump/README.md @@ -0,0 +1,8 @@ +This tool extracts coverage mapping information from an LLVM IR assembly file +(`.ll`), and prints it in a more human-readable form that can be used for +snapshot tests. + +The output format is mostly arbitrary, so it's OK to change the output as long +as any affected tests are also re-blessed. However, the output should be +consistent across different executions on different platforms, so avoid +printing any information that is platform-specific or non-deterministic. diff --git a/src/tools/coverage-dump/src/covfun.rs b/src/tools/coverage-dump/src/covfun.rs new file mode 100644 index 0000000000000..73327f08d758f --- /dev/null +++ b/src/tools/coverage-dump/src/covfun.rs @@ -0,0 +1,302 @@ +use crate::parser::{unescape_llvm_string_contents, Parser}; +use anyhow::{anyhow, Context}; +use regex::Regex; +use std::collections::HashMap; +use std::fmt::{self, Debug, Write as _}; +use std::sync::OnceLock; + +pub(crate) fn dump_covfun_mappings( + llvm_ir: &str, + function_names: &HashMap, +) -> anyhow::Result<()> { + // Extract function coverage entries from the LLVM IR assembly, and associate + // each entry with its (demangled) name. + let mut covfun_entries = llvm_ir + .lines() + .filter_map(covfun_line_data) + .map(|line_data| (function_names.get(&line_data.name_hash).map(String::as_str), line_data)) + .collect::>(); + covfun_entries.sort_by(|a, b| { + // Sort entries primarily by name, to help make the order consistent + // across platforms and relatively insensitive to changes. + // (Sadly we can't use `sort_by_key` because we would need to return references.) + Ord::cmp(&a.0, &b.0) + .then_with(|| Ord::cmp(&a.1.is_used, &b.1.is_used)) + .then_with(|| Ord::cmp(a.1.payload.as_slice(), b.1.payload.as_slice())) + }); + + for (name, line_data) in &covfun_entries { + let name = name.unwrap_or("(unknown)"); + let unused = if line_data.is_used { "" } else { " (unused)" }; + println!("Function name: {name}{unused}"); + + let payload: &[u8] = &line_data.payload; + println!("Raw bytes ({len}): 0x{payload:02x?}", len = payload.len()); + + let mut parser = Parser::new(payload); + + let num_files = parser.read_uleb128_u32()?; + println!("Number of files: {num_files}"); + + for i in 0..num_files { + let global_file_id = parser.read_uleb128_u32()?; + println!("- file {i} => global file {global_file_id}"); + } + + let num_expressions = parser.read_uleb128_u32()?; + println!("Number of expressions: {num_expressions}"); + + let mut expression_resolver = ExpressionResolver::new(); + for i in 0..num_expressions { + let lhs = parser.read_simple_operand()?; + let rhs = parser.read_simple_operand()?; + println!("- expression {i} operands: lhs = {lhs:?}, rhs = {rhs:?}"); + expression_resolver.push_operands(lhs, rhs); + } + + for i in 0..num_files { + let num_mappings = parser.read_uleb128_u32()?; + println!("Number of file {i} mappings: {num_mappings}"); + + for _ in 0..num_mappings { + let (kind, region) = parser.read_mapping_kind_and_region()?; + println!("- {kind:?} at {region:?}"); + + // If the mapping contains expressions, also print the resolved + // form of those expressions + kind.for_each_operand(|label, operand| { + if matches!(operand, Operand::Expression { .. }) { + let pad = if label.is_empty() { "" } else { " " }; + let resolved = expression_resolver.format_operand(operand); + println!(" {label}{pad}= {resolved}"); + } + }); + } + } + + parser.ensure_empty()?; + println!(); + } + Ok(()) +} + +struct CovfunLineData { + name_hash: u64, + is_used: bool, + payload: Vec, +} + +/// Checks a line of LLVM IR assembly to see if it contains an `__llvm_covfun` +/// entry, and if so extracts relevant data in a `CovfunLineData`. +fn covfun_line_data(line: &str) -> Option { + let re = { + // We cheat a little bit and match variable names `@__covrec_[HASH]u` + // rather than the section name, because the section name is harder to + // extract and differs across Linux/Windows/macOS. We also extract the + // symbol name hash from the variable name rather than the data, since + // it's easier and both should match. + static RE: OnceLock = OnceLock::new(); + RE.get_or_init(|| { + Regex::new( + r#"^@__covrec_(?[0-9A-Z]+)(?u)? = .*\[[0-9]+ x i8\] c"(?[^"]*)".*$"#, + ) + .unwrap() + }) + }; + + let captures = re.captures(line)?; + let name_hash = u64::from_str_radix(&captures["name_hash"], 16).unwrap(); + let is_used = captures.name("is_used").is_some(); + let payload = unescape_llvm_string_contents(&captures["payload"]); + + Some(CovfunLineData { name_hash, is_used, payload }) +} + +// Extra parser methods only needed when parsing `covfun` payloads. +impl<'a> Parser<'a> { + fn read_simple_operand(&mut self) -> anyhow::Result { + let raw_operand = self.read_uleb128_u32()?; + Operand::decode(raw_operand).context("decoding operand") + } + + fn read_mapping_kind_and_region(&mut self) -> anyhow::Result<(MappingKind, MappingRegion)> { + let mut kind = self.read_raw_mapping_kind()?; + let mut region = self.read_raw_mapping_region()?; + + const HIGH_BIT: u32 = 1u32 << 31; + if region.end_column & HIGH_BIT != 0 { + region.end_column &= !HIGH_BIT; + kind = match kind { + MappingKind::Code(operand) => MappingKind::Gap(operand), + // LLVM's coverage mapping reader will actually handle this + // case without complaint, but the result is almost certainly + // a meaningless implementation artifact. + _ => return Err(anyhow!("unexpected base kind for gap region: {kind:?}")), + } + } + + Ok((kind, region)) + } + + fn read_raw_mapping_kind(&mut self) -> anyhow::Result { + let raw_mapping_kind = self.read_uleb128_u32()?; + if let Some(operand) = Operand::decode(raw_mapping_kind) { + return Ok(MappingKind::Code(operand)); + } + + assert_eq!(raw_mapping_kind & 0b11, 0); + assert_ne!(raw_mapping_kind, 0); + + let (high, is_expansion) = (raw_mapping_kind >> 3, raw_mapping_kind & 0b100 != 0); + if is_expansion { + Ok(MappingKind::Expansion(high)) + } else { + match high { + 0 => unreachable!("zero kind should have already been handled as a code mapping"), + 2 => Ok(MappingKind::Skip), + 4 => { + let r#true = self.read_simple_operand()?; + let r#false = self.read_simple_operand()?; + Ok(MappingKind::Branch { r#true, r#false }) + } + _ => Err(anyhow!("unknown mapping kind: {raw_mapping_kind:#x}")), + } + } + } + + fn read_raw_mapping_region(&mut self) -> anyhow::Result { + let start_line_offset = self.read_uleb128_u32()?; + let start_column = self.read_uleb128_u32()?; + let end_line_offset = self.read_uleb128_u32()?; + let end_column = self.read_uleb128_u32()?; + Ok(MappingRegion { start_line_offset, start_column, end_line_offset, end_column }) + } +} + +// Represents an expression operand (lhs/rhs), branch region operand (true/false), +// or the value used by a code region or gap region. +#[derive(Clone, Copy, Debug)] +pub(crate) enum Operand { + Zero, + Counter(u32), + Expression(u32, Op), +} + +/// Operator (addition or subtraction) used by an expression. +#[derive(Clone, Copy, Debug)] +pub(crate) enum Op { + Sub, + Add, +} + +impl Operand { + pub(crate) fn decode(input: u32) -> Option { + let (high, tag) = (input >> 2, input & 0b11); + match tag { + 0b00 if high == 0 => Some(Self::Zero), + 0b01 => Some(Self::Counter(high)), + 0b10 => Some(Self::Expression(high, Op::Sub)), + 0b11 => Some(Self::Expression(high, Op::Add)), + // When reading expression or branch operands, the LLVM coverage + // mapping reader will always interpret a `0b00` tag as a zero + // operand, even when the high bits are non-zero. + // We treat that case as failure instead, so that this code can be + // shared by the full mapping-kind reader as well. + _ => None, + } + } +} + +#[derive(Debug)] +enum MappingKind { + Code(Operand), + Gap(Operand), + Expansion(u32), + Skip, + Branch { r#true: Operand, r#false: Operand }, +} + +impl MappingKind { + /// Visits each operand directly contained in this mapping, along with + /// a string label (possibly empty). + fn for_each_operand(&self, mut func: impl FnMut(&str, Operand)) { + match *self { + Self::Code(operand) => func("", operand), + Self::Gap(operand) => func("", operand), + Self::Expansion(_) => (), + Self::Skip => (), + Self::Branch { r#true, r#false } => { + func("true ", r#true); + func("false", r#false); + } + } + } +} + +struct MappingRegion { + /// Offset of this region's start line, relative to the *start line* of + /// the *previous mapping* (or 0). Line numbers are 1-based. + start_line_offset: u32, + /// This region's start column, absolute and 1-based. + start_column: u32, + /// Offset of this region's end line, relative to the *this mapping's* + /// start line. Line numbers are 1-based. + end_line_offset: u32, + /// This region's end column, absolute, 1-based, and exclusive. + /// + /// If the highest bit is set, that bit is cleared and the associated + /// mapping becomes a gap region mapping. + end_column: u32, +} + +impl Debug for MappingRegion { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "(prev + {}, {}) to (start + {}, {})", + self.start_line_offset, self.start_column, self.end_line_offset, self.end_column + ) + } +} + +/// Helper type that prints expressions in a "resolved" form, so that +/// developers reading the dump don't need to resolve expressions by hand. +struct ExpressionResolver { + operands: Vec<(Operand, Operand)>, +} + +impl ExpressionResolver { + fn new() -> Self { + Self { operands: Vec::new() } + } + + fn push_operands(&mut self, lhs: Operand, rhs: Operand) { + self.operands.push((lhs, rhs)); + } + + fn format_operand(&self, operand: Operand) -> String { + let mut output = String::new(); + self.write_operand(&mut output, operand); + output + } + + fn write_operand(&self, output: &mut String, operand: Operand) { + match operand { + Operand::Zero => output.push_str("Zero"), + Operand::Counter(id) => write!(output, "c{id}").unwrap(), + Operand::Expression(id, op) => { + let (lhs, rhs) = self.operands[id as usize]; + let op = match op { + Op::Sub => "-", + Op::Add => "+", + }; + + output.push('('); + self.write_operand(output, lhs); + write!(output, " {op} ").unwrap(); + self.write_operand(output, rhs); + output.push(')'); + } + } + } +} diff --git a/src/tools/coverage-dump/src/main.rs b/src/tools/coverage-dump/src/main.rs new file mode 100644 index 0000000000000..93fed1799e041 --- /dev/null +++ b/src/tools/coverage-dump/src/main.rs @@ -0,0 +1,17 @@ +mod covfun; +mod parser; +mod prf_names; + +fn main() -> anyhow::Result<()> { + use anyhow::Context as _; + + let args = std::env::args().collect::>(); + + let llvm_ir_path = args.get(1).context("LLVM IR file not specified")?; + let llvm_ir = std::fs::read_to_string(llvm_ir_path).context("couldn't read LLVM IR file")?; + + let function_names = crate::prf_names::make_function_names_table(&llvm_ir)?; + crate::covfun::dump_covfun_mappings(&llvm_ir, &function_names)?; + + Ok(()) +} diff --git a/src/tools/coverage-dump/src/parser.rs b/src/tools/coverage-dump/src/parser.rs new file mode 100644 index 0000000000000..eefac1a4f94c1 --- /dev/null +++ b/src/tools/coverage-dump/src/parser.rs @@ -0,0 +1,80 @@ +#[cfg(test)] +mod tests; + +use anyhow::ensure; +use regex::bytes; +use std::sync::OnceLock; + +/// Given the raw contents of a string literal in LLVM IR assembly, decodes any +/// backslash escapes and returns a vector containing the resulting byte string. +pub(crate) fn unescape_llvm_string_contents(contents: &str) -> Vec { + let escape_re = { + static RE: OnceLock = OnceLock::new(); + // LLVM IR supports two string escapes: `\\` and `\xx`. + RE.get_or_init(|| bytes::Regex::new(r"\\\\|\\([0-9A-Za-z]{2})").unwrap()) + }; + + fn u8_from_hex_digits(digits: &[u8]) -> u8 { + // We know that the input contains exactly 2 hex digits, so these calls + // should never fail. + assert_eq!(digits.len(), 2); + let digits = std::str::from_utf8(digits).unwrap(); + u8::from_str_radix(digits, 16).unwrap() + } + + escape_re + .replace_all(contents.as_bytes(), |captures: &bytes::Captures<'_>| { + let byte = match captures.get(1) { + None => b'\\', + Some(hex_digits) => u8_from_hex_digits(hex_digits.as_bytes()), + }; + [byte] + }) + .into_owned() +} + +pub(crate) struct Parser<'a> { + rest: &'a [u8], +} + +impl<'a> Parser<'a> { + pub(crate) fn new(input: &'a [u8]) -> Self { + Self { rest: input } + } + + pub(crate) fn ensure_empty(self) -> anyhow::Result<()> { + ensure!(self.rest.is_empty(), "unparsed bytes: 0x{:02x?}", self.rest); + Ok(()) + } + + pub(crate) fn read_n_bytes(&mut self, n: usize) -> anyhow::Result<&'a [u8]> { + ensure!(n <= self.rest.len()); + + let (bytes, rest) = self.rest.split_at(n); + self.rest = rest; + Ok(bytes) + } + + pub(crate) fn read_uleb128_u32(&mut self) -> anyhow::Result { + self.read_uleb128_u64_and_convert() + } + + pub(crate) fn read_uleb128_usize(&mut self) -> anyhow::Result { + self.read_uleb128_u64_and_convert() + } + + fn read_uleb128_u64_and_convert(&mut self) -> anyhow::Result + where + T: TryFrom + 'static, + T::Error: std::error::Error + Send + Sync, + { + let mut temp_rest = self.rest; + let raw_value: u64 = leb128::read::unsigned(&mut temp_rest)?; + let converted_value = T::try_from(raw_value)?; + + // Only update `self.rest` if the above steps succeeded, so that the + // parser position can be used for error reporting if desired. + self.rest = temp_rest; + Ok(converted_value) + } +} diff --git a/src/tools/coverage-dump/src/parser/tests.rs b/src/tools/coverage-dump/src/parser/tests.rs new file mode 100644 index 0000000000000..a673606b9c4c8 --- /dev/null +++ b/src/tools/coverage-dump/src/parser/tests.rs @@ -0,0 +1,38 @@ +use super::unescape_llvm_string_contents; + +// WARNING: These tests don't necessarily run in CI, and were mainly used to +// help track down problems when originally developing this tool. +// (The tool is still tested indirectly by snapshot tests that rely on it.) + +// Tests for `unescape_llvm_string_contents`: + +#[test] +fn unescape_empty() { + assert_eq!(unescape_llvm_string_contents(""), &[]); +} + +#[test] +fn unescape_noop() { + let input = "The quick brown fox jumps over the lazy dog."; + assert_eq!(unescape_llvm_string_contents(input), input.as_bytes()); +} + +#[test] +fn unescape_backslash() { + let input = r"\\Hello\\world\\"; + assert_eq!(unescape_llvm_string_contents(input), r"\Hello\world\".as_bytes()); +} + +#[test] +fn unescape_hex() { + let input = r"\01\02\03\04\0a\0b\0C\0D\fd\fE\FF"; + let expected: &[u8] = &[0x01, 0x02, 0x03, 0x04, 0x0a, 0x0b, 0x0c, 0x0d, 0xfd, 0xfe, 0xff]; + assert_eq!(unescape_llvm_string_contents(input), expected); +} + +#[test] +fn unescape_mixed() { + let input = r"\\01.\5c\5c"; + let expected: &[u8] = br"\01.\\"; + assert_eq!(unescape_llvm_string_contents(input), expected); +} diff --git a/src/tools/coverage-dump/src/prf_names.rs b/src/tools/coverage-dump/src/prf_names.rs new file mode 100644 index 0000000000000..2c0c942e630ce --- /dev/null +++ b/src/tools/coverage-dump/src/prf_names.rs @@ -0,0 +1,85 @@ +use crate::parser::{unescape_llvm_string_contents, Parser}; +use anyhow::{anyhow, ensure}; +use regex::Regex; +use std::collections::HashMap; +use std::sync::OnceLock; + +/// Scans through the contents of an LLVM IR assembly file to find `__llvm_prf_names` +/// entries, decodes them, and creates a table that maps name hash values to +/// (demangled) function names. +pub(crate) fn make_function_names_table(llvm_ir: &str) -> anyhow::Result> { + fn prf_names_payload(line: &str) -> Option<&str> { + let re = { + // We cheat a little bit and match the variable name `@__llvm_prf_nm` + // rather than the section name, because the section name is harder + // to extract and differs across Linux/Windows/macOS. + static RE: OnceLock = OnceLock::new(); + RE.get_or_init(|| { + Regex::new(r#"^@__llvm_prf_nm =.*\[[0-9]+ x i8\] c"([^"]*)".*$"#).unwrap() + }) + }; + + let payload = re.captures(line)?.get(1).unwrap().as_str(); + Some(payload) + } + + /// LLVM's profiler/coverage metadata often uses an MD5 hash truncated to + /// 64 bits as a way to associate data stored in different tables/sections. + fn truncated_md5(bytes: &[u8]) -> u64 { + use md5::{Digest, Md5}; + let mut hasher = Md5::new(); + hasher.update(bytes); + let hash: [u8; 8] = hasher.finalize().as_slice()[..8].try_into().unwrap(); + u64::from_le_bytes(hash) + } + + fn demangle_if_able(symbol_name_bytes: &[u8]) -> anyhow::Result { + // In practice, raw symbol names should always be ASCII. + let symbol_name_str = std::str::from_utf8(symbol_name_bytes)?; + match rustc_demangle::try_demangle(symbol_name_str) { + Ok(d) => Ok(format!("{d:#}")), + // If demangling failed, don't treat it as an error. This lets us + // run the dump tool against non-Rust coverage maps produced by + // `clang`, for testing purposes. + Err(_) => Ok(format!("(couldn't demangle) {symbol_name_str}")), + } + } + + let mut map = HashMap::new(); + + for payload in llvm_ir.lines().filter_map(prf_names_payload).map(unescape_llvm_string_contents) + { + let mut parser = Parser::new(&payload); + let uncompressed_len = parser.read_uleb128_usize()?; + let compressed_len = parser.read_uleb128_usize()?; + + let uncompressed_bytes_vec; + let uncompressed_bytes: &[u8] = if compressed_len == 0 { + // The symbol name bytes are uncompressed, so read them directly. + parser.read_n_bytes(uncompressed_len)? + } else { + // The symbol name bytes are compressed, so read and decompress them. + let compressed_bytes = parser.read_n_bytes(compressed_len)?; + + uncompressed_bytes_vec = miniz_oxide::inflate::decompress_to_vec_zlib_with_limit( + compressed_bytes, + uncompressed_len, + ) + .map_err(|e| anyhow!("{e:?}"))?; + ensure!(uncompressed_bytes_vec.len() == uncompressed_len); + + &uncompressed_bytes_vec + }; + + // Symbol names in the payload are separated by `0x01` bytes. + for raw_name in uncompressed_bytes.split(|&b| b == 0x01) { + let hash = truncated_md5(raw_name); + let demangled = demangle_if_able(raw_name)?; + map.insert(hash, demangled); + } + + parser.ensure_empty()?; + } + + Ok(map) +} diff --git a/tests/coverage-map/if.cov-map b/tests/coverage-map/if.cov-map new file mode 100644 index 0000000000000..0a2459e7a6573 --- /dev/null +++ b/tests/coverage-map/if.cov-map @@ -0,0 +1,17 @@ +Function name: if::main +Raw bytes (35): 0x[01, 01, 02, 01, 05, 05, 02, 05, 01, 03, 01, 02, 0c, 20, 05, 02, 02, 08, 00, 0c, 05, 00, 0d, 02, 06, 02, 02, 06, 00, 07, 07, 01, 05, 01, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 2 +- expression 0 operands: lhs = Counter(0), rhs = Counter(1) +- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) +Number of file 0 mappings: 5 +- Code(Counter(0)) at (prev + 3, 1) to (start + 2, 12) +- Branch { true: Counter(1), false: Expression(0, Sub) } at (prev + 2, 8) to (start + 0, 12) + false = (c0 - c1) +- Code(Counter(1)) at (prev + 0, 13) to (start + 2, 6) +- Code(Expression(0, Sub)) at (prev + 2, 6) to (start + 0, 7) + = (c0 - c1) +- Code(Expression(1, Add)) at (prev + 1, 5) to (start + 1, 2) + = (c1 + (c0 - c1)) + diff --git a/tests/coverage-map/if.rs b/tests/coverage-map/if.rs new file mode 100644 index 0000000000000..ed3f69bdc98d2 --- /dev/null +++ b/tests/coverage-map/if.rs @@ -0,0 +1,9 @@ +// compile-flags: --edition=2021 + +fn main() { + let cond = std::env::args().len() == 1; + if cond { + println!("true"); + } + println!("done"); +} diff --git a/tests/coverage-map/long_and_wide.cov-map b/tests/coverage-map/long_and_wide.cov-map new file mode 100644 index 0000000000000..97aebf9b18ab0 --- /dev/null +++ b/tests/coverage-map/long_and_wide.cov-map @@ -0,0 +1,32 @@ +Function name: long_and_wide::far_function +Raw bytes (10): 0x[01, 01, 00, 01, 01, 96, 01, 01, 00, 15] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 1 +- Code(Counter(0)) at (prev + 150, 1) to (start + 0, 21) + +Function name: long_and_wide::long_function +Raw bytes (10): 0x[01, 01, 00, 01, 01, 10, 01, 84, 01, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 1 +- Code(Counter(0)) at (prev + 16, 1) to (start + 132, 2) + +Function name: long_and_wide::main +Raw bytes (9): 0x[01, 01, 00, 01, 01, 07, 01, 04, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 1 +- Code(Counter(0)) at (prev + 7, 1) to (start + 4, 2) + +Function name: long_and_wide::wide_function +Raw bytes (10): 0x[01, 01, 00, 01, 01, 0e, 01, 00, 8b, 01] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 1 +- Code(Counter(0)) at (prev + 14, 1) to (start + 0, 139) + diff --git a/tests/coverage-map/long_and_wide.rs b/tests/coverage-map/long_and_wide.rs new file mode 100644 index 0000000000000..a7cbcd4802791 --- /dev/null +++ b/tests/coverage-map/long_and_wide.rs @@ -0,0 +1,150 @@ +// compile-flags: --edition=2021 +// ignore-tidy-linelength + +// This file deliberately contains line and column numbers larger than 127, +// to verify that `coverage-dump`'s ULEB128 parser can handle them. + +fn main() { + wide_function(); + long_function(); + far_function(); +} + +#[rustfmt::skip] +fn wide_function() { /* */ (); } + +fn long_function() { + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // +} + +fn far_function() {} diff --git a/tests/coverage-map/status-quo/README.md b/tests/coverage-map/status-quo/README.md new file mode 100644 index 0000000000000..c3b9f16ac54ff --- /dev/null +++ b/tests/coverage-map/status-quo/README.md @@ -0,0 +1,9 @@ +The tests in this directory were copied from `tests/run-coverage` in order to +capture the current behavior of the instrumentor on non-trivial programs. +The actual mappings have not been closely inspected. + +## Maintenance note + +If a MIR optimization or LLVM upgrade causes these tests to fail, +it should usually be OK to just `--bless` them, +as long as the `run-coverage` test suite still works. diff --git a/tests/coverage-map/status-quo/async2.cov-map b/tests/coverage-map/status-quo/async2.cov-map new file mode 100644 index 0000000000000..c8b1062dde531 --- /dev/null +++ b/tests/coverage-map/status-quo/async2.cov-map @@ -0,0 +1,137 @@ +Function name: async2::async_func +Raw bytes (9): 0x[01, 01, 00, 01, 01, 14, 01, 00, 17] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 1 +- Code(Counter(0)) at (prev + 20, 1) to (start + 0, 23) + +Function name: async2::async_func::{closure#0} +Raw bytes (35): 0x[01, 01, 02, 01, 05, 05, 02, 05, 01, 14, 17, 03, 09, 20, 05, 02, 03, 08, 00, 09, 05, 00, 0a, 02, 06, 02, 02, 06, 00, 07, 07, 01, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 2 +- expression 0 operands: lhs = Counter(0), rhs = Counter(1) +- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) +Number of file 0 mappings: 5 +- Code(Counter(0)) at (prev + 20, 23) to (start + 3, 9) +- Branch { true: Counter(1), false: Expression(0, Sub) } at (prev + 3, 8) to (start + 0, 9) + false = (c0 - c1) +- Code(Counter(1)) at (prev + 0, 10) to (start + 2, 6) +- Code(Expression(0, Sub)) at (prev + 2, 6) to (start + 0, 7) + = (c0 - c1) +- Code(Expression(1, Add)) at (prev + 1, 1) to (start + 0, 2) + = (c1 + (c0 - c1)) + +Function name: async2::async_func_just_println +Raw bytes (9): 0x[01, 01, 00, 01, 01, 1f, 01, 00, 24] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 1 +- Code(Counter(0)) at (prev + 31, 1) to (start + 0, 36) + +Function name: async2::async_func_just_println::{closure#0} +Raw bytes (9): 0x[01, 01, 00, 01, 01, 1f, 24, 02, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 1 +- Code(Counter(0)) at (prev + 31, 36) to (start + 2, 2) + +Function name: async2::executor::block_on:: +Raw bytes (47): 0x[01, 01, 03, 0b, 05, 01, 05, 01, 05, 07, 01, 33, 05, 0a, 36, 20, 05, 02, 0d, 14, 00, 24, 02, 00, 20, 00, 23, 0b, 00, 27, 00, 49, 02, 01, 17, 00, 1a, 05, 01, 0e, 00, 0f, 02, 02, 05, 00, 06] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 3 +- expression 0 operands: lhs = Expression(2, Add), rhs = Counter(1) +- expression 1 operands: lhs = Counter(0), rhs = Counter(1) +- expression 2 operands: lhs = Counter(0), rhs = Counter(1) +Number of file 0 mappings: 7 +- Code(Counter(0)) at (prev + 51, 5) to (start + 10, 54) +- Branch { true: Counter(1), false: Expression(0, Sub) } at (prev + 13, 20) to (start + 0, 36) + false = ((c0 + c1) - c1) +- Code(Expression(0, Sub)) at (prev + 0, 32) to (start + 0, 35) + = ((c0 + c1) - c1) +- Code(Expression(2, Add)) at (prev + 0, 39) to (start + 0, 73) + = (c0 + c1) +- Code(Expression(0, Sub)) at (prev + 1, 23) to (start + 0, 26) + = ((c0 + c1) - c1) +- Code(Counter(1)) at (prev + 1, 14) to (start + 0, 15) +- Code(Expression(0, Sub)) at (prev + 2, 5) to (start + 0, 6) + = ((c0 + c1) - c1) + +Function name: async2::executor::block_on:: +Raw bytes (47): 0x[01, 01, 03, 0b, 05, 01, 05, 01, 05, 07, 01, 33, 05, 0a, 36, 20, 05, 02, 0d, 14, 00, 24, 02, 00, 20, 00, 23, 0b, 00, 27, 00, 49, 02, 01, 17, 00, 1a, 05, 01, 0e, 00, 0f, 02, 02, 05, 00, 06] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 3 +- expression 0 operands: lhs = Expression(2, Add), rhs = Counter(1) +- expression 1 operands: lhs = Counter(0), rhs = Counter(1) +- expression 2 operands: lhs = Counter(0), rhs = Counter(1) +Number of file 0 mappings: 7 +- Code(Counter(0)) at (prev + 51, 5) to (start + 10, 54) +- Branch { true: Counter(1), false: Expression(0, Sub) } at (prev + 13, 20) to (start + 0, 36) + false = ((c0 + c1) - c1) +- Code(Expression(0, Sub)) at (prev + 0, 32) to (start + 0, 35) + = ((c0 + c1) - c1) +- Code(Expression(2, Add)) at (prev + 0, 39) to (start + 0, 73) + = (c0 + c1) +- Code(Expression(0, Sub)) at (prev + 1, 23) to (start + 0, 26) + = ((c0 + c1) - c1) +- Code(Counter(1)) at (prev + 1, 14) to (start + 0, 15) +- Code(Expression(0, Sub)) at (prev + 2, 5) to (start + 0, 6) + = ((c0 + c1) - c1) + +Function name: async2::executor::block_on::VTABLE::{closure#0} +Raw bytes (9): 0x[01, 01, 00, 01, 01, 37, 11, 00, 33] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 1 +- Code(Counter(0)) at (prev + 55, 17) to (start + 0, 51) + +Function name: async2::executor::block_on::VTABLE::{closure#1} +Raw bytes (9): 0x[01, 01, 00, 01, 01, 38, 11, 00, 33] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 1 +- Code(Counter(0)) at (prev + 56, 17) to (start + 0, 51) + +Function name: async2::executor::block_on::VTABLE::{closure#2} +Raw bytes (9): 0x[01, 01, 00, 01, 01, 39, 11, 00, 33] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 1 +- Code(Counter(0)) at (prev + 57, 17) to (start + 0, 51) + +Function name: async2::executor::block_on::VTABLE::{closure#3} +Raw bytes (9): 0x[01, 01, 00, 01, 01, 3a, 11, 00, 13] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 1 +- Code(Counter(0)) at (prev + 58, 17) to (start + 0, 19) + +Function name: async2::main +Raw bytes (9): 0x[01, 01, 00, 01, 01, 23, 01, 07, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 1 +- Code(Counter(0)) at (prev + 35, 1) to (start + 7, 2) + +Function name: async2::non_async_func +Raw bytes (31): 0x[01, 01, 00, 05, 01, 09, 01, 03, 09, 20, 05, 00, 03, 08, 00, 09, 05, 00, 0a, 02, 06, 00, 02, 06, 00, 07, 05, 01, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 5 +- Code(Counter(0)) at (prev + 9, 1) to (start + 3, 9) +- Branch { true: Counter(1), false: Zero } at (prev + 3, 8) to (start + 0, 9) +- Code(Counter(1)) at (prev + 0, 10) to (start + 2, 6) +- Code(Zero) at (prev + 2, 6) to (start + 0, 7) +- Code(Counter(1)) at (prev + 1, 1) to (start + 0, 2) + diff --git a/tests/coverage-map/status-quo/async2.rs b/tests/coverage-map/status-quo/async2.rs new file mode 100644 index 0000000000000..959d48ce9db16 --- /dev/null +++ b/tests/coverage-map/status-quo/async2.rs @@ -0,0 +1,69 @@ +// compile-flags: --edition=2018 + +use core::{ + future::Future, + marker::Send, + pin::Pin, +}; + +fn non_async_func() { + println!("non_async_func was covered"); + let b = true; + if b { + println!("non_async_func println in block"); + } +} + + + + +async fn async_func() { + println!("async_func was covered"); + let b = true; + if b { + println!("async_func println in block"); + } +} + + + + +async fn async_func_just_println() { + println!("async_func_just_println was covered"); +} + +fn main() { + println!("codecovsample::main"); + + non_async_func(); + + executor::block_on(async_func()); + executor::block_on(async_func_just_println()); +} + +mod executor { + use core::{ + future::Future, + pin::Pin, + task::{Context, Poll, RawWaker, RawWakerVTable, Waker}, + }; + + pub fn block_on(mut future: F) -> F::Output { + let mut future = unsafe { Pin::new_unchecked(&mut future) }; + use std::hint::unreachable_unchecked; + static VTABLE: RawWakerVTable = RawWakerVTable::new( + |_| unsafe { unreachable_unchecked() }, // clone + |_| unsafe { unreachable_unchecked() }, // wake + |_| unsafe { unreachable_unchecked() }, // wake_by_ref + |_| (), + ); + let waker = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VTABLE)) }; + let mut context = Context::from_waker(&waker); + + loop { + if let Poll::Ready(val) = future.as_mut().poll(&mut context) { + break val; + } + } + } +} diff --git a/tests/coverage-map/status-quo/conditions.cov-map b/tests/coverage-map/status-quo/conditions.cov-map new file mode 100644 index 0000000000000..cee1535734df2 --- /dev/null +++ b/tests/coverage-map/status-quo/conditions.cov-map @@ -0,0 +1,237 @@ +Function name: conditions::main +Raw bytes (787): 0x[01, 01, 5e, 09, 23, 2d, 31, 05, 09, 05, 09, f6, 02, 0d, 05, 09, 0d, 39, 0d, 39, 2d, 31, f6, 02, 0d, 05, 09, 35, 3d, 35, 3d, 5a, 29, 35, 3d, 5a, 29, 35, 3d, 56, 51, 5a, 29, 35, 3d, 56, 51, 5a, 29, 35, 3d, 45, 49, 3d, 67, 45, 49, 55, 59, 55, 59, 96, 01, 25, 55, 59, 96, 01, 25, 55, 59, 92, 01, 79, 96, 01, 25, 55, 59, 92, 01, 79, 96, 01, 25, 55, 59, 5d, 61, 59, a3, 01, 5d, 61, 6d, cf, 02, 71, 75, 65, 6d, 65, 6d, da, 01, 21, 65, 6d, da, 01, 21, 65, 6d, d6, 01, 7d, da, 01, 21, 65, 6d, d6, 01, 7d, da, 01, 21, 65, 6d, 71, 75, 11, df, 02, 15, 19, 6d, cf, 02, 71, 75, cb, 02, 11, 6d, cf, 02, 71, 75, cb, 02, 11, 6d, cf, 02, 71, 75, c6, 02, 1d, cb, 02, 11, 6d, cf, 02, 71, 75, c6, 02, 1d, cb, 02, 11, 6d, cf, 02, 71, 75, c2, 02, 89, 01, c6, 02, 1d, cb, 02, 11, 6d, cf, 02, 71, 75, c2, 02, 89, 01, c6, 02, 1d, cb, 02, 11, 6d, cf, 02, 71, 75, 15, 19, db, 02, e3, 02, 11, df, 02, 15, 19, e7, 02, f2, 02, eb, 02, ef, 02, 1d, 21, 25, 29, f6, 02, 0d, 05, 09, 5d, 01, 03, 01, 02, 0c, 20, 05, 00, 02, 08, 00, 0c, 05, 00, 0d, 02, 06, 00, 02, 06, 00, 07, 03, 03, 09, 00, 0a, 05, 00, 10, 00, 1d, 20, 09, f6, 02, 00, 10, 00, 1d, 09, 01, 09, 01, 0a, f6, 02, 02, 0f, 00, 1c, 20, 0d, f2, 02, 00, 0f, 00, 1c, 0d, 01, 0c, 00, 19, 20, 39, 1e, 00, 0c, 00, 2a, 20, 2d, 31, 00, 0c, 00, 3c, 1e, 00, 1d, 00, 2a, 41, 00, 2e, 00, 3c, 2d, 00, 3d, 02, 0a, 31, 02, 0a, 00, 0b, 23, 01, 09, 01, 12, f2, 02, 03, 09, 00, 0f, 03, 03, 09, 01, 0c, 20, 35, 00, 01, 08, 00, 0c, 35, 00, 0d, 02, 06, 00, 02, 06, 00, 07, 35, 02, 08, 00, 15, 20, 3d, 5a, 00, 08, 00, 15, 3d, 00, 16, 02, 06, 5a, 02, 0f, 00, 1c, 20, 56, 29, 00, 0f, 00, 1c, 56, 01, 0c, 00, 19, 20, 51, 52, 00, 0c, 00, 2a, 20, 45, 49, 00, 0c, 00, 3c, 52, 00, 1d, 00, 2a, 69, 00, 2e, 00, 3c, 45, 00, 3d, 02, 0a, 49, 02, 0a, 00, 0b, 67, 01, 09, 00, 17, 29, 02, 09, 00, 0f, 63, 03, 08, 00, 0c, 20, 4d, 00, 00, 08, 00, 0c, 4d, 01, 0d, 01, 10, 20, 55, 00, 01, 0c, 00, 10, 55, 00, 11, 02, 0a, 00, 02, 0a, 00, 0b, 55, 02, 0c, 00, 19, 20, 59, 96, 01, 00, 0c, 00, 19, 59, 00, 1a, 02, 0a, 96, 01, 03, 11, 00, 1e, 20, 92, 01, 25, 00, 11, 00, 1e, 92, 01, 01, 10, 00, 1d, 20, 79, 8e, 01, 00, 10, 00, 2e, 20, 5d, 61, 00, 10, 00, 40, 8e, 01, 00, 21, 00, 2e, 81, 01, 00, 32, 00, 40, 5d, 00, 41, 02, 0e, 61, 02, 0e, 00, 0f, a3, 01, 01, 0d, 00, 1b, 25, 02, 0d, 00, 13, 00, 02, 06, 00, 07, 9f, 01, 03, 09, 01, 0c, 20, 65, 00, 01, 08, 00, 0c, 65, 00, 0d, 02, 06, 00, 02, 06, 00, 07, cb, 02, 02, 09, 00, 0a, 65, 00, 10, 00, 1d, 20, 6d, da, 01, 00, 10, 00, 1d, 6d, 00, 1e, 02, 06, da, 01, 02, 0f, 00, 1c, 20, d6, 01, 21, 00, 0f, 00, 1c, d6, 01, 01, 0c, 00, 19, 20, 7d, d2, 01, 00, 0c, 00, 2a, 20, 71, 75, 00, 0c, 00, 3c, d2, 01, 00, 1d, 00, 2a, 85, 01, 00, 2e, 00, 3c, 71, 00, 3d, 02, 0a, 75, 02, 0a, 00, 0b, cf, 02, 01, 09, 00, 17, 21, 02, 0d, 02, 0f, db, 02, 05, 09, 00, 0a, cb, 02, 00, 10, 00, 1d, 20, 11, c6, 02, 00, 10, 00, 1d, 11, 00, 1e, 02, 06, c6, 02, 02, 0f, 00, 1c, 20, c2, 02, 1d, 00, 0f, 00, 1c, c2, 02, 01, 0c, 00, 19, 20, 89, 01, be, 02, 00, 0c, 00, 2a, 20, 15, 19, 00, 0c, 00, 3c, be, 02, 00, 1d, 00, 2a, 8d, 01, 00, 2e, 00, 3c, 15, 00, 3d, 02, 0a, 19, 02, 0a, 00, 0b, df, 02, 01, 09, 00, 17, 1d, 02, 09, 00, 0f, d7, 02, 02, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 94 +- expression 0 operands: lhs = Counter(2), rhs = Expression(8, Add) +- expression 1 operands: lhs = Counter(11), rhs = Counter(12) +- expression 2 operands: lhs = Counter(1), rhs = Counter(2) +- expression 3 operands: lhs = Counter(1), rhs = Counter(2) +- expression 4 operands: lhs = Expression(93, Sub), rhs = Counter(3) +- expression 5 operands: lhs = Counter(1), rhs = Counter(2) +- expression 6 operands: lhs = Counter(3), rhs = Counter(14) +- expression 7 operands: lhs = Counter(3), rhs = Counter(14) +- expression 8 operands: lhs = Counter(11), rhs = Counter(12) +- expression 9 operands: lhs = Expression(93, Sub), rhs = Counter(3) +- expression 10 operands: lhs = Counter(1), rhs = Counter(2) +- expression 11 operands: lhs = Counter(13), rhs = Counter(15) +- expression 12 operands: lhs = Counter(13), rhs = Counter(15) +- expression 13 operands: lhs = Expression(22, Sub), rhs = Counter(10) +- expression 14 operands: lhs = Counter(13), rhs = Counter(15) +- expression 15 operands: lhs = Expression(22, Sub), rhs = Counter(10) +- expression 16 operands: lhs = Counter(13), rhs = Counter(15) +- expression 17 operands: lhs = Expression(21, Sub), rhs = Counter(20) +- expression 18 operands: lhs = Expression(22, Sub), rhs = Counter(10) +- expression 19 operands: lhs = Counter(13), rhs = Counter(15) +- expression 20 operands: lhs = Expression(21, Sub), rhs = Counter(20) +- expression 21 operands: lhs = Expression(22, Sub), rhs = Counter(10) +- expression 22 operands: lhs = Counter(13), rhs = Counter(15) +- expression 23 operands: lhs = Counter(17), rhs = Counter(18) +- expression 24 operands: lhs = Counter(15), rhs = Expression(25, Add) +- expression 25 operands: lhs = Counter(17), rhs = Counter(18) +- expression 26 operands: lhs = Counter(21), rhs = Counter(22) +- expression 27 operands: lhs = Counter(21), rhs = Counter(22) +- expression 28 operands: lhs = Expression(37, Sub), rhs = Counter(9) +- expression 29 operands: lhs = Counter(21), rhs = Counter(22) +- expression 30 operands: lhs = Expression(37, Sub), rhs = Counter(9) +- expression 31 operands: lhs = Counter(21), rhs = Counter(22) +- expression 32 operands: lhs = Expression(36, Sub), rhs = Counter(30) +- expression 33 operands: lhs = Expression(37, Sub), rhs = Counter(9) +- expression 34 operands: lhs = Counter(21), rhs = Counter(22) +- expression 35 operands: lhs = Expression(36, Sub), rhs = Counter(30) +- expression 36 operands: lhs = Expression(37, Sub), rhs = Counter(9) +- expression 37 operands: lhs = Counter(21), rhs = Counter(22) +- expression 38 operands: lhs = Counter(23), rhs = Counter(24) +- expression 39 operands: lhs = Counter(22), rhs = Expression(40, Add) +- expression 40 operands: lhs = Counter(23), rhs = Counter(24) +- expression 41 operands: lhs = Counter(27), rhs = Expression(83, Add) +- expression 42 operands: lhs = Counter(28), rhs = Counter(29) +- expression 43 operands: lhs = Counter(25), rhs = Counter(27) +- expression 44 operands: lhs = Counter(25), rhs = Counter(27) +- expression 45 operands: lhs = Expression(54, Sub), rhs = Counter(8) +- expression 46 operands: lhs = Counter(25), rhs = Counter(27) +- expression 47 operands: lhs = Expression(54, Sub), rhs = Counter(8) +- expression 48 operands: lhs = Counter(25), rhs = Counter(27) +- expression 49 operands: lhs = Expression(53, Sub), rhs = Counter(31) +- expression 50 operands: lhs = Expression(54, Sub), rhs = Counter(8) +- expression 51 operands: lhs = Counter(25), rhs = Counter(27) +- expression 52 operands: lhs = Expression(53, Sub), rhs = Counter(31) +- expression 53 operands: lhs = Expression(54, Sub), rhs = Counter(8) +- expression 54 operands: lhs = Counter(25), rhs = Counter(27) +- expression 55 operands: lhs = Counter(28), rhs = Counter(29) +- expression 56 operands: lhs = Counter(4), rhs = Expression(87, Add) +- expression 57 operands: lhs = Counter(5), rhs = Counter(6) +- expression 58 operands: lhs = Counter(27), rhs = Expression(83, Add) +- expression 59 operands: lhs = Counter(28), rhs = Counter(29) +- expression 60 operands: lhs = Expression(82, Add), rhs = Counter(4) +- expression 61 operands: lhs = Counter(27), rhs = Expression(83, Add) +- expression 62 operands: lhs = Counter(28), rhs = Counter(29) +- expression 63 operands: lhs = Expression(82, Add), rhs = Counter(4) +- expression 64 operands: lhs = Counter(27), rhs = Expression(83, Add) +- expression 65 operands: lhs = Counter(28), rhs = Counter(29) +- expression 66 operands: lhs = Expression(81, Sub), rhs = Counter(7) +- expression 67 operands: lhs = Expression(82, Add), rhs = Counter(4) +- expression 68 operands: lhs = Counter(27), rhs = Expression(83, Add) +- expression 69 operands: lhs = Counter(28), rhs = Counter(29) +- expression 70 operands: lhs = Expression(81, Sub), rhs = Counter(7) +- expression 71 operands: lhs = Expression(82, Add), rhs = Counter(4) +- expression 72 operands: lhs = Counter(27), rhs = Expression(83, Add) +- expression 73 operands: lhs = Counter(28), rhs = Counter(29) +- expression 74 operands: lhs = Expression(80, Sub), rhs = Counter(34) +- expression 75 operands: lhs = Expression(81, Sub), rhs = Counter(7) +- expression 76 operands: lhs = Expression(82, Add), rhs = Counter(4) +- expression 77 operands: lhs = Counter(27), rhs = Expression(83, Add) +- expression 78 operands: lhs = Counter(28), rhs = Counter(29) +- expression 79 operands: lhs = Expression(80, Sub), rhs = Counter(34) +- expression 80 operands: lhs = Expression(81, Sub), rhs = Counter(7) +- expression 81 operands: lhs = Expression(82, Add), rhs = Counter(4) +- expression 82 operands: lhs = Counter(27), rhs = Expression(83, Add) +- expression 83 operands: lhs = Counter(28), rhs = Counter(29) +- expression 84 operands: lhs = Counter(5), rhs = Counter(6) +- expression 85 operands: lhs = Expression(86, Add), rhs = Expression(88, Add) +- expression 86 operands: lhs = Counter(4), rhs = Expression(87, Add) +- expression 87 operands: lhs = Counter(5), rhs = Counter(6) +- expression 88 operands: lhs = Expression(89, Add), rhs = Expression(92, Sub) +- expression 89 operands: lhs = Expression(90, Add), rhs = Expression(91, Add) +- expression 90 operands: lhs = Counter(7), rhs = Counter(8) +- expression 91 operands: lhs = Counter(9), rhs = Counter(10) +- expression 92 operands: lhs = Expression(93, Sub), rhs = Counter(3) +- expression 93 operands: lhs = Counter(1), rhs = Counter(2) +Number of file 0 mappings: 93 +- Code(Counter(0)) at (prev + 3, 1) to (start + 2, 12) +- Branch { true: Counter(1), false: Zero } at (prev + 2, 8) to (start + 0, 12) +- Code(Counter(1)) at (prev + 0, 13) to (start + 2, 6) +- Code(Zero) at (prev + 2, 6) to (start + 0, 7) +- Code(Expression(0, Add)) at (prev + 3, 9) to (start + 0, 10) + = (c2 + (c11 + c12)) +- Code(Counter(1)) at (prev + 0, 16) to (start + 0, 29) +- Branch { true: Counter(2), false: Expression(93, Sub) } at (prev + 0, 16) to (start + 0, 29) + false = (c1 - c2) +- Code(Counter(2)) at (prev + 1, 9) to (start + 1, 10) +- Code(Expression(93, Sub)) at (prev + 2, 15) to (start + 0, 28) + = (c1 - c2) +- Branch { true: Counter(3), false: Expression(92, Sub) } at (prev + 0, 15) to (start + 0, 28) + false = ((c1 - c2) - c3) +- Code(Counter(3)) at (prev + 1, 12) to (start + 0, 25) +- Branch { true: Counter(14), false: Expression(7, Sub) } at (prev + 0, 12) to (start + 0, 42) + false = (c3 - c14) +- Branch { true: Counter(11), false: Counter(12) } at (prev + 0, 12) to (start + 0, 60) +- Code(Expression(7, Sub)) at (prev + 0, 29) to (start + 0, 42) + = (c3 - c14) +- Code(Counter(16)) at (prev + 0, 46) to (start + 0, 60) +- Code(Counter(11)) at (prev + 0, 61) to (start + 2, 10) +- Code(Counter(12)) at (prev + 2, 10) to (start + 0, 11) +- Code(Expression(8, Add)) at (prev + 1, 9) to (start + 1, 18) + = (c11 + c12) +- Code(Expression(92, Sub)) at (prev + 3, 9) to (start + 0, 15) + = ((c1 - c2) - c3) +- Code(Expression(0, Add)) at (prev + 3, 9) to (start + 1, 12) + = (c2 + (c11 + c12)) +- Branch { true: Counter(13), false: Zero } at (prev + 1, 8) to (start + 0, 12) +- Code(Counter(13)) at (prev + 0, 13) to (start + 2, 6) +- Code(Zero) at (prev + 2, 6) to (start + 0, 7) +- Code(Counter(13)) at (prev + 2, 8) to (start + 0, 21) +- Branch { true: Counter(15), false: Expression(22, Sub) } at (prev + 0, 8) to (start + 0, 21) + false = (c13 - c15) +- Code(Counter(15)) at (prev + 0, 22) to (start + 2, 6) +- Code(Expression(22, Sub)) at (prev + 2, 15) to (start + 0, 28) + = (c13 - c15) +- Branch { true: Expression(21, Sub), false: Counter(10) } at (prev + 0, 15) to (start + 0, 28) + true = ((c13 - c15) - c10) +- Code(Expression(21, Sub)) at (prev + 1, 12) to (start + 0, 25) + = ((c13 - c15) - c10) +- Branch { true: Counter(20), false: Expression(20, Sub) } at (prev + 0, 12) to (start + 0, 42) + false = (((c13 - c15) - c10) - c20) +- Branch { true: Counter(17), false: Counter(18) } at (prev + 0, 12) to (start + 0, 60) +- Code(Expression(20, Sub)) at (prev + 0, 29) to (start + 0, 42) + = (((c13 - c15) - c10) - c20) +- Code(Counter(26)) at (prev + 0, 46) to (start + 0, 60) +- Code(Counter(17)) at (prev + 0, 61) to (start + 2, 10) +- Code(Counter(18)) at (prev + 2, 10) to (start + 0, 11) +- Code(Expression(25, Add)) at (prev + 1, 9) to (start + 0, 23) + = (c17 + c18) +- Code(Counter(10)) at (prev + 2, 9) to (start + 0, 15) +- Code(Expression(24, Add)) at (prev + 3, 8) to (start + 0, 12) + = (c15 + (c17 + c18)) +- Branch { true: Counter(19), false: Zero } at (prev + 0, 8) to (start + 0, 12) +- Code(Counter(19)) at (prev + 1, 13) to (start + 1, 16) +- Branch { true: Counter(21), false: Zero } at (prev + 1, 12) to (start + 0, 16) +- Code(Counter(21)) at (prev + 0, 17) to (start + 2, 10) +- Code(Zero) at (prev + 2, 10) to (start + 0, 11) +- Code(Counter(21)) at (prev + 2, 12) to (start + 0, 25) +- Branch { true: Counter(22), false: Expression(37, Sub) } at (prev + 0, 12) to (start + 0, 25) + false = (c21 - c22) +- Code(Counter(22)) at (prev + 0, 26) to (start + 2, 10) +- Code(Expression(37, Sub)) at (prev + 3, 17) to (start + 0, 30) + = (c21 - c22) +- Branch { true: Expression(36, Sub), false: Counter(9) } at (prev + 0, 17) to (start + 0, 30) + true = ((c21 - c22) - c9) +- Code(Expression(36, Sub)) at (prev + 1, 16) to (start + 0, 29) + = ((c21 - c22) - c9) +- Branch { true: Counter(30), false: Expression(35, Sub) } at (prev + 0, 16) to (start + 0, 46) + false = (((c21 - c22) - c9) - c30) +- Branch { true: Counter(23), false: Counter(24) } at (prev + 0, 16) to (start + 0, 64) +- Code(Expression(35, Sub)) at (prev + 0, 33) to (start + 0, 46) + = (((c21 - c22) - c9) - c30) +- Code(Counter(32)) at (prev + 0, 50) to (start + 0, 64) +- Code(Counter(23)) at (prev + 0, 65) to (start + 2, 14) +- Code(Counter(24)) at (prev + 2, 14) to (start + 0, 15) +- Code(Expression(40, Add)) at (prev + 1, 13) to (start + 0, 27) + = (c23 + c24) +- Code(Counter(9)) at (prev + 2, 13) to (start + 0, 19) +- Code(Zero) at (prev + 2, 6) to (start + 0, 7) +- Code(Expression(39, Add)) at (prev + 3, 9) to (start + 1, 12) + = (c22 + (c23 + c24)) +- Branch { true: Counter(25), false: Zero } at (prev + 1, 8) to (start + 0, 12) +- Code(Counter(25)) at (prev + 0, 13) to (start + 2, 6) +- Code(Zero) at (prev + 2, 6) to (start + 0, 7) +- Code(Expression(82, Add)) at (prev + 2, 9) to (start + 0, 10) + = (c27 + (c28 + c29)) +- Code(Counter(25)) at (prev + 0, 16) to (start + 0, 29) +- Branch { true: Counter(27), false: Expression(54, Sub) } at (prev + 0, 16) to (start + 0, 29) + false = (c25 - c27) +- Code(Counter(27)) at (prev + 0, 30) to (start + 2, 6) +- Code(Expression(54, Sub)) at (prev + 2, 15) to (start + 0, 28) + = (c25 - c27) +- Branch { true: Expression(53, Sub), false: Counter(8) } at (prev + 0, 15) to (start + 0, 28) + true = ((c25 - c27) - c8) +- Code(Expression(53, Sub)) at (prev + 1, 12) to (start + 0, 25) + = ((c25 - c27) - c8) +- Branch { true: Counter(31), false: Expression(52, Sub) } at (prev + 0, 12) to (start + 0, 42) + false = (((c25 - c27) - c8) - c31) +- Branch { true: Counter(28), false: Counter(29) } at (prev + 0, 12) to (start + 0, 60) +- Code(Expression(52, Sub)) at (prev + 0, 29) to (start + 0, 42) + = (((c25 - c27) - c8) - c31) +- Code(Counter(33)) at (prev + 0, 46) to (start + 0, 60) +- Code(Counter(28)) at (prev + 0, 61) to (start + 2, 10) +- Code(Counter(29)) at (prev + 2, 10) to (start + 0, 11) +- Code(Expression(83, Add)) at (prev + 1, 9) to (start + 0, 23) + = (c28 + c29) +- Code(Counter(8)) at (prev + 2, 13) to (start + 2, 15) +- Code(Expression(86, Add)) at (prev + 5, 9) to (start + 0, 10) + = (c4 + (c5 + c6)) +- Code(Expression(82, Add)) at (prev + 0, 16) to (start + 0, 29) + = (c27 + (c28 + c29)) +- Branch { true: Counter(4), false: Expression(81, Sub) } at (prev + 0, 16) to (start + 0, 29) + false = ((c27 + (c28 + c29)) - c4) +- Code(Counter(4)) at (prev + 0, 30) to (start + 2, 6) +- Code(Expression(81, Sub)) at (prev + 2, 15) to (start + 0, 28) + = ((c27 + (c28 + c29)) - c4) +- Branch { true: Expression(80, Sub), false: Counter(7) } at (prev + 0, 15) to (start + 0, 28) + true = (((c27 + (c28 + c29)) - c4) - c7) +- Code(Expression(80, Sub)) at (prev + 1, 12) to (start + 0, 25) + = (((c27 + (c28 + c29)) - c4) - c7) +- Branch { true: Counter(34), false: Expression(79, Sub) } at (prev + 0, 12) to (start + 0, 42) + false = ((((c27 + (c28 + c29)) - c4) - c7) - c34) +- Branch { true: Counter(5), false: Counter(6) } at (prev + 0, 12) to (start + 0, 60) +- Code(Expression(79, Sub)) at (prev + 0, 29) to (start + 0, 42) + = ((((c27 + (c28 + c29)) - c4) - c7) - c34) +- Code(Counter(35)) at (prev + 0, 46) to (start + 0, 60) +- Code(Counter(5)) at (prev + 0, 61) to (start + 2, 10) +- Code(Counter(6)) at (prev + 2, 10) to (start + 0, 11) +- Code(Expression(87, Add)) at (prev + 1, 9) to (start + 0, 23) + = (c5 + c6) +- Code(Counter(7)) at (prev + 2, 9) to (start + 0, 15) +- Code(Expression(85, Add)) at (prev + 2, 1) to (start + 0, 2) + = ((c4 + (c5 + c6)) + (((c7 + c8) + (c9 + c10)) + ((c1 - c2) - c3))) + diff --git a/tests/coverage-map/status-quo/conditions.rs b/tests/coverage-map/status-quo/conditions.rs new file mode 100644 index 0000000000000..057599d1b471a --- /dev/null +++ b/tests/coverage-map/status-quo/conditions.rs @@ -0,0 +1,87 @@ +#![allow(unused_assignments, unused_variables)] + +fn main() { + let mut countdown = 0; + if true { + countdown = 10; + } + + const B: u32 = 100; + let x = if countdown > 7 { + countdown -= 4; + B + } else if countdown > 2 { + if countdown < 1 || countdown > 5 || countdown != 9 { + countdown = 0; + } + countdown -= 5; + countdown + } else { + return; + }; + + let mut countdown = 0; + if true { + countdown = 10; + } + + if countdown > 7 { + countdown -= 4; + } else if countdown > 2 { + if countdown < 1 || countdown > 5 || countdown != 9 { + countdown = 0; + } + countdown -= 5; + } else { + return; + } + + if true { + let mut countdown = 0; + if true { + countdown = 10; + } + + if countdown > 7 { + countdown -= 4; + } + else if countdown > 2 { + if countdown < 1 || countdown > 5 || countdown != 9 { + countdown = 0; + } + countdown -= 5; + } else { + return; + } + } + + + let mut countdown = 0; + if true { + countdown = 1; + } + + let z = if countdown > 7 { + countdown -= 4; + } else if countdown > 2 { + if countdown < 1 || countdown > 5 || countdown != 9 { + countdown = 0; + } + countdown -= 5; + } else { + let should_be_reachable = countdown; + println!("reached"); + return; + }; + + let w = if countdown > 7 { + countdown -= 4; + } else if countdown > 2 { + if countdown < 1 || countdown > 5 || countdown != 9 { + countdown = 0; + } + countdown -= 5; + } else { + return; + }; +} diff --git a/tests/coverage-map/status-quo/drop_trait.cov-map b/tests/coverage-map/status-quo/drop_trait.cov-map new file mode 100644 index 0000000000000..0f8cd30bc55a2 --- /dev/null +++ b/tests/coverage-map/status-quo/drop_trait.cov-map @@ -0,0 +1,20 @@ +Function name: ::drop +Raw bytes (9): 0x[01, 01, 00, 01, 01, 09, 05, 02, 06] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 1 +- Code(Counter(0)) at (prev + 9, 5) to (start + 2, 6) + +Function name: drop_trait::main +Raw bytes (31): 0x[01, 01, 00, 05, 01, 0e, 01, 05, 0c, 20, 05, 00, 05, 08, 00, 0c, 05, 01, 09, 01, 16, 00, 02, 06, 04, 0b, 05, 05, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 5 +- Code(Counter(0)) at (prev + 14, 1) to (start + 5, 12) +- Branch { true: Counter(1), false: Zero } at (prev + 5, 8) to (start + 0, 12) +- Code(Counter(1)) at (prev + 1, 9) to (start + 1, 22) +- Code(Zero) at (prev + 2, 6) to (start + 4, 11) +- Code(Counter(1)) at (prev + 5, 1) to (start + 0, 2) + diff --git a/tests/coverage-map/status-quo/drop_trait.rs b/tests/coverage-map/status-quo/drop_trait.rs new file mode 100644 index 0000000000000..a9b5d1d1e7fe9 --- /dev/null +++ b/tests/coverage-map/status-quo/drop_trait.rs @@ -0,0 +1,33 @@ +#![allow(unused_assignments)] +// failure-status: 1 + +struct Firework { + strength: i32, +} + +impl Drop for Firework { + fn drop(&mut self) { + println!("BOOM times {}!!!", self.strength); + } +} + +fn main() -> Result<(),u8> { + let _firecracker = Firework { strength: 1 }; + + let _tnt = Firework { strength: 100 }; + + if true { + println!("Exiting with error..."); + return Err(1); + } + + let _ = Firework { strength: 1000 }; + + Ok(()) +} + +// Expected program output: +// Exiting with error... +// BOOM times 100!!! +// BOOM times 1!!! +// Error: 1 diff --git a/tests/coverage-map/status-quo/generics.cov-map b/tests/coverage-map/status-quo/generics.cov-map new file mode 100644 index 0000000000000..99a40ca960d24 --- /dev/null +++ b/tests/coverage-map/status-quo/generics.cov-map @@ -0,0 +1,44 @@ +Function name: as core::ops::drop::Drop>::drop +Raw bytes (9): 0x[01, 01, 00, 01, 01, 11, 05, 02, 06] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 1 +- Code(Counter(0)) at (prev + 17, 5) to (start + 2, 6) + +Function name: >::set_strength +Raw bytes (9): 0x[01, 01, 00, 01, 01, 0a, 05, 02, 06] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 1 +- Code(Counter(0)) at (prev + 10, 5) to (start + 2, 6) + +Function name: as core::ops::drop::Drop>::drop +Raw bytes (9): 0x[01, 01, 00, 01, 01, 11, 05, 02, 06] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 1 +- Code(Counter(0)) at (prev + 17, 5) to (start + 2, 6) + +Function name: >::set_strength +Raw bytes (9): 0x[01, 01, 00, 01, 01, 0a, 05, 02, 06] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 1 +- Code(Counter(0)) at (prev + 10, 5) to (start + 2, 6) + +Function name: generics::main +Raw bytes (31): 0x[01, 01, 00, 05, 01, 16, 01, 08, 0c, 20, 05, 00, 08, 08, 00, 0c, 05, 01, 09, 01, 16, 00, 02, 06, 08, 0b, 05, 09, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 5 +- Code(Counter(0)) at (prev + 22, 1) to (start + 8, 12) +- Branch { true: Counter(1), false: Zero } at (prev + 8, 8) to (start + 0, 12) +- Code(Counter(1)) at (prev + 1, 9) to (start + 1, 22) +- Code(Zero) at (prev + 2, 6) to (start + 8, 11) +- Code(Counter(1)) at (prev + 9, 1) to (start + 0, 2) + diff --git a/tests/coverage-map/status-quo/generics.rs b/tests/coverage-map/status-quo/generics.rs new file mode 100644 index 0000000000000..150ffb9db395a --- /dev/null +++ b/tests/coverage-map/status-quo/generics.rs @@ -0,0 +1,48 @@ +#![allow(unused_assignments)] +// failure-status: 1 + +struct Firework where T: Copy + std::fmt::Display { + strength: T, +} + +impl Firework where T: Copy + std::fmt::Display { + #[inline(always)] + fn set_strength(&mut self, new_strength: T) { + self.strength = new_strength; + } +} + +impl Drop for Firework where T: Copy + std::fmt::Display { + #[inline(always)] + fn drop(&mut self) { + println!("BOOM times {}!!!", self.strength); + } +} + +fn main() -> Result<(),u8> { + let mut firecracker = Firework { strength: 1 }; + firecracker.set_strength(2); + + let mut tnt = Firework { strength: 100.1 }; + tnt.set_strength(200.1); + tnt.set_strength(300.3); + + if true { + println!("Exiting with error..."); + return Err(1); + } + + + + + + let _ = Firework { strength: 1000 }; + + Ok(()) +} + +// Expected program output: +// Exiting with error... +// BOOM times 100!!! +// BOOM times 1!!! +// Error: 1 diff --git a/tests/coverage-map/status-quo/loops_branches.cov-map b/tests/coverage-map/status-quo/loops_branches.cov-map new file mode 100644 index 0000000000000..f881aa725e3c8 --- /dev/null +++ b/tests/coverage-map/status-quo/loops_branches.cov-map @@ -0,0 +1,247 @@ +Function name: ::fmt +Raw bytes (341): 0x[01, 01, 3c, 05, 09, 00, 02, eb, 01, 19, 0d, ef, 01, 11, 15, 0d, ef, 01, 11, 15, eb, 01, 19, 0d, ef, 01, 11, 15, eb, 01, 19, 0d, ef, 01, 11, 15, e6, 01, 15, eb, 01, 19, 0d, ef, 01, 11, 15, e6, 01, 15, eb, 01, 19, 0d, ef, 01, 11, 15, e2, 01, 1d, e6, 01, 15, eb, 01, 19, 0d, ef, 01, 11, 15, e2, 01, 1d, e6, 01, 15, eb, 01, 19, 0d, ef, 01, 11, 15, 00, de, 01, e2, 01, 1d, e6, 01, 15, eb, 01, 19, 0d, ef, 01, 11, 15, db, 01, 11, 00, de, 01, e2, 01, 1d, e6, 01, 15, eb, 01, 19, 0d, ef, 01, 11, 15, db, 01, 11, 00, de, 01, e2, 01, 1d, e6, 01, 15, eb, 01, 19, 0d, ef, 01, 11, 15, 25, d3, 01, d6, 01, 19, db, 01, 11, 00, de, 01, e2, 01, 1d, e6, 01, 15, eb, 01, 19, 0d, ef, 01, 11, 15, 1d, 01, 09, 05, 01, 10, 20, 05, 00, 01, 0c, 00, 10, 05, 01, 10, 00, 15, 20, 09, 02, 00, 10, 00, 15, 00, 01, 17, 00, 1b, 00, 00, 17, 00, 1b, 00, 00, 1c, 01, 12, 02, 02, 0e, 00, 0f, 07, 01, 0d, 00, 1e, 20, 25, 0d, 00, 0d, 00, 1f, 25, 00, 1e, 00, 1f, 00, 01, 10, 01, 0a, e6, 01, 03, 0d, 00, 0e, eb, 01, 00, 12, 00, 17, 20, e6, 01, 19, 00, 12, 00, 17, e6, 01, 01, 10, 00, 14, 20, e2, 01, 15, 00, 10, 00, 14, e2, 01, 01, 14, 00, 19, 20, 1d, de, 01, 00, 14, 00, 19, 00, 01, 1b, 00, 1f, 00, 00, 1b, 00, 1f, 00, 00, 20, 00, 22, de, 01, 01, 12, 00, 13, db, 01, 01, 11, 00, 22, 20, d6, 01, 11, 00, 11, 00, 23, d6, 01, 00, 22, 00, 23, 00, 01, 14, 01, 0e, 19, 03, 09, 00, 0f, cf, 01, 01, 05, 00, 06] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 60 +- expression 0 operands: lhs = Counter(1), rhs = Counter(2) +- expression 1 operands: lhs = Zero, rhs = Expression(0, Sub) +- expression 2 operands: lhs = Expression(58, Add), rhs = Counter(6) +- expression 3 operands: lhs = Counter(3), rhs = Expression(59, Add) +- expression 4 operands: lhs = Counter(4), rhs = Counter(5) +- expression 5 operands: lhs = Counter(3), rhs = Expression(59, Add) +- expression 6 operands: lhs = Counter(4), rhs = Counter(5) +- expression 7 operands: lhs = Expression(58, Add), rhs = Counter(6) +- expression 8 operands: lhs = Counter(3), rhs = Expression(59, Add) +- expression 9 operands: lhs = Counter(4), rhs = Counter(5) +- expression 10 operands: lhs = Expression(58, Add), rhs = Counter(6) +- expression 11 operands: lhs = Counter(3), rhs = Expression(59, Add) +- expression 12 operands: lhs = Counter(4), rhs = Counter(5) +- expression 13 operands: lhs = Expression(57, Sub), rhs = Counter(5) +- expression 14 operands: lhs = Expression(58, Add), rhs = Counter(6) +- expression 15 operands: lhs = Counter(3), rhs = Expression(59, Add) +- expression 16 operands: lhs = Counter(4), rhs = Counter(5) +- expression 17 operands: lhs = Expression(57, Sub), rhs = Counter(5) +- expression 18 operands: lhs = Expression(58, Add), rhs = Counter(6) +- expression 19 operands: lhs = Counter(3), rhs = Expression(59, Add) +- expression 20 operands: lhs = Counter(4), rhs = Counter(5) +- expression 21 operands: lhs = Expression(56, Sub), rhs = Counter(7) +- expression 22 operands: lhs = Expression(57, Sub), rhs = Counter(5) +- expression 23 operands: lhs = Expression(58, Add), rhs = Counter(6) +- expression 24 operands: lhs = Counter(3), rhs = Expression(59, Add) +- expression 25 operands: lhs = Counter(4), rhs = Counter(5) +- expression 26 operands: lhs = Expression(56, Sub), rhs = Counter(7) +- expression 27 operands: lhs = Expression(57, Sub), rhs = Counter(5) +- expression 28 operands: lhs = Expression(58, Add), rhs = Counter(6) +- expression 29 operands: lhs = Counter(3), rhs = Expression(59, Add) +- expression 30 operands: lhs = Counter(4), rhs = Counter(5) +- expression 31 operands: lhs = Zero, rhs = Expression(55, Sub) +- expression 32 operands: lhs = Expression(56, Sub), rhs = Counter(7) +- expression 33 operands: lhs = Expression(57, Sub), rhs = Counter(5) +- expression 34 operands: lhs = Expression(58, Add), rhs = Counter(6) +- expression 35 operands: lhs = Counter(3), rhs = Expression(59, Add) +- expression 36 operands: lhs = Counter(4), rhs = Counter(5) +- expression 37 operands: lhs = Expression(54, Add), rhs = Counter(4) +- expression 38 operands: lhs = Zero, rhs = Expression(55, Sub) +- expression 39 operands: lhs = Expression(56, Sub), rhs = Counter(7) +- expression 40 operands: lhs = Expression(57, Sub), rhs = Counter(5) +- expression 41 operands: lhs = Expression(58, Add), rhs = Counter(6) +- expression 42 operands: lhs = Counter(3), rhs = Expression(59, Add) +- expression 43 operands: lhs = Counter(4), rhs = Counter(5) +- expression 44 operands: lhs = Expression(54, Add), rhs = Counter(4) +- expression 45 operands: lhs = Zero, rhs = Expression(55, Sub) +- expression 46 operands: lhs = Expression(56, Sub), rhs = Counter(7) +- expression 47 operands: lhs = Expression(57, Sub), rhs = Counter(5) +- expression 48 operands: lhs = Expression(58, Add), rhs = Counter(6) +- expression 49 operands: lhs = Counter(3), rhs = Expression(59, Add) +- expression 50 operands: lhs = Counter(4), rhs = Counter(5) +- expression 51 operands: lhs = Counter(9), rhs = Expression(52, Add) +- expression 52 operands: lhs = Expression(53, Sub), rhs = Counter(6) +- expression 53 operands: lhs = Expression(54, Add), rhs = Counter(4) +- expression 54 operands: lhs = Zero, rhs = Expression(55, Sub) +- expression 55 operands: lhs = Expression(56, Sub), rhs = Counter(7) +- expression 56 operands: lhs = Expression(57, Sub), rhs = Counter(5) +- expression 57 operands: lhs = Expression(58, Add), rhs = Counter(6) +- expression 58 operands: lhs = Counter(3), rhs = Expression(59, Add) +- expression 59 operands: lhs = Counter(4), rhs = Counter(5) +Number of file 0 mappings: 29 +- Code(Counter(0)) at (prev + 9, 5) to (start + 1, 16) +- Branch { true: Counter(1), false: Zero } at (prev + 1, 12) to (start + 0, 16) +- Code(Counter(1)) at (prev + 1, 16) to (start + 0, 21) +- Branch { true: Counter(2), false: Expression(0, Sub) } at (prev + 0, 16) to (start + 0, 21) + false = (c1 - c2) +- Code(Zero) at (prev + 1, 23) to (start + 0, 27) +- Code(Zero) at (prev + 0, 23) to (start + 0, 27) +- Code(Zero) at (prev + 0, 28) to (start + 1, 18) +- Code(Expression(0, Sub)) at (prev + 2, 14) to (start + 0, 15) + = (c1 - c2) +- Code(Expression(1, Add)) at (prev + 1, 13) to (start + 0, 30) + = (Zero + (c1 - c2)) +- Branch { true: Counter(9), false: Counter(3) } at (prev + 0, 13) to (start + 0, 31) +- Code(Counter(9)) at (prev + 0, 30) to (start + 0, 31) +- Code(Zero) at (prev + 1, 16) to (start + 1, 10) +- Code(Expression(57, Sub)) at (prev + 3, 13) to (start + 0, 14) + = ((c3 + (c4 + c5)) - c6) +- Code(Expression(58, Add)) at (prev + 0, 18) to (start + 0, 23) + = (c3 + (c4 + c5)) +- Branch { true: Expression(57, Sub), false: Counter(6) } at (prev + 0, 18) to (start + 0, 23) + true = ((c3 + (c4 + c5)) - c6) +- Code(Expression(57, Sub)) at (prev + 1, 16) to (start + 0, 20) + = ((c3 + (c4 + c5)) - c6) +- Branch { true: Expression(56, Sub), false: Counter(5) } at (prev + 0, 16) to (start + 0, 20) + true = (((c3 + (c4 + c5)) - c6) - c5) +- Code(Expression(56, Sub)) at (prev + 1, 20) to (start + 0, 25) + = (((c3 + (c4 + c5)) - c6) - c5) +- Branch { true: Counter(7), false: Expression(55, Sub) } at (prev + 0, 20) to (start + 0, 25) + false = ((((c3 + (c4 + c5)) - c6) - c5) - c7) +- Code(Zero) at (prev + 1, 27) to (start + 0, 31) +- Code(Zero) at (prev + 0, 27) to (start + 0, 31) +- Code(Zero) at (prev + 0, 32) to (start + 0, 34) +- Code(Expression(55, Sub)) at (prev + 1, 18) to (start + 0, 19) + = ((((c3 + (c4 + c5)) - c6) - c5) - c7) +- Code(Expression(54, Add)) at (prev + 1, 17) to (start + 0, 34) + = (Zero + ((((c3 + (c4 + c5)) - c6) - c5) - c7)) +- Branch { true: Expression(53, Sub), false: Counter(4) } at (prev + 0, 17) to (start + 0, 35) + true = ((Zero + ((((c3 + (c4 + c5)) - c6) - c5) - c7)) - c4) +- Code(Expression(53, Sub)) at (prev + 0, 34) to (start + 0, 35) + = ((Zero + ((((c3 + (c4 + c5)) - c6) - c5) - c7)) - c4) +- Code(Zero) at (prev + 1, 20) to (start + 1, 14) +- Code(Counter(6)) at (prev + 3, 9) to (start + 0, 15) +- Code(Expression(51, Add)) at (prev + 1, 5) to (start + 0, 6) + = (c9 + (((Zero + ((((c3 + (c4 + c5)) - c6) - c5) - c7)) - c4) + c6)) + +Function name: ::fmt +Raw bytes (383): 0x[01, 01, 4b, 01, 05, 02, 09, 02, 09, 00, 12, 02, 09, 9f, 02, 19, a3, 02, a7, 02, 05, 0d, 11, 15, a3, 02, a7, 02, 05, 0d, 11, 15, 9f, 02, 19, a3, 02, a7, 02, 05, 0d, 11, 15, 9f, 02, 19, a3, 02, a7, 02, 05, 0d, 11, 15, 9a, 02, 11, 9f, 02, 19, a3, 02, a7, 02, 05, 0d, 11, 15, 9a, 02, 11, 9f, 02, 19, a3, 02, a7, 02, 05, 0d, 11, 15, 96, 02, 1d, 9a, 02, 11, 9f, 02, 19, a3, 02, a7, 02, 05, 0d, 11, 15, 96, 02, 1d, 9a, 02, 11, 9f, 02, 19, a3, 02, a7, 02, 05, 0d, 11, 15, 00, 92, 02, 96, 02, 1d, 9a, 02, 11, 9f, 02, 19, a3, 02, a7, 02, 05, 0d, 11, 15, 8f, 02, 15, 00, 92, 02, 96, 02, 1d, 9a, 02, 11, 9f, 02, 19, a3, 02, a7, 02, 05, 0d, 11, 15, 8f, 02, 15, 00, 92, 02, 96, 02, 1d, 9a, 02, 11, 9f, 02, 19, a3, 02, a7, 02, 05, 0d, 11, 15, 8a, 02, ab, 02, 8f, 02, 15, 00, 92, 02, 96, 02, 1d, 9a, 02, 11, 9f, 02, 19, a3, 02, a7, 02, 05, 0d, 11, 15, 19, 25, 1d, 01, 23, 05, 01, 11, 20, 05, 02, 01, 0c, 00, 11, 00, 00, 12, 01, 0a, 02, 02, 10, 00, 15, 20, 09, 12, 00, 10, 00, 15, 00, 01, 17, 00, 1b, 00, 00, 17, 00, 1b, 00, 00, 1c, 00, 1e, 12, 01, 0e, 00, 0f, 0f, 01, 0d, 00, 1e, 20, 25, 0d, 00, 0d, 00, 1f, 25, 00, 1e, 00, 1f, 9a, 02, 02, 0d, 00, 0e, 9f, 02, 00, 12, 00, 17, 20, 9a, 02, 19, 00, 12, 00, 17, 9a, 02, 01, 10, 00, 15, 20, 11, 96, 02, 00, 10, 00, 15, 00, 00, 16, 01, 0e, 96, 02, 02, 14, 00, 19, 20, 1d, 92, 02, 00, 14, 00, 19, 00, 01, 1b, 00, 1f, 00, 00, 1b, 00, 1f, 00, 00, 20, 00, 22, 92, 02, 01, 12, 00, 13, 8f, 02, 01, 11, 00, 22, 20, 8a, 02, 15, 00, 11, 00, 23, 8a, 02, 00, 22, 00, 23, 19, 03, 09, 00, 0f, 87, 02, 01, 05, 00, 06] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 75 +- expression 0 operands: lhs = Counter(0), rhs = Counter(1) +- expression 1 operands: lhs = Expression(0, Sub), rhs = Counter(2) +- expression 2 operands: lhs = Expression(0, Sub), rhs = Counter(2) +- expression 3 operands: lhs = Zero, rhs = Expression(4, Sub) +- expression 4 operands: lhs = Expression(0, Sub), rhs = Counter(2) +- expression 5 operands: lhs = Expression(71, Add), rhs = Counter(6) +- expression 6 operands: lhs = Expression(72, Add), rhs = Expression(73, Add) +- expression 7 operands: lhs = Counter(1), rhs = Counter(3) +- expression 8 operands: lhs = Counter(4), rhs = Counter(5) +- expression 9 operands: lhs = Expression(72, Add), rhs = Expression(73, Add) +- expression 10 operands: lhs = Counter(1), rhs = Counter(3) +- expression 11 operands: lhs = Counter(4), rhs = Counter(5) +- expression 12 operands: lhs = Expression(71, Add), rhs = Counter(6) +- expression 13 operands: lhs = Expression(72, Add), rhs = Expression(73, Add) +- expression 14 operands: lhs = Counter(1), rhs = Counter(3) +- expression 15 operands: lhs = Counter(4), rhs = Counter(5) +- expression 16 operands: lhs = Expression(71, Add), rhs = Counter(6) +- expression 17 operands: lhs = Expression(72, Add), rhs = Expression(73, Add) +- expression 18 operands: lhs = Counter(1), rhs = Counter(3) +- expression 19 operands: lhs = Counter(4), rhs = Counter(5) +- expression 20 operands: lhs = Expression(70, Sub), rhs = Counter(4) +- expression 21 operands: lhs = Expression(71, Add), rhs = Counter(6) +- expression 22 operands: lhs = Expression(72, Add), rhs = Expression(73, Add) +- expression 23 operands: lhs = Counter(1), rhs = Counter(3) +- expression 24 operands: lhs = Counter(4), rhs = Counter(5) +- expression 25 operands: lhs = Expression(70, Sub), rhs = Counter(4) +- expression 26 operands: lhs = Expression(71, Add), rhs = Counter(6) +- expression 27 operands: lhs = Expression(72, Add), rhs = Expression(73, Add) +- expression 28 operands: lhs = Counter(1), rhs = Counter(3) +- expression 29 operands: lhs = Counter(4), rhs = Counter(5) +- expression 30 operands: lhs = Expression(69, Sub), rhs = Counter(7) +- expression 31 operands: lhs = Expression(70, Sub), rhs = Counter(4) +- expression 32 operands: lhs = Expression(71, Add), rhs = Counter(6) +- expression 33 operands: lhs = Expression(72, Add), rhs = Expression(73, Add) +- expression 34 operands: lhs = Counter(1), rhs = Counter(3) +- expression 35 operands: lhs = Counter(4), rhs = Counter(5) +- expression 36 operands: lhs = Expression(69, Sub), rhs = Counter(7) +- expression 37 operands: lhs = Expression(70, Sub), rhs = Counter(4) +- expression 38 operands: lhs = Expression(71, Add), rhs = Counter(6) +- expression 39 operands: lhs = Expression(72, Add), rhs = Expression(73, Add) +- expression 40 operands: lhs = Counter(1), rhs = Counter(3) +- expression 41 operands: lhs = Counter(4), rhs = Counter(5) +- expression 42 operands: lhs = Zero, rhs = Expression(68, Sub) +- expression 43 operands: lhs = Expression(69, Sub), rhs = Counter(7) +- expression 44 operands: lhs = Expression(70, Sub), rhs = Counter(4) +- expression 45 operands: lhs = Expression(71, Add), rhs = Counter(6) +- expression 46 operands: lhs = Expression(72, Add), rhs = Expression(73, Add) +- expression 47 operands: lhs = Counter(1), rhs = Counter(3) +- expression 48 operands: lhs = Counter(4), rhs = Counter(5) +- expression 49 operands: lhs = Expression(67, Add), rhs = Counter(5) +- expression 50 operands: lhs = Zero, rhs = Expression(68, Sub) +- expression 51 operands: lhs = Expression(69, Sub), rhs = Counter(7) +- expression 52 operands: lhs = Expression(70, Sub), rhs = Counter(4) +- expression 53 operands: lhs = Expression(71, Add), rhs = Counter(6) +- expression 54 operands: lhs = Expression(72, Add), rhs = Expression(73, Add) +- expression 55 operands: lhs = Counter(1), rhs = Counter(3) +- expression 56 operands: lhs = Counter(4), rhs = Counter(5) +- expression 57 operands: lhs = Expression(67, Add), rhs = Counter(5) +- expression 58 operands: lhs = Zero, rhs = Expression(68, Sub) +- expression 59 operands: lhs = Expression(69, Sub), rhs = Counter(7) +- expression 60 operands: lhs = Expression(70, Sub), rhs = Counter(4) +- expression 61 operands: lhs = Expression(71, Add), rhs = Counter(6) +- expression 62 operands: lhs = Expression(72, Add), rhs = Expression(73, Add) +- expression 63 operands: lhs = Counter(1), rhs = Counter(3) +- expression 64 operands: lhs = Counter(4), rhs = Counter(5) +- expression 65 operands: lhs = Expression(66, Sub), rhs = Expression(74, Add) +- expression 66 operands: lhs = Expression(67, Add), rhs = Counter(5) +- expression 67 operands: lhs = Zero, rhs = Expression(68, Sub) +- expression 68 operands: lhs = Expression(69, Sub), rhs = Counter(7) +- expression 69 operands: lhs = Expression(70, Sub), rhs = Counter(4) +- expression 70 operands: lhs = Expression(71, Add), rhs = Counter(6) +- expression 71 operands: lhs = Expression(72, Add), rhs = Expression(73, Add) +- expression 72 operands: lhs = Counter(1), rhs = Counter(3) +- expression 73 operands: lhs = Counter(4), rhs = Counter(5) +- expression 74 operands: lhs = Counter(6), rhs = Counter(9) +Number of file 0 mappings: 29 +- Code(Counter(0)) at (prev + 35, 5) to (start + 1, 17) +- Branch { true: Counter(1), false: Expression(0, Sub) } at (prev + 1, 12) to (start + 0, 17) + false = (c0 - c1) +- Code(Zero) at (prev + 0, 18) to (start + 1, 10) +- Code(Expression(0, Sub)) at (prev + 2, 16) to (start + 0, 21) + = (c0 - c1) +- Branch { true: Counter(2), false: Expression(4, Sub) } at (prev + 0, 16) to (start + 0, 21) + false = ((c0 - c1) - c2) +- Code(Zero) at (prev + 1, 23) to (start + 0, 27) +- Code(Zero) at (prev + 0, 23) to (start + 0, 27) +- Code(Zero) at (prev + 0, 28) to (start + 0, 30) +- Code(Expression(4, Sub)) at (prev + 1, 14) to (start + 0, 15) + = ((c0 - c1) - c2) +- Code(Expression(3, Add)) at (prev + 1, 13) to (start + 0, 30) + = (Zero + ((c0 - c1) - c2)) +- Branch { true: Counter(9), false: Counter(3) } at (prev + 0, 13) to (start + 0, 31) +- Code(Counter(9)) at (prev + 0, 30) to (start + 0, 31) +- Code(Expression(70, Sub)) at (prev + 2, 13) to (start + 0, 14) + = (((c1 + c3) + (c4 + c5)) - c6) +- Code(Expression(71, Add)) at (prev + 0, 18) to (start + 0, 23) + = ((c1 + c3) + (c4 + c5)) +- Branch { true: Expression(70, Sub), false: Counter(6) } at (prev + 0, 18) to (start + 0, 23) + true = (((c1 + c3) + (c4 + c5)) - c6) +- Code(Expression(70, Sub)) at (prev + 1, 16) to (start + 0, 21) + = (((c1 + c3) + (c4 + c5)) - c6) +- Branch { true: Counter(4), false: Expression(69, Sub) } at (prev + 0, 16) to (start + 0, 21) + false = ((((c1 + c3) + (c4 + c5)) - c6) - c4) +- Code(Zero) at (prev + 0, 22) to (start + 1, 14) +- Code(Expression(69, Sub)) at (prev + 2, 20) to (start + 0, 25) + = ((((c1 + c3) + (c4 + c5)) - c6) - c4) +- Branch { true: Counter(7), false: Expression(68, Sub) } at (prev + 0, 20) to (start + 0, 25) + false = (((((c1 + c3) + (c4 + c5)) - c6) - c4) - c7) +- Code(Zero) at (prev + 1, 27) to (start + 0, 31) +- Code(Zero) at (prev + 0, 27) to (start + 0, 31) +- Code(Zero) at (prev + 0, 32) to (start + 0, 34) +- Code(Expression(68, Sub)) at (prev + 1, 18) to (start + 0, 19) + = (((((c1 + c3) + (c4 + c5)) - c6) - c4) - c7) +- Code(Expression(67, Add)) at (prev + 1, 17) to (start + 0, 34) + = (Zero + (((((c1 + c3) + (c4 + c5)) - c6) - c4) - c7)) +- Branch { true: Expression(66, Sub), false: Counter(5) } at (prev + 0, 17) to (start + 0, 35) + true = ((Zero + (((((c1 + c3) + (c4 + c5)) - c6) - c4) - c7)) - c5) +- Code(Expression(66, Sub)) at (prev + 0, 34) to (start + 0, 35) + = ((Zero + (((((c1 + c3) + (c4 + c5)) - c6) - c4) - c7)) - c5) +- Code(Counter(6)) at (prev + 3, 9) to (start + 0, 15) +- Code(Expression(65, Add)) at (prev + 1, 5) to (start + 0, 6) + = (((Zero + (((((c1 + c3) + (c4 + c5)) - c6) - c4) - c7)) - c5) + (c6 + c9)) + +Function name: loops_branches::main +Raw bytes (9): 0x[01, 01, 00, 01, 01, 38, 01, 05, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 1 +- Code(Counter(0)) at (prev + 56, 1) to (start + 5, 2) + diff --git a/tests/coverage-map/status-quo/loops_branches.rs b/tests/coverage-map/status-quo/loops_branches.rs new file mode 100644 index 0000000000000..7116ce47f4b9d --- /dev/null +++ b/tests/coverage-map/status-quo/loops_branches.rs @@ -0,0 +1,61 @@ +#![allow(unused_assignments, unused_variables, while_true)] + +// This test confirms that (1) unexecuted infinite loops are handled correctly by the +// InstrumentCoverage MIR pass; and (2) Counter Expressions that subtract from zero can be dropped. + +struct DebugTest; + +impl std::fmt::Debug for DebugTest { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + if true { + if false { + while true { + } + } + write!(f, "cool")?; + } else { + } + + for i in 0..10 { + if true { + if false { + while true {} + } + write!(f, "cool")?; + } else { + } + } + Ok(()) + } +} + +struct DisplayTest; + +impl std::fmt::Display for DisplayTest { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + if false { + } else { + if false { + while true {} + } + write!(f, "cool")?; + } + for i in 0..10 { + if false { + } else { + if false { + while true {} + } + write!(f, "cool")?; + } + } + Ok(()) + } +} + +fn main() { + let debug_test = DebugTest; + println!("{:?}", debug_test); + let display_test = DisplayTest; + println!("{}", display_test); +} diff --git a/tests/coverage-map/status-quo/unused.cov-map b/tests/coverage-map/status-quo/unused.cov-map new file mode 100644 index 0000000000000..f6a86a58fb9d3 --- /dev/null +++ b/tests/coverage-map/status-quo/unused.cov-map @@ -0,0 +1,107 @@ +Function name: unused::foo:: +Raw bytes (58): 0x[01, 01, 05, 01, 13, 05, 09, 03, 0d, 03, 0d, 05, 09, 08, 01, 01, 01, 01, 12, 03, 02, 0b, 00, 11, 20, 0e, 0d, 00, 0b, 00, 11, 0e, 01, 09, 00, 0f, 20, 05, 09, 00, 09, 00, 19, 09, 00, 13, 00, 19, 13, 01, 09, 00, 0f, 0d, 02, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 5 +- expression 0 operands: lhs = Counter(0), rhs = Expression(4, Add) +- expression 1 operands: lhs = Counter(1), rhs = Counter(2) +- expression 2 operands: lhs = Expression(0, Add), rhs = Counter(3) +- expression 3 operands: lhs = Expression(0, Add), rhs = Counter(3) +- expression 4 operands: lhs = Counter(1), rhs = Counter(2) +Number of file 0 mappings: 8 +- Code(Counter(0)) at (prev + 1, 1) to (start + 1, 18) +- Code(Expression(0, Add)) at (prev + 2, 11) to (start + 0, 17) + = (c0 + (c1 + c2)) +- Branch { true: Expression(3, Sub), false: Counter(3) } at (prev + 0, 11) to (start + 0, 17) + true = ((c0 + (c1 + c2)) - c3) +- Code(Expression(3, Sub)) at (prev + 1, 9) to (start + 0, 15) + = ((c0 + (c1 + c2)) - c3) +- Branch { true: Counter(1), false: Counter(2) } at (prev + 0, 9) to (start + 0, 25) +- Code(Counter(2)) at (prev + 0, 19) to (start + 0, 25) +- Code(Expression(4, Add)) at (prev + 1, 9) to (start + 0, 15) + = (c1 + c2) +- Code(Counter(3)) at (prev + 2, 1) to (start + 0, 2) + +Function name: unused::foo:: +Raw bytes (58): 0x[01, 01, 05, 01, 13, 05, 09, 03, 0d, 03, 0d, 05, 09, 08, 01, 01, 01, 01, 12, 03, 02, 0b, 00, 11, 20, 0e, 0d, 00, 0b, 00, 11, 0e, 01, 09, 00, 0f, 20, 05, 09, 00, 09, 00, 19, 09, 00, 13, 00, 19, 13, 01, 09, 00, 0f, 0d, 02, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 5 +- expression 0 operands: lhs = Counter(0), rhs = Expression(4, Add) +- expression 1 operands: lhs = Counter(1), rhs = Counter(2) +- expression 2 operands: lhs = Expression(0, Add), rhs = Counter(3) +- expression 3 operands: lhs = Expression(0, Add), rhs = Counter(3) +- expression 4 operands: lhs = Counter(1), rhs = Counter(2) +Number of file 0 mappings: 8 +- Code(Counter(0)) at (prev + 1, 1) to (start + 1, 18) +- Code(Expression(0, Add)) at (prev + 2, 11) to (start + 0, 17) + = (c0 + (c1 + c2)) +- Branch { true: Expression(3, Sub), false: Counter(3) } at (prev + 0, 11) to (start + 0, 17) + true = ((c0 + (c1 + c2)) - c3) +- Code(Expression(3, Sub)) at (prev + 1, 9) to (start + 0, 15) + = ((c0 + (c1 + c2)) - c3) +- Branch { true: Counter(1), false: Counter(2) } at (prev + 0, 9) to (start + 0, 25) +- Code(Counter(2)) at (prev + 0, 19) to (start + 0, 25) +- Code(Expression(4, Add)) at (prev + 1, 9) to (start + 0, 15) + = (c1 + c2) +- Code(Counter(3)) at (prev + 2, 1) to (start + 0, 2) + +Function name: unused::main +Raw bytes (9): 0x[01, 01, 00, 01, 01, 23, 01, 04, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 1 +- Code(Counter(0)) at (prev + 35, 1) to (start + 4, 2) + +Function name: unused::unused_func (unused) +Raw bytes (29): 0x[01, 01, 00, 05, 00, 11, 01, 01, 0e, 01, 01, 08, 00, 0e, 00, 00, 0f, 02, 06, 00, 02, 06, 00, 07, 00, 01, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 5 +- Code(Zero) at (prev + 17, 1) to (start + 1, 14) +- Code(Counter(0)) at (prev + 1, 8) to (start + 0, 14) +- Code(Zero) at (prev + 0, 15) to (start + 2, 6) +- Code(Zero) at (prev + 2, 6) to (start + 0, 7) +- Code(Zero) at (prev + 1, 1) to (start + 0, 2) + +Function name: unused::unused_func2 (unused) +Raw bytes (29): 0x[01, 01, 00, 05, 00, 17, 01, 01, 0e, 01, 01, 08, 00, 0e, 00, 00, 0f, 02, 06, 00, 02, 06, 00, 07, 00, 01, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 5 +- Code(Zero) at (prev + 23, 1) to (start + 1, 14) +- Code(Counter(0)) at (prev + 1, 8) to (start + 0, 14) +- Code(Zero) at (prev + 0, 15) to (start + 2, 6) +- Code(Zero) at (prev + 2, 6) to (start + 0, 7) +- Code(Zero) at (prev + 1, 1) to (start + 0, 2) + +Function name: unused::unused_func3 (unused) +Raw bytes (29): 0x[01, 01, 00, 05, 00, 1d, 01, 01, 0e, 01, 01, 08, 00, 0e, 00, 00, 0f, 02, 06, 00, 02, 06, 00, 07, 00, 01, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 5 +- Code(Zero) at (prev + 29, 1) to (start + 1, 14) +- Code(Counter(0)) at (prev + 1, 8) to (start + 0, 14) +- Code(Zero) at (prev + 0, 15) to (start + 2, 6) +- Code(Zero) at (prev + 2, 6) to (start + 0, 7) +- Code(Zero) at (prev + 1, 1) to (start + 0, 2) + +Function name: unused::unused_template_func::<_> (unused) +Raw bytes (44): 0x[01, 01, 00, 08, 01, 09, 01, 01, 12, 00, 02, 0b, 00, 11, 00, 00, 0b, 00, 11, 00, 01, 09, 00, 0f, 00, 00, 09, 00, 19, 00, 00, 13, 00, 19, 00, 01, 09, 00, 0f, 00, 02, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 8 +- Code(Counter(0)) at (prev + 9, 1) to (start + 1, 18) +- Code(Zero) at (prev + 2, 11) to (start + 0, 17) +- Code(Zero) at (prev + 0, 11) to (start + 0, 17) +- Code(Zero) at (prev + 1, 9) to (start + 0, 15) +- Code(Zero) at (prev + 0, 9) to (start + 0, 25) +- Code(Zero) at (prev + 0, 19) to (start + 0, 25) +- Code(Zero) at (prev + 1, 9) to (start + 0, 15) +- Code(Zero) at (prev + 2, 1) to (start + 0, 2) + diff --git a/tests/coverage-map/status-quo/unused.rs b/tests/coverage-map/status-quo/unused.rs new file mode 100644 index 0000000000000..fb6113eb01c2d --- /dev/null +++ b/tests/coverage-map/status-quo/unused.rs @@ -0,0 +1,39 @@ +fn foo(x: T) { + let mut i = 0; + while i < 10 { + i != 0 || i != 0; + i += 1; + } +} + +fn unused_template_func(x: T) { + let mut i = 0; + while i < 10 { + i != 0 || i != 0; + i += 1; + } +} + +fn unused_func(mut a: u32) { + if a != 0 { + a += 1; + } +} + +fn unused_func2(mut a: u32) { + if a != 0 { + a += 1; + } +} + +fn unused_func3(mut a: u32) { + if a != 0 { + a += 1; + } +} + +fn main() -> Result<(), u8> { + foo::(0); + foo::(0.0); + Ok(()) +} diff --git a/tests/coverage-map/trivial.cov-map b/tests/coverage-map/trivial.cov-map new file mode 100644 index 0000000000000..874e294a1c498 --- /dev/null +++ b/tests/coverage-map/trivial.cov-map @@ -0,0 +1,8 @@ +Function name: trivial::main +Raw bytes (9): 0x[01, 01, 00, 01, 01, 03, 01, 00, 0d] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 1 +- Code(Counter(0)) at (prev + 3, 1) to (start + 0, 13) + diff --git a/tests/coverage-map/trivial.rs b/tests/coverage-map/trivial.rs new file mode 100644 index 0000000000000..d0a9b44fb3605 --- /dev/null +++ b/tests/coverage-map/trivial.rs @@ -0,0 +1,3 @@ +// compile-flags: --edition=2021 + +fn main() {} diff --git a/tests/run-coverage/abort.coverage b/tests/run-coverage/abort.coverage index ceef638678070..1c97e75a2ec01 100644 --- a/tests/run-coverage/abort.coverage +++ b/tests/run-coverage/abort.coverage @@ -3,6 +3,9 @@ LL| | LL| 12|extern "C" fn might_abort(should_abort: bool) { LL| 12| if should_abort { + ------------------ + | Branch (LL:8): [True: 0, False: 12] + ------------------ LL| 0| println!("aborting..."); LL| 0| panic!("panics and aborts"); LL| 12| } else { @@ -13,17 +16,29 @@ LL| 1|fn main() -> Result<(), u8> { LL| 1| let mut countdown = 10; LL| 11| while countdown > 0 { + ------------------ + | Branch (LL:11): [True: 10, False: 1] + ------------------ LL| 10| if countdown < 5 { + ------------------ + | Branch (LL:12): [True: 4, False: 6] + ------------------ LL| 4| might_abort(false); LL| 6| } LL| | // See discussion (below the `Notes` section) on coverage results for the closing brace. LL| 10| if countdown < 5 { might_abort(false); } // Counts for different regions on one line. ^4 ^6 + ------------------ + | Branch (LL:12): [True: 4, False: 6] + ------------------ LL| | // For the following example, the closing brace is the last character on the line. LL| | // This shows the character after the closing brace is highlighted, even if that next LL| | // character is a newline. LL| 10| if countdown < 5 { might_abort(false); } ^4 ^6 + ------------------ + | Branch (LL:12): [True: 4, False: 6] + ------------------ LL| 10| countdown -= 1; LL| | } LL| 1| Ok(()) diff --git a/tests/run-coverage/assert.coverage b/tests/run-coverage/assert.coverage index 3c6108e436a1f..6ebb99ffcd5e3 100644 --- a/tests/run-coverage/assert.coverage +++ b/tests/run-coverage/assert.coverage @@ -10,9 +10,18 @@ LL| 1|fn main() -> Result<(),u8> { LL| 1| let mut countdown = 10; LL| 11| while countdown > 0 { + ------------------ + | Branch (LL:11): [True: 11, False: 0] + ------------------ LL| 11| if countdown == 1 { + ------------------ + | Branch (LL:12): [True: 1, False: 10] + ------------------ LL| 1| might_fail_assert(3); LL| 10| } else if countdown < 5 { + ------------------ + | Branch (LL:19): [True: 3, False: 6] + ------------------ LL| 3| might_fail_assert(2); LL| 6| } LL| 10| countdown -= 1; @@ -31,4 +40,7 @@ LL| |// 4. `TerminatoKind::Assert` is, however, also present in the MIR generated for this test LL| |// (and in many other coverage tests). The `Assert` terminator is typically generated by the LL| |// Rust compiler to check for runtime failures, such as numeric overflows. + ------------------ + | Branch (LL:193389): [True: 1, False: 3] + ------------------ diff --git a/tests/run-coverage/async.coverage b/tests/run-coverage/async.coverage index 07bc16c2d926c..a832f0dc8f331 100644 --- a/tests/run-coverage/async.coverage +++ b/tests/run-coverage/async.coverage @@ -4,6 +4,9 @@ LL| | LL| 1|async fn c(x: u8) -> u8 { LL| 1| if x == 8 { + ------------------ + | Branch (LL:8): [True: 1, False: 0] + ------------------ LL| 1| 1 LL| | } else { LL| 0| 0 @@ -42,8 +45,14 @@ LL| 1| match x { LL| 1| y if c(x).await == y + 1 => { d().await; } ^0 ^0 ^0 ^0 + ------------------ + | Branch (LL:14): [True: 0, False: 1] + ------------------ LL| 1| y if f().await == y + 1 => (), ^0 ^0 ^0 + ------------------ + | Branch (LL:14): [True: 0, False: 1] + ------------------ LL| 1| _ => (), LL| | } LL| 1|} @@ -52,6 +61,9 @@ LL| 1| // non-async versions of `c()`, `d()`, and `f()` to make it similar to async `i()`. LL| 1| fn c(x: u8) -> u8 { LL| 1| if x == 8 { + ------------------ + | Branch (LL:12): [True: 0, False: 1] + ------------------ LL| 1| 1 // This line appears covered, but the 1-character expression span covering the `1` ^0 LL| 1| // is not executed. (`llvm-cov show` displays a `^0` below the `1` ). This is because @@ -72,8 +84,14 @@ LL| 1| match x { LL| 1| y if c(x) == y + 1 => { d(); } ^0 ^0 + ------------------ + | Branch (LL:14): [True: 0, False: 1] + ------------------ LL| 1| y if f() == y + 1 => (), ^0 ^0 + ------------------ + | Branch (LL:14): [True: 0, False: 1] + ------------------ LL| 1| _ => (), LL| | } LL| 1|} @@ -131,6 +149,9 @@ LL| | LL| | loop { LL| 1| if let Poll::Ready(val) = future.as_mut().poll(&mut context) { + ------------------ + | Branch (LL:20): [True: 0, False: 1] + ------------------ LL| 1| break val; LL| 0| } LL| | } diff --git a/tests/run-coverage/async2.coverage b/tests/run-coverage/async2.coverage index 7e0139ae03623..2fdbf71ee7770 100644 --- a/tests/run-coverage/async2.coverage +++ b/tests/run-coverage/async2.coverage @@ -10,6 +10,9 @@ LL| 1| println!("non_async_func was covered"); LL| 1| let b = true; LL| 1| if b { + ------------------ + | Branch (LL:8): [True: 1, False: 0] + ------------------ LL| 1| println!("non_async_func println in block"); LL| 1| } ^0 @@ -22,6 +25,9 @@ LL| 1| println!("async_func was covered"); LL| 1| let b = true; LL| 1| if b { + ------------------ + | Branch (LL:8): [True: 1, False: 0] + ------------------ LL| 1| println!("async_func println in block"); LL| 1| } ^0 @@ -67,6 +73,10 @@ LL| | LL| | loop { LL| 2| if let Poll::Ready(val) = future.as_mut().poll(&mut context) { + ------------------ + | Branch (LL:20): [True: 0, False: 1] + | Branch (LL:20): [True: 0, False: 1] + ------------------ LL| 2| break val; LL| 0| } LL| | } @@ -87,6 +97,9 @@ | LL| | | LL| | loop { | LL| 1| if let Poll::Ready(val) = future.as_mut().poll(&mut context) { + | ------------------ + | | Branch (LL:20): [True: 0, False: 1] + | ------------------ | LL| 1| break val; | LL| 0| } | LL| | } @@ -107,6 +120,9 @@ | LL| | | LL| | loop { | LL| 1| if let Poll::Ready(val) = future.as_mut().poll(&mut context) { + | ------------------ + | | Branch (LL:20): [True: 0, False: 1] + | ------------------ | LL| 1| break val; | LL| 0| } | LL| | } diff --git a/tests/run-coverage/closure.coverage b/tests/run-coverage/closure.coverage index 809cf1f482119..4edfe39100ab7 100644 --- a/tests/run-coverage/closure.coverage +++ b/tests/run-coverage/closure.coverage @@ -19,6 +19,9 @@ LL| 0| { LL| 0| let mut countdown = 0; LL| 0| if is_false { + ------------------ + | Branch (LL:20): [True: 0, False: 0] + ------------------ LL| 0| countdown = 10; LL| 0| } LL| 0| "alt string 1".to_owned() @@ -34,6 +37,9 @@ LL| 0| { LL| 0| let mut countdown = 0; LL| 0| if is_false { + ------------------ + | Branch (LL:12): [True: 0, False: 0] + ------------------ LL| 0| countdown = 10; LL| 0| } LL| 0| "alt string 2".to_owned() @@ -61,6 +67,9 @@ LL| 1| { LL| 1| let mut countdown = 0; LL| 1| if is_false { + ------------------ + | Branch (LL:20): [True: 0, False: 1] + ------------------ LL| 0| countdown = 10; LL| 1| } LL| 1| "alt string 3".to_owned() @@ -76,6 +85,9 @@ LL| 1| { LL| 1| let mut countdown = 0; LL| 1| if is_false { + ------------------ + | Branch (LL:12): [True: 0, False: 1] + ------------------ LL| 0| countdown = 10; LL| 1| } LL| 1| "alt string 4".to_owned() @@ -98,6 +110,9 @@ LL| 5| { LL| 5| let mut countdown = 0; LL| 5| if is_false { + ------------------ + | Branch (LL:12): [True: 0, False: 5] + ------------------ LL| 0| countdown = 10; LL| 5| } LL| 5| format!("'{}'", val) @@ -177,6 +192,9 @@ LL| 0| println!( LL| 0| "not called: {}", LL| 0| if is_true { "check" } else { "me" } + ------------------ + | Branch (LL:20): [True: 0, False: 0] + ------------------ LL| 0| ) LL| | ; LL| | @@ -186,6 +204,9 @@ LL| 0| println!( LL| 0| "not called: {}", LL| 0| if is_true { "check" } else { "me" } + ------------------ + | Branch (LL:20): [True: 0, False: 0] + ------------------ LL| | ) LL| 0| } LL| | ; @@ -196,6 +217,9 @@ LL| 1| "not called: {}", LL| 1| if is_true { "check" } else { "me" } ^0 + ------------------ + | Branch (LL:20): [True: 1, False: 0] + ------------------ LL| 1| ) LL| | ; LL| | @@ -206,11 +230,17 @@ LL| 1| "not called: {}", LL| 1| if is_true { "check" } else { "me" } ^0 + ------------------ + | Branch (LL:20): [True: 1, False: 0] + ------------------ LL| | ) LL| 1| } LL| | ; LL| | LL| 1| if is_false { + ------------------ + | Branch (LL:8): [True: 0, False: 1] + ------------------ LL| 0| short_used_not_covered_closure_macro(0); LL| 0| short_used_not_covered_closure_line_break_no_block_embedded_branch(0); LL| 0| short_used_not_covered_closure_line_break_block_embedded_branch(0); diff --git a/tests/run-coverage/closure_macro.coverage b/tests/run-coverage/closure_macro.coverage index 1bfd2013da84e..da3a27f23b0e8 100644 --- a/tests/run-coverage/closure_macro.coverage +++ b/tests/run-coverage/closure_macro.coverage @@ -17,6 +17,9 @@ LL| | $value.or_else(|e| { // FIXME(85000): no coverage in closure macros LL| | let message = format!($error_message, e); LL| | if message.len() > 0 { + ------------------ + | Branch (LL:16): [True: 0, False: 0] + ------------------ LL| | println!("{}", message); LL| | Ok(String::from("ok")) LL| | } else { @@ -34,6 +37,9 @@ LL| 1| println!("Starting service"); LL| 1| let config = on_error!(load_configuration_files(), "Error loading configs: {}")?; ^0 + ------------------ + | Branch (LL:18): [True: 0, False: 1] + ------------------ LL| | LL| 1| let startup_delay_duration = String::from("arg"); LL| 1| let _ = (config, startup_delay_duration); diff --git a/tests/run-coverage/closure_macro_async.coverage b/tests/run-coverage/closure_macro_async.coverage index 0e4365fc797dc..5d91a3bc8d98c 100644 --- a/tests/run-coverage/closure_macro_async.coverage +++ b/tests/run-coverage/closure_macro_async.coverage @@ -17,6 +17,9 @@ LL| | $value.or_else(|e| { // FIXME(85000): no coverage in closure macros LL| | let message = format!($error_message, e); LL| | if message.len() > 0 { + ------------------ + | Branch (LL:16): [True: 0, False: 0] + ------------------ LL| | println!("{}", message); LL| | Ok(String::from("ok")) LL| | } else { @@ -34,6 +37,9 @@ LL| 1| println!("Starting service"); LL| 1| let config = on_error!(load_configuration_files(), "Error loading configs: {}")?; ^0 + ------------------ + | Branch (LL:18): [True: 0, False: 1] + ------------------ LL| | LL| 1| let startup_delay_duration = String::from("arg"); LL| 1| let _ = (config, startup_delay_duration); diff --git a/tests/run-coverage/conditions.coverage b/tests/run-coverage/conditions.coverage index 4749c353a64ff..ff857fa046384 100644 --- a/tests/run-coverage/conditions.coverage +++ b/tests/run-coverage/conditions.coverage @@ -3,16 +3,29 @@ LL| 1|fn main() { LL| 1| let mut countdown = 0; LL| 1| if true { + ------------------ + | Branch (LL:8): [True: 1, False: 0] + ------------------ LL| 1| countdown = 10; LL| 1| } ^0 LL| | LL| | const B: u32 = 100; LL| 1| let x = if countdown > 7 { + ------------------ + | Branch (LL:16): [True: 1, False: 0] + ------------------ LL| 1| countdown -= 4; LL| 1| B LL| 0| } else if countdown > 2 { + ------------------ + | Branch (LL:15): [True: 0, False: 0] + ------------------ LL| 0| if countdown < 1 || countdown > 5 || countdown != 9 { + ------------------ + | Branch (LL:12): [True: 0, False: 0] + | Branch (LL:12): [True: 0, False: 0] + ------------------ LL| 0| countdown = 0; LL| 0| } LL| 0| countdown -= 5; @@ -23,15 +36,28 @@ LL| | LL| 1| let mut countdown = 0; LL| 1| if true { + ------------------ + | Branch (LL:8): [True: 1, False: 0] + ------------------ LL| 1| countdown = 10; LL| 1| } ^0 LL| | LL| 1| if countdown > 7 { + ------------------ + | Branch (LL:8): [True: 1, False: 0] + ------------------ LL| 1| countdown -= 4; LL| 1| } else if countdown > 2 { ^0 + ------------------ + | Branch (LL:15): [True: 0, False: 0] + ------------------ LL| 0| if countdown < 1 || countdown > 5 || countdown != 9 { + ------------------ + | Branch (LL:12): [True: 0, False: 0] + | Branch (LL:12): [True: 0, False: 0] + ------------------ LL| 0| countdown = 0; LL| 0| } LL| 0| countdown -= 5; @@ -40,17 +66,33 @@ LL| | } LL| | LL| 1| if true { + ------------------ + | Branch (LL:8): [True: 1, False: 0] + ------------------ LL| 1| let mut countdown = 0; LL| 1| if true { + ------------------ + | Branch (LL:12): [True: 1, False: 0] + ------------------ LL| 1| countdown = 10; LL| 1| } ^0 LL| | LL| 1| if countdown > 7 { + ------------------ + | Branch (LL:12): [True: 1, False: 0] + ------------------ LL| 1| countdown -= 4; LL| 1| } LL| 0| else if countdown > 2 { + ------------------ + | Branch (LL:17): [True: 0, False: 0] + ------------------ LL| 0| if countdown < 1 || countdown > 5 || countdown != 9 { + ------------------ + | Branch (LL:16): [True: 0, False: 0] + | Branch (LL:16): [True: 0, False: 0] + ------------------ LL| 0| countdown = 0; LL| 0| } LL| 0| countdown -= 5; @@ -62,15 +104,28 @@ LL| | LL| 1| let mut countdown = 0; LL| 1| if true { + ------------------ + | Branch (LL:8): [True: 1, False: 0] + ------------------ LL| 1| countdown = 1; LL| 1| } ^0 LL| | LL| 1| let z = if countdown > 7 { ^0 + ------------------ + | Branch (LL:16): [True: 0, False: 1] + ------------------ LL| 0| countdown -= 4; LL| 1| } else if countdown > 2 { + ------------------ + | Branch (LL:15): [True: 0, False: 1] + ------------------ LL| 0| if countdown < 1 || countdown > 5 || countdown != 9 { + ------------------ + | Branch (LL:12): [True: 0, False: 0] + | Branch (LL:12): [True: 0, False: 0] + ------------------ LL| 0| countdown = 0; LL| 0| } LL| 0| countdown -= 5; @@ -81,9 +136,19 @@ LL| | }; LL| | LL| 0| let w = if countdown > 7 { + ------------------ + | Branch (LL:16): [True: 0, False: 0] + ------------------ LL| 0| countdown -= 4; LL| 0| } else if countdown > 2 { + ------------------ + | Branch (LL:15): [True: 0, False: 0] + ------------------ LL| 0| if countdown < 1 || countdown > 5 || countdown != 9 { + ------------------ + | Branch (LL:12): [True: 0, False: 0] + | Branch (LL:12): [True: 0, False: 0] + ------------------ LL| 0| countdown = 0; LL| 0| } LL| 0| countdown -= 5; diff --git a/tests/run-coverage/continue.coverage b/tests/run-coverage/continue.coverage index 4916cac0038e6..095f1a19cbfa8 100644 --- a/tests/run-coverage/continue.coverage +++ b/tests/run-coverage/continue.coverage @@ -5,7 +5,13 @@ LL| 1| LL| 1| let mut x = 0; LL| 11| for _ in 0..10 { + ------------------ + | Branch (LL:14): [True: 10, False: 1] + ------------------ LL| 10| match is_true { + ------------------ + | Branch (LL:9): [True: 10, False: 0] + ------------------ LL| | true => { LL| 10| continue; LL| | } @@ -16,7 +22,13 @@ LL| 0| x = 3; LL| | } LL| 11| for _ in 0..10 { + ------------------ + | Branch (LL:14): [True: 10, False: 1] + ------------------ LL| 10| match is_true { + ------------------ + | Branch (LL:9): [True: 10, False: 0] + ------------------ LL| 0| false => { LL| 0| x = 1; LL| 0| } @@ -27,7 +39,13 @@ LL| 0| x = 3; LL| | } LL| 11| for _ in 0..10 { + ------------------ + | Branch (LL:14): [True: 10, False: 1] + ------------------ LL| 10| match is_true { + ------------------ + | Branch (LL:9): [True: 10, False: 0] + ------------------ LL| 10| true => { LL| 10| x = 1; LL| 10| } @@ -38,13 +56,25 @@ LL| 10| x = 3; LL| | } LL| 11| for _ in 0..10 { + ------------------ + | Branch (LL:14): [True: 10, False: 1] + ------------------ LL| 10| if is_true { + ------------------ + | Branch (LL:12): [True: 10, False: 0] + ------------------ LL| 10| continue; LL| 0| } LL| 0| x = 3; LL| | } LL| 11| for _ in 0..10 { + ------------------ + | Branch (LL:14): [True: 10, False: 1] + ------------------ LL| 10| match is_true { + ------------------ + | Branch (LL:9): [True: 10, False: 0] + ------------------ LL| 0| false => { LL| 0| x = 1; LL| 0| } @@ -55,7 +85,13 @@ LL| 10| x = 3; LL| | } LL| 1| for _ in 0..10 { + ------------------ + | Branch (LL:14): [True: 1, False: 0] + ------------------ LL| 1| match is_true { + ------------------ + | Branch (LL:9): [True: 1, False: 0] + ------------------ LL| 0| false => { LL| 0| x = 1; LL| 0| } diff --git a/tests/run-coverage/dead_code.coverage b/tests/run-coverage/dead_code.coverage index 5074d8b3c3774..86d552200b5b4 100644 --- a/tests/run-coverage/dead_code.coverage +++ b/tests/run-coverage/dead_code.coverage @@ -32,6 +32,9 @@ LL| 1| LL| 1| let mut countdown = 0; LL| 1| if is_true { + ------------------ + | Branch (LL:8): [True: 1, False: 0] + ------------------ LL| 1| countdown = 10; LL| 1| } ^0 diff --git a/tests/run-coverage/drop_trait.coverage b/tests/run-coverage/drop_trait.coverage index c99b980a339ff..b00ca28019dbe 100644 --- a/tests/run-coverage/drop_trait.coverage +++ b/tests/run-coverage/drop_trait.coverage @@ -17,6 +17,9 @@ LL| 1| let _tnt = Firework { strength: 100 }; LL| 1| LL| 1| if true { + ------------------ + | Branch (LL:8): [True: 1, False: 0] + ------------------ LL| 1| println!("Exiting with error..."); LL| 1| return Err(1); LL| 0| } diff --git a/tests/run-coverage/generator.coverage b/tests/run-coverage/generator.coverage index daba2bea8b866..fd6d8a7731e22 100644 --- a/tests/run-coverage/generator.coverage +++ b/tests/run-coverage/generator.coverage @@ -11,6 +11,9 @@ LL| 1|fn get_u32(val: bool) -> Result { LL| 1| if val { Ok(1) } else { Err(String::from("some error")) } ^0 + ------------------ + | Branch (LL:8): [True: 1, False: 0] + ------------------ LL| 1|} LL| | LL| 1|fn main() { @@ -21,11 +24,21 @@ LL| 1| }; LL| | LL| 1| match Pin::new(&mut generator).resume(()) { + ------------------ + | Branch (LL:5): [True: 0, False: 1] + | Branch (LL:5): [True: 0, False: 1] + ------------------ LL| 1| GeneratorState::Yielded(Ok(1)) => {} LL| 0| _ => panic!("unexpected return from resume"), LL| | } LL| 1| match Pin::new(&mut generator).resume(()) { + ------------------ + | Branch (LL:5): [True: 0, False: 1] + ------------------ LL| 1| GeneratorState::Complete("foo") => {} + ------------------ + | Branch (LL:34): [True: 1, False: 0] + ------------------ LL| 0| _ => panic!("unexpected return from resume"), LL| | } LL| 1|} diff --git a/tests/run-coverage/generics.coverage b/tests/run-coverage/generics.coverage index 2ff8f917ed74f..68129b1c56056 100644 --- a/tests/run-coverage/generics.coverage +++ b/tests/run-coverage/generics.coverage @@ -50,6 +50,9 @@ LL| 1| tnt.set_strength(300.3); LL| 1| LL| 1| if true { + ------------------ + | Branch (LL:8): [True: 1, False: 0] + ------------------ LL| 1| println!("Exiting with error..."); LL| 1| return Err(1); LL| 0| } diff --git a/tests/run-coverage/if.coverage b/tests/run-coverage/if.coverage index 2e6845190aabf..6a63dcaf5761a 100644 --- a/tests/run-coverage/if.coverage +++ b/tests/run-coverage/if.coverage @@ -19,6 +19,9 @@ LL| 1| ; LL| 1| if LL| 1| is_true + ------------------ + | Branch (LL:9): [True: 1, False: 0] + ------------------ LL| 1| { LL| 1| countdown LL| 1| = diff --git a/tests/run-coverage/if_else.coverage b/tests/run-coverage/if_else.coverage index 0274401f0047c..4229f2f400b3d 100644 --- a/tests/run-coverage/if_else.coverage +++ b/tests/run-coverage/if_else.coverage @@ -9,6 +9,9 @@ LL| 1| let mut countdown = 0; LL| 1| if LL| 1| is_true + ------------------ + | Branch (LL:9): [True: 1, False: 0] + ------------------ LL| 1| { LL| 1| countdown LL| 1| = @@ -24,6 +27,9 @@ LL| | LL| | if LL| 1| is_true + ------------------ + | Branch (LL:9): [True: 1, False: 0] + ------------------ LL| 1| { LL| 1| countdown LL| 1| = diff --git a/tests/run-coverage/inline-dead.coverage b/tests/run-coverage/inline-dead.coverage index de96aa17acd62..bf456027aaf54 100644 --- a/tests/run-coverage/inline-dead.coverage +++ b/tests/run-coverage/inline-dead.coverage @@ -15,6 +15,9 @@ LL| |#[inline] LL| 1|fn live() -> u32 { LL| 1| if B { + ------------------ + | Branch (LL:8): [True: 0, False: 1] + ------------------ LL| 0| dead() LL| | } else { LL| 1| 0 @@ -25,4 +28,8 @@ LL| 0|fn dead() -> u32 { LL| 0| 42 LL| 0|} + ------------------ + | Branch (LL:199570): [True: 0, False: 1] + | Branch (LL:199615): [True: 0, False: 0] + ------------------ diff --git a/tests/run-coverage/inline.coverage b/tests/run-coverage/inline.coverage index 6efd9a0830b4f..0ae3c62b6349e 100644 --- a/tests/run-coverage/inline.coverage +++ b/tests/run-coverage/inline.coverage @@ -15,10 +15,19 @@ LL| 16|fn permutate(xs: &mut [T], k: usize) { LL| 16| let n = length(xs); LL| 16| if k == n { + ------------------ + | Branch (LL:8): [True: 6, False: 10] + ------------------ LL| 6| display(xs); LL| 10| } else if k < n { + ------------------ + | Branch (LL:15): [True: 10, False: 0] + ------------------ LL| 15| for i in k..n { ^10 + ------------------ + | Branch (LL:18): [True: 15, False: 10] + ------------------ LL| 15| swap(xs, i, k); LL| 15| permutate(xs, k + 1); LL| 15| swap(xs, i, k); @@ -42,6 +51,9 @@ LL| 6|fn display(xs: &[T]) { LL| 24| for x in xs { ^18 + ------------------ + | Branch (LL:14): [True: 18, False: 6] + ------------------ LL| 18| print!("{}", x); LL| 18| } LL| 6| println!(); diff --git a/tests/run-coverage/inner_items.coverage b/tests/run-coverage/inner_items.coverage index 65493bcd9db45..91d46519719f4 100644 --- a/tests/run-coverage/inner_items.coverage +++ b/tests/run-coverage/inner_items.coverage @@ -8,6 +8,9 @@ LL| 1| LL| 1| let mut countdown = 0; LL| 1| if is_true { + ------------------ + | Branch (LL:8): [True: 1, False: 0] + ------------------ LL| 1| countdown = 10; LL| 1| } ^0 @@ -47,6 +50,9 @@ LL| | type InType = String; LL| | LL| 1| if is_true { + ------------------ + | Branch (LL:8): [True: 1, False: 0] + ------------------ LL| 1| in_func(countdown); LL| 1| } ^0 diff --git a/tests/run-coverage/issue-83601.coverage b/tests/run-coverage/issue-83601.coverage index 7995332cad339..7628954d19dac 100644 --- a/tests/run-coverage/issue-83601.coverage +++ b/tests/run-coverage/issue-83601.coverage @@ -13,4 +13,8 @@ LL| 1| println!("{:?}", bar); LL| 1| println!("{:?}", baz); LL| 1|} + ------------------ + | Branch (LL:192675): [True: 0, False: 1] + | Branch (LL:194635): [True: 0, False: 1] + ------------------ diff --git a/tests/run-coverage/issue-84561.coverage b/tests/run-coverage/issue-84561.coverage index 222f877d36aad..afacc122dea55 100644 --- a/tests/run-coverage/issue-84561.coverage +++ b/tests/run-coverage/issue-84561.coverage @@ -26,6 +26,9 @@ LL| 1| LL| 1| assert_ne!(Foo(0), Foo(5), "{}", if is_true { "true message" } else { "false message" }); ^0 ^0 ^0 + ------------------ + | Branch (LL:41): [True: 0, False: 0] + ------------------ LL| 1| assert_ne!( LL| | Foo(0) LL| | , @@ -35,6 +38,9 @@ LL| 0| , LL| 0| if LL| 0| is_true + ------------------ + | Branch (LL:9): [True: 0, False: 0] + ------------------ LL| | { LL| 0| "true message" LL| | } else { @@ -62,6 +68,9 @@ LL| 1| Foo(3) LL| 1| ); LL| 1| if is_true { + ------------------ + | Branch (LL:8): [True: 1, False: 0] + ------------------ LL| 1| assert_ne!( LL| 1| Foo(0), LL| 1| Foo(4) @@ -73,6 +82,9 @@ LL| 0| ); LL| | } LL| 1| if is_true { + ------------------ + | Branch (LL:8): [True: 1, False: 0] + ------------------ LL| 1| assert_ne!( LL| | Foo(0), LL| | Foo(4), @@ -87,6 +99,9 @@ LL| | } LL| 1| assert_ne!( LL| 1| if is_true { + ------------------ + | Branch (LL:12): [True: 1, False: 0] + ------------------ LL| 1| Foo(0) LL| | } else { LL| 0| Foo(1) @@ -96,6 +111,9 @@ LL| 1| assert_ne!( LL| 1| Foo(5), LL| 1| if is_true { + ------------------ + | Branch (LL:12): [True: 1, False: 0] + ------------------ LL| 1| Foo(0) LL| | } else { LL| 0| Foo(1) @@ -103,6 +121,9 @@ LL| | ); LL| 1| assert_ne!( LL| 1| if is_true { + ------------------ + | Branch (LL:12): [True: 1, False: 0] + ------------------ LL| 1| assert_eq!( LL| 1| Foo(3), LL| 1| Foo(3) @@ -111,6 +132,9 @@ LL| | } else { LL| 0| assert_ne!( LL| 0| if is_true { + ------------------ + | Branch (LL:20): [True: 0, False: 0] + ------------------ LL| 0| Foo(0) LL| | } else { LL| 0| Foo(1) @@ -138,6 +162,9 @@ LL| | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { LL| 7| write!(f, "try and succeed")?; ^0 + ------------------ + | Branch (LL:9): [True: 0, False: 7] + ------------------ LL| 7| Ok(()) LL| 7| } LL| |} @@ -147,6 +174,14 @@ LL| |macro_rules! debug { LL| | ($($arg:tt)+) => ( LL| | if unsafe { DEBUG_LEVEL_ENABLED } { + ------------------ + | Branch (LL:12): [True: 0, False: 1] + | Branch (LL:12): [True: 0, False: 1] + | Branch (LL:12): [True: 0, False: 1] + | Branch (LL:12): [True: 1, False: 0] + ------------------ + | Branch (LL:12): [True: 1, False: 0] + ------------------ LL| | println!($($arg)+); LL| | } LL| | ); @@ -186,4 +221,30 @@ LL| 1| test2(); LL| 1| test3(); LL| 1|} + ------------------ + | Branch (LL:192675): [True: 0, False: 1] + | Branch (LL:192675): [True: 0, False: 1] + | Branch (LL:192675): [True: 0, False: 1] + | Branch (LL:192675): [True: 0, False: 1] + | Branch (LL:192675): [True: 0, False: 1] + | Branch (LL:192675): [True: 0, False: 0] + | Branch (LL:192675): [True: 0, False: 1] + | Branch (LL:193311): [True: 0, False: 0] + | Branch (LL:193311): [True: 1, False: 0] + | Branch (LL:193311): [True: 0, False: 1] + | Branch (LL:193311): [True: 0, False: 0] + | Branch (LL:194635): [True: 0, False: 1] + | Branch (LL:194635): [True: 0, False: 1] + | Branch (LL:194635): [True: 0, False: 1] + | Branch (LL:194635): [True: 0, False: 1] + | Branch (LL:194635): [True: 0, False: 1] + | Branch (LL:194635): [True: 0, False: 0] + | Branch (LL:194635): [True: 0, False: 1] + | Branch (LL:194635): [True: 0, False: 1] + | Branch (LL:194635): [True: 0, False: 1] + | Branch (LL:195272): [True: 0, False: 1] + | Branch (LL:195272): [True: 0, False: 1] + | Branch (LL:195272): [True: 0, False: 1] + | Branch (LL:195272): [True: 0, False: 1] + ------------------ diff --git a/tests/run-coverage/lazy_boolean.coverage b/tests/run-coverage/lazy_boolean.coverage index 2d927a083560f..be39cca03549a 100644 --- a/tests/run-coverage/lazy_boolean.coverage +++ b/tests/run-coverage/lazy_boolean.coverage @@ -8,6 +8,9 @@ LL| 1| LL| 1| let (mut a, mut b, mut c) = (0, 0, 0); LL| 1| if is_true { + ------------------ + | Branch (LL:8): [True: 1, False: 0] + ------------------ LL| 1| a = 1; LL| 1| b = 10; LL| 1| c = 100; @@ -17,6 +20,9 @@ LL| 1| somebool LL| | = LL| 1| a < b + ------------------ + | Branch (LL:13): [True: 1, False: 0] + ------------------ LL| | || LL| 0| b < c LL| | ; @@ -24,15 +30,27 @@ LL| 1| somebool LL| | = LL| 1| b < a + ------------------ + | Branch (LL:13): [True: 0, False: 1] + ------------------ LL| | || LL| 1| b < c LL| | ; LL| 1| let somebool = a < b && b < c; + ------------------ + | Branch (LL:20): [True: 1, False: 0] + ------------------ LL| 1| let somebool = b < a && b < c; ^0 + ------------------ + | Branch (LL:20): [True: 0, False: 1] + ------------------ LL| | LL| | if LL| 1| ! + ------------------ + | Branch (LL:9): [True: 0, False: 1] + ------------------ LL| 1| is_true LL| 0| { LL| 0| a = 2 @@ -41,6 +59,9 @@ LL| | LL| | if LL| 1| is_true + ------------------ + | Branch (LL:9): [True: 1, False: 0] + ------------------ LL| 1| { LL| 1| b = 30 LL| 1| ; @@ -52,10 +73,16 @@ LL| 0| } LL| | LL| 1| if !is_true { + ------------------ + | Branch (LL:8): [True: 0, False: 1] + ------------------ LL| 0| a = 2; LL| 1| } LL| | LL| 1| if is_true { + ------------------ + | Branch (LL:8): [True: 1, False: 0] + ------------------ LL| 1| b = 30; LL| 1| } else { LL| 0| c = 400; diff --git a/tests/run-coverage/loops_branches.coverage b/tests/run-coverage/loops_branches.coverage index 148a22377f32a..34559c06b2ad9 100644 --- a/tests/run-coverage/loops_branches.coverage +++ b/tests/run-coverage/loops_branches.coverage @@ -8,23 +8,50 @@ LL| |impl std::fmt::Debug for DebugTest { LL| 1| fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { LL| 1| if true { + ------------------ + | Branch (LL:12): [True: 1, False: 0] + ------------------ LL| 1| if false { + ------------------ + | Branch (LL:16): [True: 0, False: 1] + ------------------ LL| 0| while true { + ------------------ + | Branch (LL:23): [True: 0, False: 0] + ------------------ LL| 0| } LL| 1| } LL| 1| write!(f, "cool")?; ^0 + ------------------ + | Branch (LL:13): [True: 0, False: 1] + ------------------ LL| 0| } else { LL| 0| } LL| | LL| 11| for i in 0..10 { ^10 + ------------------ + | Branch (LL:18): [True: 10, False: 1] + ------------------ LL| 10| if true { + ------------------ + | Branch (LL:16): [True: 10, False: 0] + ------------------ LL| 10| if false { + ------------------ + | Branch (LL:20): [True: 0, False: 10] + ------------------ LL| 0| while true {} + ------------------ + | Branch (LL:27): [True: 0, False: 0] + ------------------ LL| 10| } LL| 10| write!(f, "cool")?; ^0 + ------------------ + | Branch (LL:17): [True: 0, False: 10] + ------------------ LL| 0| } else { LL| 0| } LL| | } @@ -37,22 +64,49 @@ LL| |impl std::fmt::Display for DisplayTest { LL| 1| fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { LL| 1| if false { + ------------------ + | Branch (LL:12): [True: 0, False: 1] + ------------------ LL| 0| } else { LL| 1| if false { + ------------------ + | Branch (LL:16): [True: 0, False: 1] + ------------------ LL| 0| while true {} + ------------------ + | Branch (LL:23): [True: 0, False: 0] + ------------------ LL| 1| } LL| 1| write!(f, "cool")?; ^0 + ------------------ + | Branch (LL:13): [True: 0, False: 1] + ------------------ LL| | } LL| 11| for i in 0..10 { ^10 + ------------------ + | Branch (LL:18): [True: 10, False: 1] + ------------------ LL| 10| if false { + ------------------ + | Branch (LL:16): [True: 0, False: 10] + ------------------ LL| 0| } else { LL| 10| if false { + ------------------ + | Branch (LL:20): [True: 0, False: 10] + ------------------ LL| 0| while true {} + ------------------ + | Branch (LL:27): [True: 0, False: 0] + ------------------ LL| 10| } LL| 10| write!(f, "cool")?; ^0 + ------------------ + | Branch (LL:17): [True: 0, False: 10] + ------------------ LL| | } LL| | } LL| 1| Ok(()) diff --git a/tests/run-coverage/match_or_pattern.coverage b/tests/run-coverage/match_or_pattern.coverage index 0b5a2c03dd360..f391b39fb82aa 100644 --- a/tests/run-coverage/match_or_pattern.coverage +++ b/tests/run-coverage/match_or_pattern.coverage @@ -9,6 +9,9 @@ LL| 1| let mut a: u8 = 0; LL| 1| let mut b: u8 = 0; LL| 1| if is_true { + ------------------ + | Branch (LL:8): [True: 1, False: 0] + ------------------ LL| 1| a = 2; LL| 1| b = 0; LL| 1| } @@ -17,33 +20,58 @@ LL| | // Or patterns generate MIR `SwitchInt` with multiple targets to the same `BasicBlock`. LL| | // This test confirms a fix for Issue #79569. LL| 0| (0 | 1, 2 | 3) => {} + ------------------ + | Branch (LL:10): [True: 1, False: 0] + | Branch (LL:17): [True: 0, False: 0] + ------------------ LL| 1| _ => {} LL| | } LL| 1| if is_true { + ------------------ + | Branch (LL:8): [True: 1, False: 0] + ------------------ LL| 1| a = 0; LL| 1| b = 0; LL| 1| } ^0 LL| 1| match (a, b) { LL| 0| (0 | 1, 2 | 3) => {} + ------------------ + | Branch (LL:10): [True: 0, False: 1] + | Branch (LL:17): [True: 1, False: 0] + ------------------ LL| 1| _ => {} LL| | } LL| 1| if is_true { + ------------------ + | Branch (LL:8): [True: 1, False: 0] + ------------------ LL| 1| a = 2; LL| 1| b = 2; LL| 1| } ^0 LL| 1| match (a, b) { LL| 0| (0 | 1, 2 | 3) => {} + ------------------ + | Branch (LL:10): [True: 1, False: 0] + | Branch (LL:17): [True: 0, False: 0] + ------------------ LL| 1| _ => {} LL| | } LL| 1| if is_true { + ------------------ + | Branch (LL:8): [True: 1, False: 0] + ------------------ LL| 1| a = 0; LL| 1| b = 2; LL| 1| } ^0 LL| 1| match (a, b) { LL| 1| (0 | 1, 2 | 3) => {} + ------------------ + | Branch (LL:10): [True: 0, False: 1] + | Branch (LL:17): [True: 0, False: 1] + ------------------ LL| 0| _ => {} LL| | } LL| 1|} diff --git a/tests/run-coverage/nested_loops.coverage b/tests/run-coverage/nested_loops.coverage index 143d0d26aa7f3..6af9aaec75f40 100644 --- a/tests/run-coverage/nested_loops.coverage +++ b/tests/run-coverage/nested_loops.coverage @@ -3,17 +3,32 @@ LL| 1| let mut countdown = 10; LL| | LL| 1| 'outer: while countdown > 0 { + ------------------ + | Branch (LL:19): [True: 1, False: 0] + ------------------ LL| 1| let mut a = 100; LL| 1| let mut b = 100; LL| 3| for _ in 0..50 { + ------------------ + | Branch (LL:18): [True: 3, False: 0] + ------------------ LL| 3| if a < 30 { + ------------------ + | Branch (LL:16): [True: 0, False: 3] + ------------------ LL| 0| break; LL| 3| } LL| 3| a -= 5; LL| 3| b -= 5; LL| 3| if b < 90 { + ------------------ + | Branch (LL:16): [True: 1, False: 2] + ------------------ LL| 1| a -= 10; LL| 1| if is_true { + ------------------ + | Branch (LL:20): [True: 1, False: 0] + ------------------ LL| 1| break 'outer; LL| 0| } else { LL| 0| a -= 2; diff --git a/tests/run-coverage/no_cov_crate.coverage b/tests/run-coverage/no_cov_crate.coverage index c34dbde888ae9..7e85bcd6a699d 100644 --- a/tests/run-coverage/no_cov_crate.coverage +++ b/tests/run-coverage/no_cov_crate.coverage @@ -35,6 +35,9 @@ LL| | pub fn outer_not_covered(is_true: bool) { LL| 1| fn inner(is_true: bool) { LL| 1| if is_true { + ------------------ + | Branch (LL:16): [True: 1, False: 0] + ------------------ LL| 1| println!("called and covered"); LL| 1| } else { LL| 0| println!("absolutely not covered"); @@ -64,6 +67,9 @@ LL| 1| LL| 1| fn inner(is_true: bool) { LL| 1| if is_true { + ------------------ + | Branch (LL:16): [True: 1, False: 0] + ------------------ LL| 1| println!("called and covered"); LL| 1| } else { LL| 0| println!("absolutely not covered"); diff --git a/tests/run-coverage/overflow.coverage b/tests/run-coverage/overflow.coverage index 2d60316e21582..7f8ece45fde37 100644 --- a/tests/run-coverage/overflow.coverage +++ b/tests/run-coverage/overflow.coverage @@ -3,6 +3,9 @@ LL| | LL| 4|fn might_overflow(to_add: u32) -> u32 { LL| 4| if to_add > 5 { + ------------------ + | Branch (LL:8): [True: 1, False: 3] + ------------------ LL| 1| println!("this will probably overflow"); LL| 3| } LL| 4| let add_to = u32::MAX - 5; @@ -15,10 +18,19 @@ LL| 1|fn main() -> Result<(),u8> { LL| 1| let mut countdown = 10; LL| 11| while countdown > 0 { + ------------------ + | Branch (LL:11): [True: 11, False: 0] + ------------------ LL| 11| if countdown == 1 { + ------------------ + | Branch (LL:12): [True: 1, False: 10] + ------------------ LL| 1| let result = might_overflow(10); LL| 1| println!("Result: {}", result); LL| 10| } else if countdown < 5 { + ------------------ + | Branch (LL:19): [True: 3, False: 6] + ------------------ LL| 3| let result = might_overflow(1); LL| 3| println!("Result: {}", result); LL| 6| } diff --git a/tests/run-coverage/panic_unwind.coverage b/tests/run-coverage/panic_unwind.coverage index 2b0777ef215dc..5db5504a09bf6 100644 --- a/tests/run-coverage/panic_unwind.coverage +++ b/tests/run-coverage/panic_unwind.coverage @@ -3,6 +3,9 @@ LL| | LL| 4|fn might_panic(should_panic: bool) { LL| 4| if should_panic { + ------------------ + | Branch (LL:8): [True: 1, False: 3] + ------------------ LL| 1| println!("panicking..."); LL| 1| panic!("panics"); LL| 3| } else { @@ -13,9 +16,18 @@ LL| 1|fn main() -> Result<(), u8> { LL| 1| let mut countdown = 10; LL| 11| while countdown > 0 { + ------------------ + | Branch (LL:11): [True: 11, False: 0] + ------------------ LL| 11| if countdown == 1 { + ------------------ + | Branch (LL:12): [True: 1, False: 10] + ------------------ LL| 1| might_panic(true); LL| 10| } else if countdown < 5 { + ------------------ + | Branch (LL:19): [True: 3, False: 6] + ------------------ LL| 3| might_panic(false); LL| 6| } LL| 10| countdown -= 1; diff --git a/tests/run-coverage/partial_eq.coverage b/tests/run-coverage/partial_eq.coverage index c6d9ad6cf27e8..90ae00e836bac 100644 --- a/tests/run-coverage/partial_eq.coverage +++ b/tests/run-coverage/partial_eq.coverage @@ -4,9 +4,15 @@ LL| 2|#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] ^0 ^0 ^0 ^1 ^1 ^0^0 LL| |pub struct Version { - LL| | major: usize, - LL| | minor: usize, - LL| | patch: usize, + LL| 0| major: usize, + ------------------ + | Branch (LL:5): [True: 0, False: 1] + ------------------ + LL| 0| minor: usize, + ------------------ + | Branch (LL:5): [True: 1, False: 0] + ------------------ + LL| 0| patch: usize, LL| |} LL| | LL| |impl Version { diff --git a/tests/run-coverage/simple_loop.coverage b/tests/run-coverage/simple_loop.coverage index 691c6cd1e7dda..0e96fdbc4e43e 100644 --- a/tests/run-coverage/simple_loop.coverage +++ b/tests/run-coverage/simple_loop.coverage @@ -10,6 +10,9 @@ LL| 1| LL| 1| if LL| 1| is_true + ------------------ + | Branch (LL:9): [True: 1, False: 0] + ------------------ LL| 1| { LL| 1| countdown LL| 1| = @@ -22,6 +25,9 @@ LL| | { LL| | if LL| 11| countdown + ------------------ + | Branch (LL:13): [True: 1, False: 10] + ------------------ LL| 11| == LL| 11| 0 LL| | { diff --git a/tests/run-coverage/simple_match.coverage b/tests/run-coverage/simple_match.coverage index 7f5dd3bb64630..e8ddac5a1d405 100644 --- a/tests/run-coverage/simple_match.coverage +++ b/tests/run-coverage/simple_match.coverage @@ -8,6 +8,9 @@ LL| 1| LL| 1| let mut countdown = 1; LL| 1| if is_true { + ------------------ + | Branch (LL:8): [True: 1, False: 0] + ------------------ LL| 1| countdown = 0; LL| 1| } ^0 @@ -16,6 +19,9 @@ LL| | _ LL| | in LL| 3| 0..2 + ------------------ + | Branch (LL:9): [True: 2, False: 1] + ------------------ LL| | { LL| | let z LL| | ; @@ -25,6 +31,9 @@ LL| 1| x LL| | if LL| 2| x + ------------------ + | Branch (LL:17): [True: 1, False: 1] + ------------------ LL| 2| < LL| 2| 1 LL| | => diff --git a/tests/run-coverage/sort_groups.coverage b/tests/run-coverage/sort_groups.coverage index 8733bf48a9c8d..1e4dcae1d4e9a 100644 --- a/tests/run-coverage/sort_groups.coverage +++ b/tests/run-coverage/sort_groups.coverage @@ -8,6 +8,9 @@ LL| 1| generic_fn::<()>(cond); LL| 1| generic_fn::<&'static str>(!cond); LL| 1| if false { + ------------------ + | Branch (LL:8): [True: 0, False: 1] + ------------------ LL| 0| generic_fn::(cond); LL| 1| } LL| 1| generic_fn::(cond); @@ -16,6 +19,12 @@ LL| | LL| 3|fn generic_fn(cond: bool) { LL| 3| if cond { + ------------------ + | Branch (LL:8): [True: 1, False: 0] + | Branch (LL:8): [True: 0, False: 0] + | Branch (LL:8): [True: 0, False: 1] + | Branch (LL:8): [True: 0, False: 1] + ------------------ LL| 1| println!("{}", std::any::type_name::()); LL| 2| } LL| 3|} @@ -25,6 +34,9 @@ | sort_groups::generic_fn::<&str>: | LL| 1|fn generic_fn(cond: bool) { | LL| 1| if cond { + | ------------------ + | | Branch (LL:8): [True: 1, False: 0] + | ------------------ | LL| 1| println!("{}", std::any::type_name::()); | LL| 1| } | ^0 @@ -33,6 +45,9 @@ | sort_groups::generic_fn::<()>: | LL| 1|fn generic_fn(cond: bool) { | LL| 1| if cond { + | ------------------ + | | Branch (LL:8): [True: 0, False: 1] + | ------------------ | LL| 0| println!("{}", std::any::type_name::()); | LL| 1| } | LL| 1|} @@ -40,6 +55,9 @@ | sort_groups::generic_fn::: | LL| 1|fn generic_fn(cond: bool) { | LL| 1| if cond { + | ------------------ + | | Branch (LL:8): [True: 0, False: 1] + | ------------------ | LL| 0| println!("{}", std::any::type_name::()); | LL| 1| } | LL| 1|} diff --git a/tests/run-coverage/tight_inf_loop.coverage b/tests/run-coverage/tight_inf_loop.coverage index c15c76b3aba28..e957ba2c090c0 100644 --- a/tests/run-coverage/tight_inf_loop.coverage +++ b/tests/run-coverage/tight_inf_loop.coverage @@ -1,5 +1,8 @@ LL| 1|fn main() { LL| 1| if false { + ------------------ + | Branch (LL:8): [True: 0, False: 1] + ------------------ LL| 0| loop {} LL| 1| } LL| 1|} diff --git a/tests/run-coverage/try_error_result.coverage b/tests/run-coverage/try_error_result.coverage index fcdb7437d00dc..b1412c43b7f79 100644 --- a/tests/run-coverage/try_error_result.coverage +++ b/tests/run-coverage/try_error_result.coverage @@ -3,6 +3,9 @@ LL| | LL| 6|fn call(return_error: bool) -> Result<(),()> { LL| 6| if return_error { + ------------------ + | Branch (LL:8): [True: 1, False: 5] + ------------------ LL| 1| Err(()) LL| | } else { LL| 5| Ok(()) @@ -17,20 +20,35 @@ LL| | _ LL| | in LL| 6| 0..10 + ------------------ + | Branch (LL:9): [True: 6, False: 0] + ------------------ LL| | { LL| 6| countdown LL| 6| -= 1 LL| 6| ; LL| 6| if LL| 6| countdown < 5 + ------------------ + | Branch (LL:13): [True: 1, False: 5] + ------------------ LL| | { LL| 1| call(/*return_error=*/ true)?; + ------------------ + | Branch (LL:13): [True: 1, False: 0] + ------------------ LL| 0| call(/*return_error=*/ false)?; + ------------------ + | Branch (LL:13): [True: 0, False: 0] + ------------------ LL| | } LL| | else LL| | { LL| 5| call(/*return_error=*/ false)?; ^0 + ------------------ + | Branch (LL:13): [True: 0, False: 5] + ------------------ LL| | } LL| | } LL| 0| Ok(()) @@ -40,6 +58,9 @@ LL| |impl Thing1 { LL| 18| fn get_thing_2(&self, return_error: bool) -> Result { LL| 18| if return_error { + ------------------ + | Branch (LL:12): [True: 1, False: 17] + ------------------ LL| 1| Err(()) LL| | } else { LL| 17| Ok(Thing2{}) @@ -51,6 +72,9 @@ LL| |impl Thing2 { LL| 17| fn call(&self, return_error: bool) -> Result { LL| 17| if return_error { + ------------------ + | Branch (LL:12): [True: 2, False: 15] + ------------------ LL| 2| Err(()) LL| | } else { LL| 15| Ok(57) @@ -67,16 +91,28 @@ LL| | _ LL| | in LL| 6| 0..10 + ------------------ + | Branch (LL:9): [True: 6, False: 0] + ------------------ LL| | { LL| 6| countdown LL| 6| -= 1 LL| 6| ; LL| 6| if LL| 6| countdown < 5 + ------------------ + | Branch (LL:13): [True: 1, False: 5] + ------------------ LL| | { LL| 1| thing1.get_thing_2(/*err=*/ false)?.call(/*err=*/ true).expect_err("call should fail"); ^0 + ------------------ + | Branch (LL:13): [True: 0, False: 1] + ------------------ LL| 1| thing1 + ------------------ + | Branch (LL:13): [True: 0, False: 1] + ------------------ LL| 1| . LL| 1| get_thing_2(/*return_error=*/ false) LL| 0| ? @@ -88,22 +124,42 @@ LL| 1| ); LL| 1| let val = thing1.get_thing_2(/*return_error=*/ true)?.call(/*return_error=*/ true)?; ^0 ^0 ^0 + ------------------ + | Branch (LL:23): [True: 1, False: 0] + | Branch (LL:23): [True: 0, False: 0] + ------------------ LL| 0| assert_eq!(val, 57); LL| 0| let val = thing1.get_thing_2(/*return_error=*/ true)?.call(/*return_error=*/ false)?; + ------------------ + | Branch (LL:23): [True: 0, False: 0] + | Branch (LL:23): [True: 0, False: 0] + ------------------ LL| 0| assert_eq!(val, 57); LL| | } LL| | else LL| | { LL| 5| let val = thing1.get_thing_2(/*return_error=*/ false)?.call(/*return_error=*/ false)?; ^0 ^0 + ------------------ + | Branch (LL:23): [True: 0, False: 5] + | Branch (LL:23): [True: 0, False: 5] + ------------------ LL| 5| assert_eq!(val, 57); LL| 5| let val = thing1 + ------------------ + | Branch (LL:23): [True: 0, False: 5] + | Branch (LL:23): [True: 0, False: 5] + ------------------ LL| 5| .get_thing_2(/*return_error=*/ false)? ^0 LL| 5| .call(/*return_error=*/ false)?; ^0 LL| 5| assert_eq!(val, 57); LL| 5| let val = thing1 + ------------------ + | Branch (LL:23): [True: 0, False: 5] + | Branch (LL:23): [True: 0, False: 5] + ------------------ LL| 5| .get_thing_2(/*return_error=*/ false) LL| 0| ? LL| 5| .call(/*return_error=*/ false) @@ -118,8 +174,18 @@ LL| 1|fn main() -> Result<(),()> { LL| 1| test1().expect_err("test1 should fail"); LL| 1| test2() + ------------------ + | Branch (LL:5): [True: 1, False: 0] + ------------------ LL| 1| ? LL| | ; LL| 0| Ok(()) LL| 1|} + ------------------ + | Branch (LL:192675): [True: 0, False: 5] + | Branch (LL:192675): [True: 0, False: 5] + | Branch (LL:192675): [True: 0, False: 5] + | Branch (LL:192675): [True: 0, False: 0] + | Branch (LL:192675): [True: 0, False: 0] + ------------------ diff --git a/tests/run-coverage/unused.coverage b/tests/run-coverage/unused.coverage index ba25e34bf86d8..ded3b49996122 100644 --- a/tests/run-coverage/unused.coverage +++ b/tests/run-coverage/unused.coverage @@ -1,8 +1,18 @@ LL| 2|fn foo(x: T) { LL| 2| let mut i = 0; LL| 22| while i < 10 { + ------------------ + | Branch (LL:11): [True: 10, False: 1] + ------------------ + | Branch (LL:11): [True: 10, False: 1] + ------------------ LL| 20| i != 0 || i != 0; ^2 + ------------------ + | Branch (LL:9): [True: 9, False: 1] + ------------------ + | Branch (LL:9): [True: 9, False: 1] + ------------------ LL| 20| i += 1; LL| | } LL| 2|} @@ -11,8 +21,14 @@ | LL| 1|fn foo(x: T) { | LL| 1| let mut i = 0; | LL| 11| while i < 10 { + | ------------------ + | | Branch (LL:11): [True: 10, False: 1] + | ------------------ | LL| 10| i != 0 || i != 0; | ^1 + | ------------------ + | | Branch (LL:9): [True: 9, False: 1] + | ------------------ | LL| 10| i += 1; | LL| | } | LL| 1|} @@ -21,8 +37,14 @@ | LL| 1|fn foo(x: T) { | LL| 1| let mut i = 0; | LL| 11| while i < 10 { + | ------------------ + | | Branch (LL:11): [True: 10, False: 1] + | ------------------ | LL| 10| i != 0 || i != 0; | ^1 + | ------------------ + | | Branch (LL:9): [True: 9, False: 1] + | ------------------ | LL| 10| i += 1; | LL| | } | LL| 1|} diff --git a/tests/run-coverage/uses_crate.coverage b/tests/run-coverage/uses_crate.coverage index 9da096dbd50af..17454327f2264 100644 --- a/tests/run-coverage/uses_crate.coverage +++ b/tests/run-coverage/uses_crate.coverage @@ -10,6 +10,9 @@ $DIR/auxiliary/used_crate.rs: LL| 1| let is_true = std::env::args().len() == 1; LL| 1| let mut countdown = 0; LL| 1| if is_true { + ------------------ + | Branch (LL:8): [True: 1, False: 0] + ------------------ LL| 1| countdown = 10; LL| 1| } ^0 @@ -86,6 +89,9 @@ $DIR/auxiliary/used_crate.rs: LL| 0| let is_true = std::env::args().len() == 1; LL| 0| let mut countdown = 2; LL| 0| if !is_true { + ------------------ + | Branch (LL:8): [True: 0, False: 0] + ------------------ LL| 0| countdown = 20; LL| 0| } LL| 0|} diff --git a/tests/run-coverage/uses_inline_crate.coverage b/tests/run-coverage/uses_inline_crate.coverage index 48493e2079caa..52cc0cd4f7a02 100644 --- a/tests/run-coverage/uses_inline_crate.coverage +++ b/tests/run-coverage/uses_inline_crate.coverage @@ -12,6 +12,9 @@ $DIR/auxiliary/used_inline_crate.rs: LL| 1| let is_true = std::env::args().len() == 1; LL| 1| let mut countdown = 0; LL| 1| if is_true { + ------------------ + | Branch (LL:8): [True: 1, False: 0] + ------------------ LL| 1| countdown = 10; LL| 1| } ^0 @@ -26,6 +29,9 @@ $DIR/auxiliary/used_inline_crate.rs: LL| 1| let is_true = std::env::args().len() == 1; LL| 1| let mut countdown = 0; LL| 1| if is_true { + ------------------ + | Branch (LL:8): [True: 1, False: 0] + ------------------ LL| 1| countdown = 10; LL| 1| } ^0 diff --git a/tests/run-coverage/while.coverage b/tests/run-coverage/while.coverage index c9d497651c9f2..a3f871add5e7d 100644 --- a/tests/run-coverage/while.coverage +++ b/tests/run-coverage/while.coverage @@ -1,6 +1,9 @@ LL| 1|fn main() { LL| 1| let num = 9; LL| 1| while num >= 10 { + ------------------ + | Branch (LL:11): [True: 0, False: 1] + ------------------ LL| 0| } LL| 1|} diff --git a/tests/run-coverage/while_early_ret.coverage b/tests/run-coverage/while_early_ret.coverage index 97808447ab74e..a20a22c96b3c9 100644 --- a/tests/run-coverage/while_early_ret.coverage +++ b/tests/run-coverage/while_early_ret.coverage @@ -5,17 +5,26 @@ LL| 1| let mut countdown = 10; LL| | while LL| 7| countdown + ------------------ + | Branch (LL:9): [True: 7, False: 0] + ------------------ LL| 7| > LL| 7| 0 LL| | { LL| | if LL| 7| countdown + ------------------ + | Branch (LL:13): [True: 1, False: 6] + ------------------ LL| 7| < LL| 7| 5 LL| | { LL| | return LL| | if LL| 1| countdown + ------------------ + | Branch (LL:21): [True: 0, False: 1] + ------------------ LL| 1| > LL| 1| 8 LL| | { diff --git a/tests/run-coverage/yield.coverage b/tests/run-coverage/yield.coverage index 383dd99150042..347711a1a52f3 100644 --- a/tests/run-coverage/yield.coverage +++ b/tests/run-coverage/yield.coverage @@ -11,11 +11,21 @@ LL| 1| }; LL| | LL| 1| match Pin::new(&mut generator).resume(()) { + ------------------ + | Branch (LL:5): [True: 0, False: 1] + | Branch (LL:5): [True: 0, False: 1] + ------------------ LL| 1| GeneratorState::Yielded(1) => {} LL| 0| _ => panic!("unexpected value from resume"), LL| | } LL| 1| match Pin::new(&mut generator).resume(()) { + ------------------ + | Branch (LL:5): [True: 0, False: 1] + ------------------ LL| 1| GeneratorState::Complete("foo") => {} + ------------------ + | Branch (LL:34): [True: 1, False: 0] + ------------------ LL| 0| _ => panic!("unexpected value from resume"), LL| | } LL| | @@ -27,10 +37,18 @@ LL| 0| }; LL| | LL| 1| match Pin::new(&mut generator).resume(()) { + ------------------ + | Branch (LL:5): [True: 0, False: 1] + | Branch (LL:5): [True: 0, False: 1] + ------------------ LL| 1| GeneratorState::Yielded(1) => {} LL| 0| _ => panic!("unexpected value from resume"), LL| | } LL| 1| match Pin::new(&mut generator).resume(()) { + ------------------ + | Branch (LL:5): [True: 0, False: 1] + | Branch (LL:5): [True: 0, False: 1] + ------------------ LL| 1| GeneratorState::Yielded(2) => {} LL| 0| _ => panic!("unexpected value from resume"), LL| | }