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/// Deferred work processing (`process_deferred_work`).
30pub static DEFERRED_WORK_COUNT: AtomicU64 = AtomicU64::new(0);
31pub static DEFERRED_WORK_TSC: AtomicU64 = AtomicU64::new(0);
32
33// ---------------------------------------------------------------------------
34// RAII scope helper
35// ---------------------------------------------------------------------------
36
37/// Measures elapsed TSC cycles between construction and drop, accumulating
38/// into the given counter pair.
39///
40/// Usage:
41/// ```ignore
42/// let _perf = PerfScope::new(&IRQ_TIMER_TSC, &IRQ_TIMER_COUNT);
43/// // ... measured code ...
44/// // counters updated on drop
45/// ```
46pub struct PerfScope {
47    start: u64,
48    accumulator: &'static AtomicU64,
49    counter: &'static AtomicU64,
50}
51
52impl PerfScope {
53    #[inline]
54    pub fn new(accumulator: &'static AtomicU64, counter: &'static AtomicU64) -> Self {
55        Self {
56            start: crate::arch::rdtsc(),
57            accumulator,
58            counter,
59        }
60    }
61}
62
63impl Drop for PerfScope {
64    #[inline]
65    fn drop(&mut self) {
66        let elapsed = crate::arch::rdtsc().wrapping_sub(self.start);
67        self.accumulator.fetch_add(elapsed, Ordering::Relaxed);
68        self.counter.fetch_add(1, Ordering::Relaxed);
69    }
70}
71
72// ---------------------------------------------------------------------------
73// Snapshot for display
74// ---------------------------------------------------------------------------
75
76/// Summary of one counter pair, ready for display.
77pub struct PerfStat {
78    pub name: &'static str,
79    pub count: u64,
80    pub total_tsc: u64,
81}
82
83impl PerfStat {
84    /// Average in microseconds (requires TSC_KHZ).
85    pub fn avg_us(&self, tsc_khz: u64) -> u64 {
86        if self.count == 0 || tsc_khz == 0 {
87            return 0;
88        }
89        // avg_tsc = total_tsc / count
90        // avg_us  = avg_tsc * 1000 / tsc_khz
91        (self.total_tsc / self.count).saturating_mul(1_000) / tsc_khz
92    }
93}
94
95/// Return a snapshot of all perf counters.
96pub fn snapshot() -> [PerfStat; 5] {
97    [
98        PerfStat {
99            name: "irq_timer",
100            count: IRQ_TIMER_COUNT.load(Ordering::Relaxed),
101            total_tsc: IRQ_TIMER_TSC.load(Ordering::Relaxed),
102        },
103        PerfStat {
104            name: "sched_yield",
105            count: SCHED_YIELD_COUNT.load(Ordering::Relaxed),
106            total_tsc: SCHED_YIELD_TSC.load(Ordering::Relaxed),
107        },
108        PerfStat {
109            name: "preempt",
110            count: SCHED_PREEMPT_COUNT.load(Ordering::Relaxed),
111            total_tsc: SCHED_PREEMPT_TSC.load(Ordering::Relaxed),
112        },
113        PerfStat {
114            name: "ctx_switch",
115            count: CTX_SWITCH_COUNT.load(Ordering::Relaxed),
116            total_tsc: CTX_SWITCH_TSC.load(Ordering::Relaxed),
117        },
118        PerfStat {
119            name: "dwork",
120            count: DEFERRED_WORK_COUNT.load(Ordering::Relaxed),
121            total_tsc: DEFERRED_WORK_TSC.load(Ordering::Relaxed),
122        },
123    ]
124}