Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion crates/codspeed/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@ 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
Expand Down
5 changes: 0 additions & 5 deletions crates/codspeed/build.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
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")
Expand Down
2 changes: 1 addition & 1 deletion crates/codspeed/instrument-hooks
248 changes: 100 additions & 148 deletions crates/codspeed/src/instrument_hooks/mod.rs
Original file line number Diff line number Diff line change
@@ -1,180 +1,132 @@
#[cfg(target_os = "linux")]
mod ffi;

#[cfg(target_os = "linux")]
mod linux_impl {
use nix::sys::time::TimeValLike;
use std::ffi::CString;
use std::sync::OnceLock;

use super::ffi;
use std::ffi::CString;
use std::sync::OnceLock;
mod ffi;

pub struct InstrumentHooks(*mut ffi::InstrumentHooks);
pub struct InstrumentHooks(*mut ffi::InstrumentHooks);

unsafe impl Send for InstrumentHooks {}
unsafe impl Sync for InstrumentHooks {}
unsafe impl Send for InstrumentHooks {}
unsafe impl Sync for InstrumentHooks {}

impl InstrumentHooks {
#[inline(always)]
pub fn new() -> Option<Self> {
let ptr = unsafe { ffi::instrument_hooks_init() };
if ptr.is_null() {
None
} else {
Some(InstrumentHooks(ptr))
}
impl InstrumentHooks {
#[inline(always)]
pub fn new() -> Option<Self> {
let ptr = unsafe { ffi::instrument_hooks_init() };
if ptr.is_null() {
None
} else {
Some(InstrumentHooks(ptr))
}
}

/// Returns a singleton instance of `InstrumentHooks`.
#[inline(always)]
pub fn instance() -> &'static Self {
static INSTANCE: OnceLock<InstrumentHooks> = 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
})
}
/// Returns a singleton instance of `InstrumentHooks`.
#[inline(always)]
pub fn instance() -> &'static Self {
static INSTANCE: OnceLock<InstrumentHooks> = 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 is_instrumented(&self) -> bool {
unsafe { ffi::instrument_hooks_is_instrumented(self.0) }
}
#[inline(always)]
pub fn is_instrumented(&self) -> bool {
unsafe { ffi::instrument_hooks_is_instrumented(self.0) }
}

#[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 start_benchmark(&self) -> Result<(), u8> {
let result = unsafe { ffi::instrument_hooks_start_benchmark(self.0) };
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 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 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 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 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)
}
#[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 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 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 {
#[cfg(not(target_os = "linux"))]
{
unsafe { ffi::instrument_hooks_current_timestamp() }
}

#[inline(always)]
pub fn current_timestamp() -> u64 {
// Custom implementation to avoid the extra FFI call
#[cfg(target_os = "linux")]
{
use nix::sys::time::TimeValLike;
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) };
}
}
}
}

#[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
impl Drop for InstrumentHooks {
fn drop(&mut self) {
if !self.0.is_null() {
unsafe { ffi::instrument_hooks_deinit(self.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;
Expand Down
Loading