scx_lavd: print basic system status when --monior is given

Signed-off-by: Changwoo Min <changwoo@igalia.com>
This commit is contained in:
Changwoo Min 2024-09-04 13:48:49 +09:00
parent 6b717a3f3d
commit e5d27d0553
3 changed files with 152 additions and 11 deletions

View File

@ -197,8 +197,8 @@ char _license[] SEC("license") = "GPL";
volatile u64 nr_cpus_onln;
static volatile u64 nr_cpus_big;
static struct sys_stat __sys_stats[2];
static volatile int __sys_stat_idx;
struct sys_stat __sys_stats[2];
volatile int __sys_stat_idx;
private(LAVD) struct bpf_cpumask __kptr *turbo_cpumask; /* CPU mask for turbo CPUs */
private(LAVD) struct bpf_cpumask __kptr *big_cpumask; /* CPU mask for big CPUs */

View File

@ -12,6 +12,7 @@ pub mod bpf_intf;
pub use bpf_intf::*;
mod stats;
use stats::SysStats;
use stats::SchedSample;
use stats::SchedSamples;
use stats::StatsReq;
@ -122,6 +123,14 @@ struct Opts {
#[clap(long = "no-freq-scaling", action = clap::ArgAction::SetTrue)]
no_freq_scaling: bool,
/// Enable stats monitoring with the specified interval.
#[clap(long)]
stats: Option<f64>,
/// Run in stats monitoring mode with the specified interval. Scheduler is not launched.
#[clap(long)]
monitor: Option<f64>,
/// Run in monitoring mode. Show the specified number of scheduling
/// samples every second.
#[clap(long)]
@ -452,8 +461,9 @@ struct Scheduler<'a> {
rb_mgr: libbpf_rs::RingBuffer<'static>,
intrspc: introspec,
intrspc_rx: Receiver<SchedSample>,
sampler_tid: Option<ThreadId>,
monitor_tid: Option<ThreadId>,
stats_server: StatsServer<StatsReq, StatsRes>,
mseq_id: u64,
}
impl<'a> Scheduler<'a> {
@ -498,8 +508,9 @@ impl<'a> Scheduler<'a> {
rb_mgr,
intrspc: introspec::new(),
intrspc_rx,
sampler_tid: None,
monitor_tid: None,
stats_server,
mseq_id: 0,
})
}
@ -634,15 +645,35 @@ impl<'a> Scheduler<'a> {
Ok(match req {
StatsReq::NewSampler(tid) => {
self.rb_mgr.consume().unwrap();
self.sampler_tid = Some(*tid);
self.monitor_tid = Some(*tid);
StatsRes::Ack
}
StatsReq::SysStatsReq {
tid,
} => {
if Some(*tid) != self.monitor_tid {
return Ok(StatsRes::Bye);
}
self.mseq_id += 1;
let mseq = self.mseq_id;
let avg_svc_time = self.skel.maps.bss_data.__sys_stats[0].avg_svc_time;
let nr_queued_task = self.skel.maps.bss_data.__sys_stats[0].nr_queued_task;
let nr_active = self.skel.maps.bss_data.__sys_stats[0].nr_active;
StatsRes::SysStats(SysStats {
mseq,
avg_svc_time,
nr_queued_task,
nr_active,
})
}
StatsReq::SchedSamplesNr {
tid,
nr_samples,
interval_ms,
} => {
if Some(*tid) != self.sampler_tid {
if Some(*tid) != self.monitor_tid {
return Ok(StatsRes::Bye);
}
@ -817,6 +848,17 @@ fn main() -> Result<()> {
return Ok(());
}
if let Some(intv) = opts.monitor.or(opts.stats) {
let shutdown_copy = shutdown.clone();
let jh = std::thread::spawn(move || {
stats::monitor(Duration::from_secs_f64(intv), shutdown_copy).unwrap()
});
if opts.monitor.is_some() {
let _ = jh.join();
return Ok(());
}
}
let mut open_object = MaybeUninit::uninit();
loop {
let mut sched = Scheduler::init(&opts, &mut open_object)?;

View File

@ -12,10 +12,56 @@ use std::sync::Arc;
use std::thread::ThreadId;
use std::time::Duration;
#[derive(Clone, Debug, Default, Serialize, Deserialize, Stats)]
pub struct SysStats {
#[stat(desc = "Sequence ID of this messge")]
pub mseq: u64,
#[stat(desc = "Average runtime per schedule")]
pub avg_svc_time: u64,
#[stat(desc = "Number of runnable tasks in runqueues")]
pub nr_queued_task: u64,
#[stat(desc = "Number of active CPUs when core compaction is enabled")]
pub nr_active: u32,
}
impl SysStats {
pub fn format_header<W: Write>(w: &mut W) -> Result<()> {
writeln!(
w,
"| {} | {} | {} | {} |",
"mseq",
"avg_svc_time",
"nr_queued_task",
"nr_active",
)?;
Ok(())
}
fn format<W: Write>(&self, w: &mut W) -> Result<()> {
if self.mseq % 32 == 1 {
Self::format_header(w)?;
}
writeln!(
w,
"| {} | {} | {} | {} |",
self.mseq,
self.avg_svc_time,
self.nr_queued_task,
self.nr_active,
)?;
Ok(())
}
}
#[derive(Clone, Debug, Default, Serialize, Deserialize, Stats)]
#[stat(top)]
pub struct SchedSample {
#[stat(desc = "Sequence ID of task log")]
#[stat(desc = "Sequence ID of this message")]
pub mseq: u64,
#[stat(desc = "Process ID")]
pub pid: i32,
@ -149,6 +195,9 @@ pub struct SchedSamples {
#[derive(Debug)]
pub enum StatsReq {
NewSampler(ThreadId),
SysStatsReq {
tid: ThreadId,
},
SchedSamplesNr {
tid: ThreadId,
nr_samples: u64,
@ -157,7 +206,15 @@ pub enum StatsReq {
}
impl StatsReq {
fn from_args(
fn from_args_stats(
tid: ThreadId,
) -> Result<Self> {
Ok(Self::SysStatsReq {
tid,
})
}
fn from_args_samples(
tid: ThreadId,
nr_cpus_onln: u64,
args: &BTreeMap<String, String>,
@ -187,12 +244,12 @@ impl StatsReq {
pub enum StatsRes {
Ack,
Bye,
SysStats(SysStats),
SchedSamples(SchedSamples),
}
pub fn server_data(nr_cpus_onln: u64) -> StatsServerData<StatsReq, StatsRes> {
let samples_open: Box<dyn StatsOpener<StatsReq, StatsRes>> =
Box::new(move |(req_ch, res_ch)| {
let open: Box<dyn StatsOpener<StatsReq, StatsRes>> = Box::new(move |(req_ch, res_ch)| {
let tid = std::thread::current().id();
req_ch.send(StatsReq::NewSampler(tid))?;
match res_ch.recv()? {
@ -202,7 +259,31 @@ pub fn server_data(nr_cpus_onln: u64) -> StatsServerData<StatsReq, StatsRes> {
let read: Box<dyn StatsReader<StatsReq, StatsRes>> =
Box::new(move |args, (req_ch, res_ch)| {
let req = StatsReq::from_args(tid, nr_cpus_onln, args)?;
let req = StatsReq::from_args_stats(tid)?;
req_ch.send(req)?;
let stats = match res_ch.recv()? {
StatsRes::SysStats(v) => v,
StatsRes::Bye => bail!("preempted by another sampler"),
res => bail!("invalid response: {:?}", &res),
};
stats.to_json()
});
Ok(read)
});
let samples_open: Box<dyn StatsOpener<StatsReq, StatsRes>> = Box::new(move |(req_ch, res_ch)| {
let tid = std::thread::current().id();
req_ch.send(StatsReq::NewSampler(tid))?;
match res_ch.recv()? {
StatsRes::Ack => {}
res => bail!("invalid response: {:?}", &res),
}
let read: Box<dyn StatsReader<StatsReq, StatsRes>> =
Box::new(move |args, (req_ch, res_ch)| {
let req = StatsReq::from_args_samples(tid, nr_cpus_onln, args)?;
req_ch.send(req)?;
let samples = match res_ch.recv()? {
@ -217,6 +298,14 @@ pub fn server_data(nr_cpus_onln: u64) -> StatsServerData<StatsReq, StatsRes> {
});
StatsServerData::new()
.add_meta(SysStats::meta())
.add_ops(
"top",
StatsOps {
open: open,
close: None,
},
)
.add_meta(SchedSample::meta())
.add_ops(
"sched_samples",
@ -244,3 +333,13 @@ pub fn monitor_sched_samples(nr_samples: u64, shutdown: Arc<AtomicBool>) -> Resu
},
)
}
pub fn monitor(intv: Duration, shutdown: Arc<AtomicBool>) -> Result<()> {
scx_utils::monitor_stats::<SysStats>(
&vec![],
intv,
|| shutdown.load(Ordering::Relaxed),
|sysstats| sysstats.format(&mut std::io::stdout()),
);
Ok(())
}