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 = "Average CPU utilization %")]
19 pub cpu_util: u64,
20 #[stat(desc = "Busy utilization threshold %")]
21 pub cpu_thresh: u64,
22 #[stat(desc = "Direct dispatch due to high perf events (migration)")]
23 pub nr_event_dispatches: u64,
24 #[stat(desc = "Kept on same CPU due to perf sticky threshold")]
25 pub nr_ev_sticky_dispatches: u64,
26}
27
28impl Metrics {
29 fn format<W: Write>(&self, w: &mut W) -> Result<()> {
30 writeln!(
31 w,
32 "[{}] CPUs {:>5.1}% {} ev_dispatches={} ev_sticky_dispatches={}",
33 crate::SCHEDULER_NAME,
34 (self.cpu_util as f64) * 100.0 / 1024.0,
35 if self.cpu_util >= self.cpu_thresh {
36 "[deadline]"
37 } else {
38 "[round-robin]"
39 },
40 self.nr_event_dispatches,
41 self.nr_ev_sticky_dispatches,
42 )?;
43 Ok(())
44 }
45
46 fn delta(&self, rhs: &Self) -> Self {
47 Self {
48 nr_event_dispatches: self.nr_event_dispatches - rhs.nr_event_dispatches,
49 nr_ev_sticky_dispatches: self.nr_ev_sticky_dispatches - rhs.nr_ev_sticky_dispatches,
50 ..self.clone()
51 }
52 }
53}
54
55pub fn server_data() -> StatsServerData<(), Metrics> {
56 let open: Box<dyn StatsOpener<(), Metrics>> = Box::new(move |(req_ch, res_ch)| {
57 req_ch.send(())?;
58 let mut prev = res_ch.recv()?;
59
60 let read: Box<dyn StatsReader<(), Metrics>> = Box::new(move |_args, (req_ch, res_ch)| {
61 req_ch.send(())?;
62 let cur = res_ch.recv()?;
63 let delta = cur.delta(&prev);
64 prev = cur;
65 delta.to_json()
66 });
67
68 Ok(read)
69 });
70
71 StatsServerData::new()
72 .add_meta(Metrics::meta())
73 .add_ops("top", StatsOps { open, close: None })
74}
75
76pub fn monitor(intv: Duration, shutdown: Arc<AtomicBool>) -> Result<()> {
77 scx_utils::monitor_stats::<Metrics>(
78 &[],
79 intv,
80 || shutdown.load(Ordering::Relaxed),
81 |metrics| metrics.format(&mut std::io::stdout()),
82 )
83}