Skip to content

Commit b187996

Browse files
--wip-- [skip ci]
1 parent 0944c73 commit b187996

13 files changed

Lines changed: 169 additions & 128 deletions

src/executor/wall_time/perf/debug_info.rs

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,8 @@ mod tests {
135135
fn test_golang_debug_info() {
136136
let (start_addr, end_addr, file_offset) =
137137
(0x0000000000402000_u64, 0x000000000050f000_u64, 0x2000);
138-
let module_symbols = ModuleSymbols::new(
138+
let module_symbols = ModuleSymbols::from_elf("testdata/perf_map/go_fib.bin").unwrap();
139+
let load_bias = ModuleSymbols::compute_load_bias(
139140
"testdata/perf_map/go_fib.bin",
140141
start_addr,
141142
end_addr,
@@ -145,7 +146,7 @@ mod tests {
145146
let module_debug_info = ModuleDebugInfo::from_symbols(
146147
"testdata/perf_map/go_fib.bin",
147148
&module_symbols,
148-
module_symbols.load_bias(),
149+
load_bias,
149150
)
150151
.unwrap();
151152
insta::assert_debug_snapshot!(module_debug_info.debug_infos);
@@ -155,7 +156,9 @@ mod tests {
155156
fn test_cpp_debug_info() {
156157
let (start_addr, end_addr, file_offset) =
157158
(0x0000000000400000_u64, 0x0000000000459000_u64, 0x0);
158-
let module_symbols = ModuleSymbols::new(
159+
let module_symbols =
160+
ModuleSymbols::from_elf("testdata/perf_map/cpp_my_benchmark.bin").unwrap();
161+
let load_bias = ModuleSymbols::compute_load_bias(
159162
"testdata/perf_map/cpp_my_benchmark.bin",
160163
start_addr,
161164
end_addr,
@@ -165,7 +168,7 @@ mod tests {
165168
let mut module_debug_info = ModuleDebugInfo::from_symbols(
166169
"testdata/perf_map/cpp_my_benchmark.bin",
167170
&module_symbols,
168-
module_symbols.load_bias(),
171+
load_bias,
169172
)
170173
.unwrap();
171174

@@ -178,29 +181,33 @@ mod tests {
178181
fn test_rust_divan_debug_info() {
179182
const MODULE_PATH: &str = "testdata/perf_map/divan_sleep_benches.bin";
180183

181-
let module_symbols =
182-
ModuleSymbols::new(MODULE_PATH, 0x00005555555a2000, 0x0000555555692000, 0x4d000)
183-
.unwrap();
184+
let module_symbols = ModuleSymbols::from_elf(MODULE_PATH).unwrap();
185+
let load_bias = ModuleSymbols::compute_load_bias(
186+
MODULE_PATH,
187+
0x00005555555a2000,
188+
0x0000555555692000,
189+
0x4d000,
190+
)
191+
.unwrap();
184192
let module_debug_info =
185-
ModuleDebugInfo::from_symbols(MODULE_PATH, &module_symbols, module_symbols.load_bias())
186-
.unwrap();
193+
ModuleDebugInfo::from_symbols(MODULE_PATH, &module_symbols, load_bias).unwrap();
187194
insta::assert_debug_snapshot!(module_debug_info.debug_infos);
188195
}
189196

190197
#[test]
191198
fn test_the_algorithms_debug_info() {
192199
const MODULE_PATH: &str = "testdata/perf_map/the_algorithms.bin";
193200

194-
let module_symbols = ModuleSymbols::new(
201+
let module_symbols = ModuleSymbols::from_elf(MODULE_PATH).unwrap();
202+
let load_bias = ModuleSymbols::compute_load_bias(
195203
MODULE_PATH,
196204
0x00005573e59fe000,
197205
0x00005573e5b07000,
198206
0x00052000,
199207
)
200208
.unwrap();
201209
let module_debug_info =
202-
ModuleDebugInfo::from_symbols(MODULE_PATH, &module_symbols, module_symbols.load_bias())
203-
.unwrap();
210+
ModuleDebugInfo::from_symbols(MODULE_PATH, &module_symbols, load_bias).unwrap();
204211
insta::assert_debug_snapshot!(module_debug_info.debug_infos);
205212
}
206213

@@ -210,11 +217,12 @@ mod tests {
210217

211218
let (start_addr, end_addr, file_offset) =
212219
(0x0000555555e6d000_u64, 0x0000555556813000_u64, 0x918000);
213-
let module_symbols =
214-
ModuleSymbols::new(MODULE_PATH, start_addr, end_addr, file_offset).unwrap();
215-
let module_debug_info =
216-
ModuleDebugInfo::from_symbols(MODULE_PATH, &module_symbols, module_symbols.load_bias())
220+
let module_symbols = ModuleSymbols::from_elf(MODULE_PATH).unwrap();
221+
let load_bias =
222+
ModuleSymbols::compute_load_bias(MODULE_PATH, start_addr, end_addr, file_offset)
217223
.unwrap();
224+
let module_debug_info =
225+
ModuleDebugInfo::from_symbols(MODULE_PATH, &module_symbols, load_bias).unwrap();
218226
insta::assert_debug_snapshot!(module_debug_info.debug_infos);
219227
}
220228
}

src/executor/wall_time/perf/save_artifacts.rs

Lines changed: 91 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -31,44 +31,44 @@ fn get_or_insert_key(path_to_key: &mut HashMap<PathBuf, String>, path: &Path) ->
3131
key
3232
}
3333

34-
/// Save all artifacts (symbols, debug info, unwind data) from mounted modules and JIT data.
35-
pub fn save_artifacts(
36-
profile_folder: &Path,
34+
/// Pre-register all paths from the mounted modules map.
35+
fn register_paths(
36+
path_to_key: &mut HashMap<PathBuf, String>,
3737
mounted_modules_by_path: &HashMap<PathBuf, MountedModule>,
38-
jit_unwind_data_by_pid: &HashMap<pid_t, Vec<(UnwindData, ProcessUnwindData)>>,
39-
) -> SavedArtifacts {
40-
let mut path_to_key = HashMap::<PathBuf, String>::new();
38+
) {
39+
for path in mounted_modules_by_path.keys() {
40+
get_or_insert_key(path_to_key, path);
41+
}
42+
}
4143

42-
// --- 1. Save symbol files and build per-pid symbol mappings ---
44+
/// Save deduplicated symbol files to disk and build per-pid mappings.
45+
fn save_symbols(
46+
profile_folder: &Path,
47+
mounted_modules_by_path: &HashMap<PathBuf, MountedModule>,
48+
path_to_key: &HashMap<PathBuf, String>,
49+
) -> HashMap<pid_t, Vec<ProcessModuleLoadBias>> {
4350
let symbols_count = mounted_modules_by_path
4451
.values()
4552
.filter(|m| m.module_symbols.is_some())
4653
.count();
4754
debug!("Saving symbols ({symbols_count} unique entries)");
4855

49-
// Pre-register all paths
50-
for path in mounted_modules_by_path.keys() {
51-
get_or_insert_key(&mut path_to_key, path);
52-
}
53-
54-
// Save symbol files in parallel
5556
mounted_modules_by_path.par_iter().for_each(|(path, m)| {
5657
if let Some(ref symbols) = m.module_symbols {
5758
let key = &path_to_key[path];
5859
symbols.save_to_keyed_file(profile_folder, key).unwrap();
5960
}
6061
});
6162

62-
// Build per-pid symbol mappings
63-
let mut symbol_pid_mappings_by_pid: HashMap<pid_t, Vec<ProcessModuleLoadBias>> = HashMap::new();
63+
let mut mappings_by_pid: HashMap<pid_t, Vec<ProcessModuleLoadBias>> = HashMap::new();
6464
for (path, m) in mounted_modules_by_path {
6565
if m.module_symbols.is_none() {
6666
continue;
6767
}
6868
let key = &path_to_key[path];
6969
for (&pid, pm) in &m.process_mounted_module {
7070
if let Some(load_bias) = pm.symbols_load_bias {
71-
symbol_pid_mappings_by_pid
71+
mappings_by_pid
7272
.entry(pid)
7373
.or_default()
7474
.push(ProcessModuleLoadBias {
@@ -78,19 +78,26 @@ pub fn save_artifacts(
7878
}
7979
}
8080
}
81-
// Sort each pid's mappings by key for deterministic output
82-
for mappings in symbol_pid_mappings_by_pid.values_mut() {
81+
for mappings in mappings_by_pid.values_mut() {
8382
mappings.sort_by(|a, b| a.perf_map_key.cmp(&b.perf_map_key));
8483
}
84+
mappings_by_pid
85+
}
8586

86-
// --- 2. Compute debug info and build per-pid debug info mappings ---
87+
/// Compute debug info from symbols and build per-pid debug info mappings.
88+
fn save_debug_info(
89+
mounted_modules_by_path: &HashMap<PathBuf, MountedModule>,
90+
path_to_key: &mut HashMap<PathBuf, String>,
91+
) -> (
92+
HashMap<String, ModuleDebugInfo>,
93+
HashMap<pid_t, Vec<DebugInfoPidMapping>>,
94+
) {
8795
debug!("Saving debug_info");
8896

8997
let debug_info_by_elf_path = debug_info_by_path(mounted_modules_by_path);
9098

91-
// Pre-register debug info paths
9299
for path in debug_info_by_elf_path.keys() {
93-
get_or_insert_key(&mut path_to_key, path);
100+
get_or_insert_key(path_to_key, path);
94101
}
95102

96103
let debug_info: HashMap<String, ModuleDebugInfo> = debug_info_by_elf_path
@@ -101,8 +108,7 @@ pub fn save_artifacts(
101108
})
102109
.collect();
103110

104-
let mut debug_info_pid_mappings_by_pid: HashMap<pid_t, Vec<DebugInfoPidMapping>> =
105-
HashMap::new();
111+
let mut mappings_by_pid: HashMap<pid_t, Vec<DebugInfoPidMapping>> = HashMap::new();
106112
for (path, m) in mounted_modules_by_path {
107113
if m.module_symbols.is_none() {
108114
continue;
@@ -112,7 +118,7 @@ pub fn save_artifacts(
112118
};
113119
for (&pid, pm) in &m.process_mounted_module {
114120
if let Some(load_bias) = pm.symbols_load_bias {
115-
debug_info_pid_mappings_by_pid
121+
mappings_by_pid
116122
.entry(pid)
117123
.or_default()
118124
.push(DebugInfoPidMapping {
@@ -122,41 +128,43 @@ pub fn save_artifacts(
122128
}
123129
}
124130
}
125-
for mappings in debug_info_pid_mappings_by_pid.values_mut() {
131+
for mappings in mappings_by_pid.values_mut() {
126132
mappings.sort_by(|a, b| a.debug_info_key.cmp(&b.debug_info_key));
127133
}
128134

129-
// --- 3. Save unwind data files and build per-pid unwind data mappings ---
135+
(debug_info, mappings_by_pid)
136+
}
137+
138+
/// Save deduplicated unwind data files to disk and build per-pid mappings,
139+
/// including JIT unwind data.
140+
fn save_unwind_data(
141+
profile_folder: &Path,
142+
mounted_modules_by_path: &HashMap<PathBuf, MountedModule>,
143+
jit_unwind_data_by_pid: &HashMap<pid_t, Vec<(UnwindData, ProcessUnwindData)>>,
144+
path_to_key: &mut HashMap<PathBuf, String>,
145+
) -> HashMap<pid_t, Vec<UnwindDataPidMapping>> {
130146
let unwind_data_count = mounted_modules_by_path
131147
.values()
132148
.filter(|m| m.unwind_data.is_some())
133149
.count();
134150
debug!("Saving unwind data ({unwind_data_count} unique entries)");
135151

136-
// Pre-register all paths for unwind data
137-
for path in mounted_modules_by_path.keys() {
138-
get_or_insert_key(&mut path_to_key, path);
139-
}
140-
141-
// Save unwind data files in parallel
142152
mounted_modules_by_path.par_iter().for_each(|(path, m)| {
143153
if let Some(ref unwind_data) = m.unwind_data {
144154
let key = &path_to_key[path];
145155
unwind_data.save_to(profile_folder, key).unwrap();
146156
}
147157
});
148158

149-
// Build per-pid unwind data mappings from mounted modules
150-
let mut unwind_data_pid_mappings_by_pid: HashMap<pid_t, Vec<UnwindDataPidMapping>> =
151-
HashMap::new();
159+
let mut mappings_by_pid: HashMap<pid_t, Vec<UnwindDataPidMapping>> = HashMap::new();
152160
for (path, m) in mounted_modules_by_path {
153161
if m.unwind_data.is_none() {
154162
continue;
155163
}
156164
let key = &path_to_key[path];
157165
for (&pid, pm) in &m.process_mounted_module {
158166
if let Some(ref pud) = pm.process_unwind_data {
159-
unwind_data_pid_mappings_by_pid
167+
mappings_by_pid
160168
.entry(pid)
161169
.or_default()
162170
.push(UnwindDataPidMapping {
@@ -173,9 +181,9 @@ pub fn save_artifacts(
173181
for (&pid, jit_entries) in jit_unwind_data_by_pid {
174182
for (unwind_data, process_unwind_data) in jit_entries {
175183
let jit_path = PathBuf::from(&unwind_data.path);
176-
let key = get_or_insert_key(&mut path_to_key, &jit_path);
184+
let key = get_or_insert_key(path_to_key, &jit_path);
177185
unwind_data.save_to(profile_folder, &key).unwrap();
178-
unwind_data_pid_mappings_by_pid
186+
mappings_by_pid
179187
.entry(pid)
180188
.or_default()
181189
.push(UnwindDataPidMapping {
@@ -187,27 +195,11 @@ pub fn save_artifacts(
187195
}
188196
}
189197

190-
for mappings in unwind_data_pid_mappings_by_pid.values_mut() {
198+
for mappings in mappings_by_pid.values_mut() {
191199
mappings.sort_by(|a, b| a.unwind_data_key.cmp(&b.unwind_data_key));
192200
}
193201

194-
// --- 4. Collect ignored modules ---
195-
let ignored_modules = collect_ignored_modules(mounted_modules_by_path);
196-
197-
// --- 5. Build key_to_path (inverted) ---
198-
let key_to_path = path_to_key
199-
.into_iter()
200-
.map(|(path, key)| (key, path))
201-
.collect();
202-
203-
SavedArtifacts {
204-
symbol_pid_mappings_by_pid,
205-
debug_info,
206-
debug_info_pid_mappings_by_pid,
207-
unwind_data_pid_mappings_by_pid,
208-
ignored_modules,
209-
key_to_path,
210-
}
202+
mappings_by_pid
211203
}
212204

213205
/// Collect ignored modules by finding known-ignored and python modules in the mounted modules.
@@ -221,7 +213,9 @@ fn collect_ignored_modules(
221213
for (path, m) in mounted_modules_by_path {
222214
let path_str = path.to_string_lossy();
223215

224-
let is_ignored = ignore_paths.iter().any(|ip| path_str.as_ref() == ip.as_str());
216+
let is_ignored = ignore_paths
217+
.iter()
218+
.any(|ip| path_str.as_ref() == ip.as_str());
225219
let is_python = path
226220
.file_name()
227221
.map(|name| name.to_string_lossy().starts_with("python"))
@@ -231,7 +225,6 @@ fn collect_ignored_modules(
231225
continue;
232226
}
233227

234-
// Collect address range across all pids for this module
235228
for pm in m.process_mounted_module.values() {
236229
if let Some(ref pud) = pm.process_unwind_data {
237230
to_ignore.push((
@@ -245,3 +238,43 @@ fn collect_ignored_modules(
245238

246239
to_ignore
247240
}
241+
242+
/// Save all artifacts (symbols, debug info, unwind data) from mounted modules and JIT data.
243+
pub fn save_artifacts(
244+
profile_folder: &Path,
245+
mounted_modules_by_path: &HashMap<PathBuf, MountedModule>,
246+
jit_unwind_data_by_pid: &HashMap<pid_t, Vec<(UnwindData, ProcessUnwindData)>>,
247+
) -> SavedArtifacts {
248+
let mut path_to_key = HashMap::<PathBuf, String>::new();
249+
250+
register_paths(&mut path_to_key, mounted_modules_by_path);
251+
252+
let symbol_pid_mappings_by_pid =
253+
save_symbols(profile_folder, mounted_modules_by_path, &path_to_key);
254+
255+
let (debug_info, debug_info_pid_mappings_by_pid) =
256+
save_debug_info(mounted_modules_by_path, &mut path_to_key);
257+
258+
let unwind_data_pid_mappings_by_pid = save_unwind_data(
259+
profile_folder,
260+
mounted_modules_by_path,
261+
jit_unwind_data_by_pid,
262+
&mut path_to_key,
263+
);
264+
265+
let ignored_modules = collect_ignored_modules(mounted_modules_by_path);
266+
267+
let key_to_path = path_to_key
268+
.into_iter()
269+
.map(|(path, key)| (key, path))
270+
.collect();
271+
272+
SavedArtifacts {
273+
symbol_pid_mappings_by_pid,
274+
debug_info,
275+
debug_info_pid_mappings_by_pid,
276+
unwind_data_pid_mappings_by_pid,
277+
ignored_modules,
278+
key_to_path,
279+
}
280+
}

src/executor/wall_time/perf/snapshots/codspeed_runner__executor__wall_time__perf__perf_map__tests__cpp_symbols.snap

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ source: src/executor/wall_time/perf/perf_map.rs
33
expression: module_symbols
44
---
55
ModuleSymbols {
6-
load_bias: 0,
76
symbols: [
87
Symbol { offset: 0, size: 4002f8, name: __gmon_start__ },
98
Symbol { offset: 4002f8, size: 20, name: __abi_tag },

src/executor/wall_time/perf/snapshots/codspeed_runner__executor__wall_time__perf__perf_map__tests__golang_symbols.snap

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ source: src/executor/wall_time/perf/perf_map.rs
33
expression: module_symbols
44
---
55
ModuleSymbols {
6-
load_bias: 0,
76
symbols: [
87
Symbol { offset: 0, size: 8, name: runtime.tlsg },
98
Symbol { offset: 0, size: 402000, name: seteuid },
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
version https://git-lfs.github.com/spec/v1
2-
oid sha256:b66645f8ee52271ff108bc73b69558cac3ed642c4ab9e101f99c4354b762f803
3-
size 5033521
2+
oid sha256:f65f0c08c8ee61c6fd4423c236845585d9ed6319a69beb3ad100a9f0537c8575
3+
size 5033490

0 commit comments

Comments
 (0)