Skip to main content

scx_forge/
stats.rs

1use std::io::Write;
2use std::sync::atomic::AtomicBool;
3use std::sync::atomic::Ordering;
4use std::sync::Arc;
5use std::time::Duration;
6
7use anyhow::Result;
8use scx_stats::prelude::*;
9use scx_stats_derive::stat_doc;
10use scx_stats_derive::Stats;
11use serde::Deserialize;
12use serde::Serialize;
13
14#[stat_doc]
15#[derive(Clone, Debug, Default, Serialize, Deserialize, Stats)]
16#[stat(top)]
17pub struct Metrics {
18    #[stat(desc = "Number of task direct dispatches")]
19    pub nr_direct_dispatches: u64,
20    #[stat(desc = "Number of task enqueues to vtime-ordered DSQs")]
21    pub nr_enqueues: u64,
22    #[stat(desc = "Number of preemption dispatches on sleeper wakeup")]
23    pub nr_preempt_dispatches: u64,
24    #[stat(desc = "Number of dispatches from the local CPU DSQ")]
25    pub nr_local_dispatches: u64,
26    #[stat(desc = "Number of dispatches stolen from a remote CPU DSQ")]
27    pub nr_remote_dispatches: u64,
28    #[stat(desc = "Number of dispatches from the local LLC DSQ")]
29    pub nr_llc_dispatches: u64,
30    #[stat(desc = "Number of dispatches from the local NUMA node DSQ")]
31    pub nr_node_dispatches: u64,
32    #[stat(desc = "Number of dispatches from the global DSQ")]
33    pub nr_global_dispatches: u64,
34    #[stat(desc = "Number of task dequeue callbacks")]
35    pub nr_dequeues: u64,
36    #[stat(desc = "Number of regular dispatch dequeue callbacks")]
37    pub nr_dispatch_dequeues: u64,
38    #[stat(desc = "Number of scheduling property change dequeue callbacks")]
39    pub nr_sched_change_dequeues: u64,
40    #[stat(desc = "Number of task state tracking errors")]
41    pub nr_task_state_errors: u64,
42    #[stat(desc = "Number of event-heavy task migrations to an idle CPU")]
43    pub nr_event_dispatches: u64,
44    #[stat(desc = "Number of sticky direct dispatches keeping a task on its CPU")]
45    pub nr_ev_sticky_dispatches: u64,
46}
47
48impl Metrics {
49    fn format<W: Write>(&self, w: &mut W) -> Result<()> {
50        writeln!(
51            w,
52            "[{}] tasks -> dispatch -> d: {:<5} enq: {:<5} preempt: {:<5} l: {:<5} r: {:<5} llc: {:<5} node: {:<5} g: {:<5} deq: {:<5} dd: {:<5} change: {:<5} err: {:<5} ev: {:<5} sticky: {:<5}",
53            crate::SCHEDULER_NAME,
54            self.nr_direct_dispatches,
55            self.nr_enqueues,
56            self.nr_preempt_dispatches,
57            self.nr_local_dispatches,
58            self.nr_remote_dispatches,
59            self.nr_llc_dispatches,
60            self.nr_node_dispatches,
61            self.nr_global_dispatches,
62            self.nr_dequeues,
63            self.nr_dispatch_dequeues,
64            self.nr_sched_change_dequeues,
65            self.nr_task_state_errors,
66            self.nr_event_dispatches,
67            self.nr_ev_sticky_dispatches,
68        )?;
69        Ok(())
70    }
71
72    fn delta(&self, rhs: &Self) -> Self {
73        Self {
74            nr_direct_dispatches: self.nr_direct_dispatches - rhs.nr_direct_dispatches,
75            nr_enqueues: self.nr_enqueues - rhs.nr_enqueues,
76            nr_preempt_dispatches: self.nr_preempt_dispatches - rhs.nr_preempt_dispatches,
77            nr_local_dispatches: self.nr_local_dispatches - rhs.nr_local_dispatches,
78            nr_remote_dispatches: self.nr_remote_dispatches - rhs.nr_remote_dispatches,
79            nr_llc_dispatches: self.nr_llc_dispatches - rhs.nr_llc_dispatches,
80            nr_node_dispatches: self.nr_node_dispatches - rhs.nr_node_dispatches,
81            nr_global_dispatches: self.nr_global_dispatches - rhs.nr_global_dispatches,
82            nr_dequeues: self.nr_dequeues - rhs.nr_dequeues,
83            nr_dispatch_dequeues: self.nr_dispatch_dequeues - rhs.nr_dispatch_dequeues,
84            nr_sched_change_dequeues: self.nr_sched_change_dequeues - rhs.nr_sched_change_dequeues,
85            nr_task_state_errors: self.nr_task_state_errors - rhs.nr_task_state_errors,
86            nr_event_dispatches: self.nr_event_dispatches - rhs.nr_event_dispatches,
87            nr_ev_sticky_dispatches: self.nr_ev_sticky_dispatches - rhs.nr_ev_sticky_dispatches,
88            ..self.clone()
89        }
90    }
91}
92
93pub fn server_data() -> StatsServerData<(), Metrics> {
94    let open: Box<dyn StatsOpener<(), Metrics>> = Box::new(move |(req_ch, res_ch)| {
95        req_ch.send(())?;
96        let mut prev = res_ch.recv()?;
97
98        let read: Box<dyn StatsReader<(), Metrics>> = Box::new(move |_args, (req_ch, res_ch)| {
99            req_ch.send(())?;
100            let cur = res_ch.recv()?;
101            let delta = cur.delta(&prev);
102            prev = cur;
103            delta.to_json()
104        });
105
106        Ok(read)
107    });
108
109    StatsServerData::new()
110        .add_meta(Metrics::meta())
111        .add_ops("top", StatsOps { open, close: None })
112}
113
114pub fn monitor(intv: Duration, shutdown: Arc<AtomicBool>) -> Result<()> {
115    scx_utils::monitor_stats::<Metrics>(
116        &[],
117        intv,
118        || shutdown.load(Ordering::Relaxed),
119        |metrics| metrics.format(&mut std::io::stdout()),
120    )
121}