scx_tickless/
stats.rs

1// SPDX-License-Identifier: GPL-2.0
2//
3// Copyright (c) 2025 Andrea Righi <arighi@nvidia.com>
4
5// This software may be used and distributed according to the terms of the
6// GNU General Public License version 2.
7
8use std::io::Write;
9use std::sync::Arc;
10use std::sync::atomic::AtomicBool;
11use std::sync::atomic::Ordering;
12use std::time::Duration;
13
14use anyhow::Result;
15use scx_stats::prelude::*;
16use scx_stats_derive::Stats;
17use scx_stats_derive::stat_doc;
18use serde::Deserialize;
19use serde::Serialize;
20
21#[stat_doc]
22#[derive(Clone, Debug, Default, Serialize, Deserialize, Stats)]
23#[stat(top)]
24pub struct Metrics {
25    #[stat(desc = "Number of ticks")]
26    pub nr_ticks: u64,
27    #[stat(desc = "Number of preemption events")]
28    pub nr_preemptions: u64,
29    #[stat(desc = "Number of dispatches directly consumed from the shared queue")]
30    pub nr_direct_dispatches: u64,
31    #[stat(desc = "Number of dispatches routed by the primary CPUs")]
32    pub nr_primary_dispatches: u64,
33    #[stat(desc = "Number of dispatches routed by the primary CPU timers")]
34    pub nr_timer_dispatches: u64,
35}
36
37impl Metrics {
38    fn format<W: Write>(&self, w: &mut W) -> Result<()> {
39        writeln!(
40            w,
41            "[{}] ticks -> {:<5} preempts -> {:<5} dispatch -> d: {:<5} p: {:<5} t: {:<5}",
42            crate::SCHEDULER_NAME,
43            self.nr_ticks,
44            self.nr_preemptions,
45            self.nr_direct_dispatches,
46            self.nr_primary_dispatches,
47            self.nr_timer_dispatches
48        )?;
49        Ok(())
50    }
51
52    fn delta(&self, rhs: &Self) -> Self {
53        Self {
54            nr_ticks: self.nr_ticks - rhs.nr_ticks,
55            nr_preemptions: self.nr_preemptions - rhs.nr_preemptions,
56            nr_direct_dispatches: self.nr_direct_dispatches - rhs.nr_direct_dispatches,
57            nr_primary_dispatches: self.nr_primary_dispatches - rhs.nr_primary_dispatches,
58            nr_timer_dispatches: self.nr_timer_dispatches - rhs.nr_timer_dispatches,
59            ..self.clone()
60        }
61    }
62}
63
64pub fn server_data() -> StatsServerData<(), Metrics> {
65    let open: Box<dyn StatsOpener<(), Metrics>> = Box::new(move |(req_ch, res_ch)| {
66        req_ch.send(())?;
67        let mut prev = res_ch.recv()?;
68
69        let read: Box<dyn StatsReader<(), Metrics>> = Box::new(move |_args, (req_ch, res_ch)| {
70            req_ch.send(())?;
71            let cur = res_ch.recv()?;
72            let delta = cur.delta(&prev);
73            prev = cur;
74            delta.to_json()
75        });
76
77        Ok(read)
78    });
79
80    StatsServerData::new()
81        .add_meta(Metrics::meta())
82        .add_ops("top", StatsOps { open, close: None })
83}
84
85pub fn monitor(intv: Duration, shutdown: Arc<AtomicBool>) -> Result<()> {
86    scx_utils::monitor_stats::<Metrics>(
87        &[],
88        intv,
89        || shutdown.load(Ordering::Relaxed),
90        |metrics| metrics.format(&mut std::io::stdout()),
91    )
92}