llvm_profparser/coverage/
mod.rs

1use nom::IResult;
2use rustc_hash::FxHashMap;
3use std::convert::TryFrom;
4use std::path::PathBuf;
5
6use crate::instrumentation_profile::types::MCDCParams;
7
8pub mod coverage_mapping;
9pub mod reporting;
10
11#[derive(Debug, Clone, Eq, PartialEq)]
12pub struct CoverageMappingInfo {
13    pub cov_map: FxHashMap<u64, Vec<PathBuf>>,
14    pub cov_fun: Vec<FunctionRecordV3>,
15    pub prof_counts: Option<Vec<u64>>,
16    pub prof_data: Option<Vec<ProfileData>>,
17}
18
19impl CoverageMappingInfo {
20    /// Gets the files for a given ID converted to their absolute representation
21    pub fn get_files_from_id(&self, id: u64) -> Vec<PathBuf> {
22        let mut paths = vec![];
23        if let Some(v) = self.cov_map.get(&id) {
24            let mut last_absolute = None;
25            for path in v.iter() {
26                if path.is_absolute() {
27                    // Currently all examples I've checked have the base path as first arg and any
28                    // paths not in that directory are an absolute path. Now thread/local.rs in the
29                    // rust std is given an absolute path that doesn't exist on the system (I guess
30                    // it's compiled elsewhere). And also due to not having remapping info paths
31                    // may not be present. Meaning we can't use existence as a requirement to see
32                    // if it's a directory or not. And I'd rather not do name based heuristics so
33                    // just taking the first absolute path as the folder path and hoping LLVM keeps
34                    // to that convention
35                    if last_absolute.is_none() {
36                        last_absolute = Some(path.clone());
37                    }
38                    paths.push(path.clone());
39                } else {
40                    let base = last_absolute.clone().unwrap_or_default();
41                    paths.push(base.join(path))
42                }
43            }
44        }
45        paths
46    }
47}
48
49#[derive(Debug, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
50pub struct ProfileData {
51    pub name_md5: u64,
52    pub structural_hash: u64,
53    pub counters_len: u32,
54}
55
56/// This is the type of a counter expression. The equivalent type in llvm would be
57/// `CounterExpression::ExprKind` an inner enum.
58#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd)]
59pub enum ExprKind {
60    /// Subtracts a counter from another
61    Subtract,
62    /// Adds a counter to another
63    Add,
64}
65
66/// Defines what type of region a counter maps to. The equivalent type in llvm would be
67/// `CounterMappingRegion::RegionKind`.
68#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd)]
69pub enum RegionKind {
70    /// A Code Region associates some code with a counter
71    Code = 0,
72    /// An Expansion Region represents a file expansion region that associates a source range with
73    /// the expansion of a virtual source file, such as for a macro instantiation or include file
74    Expansion = 1,
75    /// A Skipped  Region represents a source range with code that was skipped by a preprocessor or
76    /// similar means
77    Skipped = 2,
78    /// A Gap Region is like a Code Region but its count is only set as the line execution count
79    /// when its the only region in the line
80    Gap = 3,
81    /// A Branch Region represents lead-level boolean expressions and is associated with two
82    /// counters, each representing the number of times the expression evaluates to true or false.
83    Branch = 4,
84    /// A DecisionRegion represents a top-level boolean expression and is
85    /// associated with a variable length bitmap index and condition number.
86    MCDCDecision = 5,
87    /// A Branch Region can be extended to include IDs to facilitate MC/DC.
88    MCDCBranch = 6,
89}
90
91impl TryFrom<u64> for RegionKind {
92    type Error = u64;
93
94    fn try_from(v: u64) -> Result<Self, Self::Error> {
95        match v {
96            0 => Ok(RegionKind::Code),
97            1 => Ok(RegionKind::Expansion),
98            2 => Ok(RegionKind::Skipped),
99            3 => Ok(RegionKind::Gap),
100            4 => Ok(RegionKind::Branch),
101            5 => Ok(RegionKind::MCDCDecision),
102            6 => Ok(RegionKind::MCDCBranch),
103            e => Err(e),
104        }
105    }
106}
107
108/// Represents the type of a counter. The equivalent type in llvm would be `Counter::CounterKind`.
109#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
110pub enum CounterType {
111    Zero,
112    ProfileInstrumentation,
113    Expression(ExprKind),
114}
115
116impl Default for CounterType {
117    fn default() -> Self {
118        Self::Zero
119    }
120}
121
122/// A `Counter` is an abstract value that describes how to compute the execution count for a region
123/// of code using the collected profile count data. The equivalent type in llvm would be `Counter`.
124#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd)]
125pub struct Counter {
126    /// Type of the counter present
127    pub kind: CounterType,
128    /// A valid counter ID, if this counter isn't expected to have an ID then the ID must be zero.
129    pub id: u64,
130}
131
132impl Counter {
133    pub fn instrumentation(id: u64) -> Self {
134        Self {
135            kind: CounterType::ProfileInstrumentation,
136            id,
137        }
138    }
139
140    pub fn is_expression(&self) -> bool {
141        matches!(self.kind, CounterType::Expression(_))
142    }
143
144    pub fn is_instrumentation(&self) -> bool {
145        self.kind == CounterType::ProfileInstrumentation
146    }
147
148    pub fn is_zero(&self) -> bool {
149        self.kind == CounterType::Zero
150    }
151
152    /// Gets the kind of the expression
153    ///
154    /// # Panics
155    ///
156    /// Panics if not an kind of `CounterType::Expression`
157    pub fn get_expr_kind(&self) -> ExprKind {
158        match self.kind {
159            CounterType::Expression(e) => e,
160            _ => panic!("Counter is not an expression"),
161        }
162    }
163}
164
165/// A counter expression is a value that represents an arithmetic operation between two counters.
166/// The equivalent llvm type would be `CounterExpression`.
167#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd)]
168pub struct Expression {
169    pub kind: ExprKind,
170    pub lhs: Counter,
171    pub rhs: Counter,
172}
173
174impl Default for Expression {
175    fn default() -> Self {
176        Expression::new(Counter::default(), Counter::default())
177    }
178}
179
180impl Expression {
181    pub fn new(lhs: Counter, rhs: Counter) -> Self {
182        Self {
183            kind: ExprKind::Subtract,
184            lhs,
185            rhs,
186        }
187    }
188
189    pub fn set_kind(&mut self, kind: ExprKind) {
190        self.kind = kind;
191    }
192}
193
194impl Counter {
195    const ENCODING_TAG_BITS: u64 = 2;
196    const ENCODING_TAG_MASK: u64 = 3;
197    const ENCODING_TAG_AND_EXP_REGION_BITS: u64 = 3;
198    const ENCODING_EXPANSION_REGION_BIT: u64 = 4;
199}
200
201/// Associates a source code reader with a specific counter. The equivalent type in llvm would be
202/// `CounterMappingRegion`.
203#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
204pub struct CounterMappingRegion {
205    pub kind: RegionKind,
206    /// Primary counter that is also used for true branches
207    pub count: Counter,
208    /// Secondary counter that is also used for false branches
209    pub false_count: Counter,
210    pub file_id: usize,
211    pub expanded_file_id: usize,
212    pub loc: SourceLocation,
213    pub mcdc_params: Option<MCDCParams>,
214}
215
216/// Refers to a location in the source code
217#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
218pub struct SourceLocation {
219    /// The start line of the coverage region
220    pub line_start: usize,
221    /// The start column of the coverage region
222    pub column_start: usize,
223    /// The last line of the coverage region (inclusive)
224    pub line_end: usize,
225    /// The last column of the coverage region (inclusive)
226    pub column_end: usize,
227}
228
229/// The execution count information starting at a point in a file. A sequence of execution counters
230/// for a file in a format hat's simple to iterate over for processing. The equivalent llvm type is
231/// `CoverageSegment`.
232#[derive(Debug, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
233pub struct CoverageSegment {
234    /// The line the segment begins
235    pub line: usize,
236    /// The column the segment begins
237    pub col: usize,
238    /// The execution count, or zero if not executed
239    pub count: usize,
240    /// When false the segment is not instrumented or skipped
241    pub has_count: bool,
242    /// whether this enters a new region or returns to a previous count
243    pub is_region_entry: usize,
244    /// Whether this enters a gap region
245    pub is_gap_region: usize,
246}
247
248#[derive(Debug, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
249pub struct FunctionRecordHeader {
250    /// Truncated MD5 hash of the function name
251    pub name_hash: u64,
252    /// Length of the instrumentation data associated with the function
253    pub data_len: u32,
254    /// Function hash can be zero if the function isn't in the compiled binary - such as unused
255    /// generic functions
256    pub fn_hash: u64,
257    /// Hash reference of the file the function is defined in
258    pub filenames_ref: u64,
259}
260
261/// This type contains a header showing which function it refers to and then a list of regions in
262/// that function and a list of expressions. The expression IDs in the counter mapping region refer
263/// to indexes in the expressions list.
264#[derive(Debug, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
265pub struct FunctionRecordV3 {
266    pub header: FunctionRecordHeader,
267    pub regions: Vec<CounterMappingRegion>,
268    pub expressions: Vec<Expression>,
269}
270
271/// Coverage mapping information for a single function. The equivalent llvm type is
272/// `CoverageMappingRecord`.
273pub struct CoverageMappingRecord {
274    pub fn_name: String,
275    pub fn_hash: u64,
276    pub file_names: Vec<String>,
277    pub expressions: Vec<Expression>,
278    pub mapping_regions: Vec<CounterMappingRegion>,
279}
280
281/// Associates a source range with a specific counter. The equivalent llvm type is `CountedRegion`.
282pub struct CountedRegion {
283    pub execution_count: usize,
284    pub false_execution_count: usize,
285    pub folded: bool,
286    pub region: CounterMappingRegion,
287}
288
289/// This is the code coverage information for a single function. It is equivalent to
290/// `FunctionRecord` but has been renamed to avoid confusion with `FunctionRecordV3` etc
291pub struct FunctionCoverageRecord {
292    /// Raw function name
293    pub name: String,
294    /// This is a list to allow for macro expansions within a function where the macro is defined
295    /// in a different source file
296    pub filenames: Vec<String>,
297    /// regions in the function with their counts
298    pub counted_regions: Vec<CountedRegion>,
299    /// Branch regions with their counts
300    pub counted_branch_regions: Vec<CountedRegion>,
301    /// Number of times the function was executed
302    pub execution_count: usize,
303}