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}