diff --git a/src/run/runner/helpers/apt.rs b/src/run/runner/helpers/apt.rs index 5b363331..252caee3 100644 --- a/src/run/runner/helpers/apt.rs +++ b/src/run/runner/helpers/apt.rs @@ -4,6 +4,8 @@ use crate::run::check_system::SystemInfo; use std::path::Path; use std::process::Command; +const METADATA_FILENAME: &str = "./tmp/codspeed-cache-metadata.txt"; + fn is_system_compatible(system_info: &SystemInfo) -> bool { system_info.os == "ubuntu" || system_info.os == "debian" } @@ -92,7 +94,7 @@ pub fn install(system_info: &SystemInfo, packages: &[&str]) -> Result<()> { ); } - debug!("Installing packages: {packages:?}"); + info!("Installing packages: {}", packages.join(", ")); run_with_sudo(&["apt-get", "update"])?; let mut install_cmd = vec!["apt-get", "install", "-y", "--allow-downgrades"]; @@ -130,6 +132,24 @@ fn restore_from_cache(system_info: &SystemInfo, cache_dir: &Path) -> Result<()> cache_dir.display() ); + // Read and log the metadata file if it exists + let metadata_path = cache_dir.join(METADATA_FILENAME); + if metadata_path.exists() { + match std::fs::read_to_string(&metadata_path) { + Ok(content) => { + info!( + "Packages restored from cache: {}", + content.lines().join(", ") + ); + } + Err(e) => { + warn!("Failed to read metadata file: {e}"); + } + } + } else { + debug!("No metadata file found in cache directory"); + } + // Use bash to properly handle glob expansion let cache_dir_str = cache_dir .to_str() @@ -183,6 +203,27 @@ fn save_to_cache(system_info: &SystemInfo, cache_dir: &Path, packages: &[&str]) bail!("Failed to save packages to cache"); } + // Create metadata file containing the installed packages + let metadata_path = cache_dir.join(METADATA_FILENAME); + let metadata_content = packages.join("\n"); // TODO: add package versions as well, by using the output of the install command for example + if let Ok(()) = std::fs::create_dir_all(metadata_path.parent().unwrap()) { + if let Ok(()) = + std::fs::write(&metadata_path, metadata_content).context("Failed to write metadata file") + { + debug!("Metadata file created at: {}", metadata_path.display()); + } else { + warn!( + "Failed to create metadata file at: {}", + metadata_path.display() + ); + } + } else { + warn!( + "Failed to create metadata file parent directory for: {}", + metadata_path.display() + ); + } + debug!("Packages cached successfully"); Ok(()) } diff --git a/src/run/runner/valgrind/setup.rs b/src/run/runner/valgrind/setup.rs index 7859d4e3..1c08e636 100644 --- a/src/run/runner/valgrind/setup.rs +++ b/src/run/runner/valgrind/setup.rs @@ -31,11 +31,16 @@ fn is_valgrind_installed() -> bool { .output() .is_ok_and(|output| output.status.success()); if !is_valgrind_installed { + debug!("valgrind is not installed"); return false; } if let Ok(version_output) = Command::new("valgrind").arg("--version").output() { if !version_output.status.success() { + debug!( + "Failed to get valgrind version. stderr: {}", + String::from_utf8_lossy(&version_output.stderr) + ); return false; } diff --git a/src/run/runner/wall_time/perf/mod.rs b/src/run/runner/wall_time/perf/mod.rs index 0aa1dbb9..73a09578 100644 --- a/src/run/runner/wall_time/perf/mod.rs +++ b/src/run/runner/wall_time/perf/mod.rs @@ -9,6 +9,7 @@ use crate::run::runner::helpers::run_with_sudo::run_with_sudo; use crate::run::runner::valgrind::helpers::ignored_objects_path::get_objects_path_to_ignore; use crate::run::runner::valgrind::helpers::perf_maps::harvest_perf_maps_for_pids; use crate::run::runner::wall_time::perf::jit_dump::harvest_perf_jit_for_pids; +use crate::run::runner::wall_time::perf::perf_executable::get_working_perf_executable; use crate::run::runner::wall_time::perf::unwind_data::UnwindDataExt; use anyhow::Context; use fifo::{PerfFifo, RunnerFifo}; @@ -31,6 +32,7 @@ mod setup; pub mod elf_helper; pub mod fifo; +pub mod perf_executable; pub mod perf_map; pub mod unwind_data; @@ -118,15 +120,18 @@ impl PerfRunner { "" }; + let working_perf_executable = + get_working_perf_executable().context("Failed to find a working perf executable")?; + let perf_args = [ - "perf", + working_perf_executable.as_str(), "record", quiet_flag, "--timestamp", // Required for matching the markers and URIs to the samples. "-k", "CLOCK_MONOTONIC", - "--freq=999", + "--freq=997", // Use a prime number to avoid synchronization with periodic tasks "--delay=-1", "-g", "--user-callchains", diff --git a/src/run/runner/wall_time/perf/perf_executable.rs b/src/run/runner/wall_time/perf/perf_executable.rs new file mode 100644 index 00000000..1a6d2d5f --- /dev/null +++ b/src/run/runner/wall_time/perf/perf_executable.rs @@ -0,0 +1,64 @@ +use crate::prelude::*; + +use std::process::Command; + +const FIND_PERF_CMD: &str = + "find /usr/lib -executable -path \"/usr/lib/linux-tools-*/perf\" | sort | tail -n1"; + +/// Attempts to find the path to the `perf` executable that is installed and working. +/// Returns None if `perf` is not installed or not functioning correctly. +pub fn get_working_perf_executable() -> Option { + let is_installed = Command::new("which") + .arg("perf") + .output() + .is_ok_and(|output| output.status.success()); + if !is_installed { + debug!("perf is not installed"); + return None; + } + + debug!("perf is installed, checking if it is functioning correctly"); + if Command::new("perf") + .arg("--version") // here we use --version to check if perf is working + .output() + .is_ok_and(|output| output.status.success()) + { + return Some("perf".to_string()); + } else { + // The following is a workaround for this outstanding Ubuntu issue: https://bugs.launchpad.net/ubuntu/+source/linux-hwe-6.14/+bug/2117159/ + debug!( + "perf command is not functioning correctly, trying to find alternative path using \"{FIND_PERF_CMD}\"" + ); + if let Ok(perf_path) = Command::new("sh").arg("-c").arg(FIND_PERF_CMD).output() { + if perf_path.status.success() { + let path = String::from_utf8_lossy(&perf_path.stdout) + .trim() + .to_string(); + if path.is_empty() { + debug!("No alternative perf path found"); + return None; + } + debug!("Found perf path: {path}"); + // Check if this perf is working by getting its version + if let Ok(version_output) = Command::new(&path).arg("--version").output() { + if !version_output.status.success() { + debug!( + "Failed to get perf version from alternative path. stderr: {}", + String::from_utf8_lossy(&version_output.stderr) + ); + return None; + } + + let version = String::from_utf8_lossy(&version_output.stdout) + .trim() + .to_string(); + debug!("Found perf version from alternative path: {version}"); + return Some(path); + } + } + } + } + + debug!("perf is installed but not functioning correctly"); + None +} diff --git a/src/run/runner/wall_time/perf/setup.rs b/src/run/runner/wall_time/perf/setup.rs index 2e633aea..68b03b1b 100644 --- a/src/run/runner/wall_time/perf/setup.rs +++ b/src/run/runner/wall_time/perf/setup.rs @@ -1,28 +1,11 @@ use crate::run::runner::helpers::apt; +use crate::run::runner::wall_time::perf::perf_executable::get_working_perf_executable; use crate::{prelude::*, run::check_system::SystemInfo}; use std::{path::Path, process::Command}; -fn cmd_version(cmd: &str) -> anyhow::Result { - let is_installed = Command::new("which") - .arg(cmd) - .output() - .is_ok_and(|output| output.status.success()); - if !is_installed { - bail!("{cmd} is not installed") - } - - Ok(Command::new(cmd) - .arg("--version") - .output() - .map(|out| String::from_utf8_lossy(&out.stdout).to_string())?) -} - fn is_perf_installed() -> bool { - let version_str = cmd_version("perf"); - debug!("Perf version: {version_str:?}"); - - version_str.is_ok() + get_working_perf_executable().is_some() } pub async fn install_perf(system_info: &SystemInfo, setup_cache_dir: Option<&Path>) -> Result<()> {