diff --git a/.gitignore b/.gitignore index c59e2de8..d847773a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ /target .DS_Store .codspeed +compile_commands.json +.cache diff --git a/Cargo.lock b/Cargo.lock index 2863e2fa..a686df35 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -494,7 +494,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" dependencies = [ "lazy_static", - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] @@ -756,11 +756,14 @@ name = "exec-harness" version = "1.0.0" dependencies = [ "anyhow", + "cargo_metadata", + "cc", "clap", "codspeed", "env_logger", "humantime", "log", + "object 0.36.7", "runner-shared", "serde", "serde_json", @@ -2213,7 +2216,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9120690fafc389a67ba3803df527d0ec9cbbc9cc45e4cc20b332996dfb672425" dependencies = [ "anyhow", - "itertools 0.13.0", + "itertools 0.14.0", "proc-macro2", "quote", "syn 2.0.111", @@ -2594,7 +2597,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.4.15", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 191e1d37..c132c79e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -97,6 +97,10 @@ ipc-channel = "0.18" itertools = "0.14.0" env_logger = "0.11.8" tempfile = "3.10.0" +object = { version = "0.36", default-features = false, features = [ + "read_core", + "elf", +] } [workspace.metadata.release] sign-tag = true diff --git a/crates/exec-harness/Cargo.toml b/crates/exec-harness/Cargo.toml index 27205893..cd8a034b 100644 --- a/crates/exec-harness/Cargo.toml +++ b/crates/exec-harness/Cargo.toml @@ -11,7 +11,7 @@ path = "src/main.rs" [dependencies] anyhow = { workspace = true } -codspeed = "4.1.0" +codspeed = "4.2.0" log = { workspace = true } env_logger = { workspace = true } clap = { workspace = true } @@ -19,9 +19,12 @@ serde_json = { workspace = true } serde = { workspace = true } humantime = "2.1" runner-shared = { path = "../runner-shared" } - -[dev-dependencies] tempfile = { workspace = true } +object = { workspace = true } + +[build-dependencies] +cargo_metadata = "0.19" +cc = "1" [package.metadata.dist] -targets = ["aarch64-unknown-linux-musl", "x86_64-unknown-linux-musl"] +targets = ["aarch64-unknown-linux-gnu", "x86_64-unknown-linux-gnu"] diff --git a/crates/exec-harness/build.rs b/crates/exec-harness/build.rs new file mode 100644 index 00000000..2c05f52e --- /dev/null +++ b/crates/exec-harness/build.rs @@ -0,0 +1,203 @@ +//! Build script for exec-harness +//! +//! This script compiles the `libcodspeed_preload.so` shared library that is used +//! to inject instrumentation into child processes via LD_PRELOAD. +//! +//! The library is built using the `core.c` and headers from the `codspeed` crate's +//! `instrument-hooks` directory. +//! +//! # Environment Variables +//! +//! - `CODSPEED_INSTRUMENT_HOOKS_DIR`: Optional override for the instrument-hooks +//! source directory. If not set, the build script will locate it from the +//! `codspeed` crate in the cargo registry. + +use cargo_metadata::MetadataCommand; +use std::env; +use std::path::PathBuf; + +/// Shared constants for the preload library. +/// These are passed as C defines during compilation and exported as environment +/// variables for the Rust code to use via `env!()`. +struct PreloadConstants { + /// Environment variable name for the benchmark URI. + uri_env: &'static str, + /// Integration name reported to CodSpeed. + integration_name: &'static str, + /// Integration version reported to CodSpeed. + /// This should match the version of the `codspeed` crate dependency. + integration_version: &'static str, + /// Filename for the preload shared library. + preload_lib_filename: &'static str, +} + +fn main() { + println!("cargo:rerun-if-changed=preload/codspeed_preload.c"); + println!("cargo:rerun-if-env-changed=CODSPEED_INSTRUMENT_HOOKS_DIR"); + + let preload_constants: PreloadConstants = PreloadConstants::default(); + + // Export constants as environment variables for the Rust code + println!( + "cargo:rustc-env=CODSPEED_URI_ENV={}", + preload_constants.uri_env + ); + println!( + "cargo:rustc-env=CODSPEED_INTEGRATION_NAME={}", + preload_constants.integration_name + ); + println!( + "cargo:rustc-env=CODSPEED_INTEGRATION_VERSION={}", + preload_constants.integration_version + ); + println!( + "cargo:rustc-env=CODSPEED_PRELOAD_LIB_FILENAME={}", + preload_constants.preload_lib_filename + ); + + let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); + + // Try to get the instrument-hooks directory from the environment variable first, + // otherwise locate it from the codspeed crate + let instrument_hooks_dir = match env::var("CODSPEED_INSTRUMENT_HOOKS_DIR") { + Ok(dir) => PathBuf::from(dir), + Err(_) => find_codspeed_instrument_hooks_dir(), + }; + + // Build the preload shared library + let paths = PreloadBuildPaths { + preload_c: manifest_dir.join("preload/codspeed_preload.c"), + core_c: instrument_hooks_dir.join("dist/core.c"), + includes_dir: instrument_hooks_dir.join("includes"), + }; + paths.check_sources_exist(); + build_shared_library(&paths, &preload_constants); +} + +/// Build the shared library using the cc crate +fn build_shared_library(paths: &PreloadBuildPaths, constants: &PreloadConstants) { + let uri_env_val = format!("\"{}\"", constants.uri_env); + let integration_name_val = format!("\"{}\"", constants.integration_name); + let integration_version_val = format!("\"{}\"", constants.integration_version); + let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); + let out_file = out_dir.join(constants.preload_lib_filename); + + let mut build = cc::Build::new(); + build + .file(&paths.preload_c) + .file(&paths.core_c) + .include(&paths.includes_dir) + .pic(true) + .opt_level(3) + // There's no need to output cargo metadata as we are just building a shared library + // that will be copied to disk and loaded through LD_PRELOAD at runtime + .cargo_metadata(false) + // Pass constants as C defines + .define("CODSPEED_URI_ENV", uri_env_val.as_str()) + .define("CODSPEED_INTEGRATION_NAME", integration_name_val.as_str()) + .define( + "CODSPEED_INTEGRATION_VERSION", + integration_version_val.as_str(), + ) + // Suppress warnings from generated Zig code + .flag("-Wno-format") + .flag("-Wno-format-security") + .flag("-Wno-unused-but-set-variable") + .flag("-Wno-unused-const-variable") + .flag("-Wno-type-limits") + .flag("-Wno-uninitialized") + .flag("-Wno-overflow") + .flag("-Wno-unused-function"); + + // Compile source files to object files + let objects = build.compile_intermediates(); + + // Link object files into shared library + let compiler = build.get_compiler(); + let mut link_cmd = compiler.to_command(); + link_cmd + .arg("-shared") + .arg("-o") + .arg(&out_file) + .args(&objects) + .arg("-lpthread"); + + let status = link_cmd.status().expect("Failed to run linker"); + if !status.success() { + panic!("Failed to link libcodspeed_preload.so"); + } +} + +/// Find the instrument-hooks directory from the codspeed crate using cargo_metadata +fn find_codspeed_instrument_hooks_dir() -> PathBuf { + let metadata = MetadataCommand::new() + .exec() + .expect("Failed to run cargo metadata"); + + // Find the codspeed package in the resolved dependencies + let codspeed_pkg = metadata + .packages + .iter() + .find(|p| p.name == "codspeed") + .expect("codspeed crate not found in dependencies"); + + let codspeed_dir = codspeed_pkg + .manifest_path + .parent() + .expect("Failed to get codspeed crate directory"); + + let instrument_hooks_dir = codspeed_dir.join("instrument-hooks"); + + if !instrument_hooks_dir.exists() { + panic!("instrument-hooks directory not found at {instrument_hooks_dir}"); + } + + instrument_hooks_dir.into_std_path_buf() +} + +impl Default for PreloadConstants { + // TODO(COD-1736): Stop impersonating codspeed-rust 🥸 + fn default() -> Self { + Self { + uri_env: "CODSPEED_BENCH_URI", + integration_name: "codspeed-rust", + integration_version: "4.2.0", + preload_lib_filename: "libcodspeed_preload.so", + } + } +} + +/// Paths required to build the preload shared library. +struct PreloadBuildPaths { + /// Path to the preload C source file (codspeed_preload.c). + preload_c: PathBuf, + /// Path to the core C source file from instrument-hooks. + core_c: PathBuf, + /// Path to the includes directory from instrument-hooks. + includes_dir: PathBuf, +} + +impl PreloadBuildPaths { + /// Verify that all required source files and directories exist. + /// Panics with a descriptive message if any path is missing. + fn check_sources_exist(&self) { + if !self.core_c.exists() { + panic!( + "core.c not found at {}. Make sure the codspeed crate is available.", + self.core_c.display() + ); + } + if !self.includes_dir.exists() { + panic!( + "includes directory not found at {}. Make sure the codspeed crate is available.", + self.includes_dir.display() + ); + } + if !self.preload_c.exists() { + panic!( + "codspeed_preload.c not found at {}", + self.preload_c.display() + ); + } + } +} diff --git a/crates/exec-harness/preload/codspeed_preload.c b/crates/exec-harness/preload/codspeed_preload.c new file mode 100644 index 00000000..0b908e04 --- /dev/null +++ b/crates/exec-harness/preload/codspeed_preload.c @@ -0,0 +1,99 @@ +// LD_PRELOAD library for enabling Valgrind instrumentation in child processes +// +// This library is loaded via LD_PRELOAD into benchmark processes spawned by +// exec-harness. It enables callgrind instrumentation on load and disables it on +// exit, allowing exec-harness to measure arbitrary commands without requiring +// them to link against instrument-hooks. +// +// Environment variables: +// CODSPEED_BENCH_URI - The benchmark URI to report (required) +// CODSPEED_PRELOAD_LOCK - Set by the first process to prevent child processes +// from re-initializing instrumentation + +#include +#include + +#include "core.h" + +#ifndef RUNNING_ON_VALGRIND +// If somehow the core.h did not include the valgrind header, something is +// wrong, but still have a fallback +#warning "RUNNING_ON_VALGRIND not defined, headers may be missing" +#define RUNNING_ON_VALGRIND 0 +#endif + +static const char *LOCK_ENV = "CODSPEED_PRELOAD_LOCK"; + +// These constants are defined by the build script (build.rs) via -D flags +#ifndef CODSPEED_URI_ENV +#error "CODSPEED_URI_ENV must be defined by the build system" +#endif +#ifndef CODSPEED_INTEGRATION_NAME +#error "CODSPEED_INTEGRATION_NAME must be defined by the build system" +#endif +#ifndef CODSPEED_INTEGRATION_VERSION +#error "CODSPEED_INTEGRATION_VERSION must be defined by the build system" +#endif + +static const char *URI_ENV = CODSPEED_URI_ENV; +static const char *INTEGRATION_NAME = CODSPEED_INTEGRATION_NAME; +static const char *INTEGRATION_VERSION = CODSPEED_INTEGRATION_VERSION; + +static InstrumentHooks *g_hooks = NULL; +static const char *g_bench_uri = NULL; + +__attribute__((constructor)) static void codspeed_preload_init(void) { + // Skip initialization if not running under Valgrind yet. + // When using LD_PRELOAD with Valgrind, the constructor runs twice: + // once before Valgrind takes over, and once after. We only want to + // initialize when Valgrind is active. + // + // This is purely empirical, and is not (yet) backed up by documented + // behavior. + if (!RUNNING_ON_VALGRIND) { + return; + } + + // Check if another process already owns the instrumentation + if (getenv(LOCK_ENV)) { + return; + } + + // Set the lock to prevent child processes from initializing + setenv(LOCK_ENV, "1", 1); + + g_bench_uri = getenv(URI_ENV); + if (!g_bench_uri) { + return; + } + + g_hooks = instrument_hooks_init(); + if (!g_hooks) { + return; + } + + instrument_hooks_set_integration(g_hooks, INTEGRATION_NAME, + INTEGRATION_VERSION); + + if (instrument_hooks_start_benchmark_inline(g_hooks) != 0) { + instrument_hooks_deinit(g_hooks); + g_hooks = NULL; + return; + } +} + +__attribute__((destructor)) static void codspeed_preload_fini(void) { + // If the process is not the owner of the lock, this means g_hooks was not + // initialized + if (!g_hooks) { + return; + } + + instrument_hooks_stop_benchmark_inline(g_hooks); + + int32_t pid = getpid(); + instrument_hooks_set_executed_benchmark(g_hooks, pid, g_bench_uri); + + instrument_hooks_deinit(g_hooks); + g_hooks = NULL; +} diff --git a/crates/exec-harness/src/analysis.rs b/crates/exec-harness/src/analysis.rs deleted file mode 100644 index 4469380c..00000000 --- a/crates/exec-harness/src/analysis.rs +++ /dev/null @@ -1,29 +0,0 @@ -use crate::prelude::*; - -use crate::BenchmarkCommand; -use crate::uri; -use codspeed::instrument_hooks::InstrumentHooks; -use std::process::Command; - -pub fn perform(commands: Vec) -> Result<()> { - let hooks = InstrumentHooks::instance(); - - for benchmark_cmd in commands { - let name_and_uri = uri::generate_name_and_uri(&benchmark_cmd.name, &benchmark_cmd.command); - - let mut cmd = Command::new(&benchmark_cmd.command[0]); - cmd.args(&benchmark_cmd.command[1..]); - hooks.start_benchmark().unwrap(); - let status = cmd.status(); - hooks.stop_benchmark().unwrap(); - let status = status.context("Failed to execute command")?; - - if !status.success() { - bail!("Command exited with non-zero status: {status}"); - } - - hooks.set_executed_benchmark(&name_and_uri.uri).unwrap(); - } - - Ok(()) -} diff --git a/crates/exec-harness/src/analysis/ld_preload_check.rs b/crates/exec-harness/src/analysis/ld_preload_check.rs new file mode 100644 index 00000000..702f18d2 --- /dev/null +++ b/crates/exec-harness/src/analysis/ld_preload_check.rs @@ -0,0 +1,120 @@ +use crate::prelude::*; +use std::fs; +use std::path::Path; + +/// Checks if the given executable will honor LD_PRELOAD. +/// +/// Returns `Ok(())` if LD_PRELOAD will work, or an error with a descriptive message if not. +/// +/// LD_PRELOAD works for: +/// - Dynamically linked ELF binaries +/// - Scripts (the interpreter is typically dynamically linked) +/// +/// LD_PRELOAD does NOT work for: +/// - Statically linked ELF binaries (no dynamic linker involved) +pub fn check_ld_preload_compatible(executable: &str) -> Result<()> { + let path = resolve_executable(executable)?; + let data = fs::read(&path) + .with_context(|| format!("Failed to read executable: {}", path.display()))?; + + // Check ELF magic bytes + if data.len() >= 4 && &data[0..4] == b"\x7FELF" { + check_elf_is_dynamic(&data, &path) + } else { + // Not an ELF file - likely a script with a shebang. + // Scripts use an interpreter which is typically dynamically linked. + Ok(()) + } +} + +/// Resolve executable name to its full path using PATH lookup. +fn resolve_executable(executable: &str) -> Result { + let path = Path::new(executable); + + // If it's already an absolute or relative path, use it directly + if path.is_absolute() || executable.contains('/') { + return Ok(path.to_path_buf()); + } + + // Search in PATH + if let Ok(path_env) = std::env::var("PATH") { + for dir in path_env.split(':') { + let candidate = Path::new(dir).join(executable); + if candidate.is_file() { + return Ok(candidate); + } + } + } + + bail!("Executable not found in PATH: {executable}") +} + +/// Check if an ELF binary is dynamically linked. +fn check_elf_is_dynamic(data: &[u8], path: &Path) -> Result<()> { + use object::Endianness; + use object::read::elf::ElfFile; + + // Try parsing as 64-bit ELF first, then 32-bit + if let Ok(elf) = ElfFile::>::parse(data) { + return check_elf_has_interp(elf, path); + } + + if let Ok(elf) = ElfFile::>::parse(data) { + return check_elf_has_interp(elf, path); + } + + bail!("Failed to parse ELF file: {}", path.display()) +} + +/// Check if an ELF file has a PT_INTERP or PT_DYNAMIC segment, indicating dynamic linking. +fn check_elf_has_interp<'data, Elf>( + elf: object::read::elf::ElfFile<'data, Elf>, + path: &Path, +) -> Result<()> +where + Elf: object::read::elf::FileHeader, +{ + use object::read::elf::ProgramHeader; + + let endian = elf.endian(); + + for segment in elf.elf_program_headers() { + let p_type = segment.p_type(endian); + // Either PT_INTERP or PT_DYNAMIC indicates a dynamically linked binary + if p_type == object::elf::PT_INTERP || p_type == object::elf::PT_DYNAMIC { + return Ok(()); + } + } + + // No PT_INTERP found - this is a statically linked binary + bail!( + "The codspeed CLI in CPU Simulation mode does not support statically linked binaries.\n\n\ + Executable '{}' is statically linked.\n\n\ + Please either:\n\ + - Use a dynamically linked executable, or\n\ + - Use a different measurement mode, or\n\ + - Use one of the CodSpeed framework benchmark integrations", + path.display() + ) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_dynamic_binary() { + // /bin/sh or similar should be dynamically linked on most systems + let result = check_ld_preload_compatible("sh"); + assert!( + result.is_ok(), + "sh should be dynamically linked: {result:?}" + ); + } + + #[test] + fn test_nonexistent_binary() { + let result = check_ld_preload_compatible("nonexistent_binary_12345"); + assert!(result.is_err()); + } +} diff --git a/crates/exec-harness/src/analysis/mod.rs b/crates/exec-harness/src/analysis/mod.rs new file mode 100644 index 00000000..49442d1c --- /dev/null +++ b/crates/exec-harness/src/analysis/mod.rs @@ -0,0 +1,65 @@ +use crate::prelude::*; + +use crate::BenchmarkCommand; +use crate::constants; +use crate::uri; +use codspeed::instrument_hooks::InstrumentHooks; +use std::process::Command; + +mod ld_preload_check; +mod preload_lib_file; + +pub fn perform(commands: Vec) -> Result<()> { + let hooks = InstrumentHooks::instance(); + + for benchmark_cmd in commands { + let name_and_uri = uri::generate_name_and_uri(&benchmark_cmd.name, &benchmark_cmd.command); + name_and_uri.print_executing(); + + let mut cmd = Command::new(&benchmark_cmd.command[0]); + cmd.args(&benchmark_cmd.command[1..]); + hooks.start_benchmark().unwrap(); + let status = cmd.status(); + hooks.stop_benchmark().unwrap(); + let status = status.context("Failed to execute command")?; + + if !status.success() { + bail!("Command exited with non-zero status: {status}"); + } + + hooks.set_executed_benchmark(&name_and_uri.uri).unwrap(); + } + + Ok(()) +} + +/// Executes the given benchmark commands using a preload based trick to handle valgrind control. +/// +/// This function is only supported on Unix-like platforms, as it relies on the +/// `LD_PRELOAD` environment variable and Unix file permissions for shared libraries. +/// It will not work on non-Unix platforms or with statically linked binaries. +pub fn perform_with_valgrind(commands: Vec) -> Result<()> { + let preload_lib_path = preload_lib_file::get_preload_lib_path()?; + + for benchmark_cmd in commands { + // Check if the executable will honor LD_PRELOAD before running + ld_preload_check::check_ld_preload_compatible(&benchmark_cmd.command[0])?; + + let name_and_uri = uri::generate_name_and_uri(&benchmark_cmd.name, &benchmark_cmd.command); + name_and_uri.print_executing(); + + let mut cmd = Command::new(&benchmark_cmd.command[0]); + cmd.args(&benchmark_cmd.command[1..]); + // Use LD_PRELOAD to inject instrumentation into the child process + cmd.env("LD_PRELOAD", preload_lib_path); + cmd.env(constants::URI_ENV, &name_and_uri.uri); + + let status = cmd.status().context("Failed to execute command")?; + + if !status.success() { + bail!("Command exited with non-zero status: {status}"); + } + } + + Ok(()) +} diff --git a/crates/exec-harness/src/analysis/preload_lib_file.rs b/crates/exec-harness/src/analysis/preload_lib_file.rs new file mode 100644 index 00000000..2d53804c --- /dev/null +++ b/crates/exec-harness/src/analysis/preload_lib_file.rs @@ -0,0 +1,46 @@ +use crate::prelude::*; + +use std::io::Write; +use std::sync::OnceLock; + +/// Filename for the preload shared library. +const PRELOAD_LIB_FILENAME: &str = env!("CODSPEED_PRELOAD_LIB_FILENAME"); + +/// The preload library binary embedded at compile time. +const PRELOAD_LIB_BYTES: &[u8] = include_bytes!(concat!( + env!("OUT_DIR"), + "/", + env!("CODSPEED_PRELOAD_LIB_FILENAME") +)); + +/// Lazily initialized temp file containing the extracted preload library. +/// Kept in a static to prevent cleanup until process exit. +static PRELOAD_LIB_FILE: OnceLock = OnceLock::new(); + +/// Extracts the preload library to a temp file. +fn extract_preload_lib() -> Result { + let mut file = tempfile::Builder::new() + .suffix(PRELOAD_LIB_FILENAME) + .tempfile() + .context("Failed to create temp file for preload library")?; + + file.write_all(PRELOAD_LIB_BYTES) + .context("Failed to write preload library to temp file")?; + + debug!( + "Extracted preload library to temp file: {}", + file.path().display() + ); + + Ok(file) +} + +/// Returns the path to the preload library, extracting it to a temp file if needed. +pub(super) fn get_preload_lib_path() -> Result<&'static std::path::Path> { + if let Some(file) = PRELOAD_LIB_FILE.get() { + return Ok(file.path()); + } + + let file = extract_preload_lib()?; + Ok(PRELOAD_LIB_FILE.get_or_init(|| file).path()) +} diff --git a/crates/exec-harness/src/constants.rs b/crates/exec-harness/src/constants.rs new file mode 100644 index 00000000..9a47591c --- /dev/null +++ b/crates/exec-harness/src/constants.rs @@ -0,0 +1,15 @@ +//! Shared constants for the exec-harness crate. +//! +//! These constants are defined in the build script (build.rs) and exported as +//! environment variables. The same values are passed to the C preload library +//! as compiler defines, ensuring both Rust and C code use the same source of truth. + +/// Environment variable name for the benchmark URI. +pub const URI_ENV: &str = env!("CODSPEED_URI_ENV"); + +/// Integration name reported to CodSpeed. +pub const INTEGRATION_NAME: &str = env!("CODSPEED_INTEGRATION_NAME"); + +/// Integration version reported to CodSpeed. +/// This should match the version of the `codspeed` crate dependency. +pub const INTEGRATION_VERSION: &str = env!("CODSPEED_INTEGRATION_VERSION"); diff --git a/crates/exec-harness/src/lib.rs b/crates/exec-harness/src/lib.rs index 8fea15ee..b1ed3933 100644 --- a/crates/exec-harness/src/lib.rs +++ b/crates/exec-harness/src/lib.rs @@ -4,6 +4,7 @@ use serde::{Deserialize, Serialize}; use std::io::{self, BufRead}; pub mod analysis; +pub mod constants; pub mod prelude; mod uri; pub mod walltime; @@ -13,6 +14,7 @@ pub mod walltime; pub enum MeasurementMode { Walltime, Memory, + #[value(alias = "instrumentation")] Simulation, } @@ -75,7 +77,7 @@ pub fn execute_benchmarks( analysis::perform(commands)?; } Some(MeasurementMode::Simulation) => { - bail!("Simulation measurement mode is not yet supported by exec-harness"); + analysis::perform_with_valgrind(commands)?; } } diff --git a/crates/exec-harness/src/uri.rs b/crates/exec-harness/src/uri.rs index 6e599217..77c996a2 100644 --- a/crates/exec-harness/src/uri.rs +++ b/crates/exec-harness/src/uri.rs @@ -3,6 +3,7 @@ use crate::prelude::*; pub struct NameAndUri { pub(crate) name: String, pub(crate) uri: String, + command: Vec, } /// Maximum length for benchmark name to avoid excessively long URIs @@ -22,5 +23,16 @@ pub fn generate_name_and_uri(name: &Option, command: &[String]) -> NameA name.truncate(MAX_NAME_LENGTH); } - NameAndUri { name, uri } + NameAndUri { + name, + uri, + command: command.to_vec(), + } +} + +impl NameAndUri { + pub fn print_executing(&self) { + info!("Executing: {}", self.name); + debug!("Command: {:?}", self.command); + } } diff --git a/crates/exec-harness/src/walltime/mod.rs b/crates/exec-harness/src/walltime/mod.rs index 32310f47..b121063d 100644 --- a/crates/exec-harness/src/walltime/mod.rs +++ b/crates/exec-harness/src/walltime/mod.rs @@ -16,11 +16,13 @@ pub fn perform(commands: Vec) -> Result<()> { for cmd in commands { let name_and_uri = generate_name_and_uri(&cmd.name, &cmd.command); + name_and_uri.print_executing(); let execution_options: ExecutionOptions = cmd.walltime_args.try_into()?; let NameAndUri { name: bench_name, uri: bench_uri, + .. } = name_and_uri; let times_per_round_ns = diff --git a/crates/memtrack/Cargo.toml b/crates/memtrack/Cargo.toml index 178fc61a..28b80f4e 100644 --- a/crates/memtrack/Cargo.toml +++ b/crates/memtrack/Cargo.toml @@ -33,7 +33,7 @@ itertools = { workspace = true } paste = "1.0" libbpf-rs = { version = "0.25.0", features = ["vendored"], optional = true } glob = "0.3.3" -object = { version = "0.36", default-features = false, features = ["read_core", "elf"] } +object = { workspace = true } [build-dependencies] libbpf-cargo = { version = "0.25.0", optional = true } diff --git a/dist-workspace.toml b/dist-workspace.toml index 2a6b736c..d60dffaf 100644 --- a/dist-workspace.toml +++ b/dist-workspace.toml @@ -34,4 +34,4 @@ local-artifacts-jobs = ["./create-draft-release"] allow-dirty = ["ci"] [dist.github-custom-runners] -aarch64-unknown-linux-musl = "buildjet-2vcpu-ubuntu-2204-arm" +aarch64-unknown-linux-musl = "codspeedhq-arm64-ubuntu-24.04"