use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
static DEEP_PROFILING_PERMITTED: AtomicBool = AtomicBool::new(false);
static DEEP_PROFILING_CONSUMERS: AtomicUsize = AtomicUsize::new(0);
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct DebugOptions {
pub inspection_and_agent_control: bool,
pub deep_intrusive_profiling: bool,
}
impl DebugOptions {
#[must_use]
pub const fn effective_deep_profiling(self) -> bool {
self.deep_intrusive_profiling
}
}
pub fn set_deep_profiling_permitted(permitted: bool) {
DEEP_PROFILING_PERMITTED.store(permitted, Ordering::Relaxed);
}
#[inline]
#[must_use]
pub fn deep_profiling_permitted() -> bool {
DEEP_PROFILING_PERMITTED.load(Ordering::Relaxed)
}
#[must_use = "sampling stops as soon as the guard is dropped"]
pub fn begin_deep_profiling() -> Option<DeepProfilingGuard> {
if !deep_profiling_permitted() {
return None;
}
DEEP_PROFILING_CONSUMERS.fetch_add(1, Ordering::Relaxed);
Some(DeepProfilingGuard { _private: () })
}
#[inline]
#[must_use]
pub fn deep_profiling_consumers() -> usize {
DEEP_PROFILING_CONSUMERS.load(Ordering::Relaxed)
}
#[derive(Debug)]
pub struct DeepProfilingGuard {
_private: (),
}
impl Drop for DeepProfilingGuard {
fn drop(&mut self) {
let _ =
DEEP_PROFILING_CONSUMERS.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |count| {
Some(count.saturating_sub(1))
});
}
}
#[inline(always)]
pub fn deep_profiling_enabled() -> bool {
DEEP_PROFILING_PERMITTED.load(Ordering::Relaxed)
&& DEEP_PROFILING_CONSUMERS.load(Ordering::Relaxed) > 0
}
#[cfg(test)]
mod tests {
use super::*;
static SERIALISE: std::sync::Mutex<()> = std::sync::Mutex::new(());
fn exclusive() -> std::sync::MutexGuard<'static, ()> {
let guard = SERIALISE.lock().unwrap_or_else(|e| e.into_inner());
set_deep_profiling_permitted(false);
assert_eq!(
deep_profiling_consumers(),
0,
"a previous test leaked a guard",
);
guard
}
#[test]
fn permission_alone_does_not_start_sampling() {
let _lock = exclusive();
set_deep_profiling_permitted(true);
assert!(deep_profiling_permitted());
assert!(
!deep_profiling_enabled(),
"sampling must wait for a consumer, not begin with the setting",
);
set_deep_profiling_permitted(false);
}
#[test]
fn a_forbidden_profile_hands_back_no_guard() {
let _lock = exclusive();
assert!(begin_deep_profiling().is_none());
assert!(!deep_profiling_enabled());
}
#[test]
fn sampling_runs_until_the_last_consumer_drops() {
let _lock = exclusive();
set_deep_profiling_permitted(true);
let first = begin_deep_profiling().expect("permitted, so a guard is available");
let second = begin_deep_profiling().expect("a second consumer may overlap the first");
assert!(deep_profiling_enabled());
drop(first);
assert!(
deep_profiling_enabled(),
"one consumer finishing must not stop the other's collection",
);
drop(second);
assert!(!deep_profiling_enabled(), "the last drop stops sampling");
assert_eq!(deep_profiling_consumers(), 0);
set_deep_profiling_permitted(false);
}
#[test]
fn an_unwound_guard_still_stops_sampling() {
let _lock = exclusive();
set_deep_profiling_permitted(true);
let panicked = std::panic::catch_unwind(|| {
let _guard = begin_deep_profiling().expect("permitted");
panic!("a profiling request failed mid-flight");
});
assert!(panicked.is_err(), "the closure above must have panicked");
assert_eq!(
deep_profiling_consumers(),
0,
"the guard's drop must have run while unwinding",
);
assert!(!deep_profiling_enabled());
set_deep_profiling_permitted(false);
}
#[test]
fn withdrawing_permission_stops_a_live_consumer() {
let _lock = exclusive();
set_deep_profiling_permitted(true);
let guard = begin_deep_profiling().expect("permitted");
assert!(deep_profiling_enabled());
set_deep_profiling_permitted(false);
assert!(!deep_profiling_enabled());
drop(guard);
assert_eq!(deep_profiling_consumers(), 0);
}
#[test]
fn deep_profiling_is_independent_of_the_inspection_plane() {
assert!(!DebugOptions::default().effective_deep_profiling());
for inspection in [false, true] {
assert!(
DebugOptions {
inspection_and_agent_control: inspection,
deep_intrusive_profiling: true,
}
.effective_deep_profiling(),
"deep profiling should follow its own switch (inspection {inspection})"
);
assert!(
!DebugOptions {
inspection_and_agent_control: inspection,
deep_intrusive_profiling: false,
}
.effective_deep_profiling(),
"deep profiling off means off (inspection {inspection})"
);
}
}
}