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}