diff --git a/src/run/runner/wall_time/perf/mod.rs b/src/run/runner/wall_time/perf/mod.rs index e8b0e68b..f5d18499 100644 --- a/src/run/runner/wall_time/perf/mod.rs +++ b/src/run/runner/wall_time/perf/mod.rs @@ -213,6 +213,62 @@ impl PerfRunner { Ok(()) } + #[cfg(target_os = "linux")] + fn process_memory_mappings( + pid: u32, + symbols_by_pid: &mut HashMap, + unwind_data_by_pid: &mut HashMap>, + ) -> 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, @@ -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") @@ -312,10 +308,21 @@ impl PerfRunner { .parse::() .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?; } diff --git a/src/run/runner/wall_time/perf/perf_map.rs b/src/run/runner/wall_time/perf/perf_map.rs index 3bc2ba65..767e2925 100644 --- a/src/run/runner/wall_time/perf/perf_map.rs +++ b/src/run/runner/wall_time/perf/perf_map.rs @@ -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,