llvm_profparser/coverage/
mod.rs

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