diff --git a/src/executor/valgrind/executor.rs b/src/executor/valgrind/executor.rs index accd5b34a..f1651c3f3 100644 --- a/src/executor/valgrind/executor.rs +++ b/src/executor/valgrind/executor.rs @@ -1,5 +1,7 @@ use async_trait::async_trait; -use std::path::Path; +use std::collections::HashSet; +use std::fs; +use std::path::{Path, PathBuf}; use crate::executor::Executor; use crate::executor::ToolStatus; @@ -15,6 +17,47 @@ use super::{helpers::perf_maps::harvest_perf_maps, helpers::venv_compat, measure pub struct ValgrindExecutor; +fn harvest_jit_dumps( + profile_folder: &Path, + pids: &HashSet, + tmp_dir: &Path, + jit_dump_base_dir: Option<&Path>, +) -> Result<()> { + let mut jit_dump_paths = pids + .iter() + .map(|pid| tmp_dir.join(format!("jit-{pid}.dump"))) + .filter(|path| path.exists()) + .collect::>(); + + if let Some(base_dir) = jit_dump_base_dir { + let jit_dir = base_dir.join(".debug/jit"); + for entry in fs::read_dir(jit_dir).into_iter().flatten().flatten() { + for pid in pids { + let path = entry.path().join(format!("jit-{pid}.dump")); + if path.exists() { + jit_dump_paths.push(path); + } + } + } + } + + debug!("Found {} jit dumps", jit_dump_paths.len()); + for path in jit_dump_paths { + let Some(file_name) = path.file_name() else { + continue; + }; + fs::copy(&path, profile_folder.join(file_name)).with_context(|| { + format!( + "Failed to copy jit dump file: {:?} to {}", + file_name, + profile_folder.display() + ) + })?; + } + + Ok(()) +} + #[async_trait(?Send)] impl Executor for ValgrindExecutor { fn name(&self) -> ExecutorName { @@ -66,7 +109,27 @@ impl Executor for ValgrindExecutor { } async fn teardown(&self, execution_context: &ExecutionContext) -> Result<()> { - harvest_perf_maps(&execution_context.profile_folder).await?; + let pids = harvest_perf_maps(&execution_context.profile_folder).await?; + let jit_dump_base_dir = execution_context + .config + .extra_env + .get("JITDUMPDIR") + .map(PathBuf::from) + .or_else(|| std::env::var_os("JITDUMPDIR").map(PathBuf::from)) + .or_else(|| { + execution_context + .config + .extra_env + .get("HOME") + .map(PathBuf::from) + }) + .or_else(|| std::env::var_os("HOME").map(PathBuf::from)); + harvest_jit_dumps( + &execution_context.profile_folder, + &pids, + Path::new("/tmp"), + jit_dump_base_dir.as_deref(), + )?; // No matter the command in input, at this point valgrind will have been run and have produced output files. // @@ -78,3 +141,41 @@ impl Executor for ValgrindExecutor { Ok(()) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn harvests_jit_dumps_for_profile_pids() { + let temp_dir = tempfile::tempdir().unwrap(); + let profile_folder = temp_dir.path().join("profile"); + let tmp_dir = temp_dir.path().join("tmp"); + let jit_dump_base_dir = temp_dir.path().join("home"); + let llvm_jit_dir = jit_dump_base_dir.join(".debug/jit/llvm"); + fs::create_dir_all(&profile_folder).unwrap(); + fs::create_dir_all(&tmp_dir).unwrap(); + fs::create_dir_all(&llvm_jit_dir).unwrap(); + fs::write(tmp_dir.join("jit-123.dump"), b"tmp").unwrap(); + fs::write(llvm_jit_dir.join("jit-456.dump"), b"llvm").unwrap(); + fs::write(llvm_jit_dir.join("jit-789.dump"), b"untracked").unwrap(); + + harvest_jit_dumps( + &profile_folder, + &HashSet::from([123, 456]), + &tmp_dir, + Some(&jit_dump_base_dir), + ) + .unwrap(); + + assert_eq!( + fs::read(profile_folder.join("jit-123.dump")).unwrap(), + b"tmp" + ); + assert_eq!( + fs::read(profile_folder.join("jit-456.dump")).unwrap(), + b"llvm" + ); + assert!(!profile_folder.join("jit-789.dump").exists()); + } +} diff --git a/src/executor/valgrind/helpers/perf_maps.rs b/src/executor/valgrind/helpers/perf_maps.rs index 583ca2f95..f46a19433 100644 --- a/src/executor/valgrind/helpers/perf_maps.rs +++ b/src/executor/valgrind/helpers/perf_maps.rs @@ -15,12 +15,14 @@ fn extract_pid_from_profile_file(path: &Path) -> Option { } } -pub async fn harvest_perf_maps(profile_folder: &Path) -> Result<()> { +pub async fn harvest_perf_maps(profile_folder: &Path) -> Result> { let pids = fs::read_dir(profile_folder)? .filter_map(|entry| entry.ok()) .map(|entry| entry.path()) .filter_map(|path| extract_pid_from_profile_file(&path)) .collect::>(); - harvest_perf_maps_for_pids(profile_folder, &pids).await + harvest_perf_maps_for_pids(profile_folder, &pids).await?; + + Ok(pids) }