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}