diff --git a/crates/codspeed/build.rs b/crates/codspeed/build.rs index 72ff2677..c2157f37 100644 --- a/crates/codspeed/build.rs +++ b/crates/codspeed/build.rs @@ -5,6 +5,9 @@ fn main() { println!("cargo:rerun-if-changed=instrument-hooks/includes/core.h"); println!("cargo:rerun-if-changed=build.rs"); + collect_rustc_info(); + collect_cargo_info(); + let mut build = cc::Build::new(); build .flag("-std=c11") @@ -44,3 +47,55 @@ fn main() { } } } + +/// Collect rustc toolchain info at build time and expose as env vars. +/// These env var names must be kept in sync with `src/instrument_hooks/mod.rs`. +fn collect_rustc_info() { + let Ok(output) = std::process::Command::new("rustc") + .args(["--version", "--verbose"]) + .output() + else { + return; + }; + if !output.status.success() { + return; + } + + let stdout = String::from_utf8_lossy(&output.stdout); + for line in stdout.lines() { + if let Some(rest) = line.strip_prefix("rustc ") { + println!("cargo:rustc-env=CODSPEED_RUSTC_VERSION={rest}"); + } else if let Some((key, value)) = line.split_once(':') { + let key = key.trim(); + let value = value.trim(); + let env_key = match key { + "host" => Some("CODSPEED_RUSTC_HOST"), + "release" => Some("CODSPEED_RUSTC_RELEASE"), + "LLVM version" => Some("CODSPEED_RUSTC_LLVM_VERSION"), + _ => None, + }; + if let Some(env_key) = env_key { + println!("cargo:rustc-env={env_key}={value}"); + } + } + } +} + +/// Collect cargo version at build time and expose as an env var. +/// This env var name must be kept in sync with `src/instrument_hooks/mod.rs`. +fn collect_cargo_info() { + let Ok(output) = std::process::Command::new("cargo") + .arg("--version") + .output() + else { + return; + }; + if !output.status.success() { + return; + } + + let stdout = String::from_utf8_lossy(&output.stdout); + if let Some(rest) = stdout.trim().strip_prefix("cargo ") { + println!("cargo:rustc-env=CODSPEED_CARGO_VERSION={rest}"); + } +} diff --git a/crates/codspeed/instrument-hooks b/crates/codspeed/instrument-hooks index 89fb72a0..e86719c7 160000 --- a/crates/codspeed/instrument-hooks +++ b/crates/codspeed/instrument-hooks @@ -1 +1 @@ -Subproject commit 89fb72a076ec71c9eca6eee9bca98bada4b4dfb4 +Subproject commit e86719c70c9c0b1646db182a7c748230e243dace diff --git a/crates/codspeed/src/codspeed.rs b/crates/codspeed/src/codspeed.rs index 490f3370..1a4644d2 100644 --- a/crates/codspeed/src/codspeed.rs +++ b/crates/codspeed/src/codspeed.rs @@ -36,19 +36,20 @@ impl CodSpeed { pub fn new() -> Self { use crate::instrument_hooks::InstrumentHooks; let instrumentation_status = { - // We completely bypass InstrumentHooks if we detect Valgrind via inline assembly + // Always initialize InstrumentHooks for environment collection, + // even when using Valgrind for the actual measurements. + let hooks_instance = InstrumentHooks::instance(); + + // We bypass InstrumentHooks if we detect Valgrind via inline assembly // Until we can reliably get rid of the inline assembly without causing breaking // changes in CPU simulation measurements by switching to InstrumentHooks only, we need // to keep this separation. if measurement::is_instrumented() { InstrumentationStatus::Valgrind + } else if hooks_instance.is_instrumented() { + InstrumentationStatus::InstrumentHooks(hooks_instance) } else { - let hooks_instance = InstrumentHooks::instance(); - if hooks_instance.is_instrumented() { - InstrumentationStatus::InstrumentHooks(hooks_instance) - } else { - InstrumentationStatus::NotInstrumented - } + InstrumentationStatus::NotInstrumented } }; diff --git a/crates/codspeed/src/instrument_hooks/bindings.rs b/crates/codspeed/src/instrument_hooks/bindings.rs index 4a29394f..692b8501 100644 --- a/crates/codspeed/src/instrument_hooks/bindings.rs +++ b/crates/codspeed/src/instrument_hooks/bindings.rs @@ -58,3 +58,14 @@ pub type instrument_hooks_feature_t = ::std::os::raw::c_uint; extern "C" { pub fn instrument_hooks_set_feature(feature: instrument_hooks_feature_t, enabled: bool); } +extern "C" { + pub fn instrument_hooks_set_environment( + arg1: *mut InstrumentHooks, + section_name: *const ::std::os::raw::c_char, + key: *const ::std::os::raw::c_char, + value: *const ::std::os::raw::c_char, + ) -> u8; +} +extern "C" { + pub fn instrument_hooks_write_environment(arg1: *mut InstrumentHooks, pid: u32) -> u8; +} diff --git a/crates/codspeed/src/instrument_hooks/mod.rs b/crates/codspeed/src/instrument_hooks/mod.rs index ff2edd93..97cdffbe 100644 --- a/crates/codspeed/src/instrument_hooks/mod.rs +++ b/crates/codspeed/src/instrument_hooks/mod.rs @@ -35,10 +35,39 @@ mod linux_impl { instance .set_integration("codspeed-rust", env!("CARGO_PKG_VERSION")) .expect("Failed to set integration"); + instance.register_toolchain_environment(); instance }) } + /// Registers Rust toolchain information (captured at build time) via the + /// environment API. + /// + /// The env var names here must be kept in sync with `build.rs`. + fn register_toolchain_environment(&self) { + const SECTION: &str = "Rust"; + + if let Some(v) = option_env!("CODSPEED_RUSTC_VERSION") { + let _ = self.set_environment(SECTION, "rustc", v); + } + if let Some(v) = option_env!("CODSPEED_RUSTC_HOST") { + let _ = self.set_environment(SECTION, "host", v); + } + if let Some(v) = option_env!("CODSPEED_RUSTC_RELEASE") { + let _ = self.set_environment(SECTION, "release", v); + } + if let Some(v) = option_env!("CODSPEED_RUSTC_LLVM_VERSION") { + let _ = self.set_environment(SECTION, "LLVM version", v); + } + if let Some(v) = option_env!("CODSPEED_CARGO_VERSION") { + let _ = self.set_environment(SECTION, "cargo", v); + } + + if let Err(e) = self.write_environment() { + eprintln!("Warning: failed to write environment info: {e}"); + } + } + #[inline(always)] pub fn is_instrumented(&self) -> bool { unsafe { ffi::instrument_hooks_is_instrumented(self.0) } @@ -131,6 +160,40 @@ mod linux_impl { } } + pub fn set_environment( + &self, + section_name: &str, + key: &str, + value: &str, + ) -> Result<(), u8> { + let c_section = CString::new(section_name).map_err(|_| 1u8)?; + let c_key = CString::new(key).map_err(|_| 1u8)?; + let c_value = CString::new(value).map_err(|_| 1u8)?; + let result = unsafe { + ffi::instrument_hooks_set_environment( + self.0, + c_section.as_ptr(), + c_key.as_ptr(), + c_value.as_ptr(), + ) + }; + if result == 0 { + Ok(()) + } else { + Err(result) + } + } + + pub fn write_environment(&self) -> Result<(), u8> { + let pid = std::process::id(); + let result = unsafe { ffi::instrument_hooks_write_environment(self.0, pid) }; + if result == 0 { + Ok(()) + } else { + Err(result) + } + } + pub fn disable_callgrind_markers() { unsafe { ffi::instrument_hooks_set_feature( @@ -187,6 +250,19 @@ mod other_impl { 0 } + pub fn set_environment( + &self, + _section_name: &str, + _key: &str, + _value: &str, + ) -> Result<(), u8> { + Ok(()) + } + + pub fn write_environment(&self) -> Result<(), u8> { + Ok(()) + } + pub fn disable_callgrind_markers() {} } }