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}
23
24impl Metrics {
25 fn format<W: Write>(&self, w: &mut W) -> Result<()> {
26 writeln!(
27 w,
28 "[{}] CPUs {:>5.1}% {}",
29 crate::SCHEDULER_NAME,
30 (self.cpu_util as f64) * 100.0 / 1024.0,
31 if self.cpu_util >= self.cpu_thresh {
32 "[deadline]"
33 } else {
34 "[round-robin]"
35 },
36 )?;
37 Ok(())
38 }
39
40 fn delta(&self, _: &Self) -> Self {
41 Self { ..self.clone() }
42 }
43}
44
45pub fn server_data() -> StatsServerData<(), Metrics> {
46 let open: Box<dyn StatsOpener<(), Metrics>> = Box::new(move |(req_ch, res_ch)| {
47 req_ch.send(())?;
48 let mut prev = res_ch.recv()?;
49
50 let read: Box<dyn StatsReader<(), Metrics>> = Box::new(move |_args, (req_ch, res_ch)| {
51 req_ch.send(())?;
52 let cur = res_ch.recv()?;
53 let delta = cur.delta(&prev);
54 prev = cur;
55 delta.to_json()
56 });
57
58 Ok(read)
59 });
60
61 StatsServerData::new()
62 .add_meta(Metrics::meta())
63 .add_ops("top", StatsOps { open, close: None })
64}
65
66pub fn monitor(intv: Duration, shutdown: Arc<AtomicBool>) -> Result<()> {
67 scx_utils::monitor_stats::<Metrics>(
68 &[],
69 intv,
70 || shutdown.load(Ordering::Relaxed),
71 |metrics| metrics.format(&mut std::io::stdout()),
72 )
73}