use crate::sync::atomic::{AtomicU32, Ordering};
pub const BUCKETS: usize = 16;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Kind {
IrqEntry,
DispatchDecision,
CriticalSection,
SchedulingWake,
}
const KINDS: usize = 4;
fn index(kind: Kind) -> usize {
match kind {
Kind::IrqEntry => 0,
Kind::DispatchDecision => 1,
Kind::CriticalSection => 2,
Kind::SchedulingWake => 3,
}
}
#[cfg(not(loom))]
static HISTOGRAMS: [[AtomicU32; BUCKETS]; KINDS] =
[const { [const { AtomicU32::new(0) }; BUCKETS] }; KINDS];
#[cfg(loom)]
loom::lazy_static! {
static ref HISTOGRAMS: [[AtomicU32; BUCKETS]; KINDS] =
core::array::from_fn(|_| core::array::from_fn(|_| AtomicU32::new(0)));
}
#[cfg(not(loom))]
static MAX_CYCLES: [AtomicU32; KINDS] = [const { AtomicU32::new(0) }; KINDS];
#[cfg(loom)]
loom::lazy_static! {
static ref MAX_CYCLES: [AtomicU32; KINDS] = core::array::from_fn(|_| AtomicU32::new(0));
}
#[cfg(any(feature = "latency-histograms", test))]
fn bucket_of(cycles: u64) -> usize {
if cycles == 0 {
return 0;
}
let b = 63 - cycles.leading_zeros() as usize;
b.min(BUCKETS - 1)
}
#[cfg(feature = "latency-histograms")]
pub fn record(kind: Kind, cycles: u64) {
HISTOGRAMS[index(kind)][bucket_of(cycles)].fetch_add(1, Ordering::Relaxed);
let capped = cycles.min(u32::MAX as u64) as u32;
MAX_CYCLES[index(kind)].fetch_max(capped, Ordering::Relaxed);
}
#[cfg(not(feature = "latency-histograms"))]
#[inline(always)]
pub fn record(_kind: Kind, _cycles: u64) {}
pub fn snapshot(kind: Kind) -> [u32; BUCKETS] {
core::array::from_fn(|b| HISTOGRAMS[index(kind)][b].load(Ordering::Relaxed))
}
pub fn max_cycles(kind: Kind) -> u32 {
MAX_CYCLES[index(kind)].load(Ordering::Relaxed)
}
pub fn name(kind: Kind) -> &'static str {
match kind {
Kind::IrqEntry => "irq_entry",
Kind::DispatchDecision => "dispatch",
Kind::CriticalSection => "critsec",
Kind::SchedulingWake => "sched_wake",
}
}
pub const ALL_KINDS: [Kind; KINDS] = [
Kind::IrqEntry,
Kind::DispatchDecision,
Kind::CriticalSection,
Kind::SchedulingWake,
];
#[cfg(feature = "test-support")]
pub(crate) fn reset_for_test() {
for hist in HISTOGRAMS.iter() {
for b in hist.iter() {
b.store(0, Ordering::Relaxed);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn bucket_of_boundaries() {
assert_eq!(bucket_of(0), 0);
assert_eq!(bucket_of(1), 0);
assert_eq!(bucket_of(2), 1);
assert_eq!(bucket_of(3), 1);
assert_eq!(bucket_of(4), 2);
assert_eq!(bucket_of(u64::MAX), BUCKETS - 1);
}
#[cfg(feature = "latency-histograms")]
#[test]
fn record_and_snapshot() {
crate::kernel_test! {
record(Kind::IrqEntry, 5);
record(Kind::IrqEntry, 5);
record(Kind::IrqEntry, 100);
let snap = snapshot(Kind::IrqEntry);
assert_eq!(snap[bucket_of(5)], 2);
assert_eq!(snap[bucket_of(100)], 1);
}
}
}