• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

xd009642 / llvm-profparser / #206

07 Aug 2025 02:14PM UTC coverage: 69.151% (-0.3%) from 69.494%
#206

push

web-flow
support mc/dc mapping regions in profdata files (#52)

2 of 27 new or added lines in 2 files covered. (7.41%)

13 existing lines in 6 files now uncovered.

1206 of 1744 relevant lines covered (69.15%)

3.4 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

64.1
/src/coverage/mod.rs
1
use nom::IResult;
2
use rustc_hash::FxHashMap;
3
use std::convert::TryFrom;
4
use std::path::PathBuf;
5

6
use crate::instrumentation_profile::types::MCDCParams;
7

8
pub mod coverage_mapping;
9
pub mod reporting;
10

11
#[derive(Debug, Clone, Eq, PartialEq)]
12
pub 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

19
impl 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> {
1✔
22
        let mut paths = vec![];
1✔
23
        if let Some(v) = self.cov_map.get(&id) {
2✔
24
            let mut last_absolute = None;
1✔
25
            for path in v.iter() {
2✔
26
                if path.is_absolute() {
2✔
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() {
3✔
36
                        last_absolute = Some(path.clone());
1✔
37
                    }
38
                    paths.push(path.clone());
2✔
39
                } else {
40
                    let base = last_absolute.clone().unwrap_or_default();
2✔
41
                    paths.push(base.join(path))
2✔
42
                }
43
            }
44
        }
45
        paths
1✔
46
    }
47
}
48

49
#[derive(Debug, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
50
pub 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)]
59
pub 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)]
69
pub 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

91
impl 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),
×
NEW
101
            5 => Ok(RegionKind::MCDCDecision),
×
NEW
102
            6 => Ok(RegionKind::MCDCBranch),
×
UNCOV
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)]
110
pub enum CounterType {
111
    Zero,
112
    ProfileInstrumentation,
113
    Expression(ExprKind),
114
}
115

116
impl Default for CounterType {
117
    fn default() -> Self {
118
        Self::Zero
1✔
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)]
125
pub 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

132
impl Counter {
133
    pub fn instrumentation(id: u64) -> Self {
1✔
134
        Self {
135
            kind: CounterType::ProfileInstrumentation,
136
            id,
137
        }
138
    }
139

140
    pub fn is_expression(&self) -> bool {
1✔
141
        matches!(self.kind, CounterType::Expression(_))
1✔
142
    }
143

144
    pub fn is_instrumentation(&self) -> bool {
1✔
145
        self.kind == CounterType::ProfileInstrumentation
1✔
146
    }
147

148
    pub fn is_zero(&self) -> bool {
1✔
149
        self.kind == CounterType::Zero
1✔
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)]
168
pub struct Expression {
169
    pub kind: ExprKind,
170
    pub lhs: Counter,
171
    pub rhs: Counter,
172
}
173

174
impl Default for Expression {
175
    fn default() -> Self {
1✔
176
        Expression::new(Counter::default(), Counter::default())
1✔
177
    }
178
}
179

180
impl Expression {
181
    pub fn new(lhs: Counter, rhs: Counter) -> Self {
1✔
182
        Self {
183
            kind: ExprKind::Subtract,
184
            lhs,
185
            rhs,
186
        }
187
    }
188

189
    pub fn set_kind(&mut self, kind: ExprKind) {
1✔
190
        self.kind = kind;
1✔
191
    }
192
}
193

194
impl 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)]
204
pub 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)]
218
pub 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)]
233
pub 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)]
249
pub 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)]
265
pub 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`.
273
pub 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`.
282
pub 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
291
pub 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
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc