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 = "Scheduler mode")]
19 pub sched_mode: u32,
20 #[stat(desc = "Number of times a task was enqueued to a ATQ")]
21 pub atq_enq: u64,
22 #[stat(desc = "Number of times a task was re-enqueued to a ATQ")]
23 pub atq_reenq: u64,
24 #[stat(desc = "Number of times tasks have switched DSQs")]
25 pub dsq_change: u64,
26 #[stat(desc = "Number of times tasks have stayed on the same DSQ")]
27 pub same_dsq: u64,
28 #[stat(desc = "Number of times a task kept running")]
29 pub keep: u64,
30 #[stat(desc = "Number of times a task was enqueued to CPUC DSQ")]
31 pub enq_cpu: u64,
32 #[stat(desc = "Number of times a task was enqueued to LLC DSQ")]
33 pub enq_llc: u64,
34 #[stat(desc = "Number of times a task was enqueued to interactive DSQ")]
35 pub enq_intr: u64,
36 #[stat(desc = "Number of times a task was enqueued to migration DSQ")]
37 pub enq_mig: u64,
38 #[stat(desc = "Number of times a select_cpu pick 2 load balancing occured")]
39 pub select_pick2: u64,
40 #[stat(desc = "Number of times a dispatch pick 2 load balancing occured")]
41 pub dispatch_pick2: u64,
42 #[stat(desc = "Number of times a task migrated LLCs")]
43 pub llc_migrations: u64,
44 #[stat(desc = "Number of times a task migrated NUMA nodes")]
45 pub node_migrations: u64,
46 #[stat(desc = "Number of times tasks have directly been dispatched to local per CPU DSQs")]
47 pub direct: u64,
48 #[stat(desc = "Number of times tasks have dispatched to an idle local per CPU DSQs")]
49 pub idle: u64,
50 #[stat(desc = "Number of times tasks have been woken to the previous CPU")]
51 pub wake_prev: u64,
52 #[stat(desc = "Number of times tasks have been woken to the previous llc")]
53 pub wake_llc: u64,
54 #[stat(desc = "Number of times tasks have been woken and migrated llc")]
55 pub wake_mig: u64,
56}
57
58impl Metrics {
59 fn format<W: Write>(&self, w: &mut W) -> Result<()> {
60 writeln!(
61 w,
62 "direct/idle/keep {}/{}/{}\n\tdsq same/migrate {}/{}\n\tatq enq/reenq {}/{}\n\tenq cpu/llc/intr/mig {}/{}/{}/{}",
63 self.direct,
64 self.idle,
65 self.keep,
66 self.same_dsq,
67 self.dsq_change,
68 self.atq_enq,
69 self.atq_reenq,
70 self.enq_cpu,
71 self.enq_llc,
72 self.enq_intr,
73 self.enq_mig,
74 )?;
75 writeln!(
76 w,
77 "\twake prev/llc/mig {}/{}/{}\n\tpick2 select/dispatch {}/{}\n\tmigrations llc/node: {}/{}",
78 self.wake_prev,
79 self.wake_llc,
80 self.wake_mig,
81 self.select_pick2,
82 self.dispatch_pick2,
83 self.llc_migrations,
84 self.node_migrations,
85 )?;
86 Ok(())
87 }
88
89 fn delta(&self, rhs: &Self) -> Self {
90 Self {
91 atq_enq: self.atq_enq - rhs.atq_enq,
92 atq_reenq: self.atq_reenq - rhs.atq_reenq,
93 direct: self.direct - rhs.direct,
94 idle: self.idle - rhs.idle,
95 dsq_change: self.dsq_change - rhs.dsq_change,
96 same_dsq: self.same_dsq - rhs.same_dsq,
97 keep: self.keep - rhs.keep,
98 enq_cpu: self.enq_cpu - rhs.enq_cpu,
99 enq_llc: self.enq_llc - rhs.enq_llc,
100 enq_intr: self.enq_intr - rhs.enq_intr,
101 enq_mig: self.enq_mig - rhs.enq_mig,
102 select_pick2: self.select_pick2 - rhs.select_pick2,
103 dispatch_pick2: self.dispatch_pick2 - rhs.dispatch_pick2,
104 llc_migrations: self.llc_migrations - rhs.llc_migrations,
105 node_migrations: self.node_migrations - rhs.node_migrations,
106 wake_prev: self.wake_prev - rhs.wake_prev,
107 wake_llc: self.wake_llc - rhs.wake_llc,
108 wake_mig: self.wake_mig - rhs.wake_mig,
109 ..self.clone()
110 }
111 }
112}
113pub fn server_data() -> StatsServerData<(), Metrics> {
114 let open: Box<dyn StatsOpener<(), Metrics>> = Box::new(move |(req_ch, res_ch)| {
115 req_ch.send(())?;
116 let mut prev = res_ch.recv()?;
117
118 let read: Box<dyn StatsReader<(), Metrics>> = Box::new(move |_args, (req_ch, res_ch)| {
119 req_ch.send(())?;
120 let cur = res_ch.recv()?;
121 let delta = cur.delta(&prev);
122 prev = cur;
123 delta.to_json()
124 });
125
126 Ok(read)
127 });
128
129 StatsServerData::new()
130 .add_meta(Metrics::meta())
131 .add_ops("top", StatsOps { open, close: None })
132}
133
134pub fn monitor(intv: Duration, shutdown: Arc<AtomicBool>) -> Result<()> {
135 scx_utils::monitor_stats::<Metrics>(
136 &[],
137 intv,
138 || shutdown.load(Ordering::Relaxed),
139 |metrics| metrics.format(&mut std::io::stdout()),
140 )
141}