1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
mod leb128;
mod llvm_coverage;
#[cfg(feature = "serde_json_serializer")]
mod serialized;
use std::collections::HashMap;
use std::convert::TryFrom;
use std::path::{Path, PathBuf};
use self::llvm_coverage::{get_counters, get_prf_data, read_covmap, Coverage, LLVMCovSections};
use crate::traits::{SaveToStatsFolder, Sensor};
pub struct CodeCoverageSensor {
pub(crate) coverage: Vec<Coverage>,
needs_clearing: Vec<usize>,
pub count_instrumented: usize,
}
impl CodeCoverageSensor {
#[no_coverage]
pub fn observing_only_files_from_current_dir() -> Self {
Self::new(
#[no_coverage]
|f| f.is_relative(),
)
}
#[no_coverage]
pub fn new<K>(keep: K) -> Self
where
K: Fn(&Path) -> bool,
{
let exec = std::env::current_exe().expect("could not read current executable");
let LLVMCovSections {
covfun,
covmap,
prf_names,
} = llvm_coverage::get_llvm_cov_sections(&exec).expect("could not find all relevant LLVM coverage sections");
let prf_data = unsafe { get_prf_data() };
let covmap = read_covmap(&covmap, &mut 0).expect("failed to parse LLVM covmap");
let prf_names = llvm_coverage::read_prf_names(&prf_names, &mut 0).expect("failed to parse LLVM prf_names");
let mut map = HashMap::new();
for prf_name in prf_names {
let name_md5 = md5::compute(prf_name.as_bytes());
let name_md5 = i64::from_le_bytes(<[u8; 8]>::try_from(&name_md5[0..8]).unwrap());
map.insert(name_md5, prf_name);
}
let covfun = llvm_coverage::read_covfun(&covfun).expect("failed to parse LLVM covfun");
let covfun = llvm_coverage::filter_covfun(covfun, map, &covmap, keep);
let covfun = llvm_coverage::process_function_records(covfun);
let prf_data = llvm_coverage::read_prf_data(prf_data).expect("failed to parse LLVM prf_data");
let mut coverage = Coverage::new(covfun, prf_data, unsafe { get_counters() })
.expect("failed to properly link the different LLVM coverage sections");
coverage.retain(
#[no_coverage]
|coverage| {
!(coverage.single_counters.is_empty()
|| (coverage.single_counters.len() + coverage.expression_counters.len() < 1))
},
);
let mut count_instrumented = 0;
for coverage in coverage.iter() {
count_instrumented += coverage.single_counters.len() + coverage.expression_counters.len();
}
let needs_clearing = (0..coverage.len()).collect();
CodeCoverageSensor {
coverage,
needs_clearing,
count_instrumented,
}
}
#[no_coverage]
unsafe fn clear(&mut self) {
for &coverage_idx in &self.needs_clearing {
let coverage = &self.coverage[coverage_idx];
let slice = std::slice::from_raw_parts_mut(coverage.start_counters, coverage.counters_len);
for c in slice.iter_mut() {
*c = 0;
}
}
self.needs_clearing.clear();
}
}
impl Sensor for CodeCoverageSensor {
type Observations = Vec<(usize, u64)>;
#[no_coverage]
fn start_recording(&mut self) {
unsafe {
self.clear();
}
}
#[no_coverage]
fn stop_recording(&mut self) {}
#[no_coverage]
fn get_observations(&mut self) -> Self::Observations {
self.needs_clearing.clear();
let mut observations = Vec::with_capacity(self.count_instrumented);
unsafe {
let CodeCoverageSensor { coverage, .. } = self;
let mut index = 0;
let mut old_observations_len = 0;
for (i, coverage) in coverage.iter().enumerate() {
for &single in coverage.single_counters.iter() {
if *single != 0 {
observations.push((index, *single));
}
index += 1;
}
for expr in &coverage.expression_counters {
let computed = expr.compute();
if computed != 0 {
observations.push((index, computed));
}
index += 1;
}
if observations.len() != old_observations_len {
self.needs_clearing.push(i);
old_observations_len = observations.len();
}
}
}
observations
}
}
impl SaveToStatsFolder for CodeCoverageSensor {
#[no_coverage]
fn save_to_stats_folder(&self) -> Vec<(PathBuf, Vec<u8>)> {
cfg_if::cfg_if! {
if #[cfg(feature = "serde_json_serializer")] {
let coverage_map = self.coverage_map();
let content = serde_json::to_vec(&coverage_map).unwrap();
vec![(PathBuf::new().join("coverage_sensor.json"), content)]
} else {
vec![]
}
}
}
}