coz_temporary/
lib.rs

1//! Rust support for the `coz` Causal Profiler
2//!
3//! This crate is a translation of the `coz.h` header file provided by `coz` to
4//! Rust, and enables profiling Rust programs with `coz` to profile throughput
5//! and latency.
6//!
7//! For usage information, consult the [`README.md` for the `coz`
8//! repository][coz-readme] as well as the [`README.md` for
9//! `coz-rs`][rust-readme].
10//!
11//! [coz-readme]: https://github.com/plasma-umass/coz/blob/master/README.md
12//! [rust-readme]: https://github.com/alexcrichton/coz-rs/blob/master/README.md
13
14use once_cell::sync::OnceCell;
15use std::cell::Cell;
16use std::ffi::{CStr, CString};
17use std::mem;
18use std::ptr;
19use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
20
21/// Equivalent of the `COZ_PROGRESS` and `COZ_PROGRESS_NAMED` macros
22///
23/// This can be executed as:
24///
25/// ```
26/// coz::progress!();
27/// ```
28///
29/// or ...
30///
31/// ```
32/// coz::progress!("my unique name");
33/// ```
34#[macro_export]
35macro_rules! progress {
36    () => {{
37        static COUNTER: $crate::Counter =
38            $crate::Counter::progress(concat!(file!(), ":", line!()));
39        COUNTER.increment();
40    }};
41    ($name:expr) => {{
42        static COUNTER: $crate::Counter = $crate::Counter::progress($name);
43        COUNTER.increment();
44    }};
45}
46
47/// Equivalent of the `COZ_BEGIN` macro
48///
49/// This can be executed as:
50///
51/// ```
52/// coz::begin!("foo");
53/// ```
54#[macro_export]
55macro_rules! begin {
56    ($name:expr) => {{
57        static COUNTER: $crate::Counter = $crate::Counter::begin($name);
58        COUNTER.increment();
59    }};
60}
61
62/// Equivalent of the `COZ_END` macro
63///
64/// This can be executed as:
65///
66/// ```
67/// coz::end!("foo");
68/// ```
69#[macro_export]
70macro_rules! end {
71    ($name:expr) => {{
72        static COUNTER: $crate::Counter = $crate::Counter::end($name);
73        COUNTER.increment();
74    }};
75}
76
77/// Marks a lexical scope with `coz::begin!` and `coz::end!` which are executed
78/// even on early exit (e.g. via `return`, `?` or `panic!`).
79///
80/// Where this macro is invoked is where a `begin` counter is placed, and then
81/// at the end of the lexical scope (when this macro's local variable goes out
82/// of scope) an `end` counter is placed.
83///
84/// # Examples
85///
86/// ```rust
87/// coz::scope!("outer");
88/// {
89///     coz::scope!("inner");
90/// }
91/// ```
92#[macro_export]
93macro_rules! scope {
94    ($name:expr) => {
95        static BEGIN_COUNTER: $crate::Counter = $crate::Counter::begin($name);
96        static END_COUNTER: $crate::Counter = $crate::Counter::end($name);
97        BEGIN_COUNTER.increment();
98        let _coz_scope_guard = $crate::Guard::new(&END_COUNTER);
99    };
100}
101
102/// Perform one-time per-thread initialization for `coz`.
103///
104/// This may not be necessary to call, but for good measure it's recommended to
105/// call once per thread in your application near where the thread starts.
106/// If you run into issues with segfaults related to SIGPROF handlers this may
107/// help fix the issue since it installs a bigger stack earlier on in the
108/// process.
109pub fn thread_init() {
110    // As one-time program initialization, make sure that our sigaltstack is big
111    // enough. By default coz uses SIGPROF on an alternate signal stack, but the
112    // Rust standard library already sets up a SIGALTSTACK which is
113    // unfortunately too small to run coz's handler. If our sigaltstack looks
114    // too small let's allocate a bigger one and use it here.
115    thread_local!(static SIGALTSTACK_DISABLED: Cell<bool> = Cell::new(false));
116    if SIGALTSTACK_DISABLED.with(|s| s.replace(true)) {
117        return;
118    }
119    unsafe {
120        let mut stack = mem::zeroed();
121        libc::sigaltstack(ptr::null(), &mut stack);
122        let size = 1 << 20; // 1mb
123        if stack.ss_size >= size {
124            return;
125        }
126        let ss_sp = libc::mmap(
127            ptr::null_mut(),
128            size,
129            libc::PROT_READ | libc::PROT_WRITE,
130            libc::MAP_PRIVATE | libc::MAP_ANON,
131            -1,
132            0,
133        );
134        if ss_sp == libc::MAP_FAILED {
135            panic!("failed to allocate alternative stack");
136        }
137        let new_stack = libc::stack_t {
138            ss_sp,
139            ss_flags: 0,
140            ss_size: size,
141        };
142        libc::sigaltstack(&new_stack, ptr::null_mut());
143    }
144}
145
146/// A `coz`-counter which is either intended for throughput or `begin`/`end`
147/// points.
148///
149/// This is typically created by macros above via `progress!()`, `begin!()`, or
150/// `end!()`, but if necessary you can also create one of these manually in your
151/// own application for your own macros.
152pub struct Counter {
153    slot: OnceCell<Option<&'static coz_counter_t>>,
154    ty: libc::c_int,
155    name: &'static str,
156}
157
158const COZ_COUNTER_TYPE_THROUGHPUT: libc::c_int = 1;
159const COZ_COUNTER_TYPE_BEGIN: libc::c_int = 2;
160const COZ_COUNTER_TYPE_END: libc::c_int = 3;
161
162impl Counter {
163    /// Creates a throughput coz counter with the given name.
164    pub const fn progress(name: &'static str) -> Counter {
165        Counter::new(COZ_COUNTER_TYPE_THROUGHPUT, name)
166    }
167
168    /// Creates a latency coz counter with the given name, used for when an
169    /// operation begins.
170    ///
171    /// Note that this counter should be paired with an `end` counter of the
172    /// same name.
173    pub const fn begin(name: &'static str) -> Counter {
174        Counter::new(COZ_COUNTER_TYPE_BEGIN, name)
175    }
176
177    /// Creates a latency coz counter with the given name, used for when an
178    /// operation ends.
179    ///
180    /// Note that this counter should be paired with an `begin` counter of the
181    /// same name.
182    pub const fn end(name: &'static str) -> Counter {
183        Counter::new(COZ_COUNTER_TYPE_END, name)
184    }
185
186    const fn new(ty: libc::c_int, name: &'static str) -> Counter {
187        Counter {
188            slot: OnceCell::new(),
189            ty,
190            name,
191        }
192    }
193
194    /// Increment that an operation happened on this counter.
195    ///
196    /// For throughput counters this should be called in a location where you
197    /// want something to happen more often.
198    ///
199    /// For latency-based counters this should be called before and after the
200    /// operation you'd like to measure the latency of.
201    pub fn increment(&self) {
202        let counter = self.slot.get_or_init(|| self.create_counter());
203        if let Some(counter) = counter {
204            assert_eq!(
205                mem::size_of_val(&counter.count),
206                mem::size_of::<libc::size_t>()
207            );
208            counter.count.fetch_add(1, SeqCst);
209        }
210    }
211
212    fn create_counter(&self) -> Option<&'static coz_counter_t> {
213        let name = CString::new(self.name).unwrap();
214        let ptr = coz_get_counter(self.ty, &name);
215        if ptr.is_null() {
216            None
217        } else {
218            Some(unsafe { &*ptr })
219        }
220    }
221}
222
223/// A type that increments a counter on drop. This allows us to issue the right
224/// coz calls to `begin` and `end` for the duration of a scope, regardless of how
225/// the scope was exited (e.g. by early return, `?` or panic).
226pub struct Guard<'t> {
227    counter: &'t Counter
228}
229
230impl<'t> Guard<'t> {
231    pub fn new(counter: &'t Counter) -> Self {
232        Guard {
233            counter
234        }
235    }
236}
237
238impl<'t> Drop for Guard<'t> {
239    fn drop(&mut self) {
240        self.counter.increment();
241    }
242}
243
244#[repr(C)]
245struct coz_counter_t {
246    count: AtomicUsize,
247    backoff: libc::size_t,
248}
249
250#[cfg(target_os = "linux")]
251fn coz_get_counter(ty: libc::c_int, name: &CStr) -> *mut coz_counter_t {
252    static PTR: AtomicUsize = AtomicUsize::new(1);
253    let mut ptr = PTR.load(SeqCst);
254    if ptr == 1 {
255        let name = CStr::from_bytes_with_nul(b"_coz_get_counter\0").unwrap();
256        ptr = unsafe { libc::dlsym(libc::RTLD_DEFAULT, name.as_ptr() as *const _) as usize };
257        PTR.store(ptr, SeqCst);
258    }
259    if ptr == 0 {
260        return ptr::null_mut();
261    }
262
263    thread_init(); // just in case we haven't already
264
265    unsafe {
266        mem::transmute::<
267            usize,
268            unsafe extern "C" fn(libc::c_int, *const libc::c_char) -> *mut coz_counter_t,
269        >(ptr)(ty, name.as_ptr())
270    }
271}
272
273#[cfg(not(target_os = "linux"))]
274fn coz_get_counter(_ty: libc::c_int, _name: &CStr) -> *mut coz_counter_t {
275    ptr::null_mut()
276}