From 44faa951d5b099464e327d748388bfc133f8be0c Mon Sep 17 00:00:00 2001 From: Adrien Cacciaguerra Date: Fri, 17 Oct 2025 11:48:51 +0200 Subject: [PATCH 1/4] fix(run): correctly check if perf is installed --- src/run/runner/valgrind/setup.rs | 5 ++++ src/run/runner/wall_time/perf/setup.rs | 33 +++++++++++++++----------- 2 files changed, 24 insertions(+), 14 deletions(-) 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/setup.rs b/src/run/runner/wall_time/perf/setup.rs index 2e633aea..73391925 100644 --- a/src/run/runner/wall_time/perf/setup.rs +++ b/src/run/runner/wall_time/perf/setup.rs @@ -3,26 +3,31 @@ use crate::{prelude::*, run::check_system::SystemInfo}; use std::{path::Path, process::Command}; -fn cmd_version(cmd: &str) -> anyhow::Result { +fn is_perf_installed() -> bool { let is_installed = Command::new("which") - .arg(cmd) + .arg("perf") .output() .is_ok_and(|output| output.status.success()); if !is_installed { - bail!("{cmd} is not installed") + debug!("perf is not installed"); + return false; } - 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() + if let Ok(version_output) = Command::new("perf").arg("--version").output() { + if !version_output.status.success() { + debug!( + "Failed to get perf version. stderr: {}", + String::from_utf8_lossy(&version_output.stderr) + ); + return false; + } + + let version = String::from_utf8_lossy(&version_output.stdout); + debug!("Found perf version: {}", version.trim()); + true + } else { + false + } } pub async fn install_perf(system_info: &SystemInfo, setup_cache_dir: Option<&Path>) -> Result<()> { From d0c87a537dd0cc2c6d5a30b508faa19aaf5558a7 Mon Sep 17 00:00:00 2001 From: Adrien Cacciaguerra Date: Fri, 17 Oct 2025 12:01:05 +0200 Subject: [PATCH 2/4] chore(run): add a metadata file in the cache that lists the installed packages --- src/run/runner/helpers/apt.rs | 43 ++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) 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(()) } From e0deeff2f2295611c63953b6eb844fce0596abde Mon Sep 17 00:00:00 2001 From: Adrien Cacciaguerra Date: Fri, 17 Oct 2025 10:59:12 +0000 Subject: [PATCH 3/4] feat(perf): use a prime number as frequency to avoid synchronization with periodic tasks --- src/run/runner/wall_time/perf/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/run/runner/wall_time/perf/mod.rs b/src/run/runner/wall_time/perf/mod.rs index 0aa1dbb9..43c4ddd4 100644 --- a/src/run/runner/wall_time/perf/mod.rs +++ b/src/run/runner/wall_time/perf/mod.rs @@ -126,7 +126,7 @@ impl PerfRunner { // 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", From 2fe4fca78c90fa26ef2941162252a68e7bc21f5c Mon Sep 17 00:00:00 2001 From: Adrien Cacciaguerra Date: Fri, 17 Oct 2025 13:01:30 +0000 Subject: [PATCH 4/4] fix(perf): ensure perf is always found on the machine Workaround for https://bugs.launchpad.net/ubuntu/+source/linux-hwe-6.14/+bug/2117159/ --- src/run/runner/wall_time/perf/mod.rs | 7 +- .../runner/wall_time/perf/perf_executable.rs | 64 +++++++++++++++++++ src/run/runner/wall_time/perf/setup.rs | 26 +------- 3 files changed, 72 insertions(+), 25 deletions(-) create mode 100644 src/run/runner/wall_time/perf/perf_executable.rs diff --git a/src/run/runner/wall_time/perf/mod.rs b/src/run/runner/wall_time/perf/mod.rs index 43c4ddd4..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,8 +120,11 @@ 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", 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 73391925..68b03b1b 100644 --- a/src/run/runner/wall_time/perf/setup.rs +++ b/src/run/runner/wall_time/perf/setup.rs @@ -1,33 +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 is_perf_installed() -> bool { - 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 false; - } - - if let Ok(version_output) = Command::new("perf").arg("--version").output() { - if !version_output.status.success() { - debug!( - "Failed to get perf version. stderr: {}", - String::from_utf8_lossy(&version_output.stderr) - ); - return false; - } - - let version = String::from_utf8_lossy(&version_output.stdout); - debug!("Found perf version: {}", version.trim()); - true - } else { - false - } + get_working_perf_executable().is_some() } pub async fn install_perf(system_info: &SystemInfo, setup_cache_dir: Option<&Path>) -> Result<()> {