From 5f43abd62dfa001c841ec7c3a8a38e68cf8d89c4 Mon Sep 17 00:00:00 2001 From: not-matthias Date: Tue, 7 Oct 2025 10:47:47 +0200 Subject: [PATCH 1/3] fix(ci): test codspeed on mac/windows without libclang --- .github/workflows/ci.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 88d64053..57342c8f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,7 +35,18 @@ jobs: cache-target: release env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - run: cargo test -p codspeed + if: runner.os == 'Linux' + + # We don't support Windows/MacOS for now, due to libclang not found on some runners: + - run: LIBCLANG_PATH= cargo test -p codspeed + if: runner.os == 'macOS' + - run: | + $env:LIBCLANG_PATH="" + cargo test -p codspeed + if: runner.os == 'Windows' + shell: powershell msrv-check: runs-on: ubuntu-latest From 408ed5aa0161a525b4083f01e76ed109cae3925c Mon Sep 17 00:00:00 2001 From: not-matthias Date: Tue, 7 Oct 2025 10:37:47 +0200 Subject: [PATCH 2/3] Revert "fix(codspeed): always compile instrument-hooks (with stubs)" CI runners that don't have LIBCLANG pre-installed would fail. We don't support Windows/MacOS for now, so we shouldn't do extra work with bindgen and cc. This reverts commit 8054636ee6e2e572a8f5fc9ec988474942de5d8f. --- crates/codspeed/Cargo.toml | 4 +- crates/codspeed/build.rs | 5 + crates/codspeed/instrument-hooks | 2 +- crates/codspeed/src/instrument_hooks/mod.rs | 248 ++++++++++++-------- 4 files changed, 155 insertions(+), 104 deletions(-) diff --git a/crates/codspeed/Cargo.toml b/crates/codspeed/Cargo.toml index 2294d4b6..3ce78d11 100644 --- a/crates/codspeed/Cargo.toml +++ b/crates/codspeed/Cargo.toml @@ -22,14 +22,12 @@ anyhow = { workspace = true } colored = "2.0.0" glob = "0.3.2" libc = "^0.2" +nix = { version = "0.30.1", features = ["time"] } serde = { workspace = true } serde_json = { workspace = true } statrs = { version = "0.18.0", default-features = false } uuid = { version = "1.12.1", features = ["v4"] } -[target.'cfg(target_os = "linux")'.dependencies] -nix = { version = "0.30.1", features = ["time"] } - [[bench]] name = "native" harness = false diff --git a/crates/codspeed/build.rs b/crates/codspeed/build.rs index 3eef6527..22f58401 100644 --- a/crates/codspeed/build.rs +++ b/crates/codspeed/build.rs @@ -1,6 +1,11 @@ use std::{env, path::PathBuf}; fn main() { + if cfg!(not(target_os = "linux")) { + // The instrument-hooks library is only supported on Linux. + return; + } + // Compile the C library cc::Build::new() .file("instrument-hooks/dist/core.c") diff --git a/crates/codspeed/instrument-hooks b/crates/codspeed/instrument-hooks index b1e401a4..b3d4b78e 160000 --- a/crates/codspeed/instrument-hooks +++ b/crates/codspeed/instrument-hooks @@ -1 +1 @@ -Subproject commit b1e401a4d031ad308edb22ed59a52253a1ebe924 +Subproject commit b3d4b78ef68498d59afd45a9c8b2ef096652f034 diff --git a/crates/codspeed/src/instrument_hooks/mod.rs b/crates/codspeed/src/instrument_hooks/mod.rs index 287b6762..ef4e08c1 100644 --- a/crates/codspeed/src/instrument_hooks/mod.rs +++ b/crates/codspeed/src/instrument_hooks/mod.rs @@ -1,132 +1,180 @@ -use std::ffi::CString; -use std::sync::OnceLock; - +#[cfg(target_os = "linux")] mod ffi; -pub struct InstrumentHooks(*mut ffi::InstrumentHooks); +#[cfg(target_os = "linux")] +mod linux_impl { + use nix::sys::time::TimeValLike; -unsafe impl Send for InstrumentHooks {} -unsafe impl Sync for InstrumentHooks {} + use super::ffi; + use std::ffi::CString; + use std::sync::OnceLock; -impl InstrumentHooks { - #[inline(always)] - pub fn new() -> Option { - let ptr = unsafe { ffi::instrument_hooks_init() }; - if ptr.is_null() { - None - } else { - Some(InstrumentHooks(ptr)) - } - } + pub struct InstrumentHooks(*mut ffi::InstrumentHooks); - /// Returns a singleton instance of `InstrumentHooks`. - #[inline(always)] - pub fn instance() -> &'static Self { - static INSTANCE: OnceLock = OnceLock::new(); - INSTANCE.get_or_init(|| { - let instance = InstrumentHooks::new().expect("Failed to initialize InstrumentHooks"); - instance - .set_integration("codspeed-rust", env!("CARGO_PKG_VERSION")) - .expect("Failed to set integration"); - instance - }) - } + unsafe impl Send for InstrumentHooks {} + unsafe impl Sync for InstrumentHooks {} - #[inline(always)] - pub fn is_instrumented(&self) -> bool { - unsafe { ffi::instrument_hooks_is_instrumented(self.0) } - } + impl InstrumentHooks { + #[inline(always)] + pub fn new() -> Option { + let ptr = unsafe { ffi::instrument_hooks_init() }; + if ptr.is_null() { + None + } else { + Some(InstrumentHooks(ptr)) + } + } - #[inline(always)] - pub fn start_benchmark(&self) -> Result<(), u8> { - let result = unsafe { ffi::instrument_hooks_start_benchmark(self.0) }; - if result == 0 { - Ok(()) - } else { - Err(result) + /// Returns a singleton instance of `InstrumentHooks`. + #[inline(always)] + pub fn instance() -> &'static Self { + static INSTANCE: OnceLock = OnceLock::new(); + INSTANCE.get_or_init(|| { + let instance = + InstrumentHooks::new().expect("Failed to initialize InstrumentHooks"); + instance + .set_integration("codspeed-rust", env!("CARGO_PKG_VERSION")) + .expect("Failed to set integration"); + instance + }) } - } - #[inline(always)] - pub fn stop_benchmark(&self) -> Result<(), u8> { - let result = unsafe { ffi::instrument_hooks_stop_benchmark(self.0) }; - if result == 0 { - Ok(()) - } else { - Err(result) + #[inline(always)] + pub fn is_instrumented(&self) -> bool { + unsafe { ffi::instrument_hooks_is_instrumented(self.0) } } - } - #[inline(always)] - pub fn set_executed_benchmark(&self, uri: &str) -> Result<(), u8> { - let pid = std::process::id() as i32; - let c_uri = CString::new(uri).map_err(|_| 1u8)?; - let result = - unsafe { ffi::instrument_hooks_set_executed_benchmark(self.0, pid, c_uri.as_ptr()) }; - if result == 0 { - Ok(()) - } else { - Err(result) + #[inline(always)] + pub fn start_benchmark(&self) -> Result<(), i8> { + let result = unsafe { ffi::instrument_hooks_start_benchmark(self.0) }; + if result == 0 { + Ok(()) + } else { + Err(result) + } } - } - #[inline(always)] - pub fn set_integration(&self, name: &str, version: &str) -> Result<(), u8> { - let c_name = CString::new(name).map_err(|_| 1u8)?; - let c_version = CString::new(version).map_err(|_| 1u8)?; - let result = unsafe { - ffi::instrument_hooks_set_integration(self.0, c_name.as_ptr(), c_version.as_ptr()) - }; - if result == 0 { - Ok(()) - } else { - Err(result) + #[inline(always)] + pub fn stop_benchmark(&self) -> Result<(), i8> { + let result = unsafe { ffi::instrument_hooks_stop_benchmark(self.0) }; + if result == 0 { + Ok(()) + } else { + Err(result) + } } - } - #[inline(always)] - pub fn add_benchmark_timestamps(&self, start: u64, end: u64) { - let pid = std::process::id(); - - unsafe { - ffi::instrument_hooks_add_marker( - self.0, - pid, - ffi::MARKER_TYPE_BENCHMARK_START as u8, - start, - ) - }; - unsafe { - ffi::instrument_hooks_add_marker(self.0, pid, ffi::MARKER_TYPE_BENCHMARK_END as u8, end) - }; - } + #[inline(always)] + pub fn set_executed_benchmark(&self, uri: &str) -> Result<(), i8> { + let pid = std::process::id() as i32; + let c_uri = CString::new(uri).map_err(|_| -1i8)?; + let result = unsafe { + ffi::instrument_hooks_set_executed_benchmark(self.0, pid, c_uri.as_ptr()) + }; + if result == 0 { + Ok(()) + } else { + Err(result) + } + } - #[inline(always)] - pub fn current_timestamp() -> u64 { - #[cfg(not(target_os = "linux"))] - { - unsafe { ffi::instrument_hooks_current_timestamp() } + #[inline(always)] + pub fn set_integration(&self, name: &str, version: &str) -> Result<(), i8> { + let c_name = CString::new(name).map_err(|_| -1i8)?; + let c_version = CString::new(version).map_err(|_| -1i8)?; + let result = unsafe { + ffi::instrument_hooks_set_integration(self.0, c_name.as_ptr(), c_version.as_ptr()) + }; + if result == 0 { + Ok(()) + } else { + Err(result) + } } - // Custom implementation to avoid the extra FFI call - #[cfg(target_os = "linux")] - { - use nix::sys::time::TimeValLike; + #[inline(always)] + pub fn add_benchmark_timestamps(&self, start: u64, end: u64) { + let pid = std::process::id(); + + unsafe { + ffi::instrument_hooks_add_marker( + self.0, + pid, + ffi::MARKER_TYPE_BENCHMARK_START as u8, + start, + ) + }; + unsafe { + ffi::instrument_hooks_add_marker( + self.0, + pid, + ffi::MARKER_TYPE_BENCHMARK_END as u8, + end, + ) + }; + } + + #[inline(always)] + pub fn current_timestamp() -> u64 { nix::time::clock_gettime(nix::time::ClockId::CLOCK_MONOTONIC) .expect("Failed to get current time") .num_nanoseconds() as u64 } } + + impl Drop for InstrumentHooks { + fn drop(&mut self) { + if !self.0.is_null() { + unsafe { ffi::instrument_hooks_deinit(self.0) }; + } + } + } } -impl Drop for InstrumentHooks { - fn drop(&mut self) { - if !self.0.is_null() { - unsafe { ffi::instrument_hooks_deinit(self.0) }; +#[cfg(not(target_os = "linux"))] +mod other_impl { + pub struct InstrumentHooks; + + impl InstrumentHooks { + pub fn instance() -> &'static Self { + static INSTANCE: InstrumentHooks = InstrumentHooks; + &INSTANCE + } + + pub fn is_instrumented(&self) -> bool { + false + } + + pub fn start_benchmark(&self) -> Result<(), i8> { + Ok(()) + } + + pub fn stop_benchmark(&self) -> Result<(), i8> { + Ok(()) + } + + pub fn set_executed_benchmark(&self, _uri: &str) -> Result<(), i8> { + Ok(()) + } + + pub fn set_integration(&self, _name: &str, _version: &str) -> Result<(), i8> { + Ok(()) + } + + pub fn add_benchmark_timestamps(&self, _start: u64, _end: u64) {} + + pub fn current_timestamp() -> u64 { + 0 } } } +#[cfg(target_os = "linux")] +pub use linux_impl::InstrumentHooks; + +#[cfg(not(target_os = "linux"))] +pub use other_impl::InstrumentHooks; + #[cfg(test)] mod tests { use super::InstrumentHooks; From 9502f62519eeae6208047dc1de22f25566f5edaf Mon Sep 17 00:00:00 2001 From: not-matthias Date: Tue, 7 Oct 2025 11:06:28 +0200 Subject: [PATCH 3/3] chore: bump instrument-hooks --- crates/codspeed/instrument-hooks | 2 +- crates/codspeed/src/instrument_hooks/mod.rs | 22 ++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/crates/codspeed/instrument-hooks b/crates/codspeed/instrument-hooks index b3d4b78e..1752e9e4 160000 --- a/crates/codspeed/instrument-hooks +++ b/crates/codspeed/instrument-hooks @@ -1 +1 @@ -Subproject commit b3d4b78ef68498d59afd45a9c8b2ef096652f034 +Subproject commit 1752e9e4eae585e26703932d0055a1473dd77048 diff --git a/crates/codspeed/src/instrument_hooks/mod.rs b/crates/codspeed/src/instrument_hooks/mod.rs index ef4e08c1..6596390c 100644 --- a/crates/codspeed/src/instrument_hooks/mod.rs +++ b/crates/codspeed/src/instrument_hooks/mod.rs @@ -45,7 +45,7 @@ mod linux_impl { } #[inline(always)] - pub fn start_benchmark(&self) -> Result<(), i8> { + pub fn start_benchmark(&self) -> Result<(), u8> { let result = unsafe { ffi::instrument_hooks_start_benchmark(self.0) }; if result == 0 { Ok(()) @@ -55,7 +55,7 @@ mod linux_impl { } #[inline(always)] - pub fn stop_benchmark(&self) -> Result<(), i8> { + pub fn stop_benchmark(&self) -> Result<(), u8> { let result = unsafe { ffi::instrument_hooks_stop_benchmark(self.0) }; if result == 0 { Ok(()) @@ -65,9 +65,9 @@ mod linux_impl { } #[inline(always)] - pub fn set_executed_benchmark(&self, uri: &str) -> Result<(), i8> { + pub fn set_executed_benchmark(&self, uri: &str) -> Result<(), u8> { let pid = std::process::id() as i32; - let c_uri = CString::new(uri).map_err(|_| -1i8)?; + let c_uri = CString::new(uri).map_err(|_| 1u8)?; let result = unsafe { ffi::instrument_hooks_set_executed_benchmark(self.0, pid, c_uri.as_ptr()) }; @@ -79,9 +79,9 @@ mod linux_impl { } #[inline(always)] - pub fn set_integration(&self, name: &str, version: &str) -> Result<(), i8> { - let c_name = CString::new(name).map_err(|_| -1i8)?; - let c_version = CString::new(version).map_err(|_| -1i8)?; + pub fn set_integration(&self, name: &str, version: &str) -> Result<(), u8> { + let c_name = CString::new(name).map_err(|_| 1u8)?; + let c_version = CString::new(version).map_err(|_| 1u8)?; let result = unsafe { ffi::instrument_hooks_set_integration(self.0, c_name.as_ptr(), c_version.as_ptr()) }; @@ -145,19 +145,19 @@ mod other_impl { false } - pub fn start_benchmark(&self) -> Result<(), i8> { + pub fn start_benchmark(&self) -> Result<(), u8> { Ok(()) } - pub fn stop_benchmark(&self) -> Result<(), i8> { + pub fn stop_benchmark(&self) -> Result<(), u8> { Ok(()) } - pub fn set_executed_benchmark(&self, _uri: &str) -> Result<(), i8> { + pub fn set_executed_benchmark(&self, _uri: &str) -> Result<(), u8> { Ok(()) } - pub fn set_integration(&self, _name: &str, _version: &str) -> Result<(), i8> { + pub fn set_integration(&self, _name: &str, _version: &str) -> Result<(), u8> { Ok(()) }