codspeed/instrument_hooks/
mod.rs1#[cfg(use_instrument_hooks)]
2mod ffi;
3
4#[cfg(use_instrument_hooks)]
5mod linux_impl {
6
7 use super::ffi;
8 use std::ffi::CString;
9 use std::sync::OnceLock;
10
11 #[derive(PartialEq)]
12 pub struct InstrumentHooks(*mut ffi::InstrumentHooks);
13
14 unsafe impl Send for InstrumentHooks {}
15 unsafe impl Sync for InstrumentHooks {}
16
17 impl InstrumentHooks {
18 #[inline(always)]
19 pub fn new() -> Option<Self> {
20 let ptr = unsafe { ffi::instrument_hooks_init() };
21 if ptr.is_null() {
22 None
23 } else {
24 Some(InstrumentHooks(ptr))
25 }
26 }
27
28 #[inline(always)]
30 pub fn instance() -> &'static Self {
31 static INSTANCE: OnceLock<InstrumentHooks> = OnceLock::new();
32 INSTANCE.get_or_init(|| {
33 let instance =
34 InstrumentHooks::new().expect("Failed to initialize InstrumentHooks");
35 instance
36 .set_integration("codspeed-rust", env!("CARGO_PKG_VERSION"))
37 .expect("Failed to set integration");
38 instance
39 })
40 }
41
42 #[inline(always)]
43 pub fn is_instrumented(&self) -> bool {
44 unsafe { ffi::instrument_hooks_is_instrumented(self.0) }
45 }
46
47 #[inline(always)]
48 pub fn start_benchmark(&self) -> Result<(), u8> {
49 let result = unsafe { ffi::instrument_hooks_start_benchmark(self.0) };
50 if result == 0 {
51 Ok(())
52 } else {
53 Err(result)
54 }
55 }
56
57 #[inline(always)]
58 pub fn stop_benchmark(&self) -> Result<(), u8> {
59 let result = unsafe { ffi::instrument_hooks_stop_benchmark(self.0) };
60 if result == 0 {
61 Ok(())
62 } else {
63 Err(result)
64 }
65 }
66
67 #[inline(always)]
68 pub fn set_executed_benchmark(&self, uri: &str) -> Result<(), u8> {
69 let pid = std::process::id() as i32;
70 let c_uri = CString::new(uri).map_err(|_| 1u8)?;
71 let result = unsafe {
72 ffi::instrument_hooks_set_executed_benchmark(self.0, pid, c_uri.as_ptr())
73 };
74 if result == 0 {
75 Ok(())
76 } else {
77 Err(result)
78 }
79 }
80
81 #[inline(always)]
82 pub fn set_integration(&self, name: &str, version: &str) -> Result<(), u8> {
83 let c_name = CString::new(name).map_err(|_| 1u8)?;
84 let c_version = CString::new(version).map_err(|_| 1u8)?;
85 let result = unsafe {
86 ffi::instrument_hooks_set_integration(self.0, c_name.as_ptr(), c_version.as_ptr())
87 };
88 if result == 0 {
89 Ok(())
90 } else {
91 Err(result)
92 }
93 }
94
95 #[inline(always)]
96 pub fn add_benchmark_timestamps(&self, start: u64, end: u64) {
97 let pid = std::process::id();
98
99 unsafe {
100 ffi::instrument_hooks_add_marker(
101 self.0,
102 pid,
103 ffi::MARKER_TYPE_BENCHMARK_START as u8,
104 start,
105 )
106 };
107 unsafe {
108 ffi::instrument_hooks_add_marker(
109 self.0,
110 pid,
111 ffi::MARKER_TYPE_BENCHMARK_END as u8,
112 end,
113 )
114 };
115 }
116
117 #[inline(always)]
118 pub fn current_timestamp() -> u64 {
119 #[cfg(target_os = "linux")]
120 {
121 use nix::sys::time::TimeValLike;
122
123 nix::time::clock_gettime(nix::time::ClockId::CLOCK_MONOTONIC)
124 .expect("Failed to get current time")
125 .num_nanoseconds() as u64
126 }
127
128 #[cfg(not(target_os = "linux"))]
129 unsafe {
130 ffi::instrument_hooks_current_timestamp()
131 }
132 }
133
134 pub fn disable_callgrind_markers() {
135 unsafe {
136 ffi::instrument_hooks_set_feature(
137 ffi::instrument_hooks_feature_t_FEATURE_DISABLE_CALLGRIND_MARKERS,
138 true,
139 )
140 };
141 }
142 }
143
144 impl Drop for InstrumentHooks {
145 fn drop(&mut self) {
146 if !self.0.is_null() {
147 unsafe { ffi::instrument_hooks_deinit(self.0) };
148 }
149 }
150 }
151}
152
153#[cfg(not(use_instrument_hooks))]
154mod other_impl {
155 #[derive(PartialEq)]
156 pub struct InstrumentHooks;
157
158 impl InstrumentHooks {
159 pub fn instance() -> &'static Self {
160 static INSTANCE: InstrumentHooks = InstrumentHooks;
161 &INSTANCE
162 }
163
164 pub fn is_instrumented(&self) -> bool {
165 false
166 }
167
168 pub fn start_benchmark(&self) -> Result<(), u8> {
169 Ok(())
170 }
171
172 pub fn stop_benchmark(&self) -> Result<(), u8> {
173 Ok(())
174 }
175
176 pub fn set_executed_benchmark(&self, _uri: &str) -> Result<(), u8> {
177 Ok(())
178 }
179
180 pub fn set_integration(&self, _name: &str, _version: &str) -> Result<(), u8> {
181 Ok(())
182 }
183
184 pub fn add_benchmark_timestamps(&self, _start: u64, _end: u64) {}
185
186 pub fn current_timestamp() -> u64 {
187 0
188 }
189
190 pub fn disable_callgrind_markers() {}
191 }
192}
193
194#[cfg(use_instrument_hooks)]
195pub use linux_impl::InstrumentHooks;
196
197#[cfg(not(use_instrument_hooks))]
198pub use other_impl::InstrumentHooks;
199
200#[cfg(test)]
201mod tests {
202 use super::InstrumentHooks;
203
204 #[test]
205 fn test_instrument_hooks() {
206 let hooks = InstrumentHooks::instance();
207 assert!(!hooks.is_instrumented() || hooks.start_benchmark().is_ok());
208 assert!(hooks.set_executed_benchmark("test_uri").is_ok());
209 assert!(hooks.set_integration("test_integration", "1.0.0").is_ok());
210 let start = InstrumentHooks::current_timestamp();
211 let end = start + 1_000_000; hooks.add_benchmark_timestamps(start, end);
213 assert!(!hooks.is_instrumented() || hooks.stop_benchmark().is_ok());
214 }
215}