Skip to content

Commit eaf949b

Browse files
committed
refactor: discover jitdump paths from MMAP2 records
Instead of searching hardcoded filesystem paths, extract jitdump file paths from MMAP2 records in the perf data. This is how perf inject finds them and works for any jitdump location.
1 parent a8e339a commit eaf949b

3 files changed

Lines changed: 39 additions & 56 deletions

File tree

src/executor/wall_time/perf/jit_dump.rs

Lines changed: 10 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use crate::{
55
use linux_perf_data::jitdump::{JitDumpReader, JitDumpRecord};
66
use runner_shared::unwind_data::{ProcessUnwindData, UnwindData};
77
use std::{
8-
collections::{HashMap, HashSet},
8+
collections::HashMap,
99
path::{Path, PathBuf},
1010
};
1111

@@ -108,44 +108,10 @@ impl JitDump {
108108
}
109109
}
110110

111-
/// Finds all jitdump file paths for a given PID.
111+
/// Converts all the `jit-<pid>.dump` into a perf-<pid>.map with symbols, and collects the unwind data.
112112
///
113-
/// Searches in order:
114-
/// 1. `/tmp/jit-{pid}.dump` (standard perf jitdump location)
115-
/// 2. `{jitdumpdir}/.debug/jit/*/jit-{pid}.dump` where `jitdumpdir` is `$JITDUMPDIR` or `$HOME`
116-
/// (LLVM ORC JIT / PerfSupportPlugin location)
117-
fn find_jit_dumps_for_pid(pid: libc::pid_t) -> Vec<PathBuf> {
118-
let name = format!("jit-{pid}.dump");
119-
let mut paths = Vec::new();
120-
121-
// Standard perf location.
122-
let tmp_path = PathBuf::from("/tmp").join(&name);
123-
if tmp_path.exists() {
124-
paths.push(tmp_path);
125-
}
126-
127-
// LLVM ORC JIT location: {base}/.debug/jit/*/jit-{pid}.dump
128-
// See LLVM's JITLoaderPerf.cpp for the path construction logic.
129-
let base_dir = std::env::var("JITDUMPDIR")
130-
.or_else(|_| std::env::var("HOME"))
131-
.ok()
132-
.map(PathBuf::from);
133-
if let Some(base) = base_dir {
134-
let jit_dir = base.join(".debug/jit");
135-
if let Ok(entries) = std::fs::read_dir(&jit_dir) {
136-
for entry in entries.filter_map(|e| e.ok()) {
137-
let candidate = entry.path().join(&name);
138-
if candidate.exists() {
139-
paths.push(candidate);
140-
}
141-
}
142-
}
143-
}
144-
145-
paths
146-
}
147-
148-
/// Converts all the `jit-<pid>.dump` into a perf-<pid>.map with symbols, and collects the unwind data
113+
/// Jitdump file paths are discovered from MMAP2 records in the perf data, since JIT runtimes
114+
/// mmap the jitdump file and perf records the mapping with the actual path on disk.
149115
///
150116
/// # Symbols
151117
/// Since a jit dump is by definition specific to a single pid, we append the harvested symbols
@@ -155,16 +121,11 @@ fn find_jit_dumps_for_pid(pid: libc::pid_t) -> Vec<PathBuf> {
155121
/// Unwind data is generated as a list
156122
pub async fn save_symbols_and_harvest_unwind_data_for_pids(
157123
profile_folder: &Path,
158-
pids: &HashSet<libc::pid_t>,
124+
jit_dump_paths_by_pid: &HashMap<libc::pid_t, Vec<PathBuf>>,
159125
) -> Result<HashMap<i32, Vec<(UnwindData, ProcessUnwindData)>>> {
160-
let mut jit_unwind_data_by_path = HashMap::new();
161-
162-
for pid in pids {
163-
let paths = find_jit_dumps_for_pid(*pid);
164-
if paths.is_empty() {
165-
continue;
166-
}
126+
let mut jit_unwind_data_by_pid = HashMap::new();
167127

128+
for (pid, paths) in jit_dump_paths_by_pid {
168129
for path in paths {
169130
debug!("Found JIT dump file: {path:?}");
170131

@@ -178,20 +139,20 @@ pub async fn save_symbols_and_harvest_unwind_data_for_pids(
178139

179140
symbols.append_to_file(profile_folder.join(format!("perf-{pid}.map")))?;
180141

181-
let jit_unwind_data = match JitDump::new(path).into_unwind_data() {
142+
let jit_unwind_data = match JitDump::new(path.clone()).into_unwind_data() {
182143
Ok(data) => data,
183144
Err(error) => {
184145
warn!("Failed to convert jit dump into unwind data: {error:?}");
185146
continue;
186147
}
187148
};
188149

189-
jit_unwind_data_by_path
150+
jit_unwind_data_by_pid
190151
.entry(*pid)
191152
.or_insert_with(Vec::new)
192153
.extend(jit_unwind_data);
193154
}
194155
}
195156

196-
Ok(jit_unwind_data_by_path)
157+
Ok(jit_unwind_data_by_pid)
197158
}

src/executor/wall_time/perf/mod.rs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,7 @@ impl BenchmarkData {
300300
let MemmapRecordsOutput {
301301
loaded_modules_by_path,
302302
tracked_pids,
303+
jit_dump_paths_by_pid,
303304
} = {
304305
parse_perf_file::parse_for_memmap2(perf_file_path, pid_filter).map_err(|e| {
305306
error!("Failed to parse perf file: {e}");
@@ -317,13 +318,15 @@ impl BenchmarkData {
317318
error!("Failed to harvest perf maps: {e}");
318319
BenchmarkDataSaveError::FailedToHarvestPerfMaps
319320
})?;
320-
let jit_unwind_data_by_pid =
321-
jit_dump::save_symbols_and_harvest_unwind_data_for_pids(path_ref, &tracked_pids)
322-
.await
323-
.map_err(|e| {
324-
error!("Failed to harvest jit dumps: {e}");
325-
BenchmarkDataSaveError::FailedToHarvestJitDumps
326-
})?;
321+
let jit_unwind_data_by_pid = jit_dump::save_symbols_and_harvest_unwind_data_for_pids(
322+
path_ref,
323+
&jit_dump_paths_by_pid,
324+
)
325+
.await
326+
.map_err(|e| {
327+
error!("Failed to harvest jit dumps: {e}");
328+
BenchmarkDataSaveError::FailedToHarvestJitDumps
329+
})?;
327330

328331
let artifacts = save_artifacts::save_artifacts(
329332
path_ref,

src/executor/wall_time/perf/parse_perf_file.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ pub struct MemmapRecordsOutput {
4242
/// Module symbols and the computed load bias for each pid that maps the ELF path.
4343
pub loaded_modules_by_path: HashMap<PathBuf, LoadedModule>,
4444
pub tracked_pids: HashSet<pid_t>,
45+
/// Jitdump file paths discovered from MMAP2 records, keyed by PID.
46+
pub jit_dump_paths_by_pid: HashMap<pid_t, Vec<PathBuf>>,
4547
}
4648

4749
/// Parse the perf file at `perf_file_path` and look for MMAP2 records for the given `pids`.
@@ -53,6 +55,7 @@ pub fn parse_for_memmap2<P: AsRef<Path>>(
5355
mut pid_filter: PidFilter,
5456
) -> Result<MemmapRecordsOutput> {
5557
let mut loaded_modules_by_path = HashMap::<PathBuf, LoadedModule>::new();
58+
let mut jit_dump_paths_by_pid = HashMap::<pid_t, Vec<PathBuf>>::new();
5659

5760
// 1MiB buffer
5861
let reader = std::io::BufReader::with_capacity(
@@ -105,6 +108,21 @@ pub fn parse_for_memmap2<P: AsRef<Path>>(
105108
continue;
106109
}
107110

111+
// Collect jitdump file paths before the PROT_EXEC filter in process_mmap2_record
112+
// skips them. JIT runtimes mmap the jitdump file so perf records it.
113+
if mmap2_record.path.as_slice().ends_with(b".dump") {
114+
let path = PathBuf::from(
115+
String::from_utf8_lossy(&mmap2_record.path.as_slice()).into_owned(),
116+
);
117+
if path.exists() {
118+
debug!("Found jitdump path from MMAP2 record: {path:?}");
119+
jit_dump_paths_by_pid
120+
.entry(mmap2_record.pid)
121+
.or_default()
122+
.push(path);
123+
}
124+
}
125+
108126
process_mmap2_record(mmap2_record, &mut loaded_modules_by_path);
109127
}
110128
_ => continue,
@@ -123,6 +141,7 @@ pub fn parse_for_memmap2<P: AsRef<Path>>(
123141
Ok(MemmapRecordsOutput {
124142
loaded_modules_by_path,
125143
tracked_pids,
144+
jit_dump_paths_by_pid,
126145
})
127146
}
128147

0 commit comments

Comments
 (0)