Skip to content
Draft
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
105 changes: 103 additions & 2 deletions src/executor/valgrind/executor.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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<libc::pid_t>,
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::<Vec<_>>();

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 {
Expand Down Expand Up @@ -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.
//
Expand All @@ -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());
}
}
6 changes: 4 additions & 2 deletions src/executor/valgrind/helpers/perf_maps.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@ fn extract_pid_from_profile_file(path: &Path) -> Option<libc::pid_t> {
}
}

pub async fn harvest_perf_maps(profile_folder: &Path) -> Result<()> {
pub async fn harvest_perf_maps(profile_folder: &Path) -> Result<HashSet<libc::pid_t>> {
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::<HashSet<_>>();

harvest_perf_maps_for_pids(profile_folder, &pids).await
harvest_perf_maps_for_pids(profile_folder, &pids).await?;

Ok(pids)
}