From 36094bcec44c3c90fe79e6aa4623236560fd1932 Mon Sep 17 00:00:00 2001 From: not-matthias Date: Thu, 10 Jul 2025 15:31:51 +0200 Subject: [PATCH 1/3] fix: only split perf data when setting the executed benchmark --- src/run/runner/wall_time/perf/mod.rs | 31 ++++++++++++++-------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/run/runner/wall_time/perf/mod.rs b/src/run/runner/wall_time/perf/mod.rs index e8b0e68b..d0de918d 100644 --- a/src/run/runner/wall_time/perf/mod.rs +++ b/src/run/runner/wall_time/perf/mod.rs @@ -239,6 +239,21 @@ impl PerfRunner { FifoCommand::CurrentBenchmark { pid, uri } => { bench_order_by_pid.entry(pid).or_default().push(uri); + // Split the perf.data file + let perf_pid = perf_pid.get_or_init(|| { + let output = std::process::Command::new("sh") + .arg("-c") + .arg("pidof -s perf") + .output() + .expect("Failed to run pidof command"); + + String::from_utf8_lossy(&output.stdout) + .trim() + .parse::() + .expect("Failed to parse perf pid") + }); + 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) { @@ -300,22 +315,6 @@ impl PerfRunner { runner_fifo.send_cmd(FifoCommand::Ack).await?; } FifoCommand::StartBenchmark => { - let perf_pid = perf_pid.get_or_init(|| { - let output = std::process::Command::new("sh") - .arg("-c") - .arg("pidof -s perf") - .output() - .expect("Failed to run pidof command"); - - String::from_utf8_lossy(&output.stdout) - .trim() - .parse::() - .expect("Failed to parse perf pid") - }); - - // Split the perf.data file - run_with_sudo(&["kill", "-USR2", &perf_pid.to_string()])?; - perf_fifo.start_events().await?; runner_fifo.send_cmd(FifoCommand::Ack).await?; } From a6f64fccf0a26326bfd13ebc1a523fb7067f22be Mon Sep 17 00:00:00 2001 From: not-matthias Date: Thu, 10 Jul 2025 15:32:18 +0200 Subject: [PATCH 2/3] refactor: process memory mappings in separate function --- src/run/runner/wall_time/perf/mod.rs | 114 ++++++++++++++------------- 1 file changed, 61 insertions(+), 53 deletions(-) diff --git a/src/run/runner/wall_time/perf/mod.rs b/src/run/runner/wall_time/perf/mod.rs index d0de918d..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, @@ -257,59 +313,11 @@ impl PerfRunner { #[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}" - ); - } - } + Self::process_memory_mappings( + pid, + &mut symbols_by_pid, + &mut unwind_data_by_pid, + )?; } runner_fifo.send_cmd(FifoCommand::Ack).await?; From e2a27b22c2e947747461d32f4a5ded68df841480 Mon Sep 17 00:00:00 2001 From: not-matthias Date: Thu, 10 Jul 2025 17:23:18 +0200 Subject: [PATCH 3/3] fix: adjust offset for symbols of module loaded at preferred base --- src/run/runner/wall_time/perf/perf_map.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) 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,