1use std::io::Write;
2use std::time::Duration;
3
4use anyhow::Result;
5use scx_stats::prelude::*;
6use scx_stats_derive::Stats;
7use scx_stats_derive::stat_doc;
8use serde::Deserialize;
9use serde::Serialize;
10
11#[stat_doc]
12#[derive(Clone, Debug, Default, Serialize, Deserialize, Stats)]
13#[stat(top)]
14pub struct Metrics {
15 #[stat(desc = "Number of online CPUs")]
16 pub nr_cpus: u64,
17 #[stat(desc = "Amount of tasks currently running")]
18 pub nr_running: u64,
19 #[stat(desc = "Amount of tasks queued to the user-space scheduler")]
20 pub nr_queued: u64,
21 #[stat(desc = "Amount of tasks in the user-space scheduler waiting to be dispatched")]
22 pub nr_scheduled: u64,
23 #[stat(desc = "Amount of user-space scheduler's page faults (should be always 0)")]
24 pub nr_page_faults: u64,
25 #[stat(desc = "Number of task dispatched by the user-space scheduler")]
26 pub nr_user_dispatches: u64,
27 #[stat(desc = "Number of task dispatched directly by the kernel")]
28 pub nr_kernel_dispatches: u64,
29 #[stat(desc = "Number of cancelled dispatches")]
30 pub nr_cancel_dispatches: u64,
31 #[stat(desc = "Number of dispatches bounced to another DSQ")]
32 pub nr_bounce_dispatches: u64,
33 #[stat(desc = "Number of failed dispatches")]
34 pub nr_failed_dispatches: u64,
35 #[stat(desc = "Number of scheduler congestion events")]
36 pub nr_sched_congested: u64,
37}
38
39impl Metrics {
40 fn format<W: Write>(&self, w: &mut W) -> Result<()> {
41 writeln!(
42 w,
43 "[{}] tasks -> r: {:>2}/{:<2} w: {:<2}/{:<2} | pf: {:<5} | dispatch -> u: {:<5} k: {:<5} c: {:<5} b: {:<5} f: {:<5} | cg: {:<5}",
44 crate::SCHEDULER_NAME,
45 self.nr_running,
46 self.nr_cpus,
47 self.nr_queued,
48 self.nr_scheduled,
49 self.nr_page_faults,
50 self.nr_user_dispatches,
51 self.nr_kernel_dispatches,
52 self.nr_cancel_dispatches,
53 self.nr_bounce_dispatches,
54 self.nr_failed_dispatches,
55 self.nr_sched_congested,
56 )?;
57 Ok(())
58 }
59
60 fn delta(&self, rhs: &Self) -> Self {
61 Self {
62 nr_user_dispatches: self.nr_user_dispatches - rhs.nr_user_dispatches,
63 nr_kernel_dispatches: self.nr_kernel_dispatches - rhs.nr_kernel_dispatches,
64 nr_cancel_dispatches: self.nr_cancel_dispatches - rhs.nr_cancel_dispatches,
65 nr_bounce_dispatches: self.nr_bounce_dispatches - rhs.nr_bounce_dispatches,
66 nr_failed_dispatches: self.nr_failed_dispatches - rhs.nr_failed_dispatches,
67 nr_sched_congested: self.nr_sched_congested - rhs.nr_sched_congested,
68 ..self.clone()
69 }
70 }
71}
72
73pub fn server_data() -> StatsServerData<(), Metrics> {
74 let open: Box<dyn StatsOpener<(), Metrics>> = Box::new(move |(req_ch, res_ch)| {
75 req_ch.send(())?;
76 let mut prev = res_ch.recv()?;
77
78 let read: Box<dyn StatsReader<(), Metrics>> = Box::new(move |_args, (req_ch, res_ch)| {
79 req_ch.send(())?;
80 let cur = res_ch.recv()?;
81 let delta = cur.delta(&prev);
82 prev = cur;
83 delta.to_json()
84 });
85
86 Ok(read)
87 });
88
89 StatsServerData::new()
90 .add_meta(Metrics::meta())
91 .add_ops("top", StatsOps { open, close: None })
92}
93
94pub fn monitor(intv: Duration) -> Result<()> {
95 scx_utils::monitor_stats::<Metrics>(
96 &vec![],
97 intv,
98 || false,
99 |metrics| metrics.format(&mut std::io::stdout()),
100 )
101}