Skip to main content

strat9_kernel/process/scheduler/
perf_counters.rs

1//! Lightweight TSC-based performance counters for critical kernel paths.
2//!
3//! Each counter pair tracks total TSC cycles spent + call count.
4//! Use `PerfScope` (RAII) to instrument a section without touching the
5//! hot path more than two `rdtsc` calls + two relaxed atomic adds.
6
7use core::sync::atomic::{AtomicU64, Ordering};
8
9// ---------------------------------------------------------------------------
10// Counters
11// ---------------------------------------------------------------------------
12
13/// Timer IRQ handler (`timer_tick`).
14pub static IRQ_TIMER_COUNT: AtomicU64 = AtomicU64::new(0);
15pub static IRQ_TIMER_TSC: AtomicU64 = AtomicU64::new(0);
16
17/// Scheduler lock acquisition in `yield_task`.
18pub static SCHED_YIELD_COUNT: AtomicU64 = AtomicU64::new(0);
19pub static SCHED_YIELD_TSC: AtomicU64 = AtomicU64::new(0);
20
21/// Preemption path (`maybe_preempt`).
22pub static SCHED_PREEMPT_COUNT: AtomicU64 = AtomicU64::new(0);
23pub static SCHED_PREEMPT_TSC: AtomicU64 = AtomicU64::new(0);
24
25/// Post-switch fixup (`finish_interrupt_switch`).
26pub static CTX_SWITCH_COUNT: AtomicU64 = AtomicU64::new(0);
27pub static CTX_SWITCH_TSC: AtomicU64 = AtomicU64::new(0);
28
29// ---------------------------------------------------------------------------
30// RAII scope helper
31// ---------------------------------------------------------------------------
32
33/// Measures elapsed TSC cycles between construction and drop, accumulating
34/// into the given counter pair.
35///
36/// Usage:
37/// ```ignore
38/// let _perf = PerfScope::new(&IRQ_TIMER_TSC, &IRQ_TIMER_COUNT);
39/// // ... measured code ...
40/// // counters updated on drop
41/// ```
42pub struct PerfScope {
43    start: u64,
44    accumulator: &'static AtomicU64,
45    counter: &'static AtomicU64,
46}
47
48impl PerfScope {
49    #[inline]
50    pub fn new(accumulator: &'static AtomicU64, counter: &'static AtomicU64) -> Self {
51        Self {
52            start: crate::arch::x86_64::rdtsc(),
53            accumulator,
54            counter,
55        }
56    }
57}
58
59impl Drop for PerfScope {
60    #[inline]
61    fn drop(&mut self) {
62        let elapsed = crate::arch::x86_64::rdtsc().wrapping_sub(self.start);
63        self.accumulator.fetch_add(elapsed, Ordering::Relaxed);
64        self.counter.fetch_add(1, Ordering::Relaxed);
65    }
66}
67
68// ---------------------------------------------------------------------------
69// Snapshot for display
70// ---------------------------------------------------------------------------
71
72/// Summary of one counter pair, ready for display.
73pub struct PerfStat {
74    pub name: &'static str,
75    pub count: u64,
76    pub total_tsc: u64,
77}
78
79impl PerfStat {
80    /// Average in microseconds (requires TSC_KHZ).
81    pub fn avg_us(&self, tsc_khz: u64) -> u64 {
82        if self.count == 0 || tsc_khz == 0 {
83            return 0;
84        }
85        // avg_tsc = total_tsc / count
86        // avg_us  = avg_tsc * 1000 / tsc_khz
87        (self.total_tsc / self.count).saturating_mul(1_000) / tsc_khz
88    }
89}
90
91/// Return a snapshot of all perf counters.
92pub fn snapshot() -> [PerfStat; 4] {
93    [
94        PerfStat {
95            name: "irq_timer",
96            count: IRQ_TIMER_COUNT.load(Ordering::Relaxed),
97            total_tsc: IRQ_TIMER_TSC.load(Ordering::Relaxed),
98        },
99        PerfStat {
100            name: "sched_yield",
101            count: SCHED_YIELD_COUNT.load(Ordering::Relaxed),
102            total_tsc: SCHED_YIELD_TSC.load(Ordering::Relaxed),
103        },
104        PerfStat {
105            name: "preempt",
106            count: SCHED_PREEMPT_COUNT.load(Ordering::Relaxed),
107            total_tsc: SCHED_PREEMPT_TSC.load(Ordering::Relaxed),
108        },
109        PerfStat {
110            name: "ctx_switch",
111            count: CTX_SWITCH_COUNT.load(Ordering::Relaxed),
112            total_tsc: CTX_SWITCH_TSC.load(Ordering::Relaxed),
113        },
114    ]
115}