Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 70 additions & 63 deletions src/run/runner/wall_time/perf/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,62 @@ impl PerfRunner {
Ok(())
}

#[cfg(target_os = "linux")]
fn process_memory_mappings(
pid: u32,
symbols_by_pid: &mut HashMap<u32, ProcessSymbols>,
unwind_data_by_pid: &mut HashMap<u32, Vec<UnwindData>>,
) -> anyhow::Result<()> {
use procfs::process::MMPermissions;

let bench_proc =
procfs::process::Process::new(pid as _).expect("Failed to find benchmark process");
let exe_maps = bench_proc.maps().expect("Failed to read /proc/{pid}/maps");

for map in &exe_maps {
let page_offset = map.offset;
let (base_addr, end_addr) = map.address;
let path = match &map.pathname {
procfs::process::MMapPath::Path(path) => Some(path.clone()),
_ => None,
};

if let Some(path) = &path {
symbols_by_pid
.entry(pid)
.or_insert(ProcessSymbols::new(pid))
.add_mapping(pid, path, base_addr, end_addr);
debug!("Added mapping for module {path:?}");

if map.perms.contains(MMPermissions::EXECUTE) {
match UnwindData::new(
path.to_string_lossy().as_bytes(),
page_offset,
base_addr,
end_addr - base_addr,
None,
) {
Ok(unwind_data) => {
unwind_data_by_pid.entry(pid).or_default().push(unwind_data);
debug!("Added unwind data for {path:?} ({base_addr:x} - {end_addr:x})");
}
Err(error) => {
debug!(
"Failed to create unwind data for module {}: {}",
path.display(),
error
);
}
}
}
} else if map.perms.contains(MMPermissions::EXECUTE) {
debug!("Found executable mapping without path: {base_addr:x} - {end_addr:x}");
}
}

Ok(())
}

async fn handle_fifo(
mut runner_fifo: RunnerFifo,
mut perf_fifo: PerfFifo,
Expand All @@ -239,67 +295,7 @@ impl PerfRunner {
FifoCommand::CurrentBenchmark { pid, uri } => {
bench_order_by_pid.entry(pid).or_default().push(uri);

#[cfg(target_os = "linux")]
if !symbols_by_pid.contains_key(&pid) && !unwind_data_by_pid.contains_key(&pid)
{
use procfs::process::MMPermissions;

let bench_proc = procfs::process::Process::new(pid as _)
.expect("Failed to find benchmark process");
let exe_maps = bench_proc.maps().expect("Failed to read /proc/{pid}/maps");

for map in &exe_maps {
let page_offset = map.offset;
let (base_addr, end_addr) = map.address;
let path = match &map.pathname {
procfs::process::MMapPath::Path(path) => Some(path.clone()),
_ => None,
};

if let Some(path) = &path {
symbols_by_pid
.entry(pid)
.or_insert(ProcessSymbols::new(pid))
.add_mapping(pid, path, base_addr, end_addr);
debug!("Added mapping for module {path:?}");

if map.perms.contains(MMPermissions::EXECUTE) {
match UnwindData::new(
path.to_string_lossy().as_bytes(),
page_offset,
base_addr,
end_addr - base_addr,
None,
) {
Ok(unwind_data) => {
unwind_data_by_pid
.entry(pid)
.or_default()
.push(unwind_data);
debug!(
"Added unwind data for {path:?} ({base_addr:x} - {end_addr:x})"
);
}
Err(error) => {
debug!(
"Failed to create unwind data for module {}: {}",
path.display(),
error
);
}
}
}
} else if map.perms.contains(MMPermissions::EXECUTE) {
debug!(
"Found executable mapping without path: {base_addr:x} - {end_addr:x}"
);
}
}
}

runner_fifo.send_cmd(FifoCommand::Ack).await?;
}
FifoCommand::StartBenchmark => {
// Split the perf.data file
let perf_pid = perf_pid.get_or_init(|| {
let output = std::process::Command::new("sh")
.arg("-c")
Expand All @@ -312,10 +308,21 @@ impl PerfRunner {
.parse::<u32>()
.expect("Failed to parse perf pid")
});

// Split the perf.data file
run_with_sudo(&["kill", "-USR2", &perf_pid.to_string()])?;

#[cfg(target_os = "linux")]
if !symbols_by_pid.contains_key(&pid) && !unwind_data_by_pid.contains_key(&pid)
{
Self::process_memory_mappings(
pid,
&mut symbols_by_pid,
&mut unwind_data_by_pid,
)?;
}

runner_fifo.send_cmd(FifoCommand::Ack).await?;
}
FifoCommand::StartBenchmark => {
perf_fifo.start_events().await?;
runner_fifo.send_cmd(FifoCommand::Ack).await?;
}
Expand Down
14 changes: 14 additions & 0 deletions src/run/runner/wall_time/perf/perf_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,20 @@ impl ModuleSymbols {
return Err(anyhow::anyhow!("No symbols found"));
}

// The base_addr from the mapping is where the module is actually loaded in memory (See ProcessSymbols::add_mapping),
// but the symbol addresses from the ELF file assume the module is loaded at its preferred virtual address. We need to:
// 1. Find the module's preferred base address from the ELF file or symbols
// 2. Calculate the offset: actual_base - preferred_base
// 3. Apply this offset to the symbol addresses

// Find the preferred base address from the minimum symbol address
let preferred_base = symbols.iter().map(|s| s.offset).min().unwrap_or(0) & !0xfff; // Align to page boundary

// Convert absolute addresses to relative offsets
for symbol in &mut symbols {
symbol.offset = symbol.offset.saturating_sub(preferred_base);
}

Ok(Self {
path: path.as_ref().to_path_buf(),
symbols,
Expand Down