mirror of
https://github.com/sched-ext/scx.git
synced 2024-10-29 02:42:21 +00:00
scx_stats: Implement #stat_doc to autogen doc from stat desc
The doc of scx_layered `Opt` is out of sync. Implement attribute macro #stat_doc to generate doc from the `desc` property. Apply #stat_doc to `LayerStats` and `SysStats in scx_layered. Signed-off-by : Ming Yang <minos.future@gmail.com>
This commit is contained in:
parent
e8ebc09ced
commit
28bfd2986a
2
.gitignore
vendored
2
.gitignore
vendored
@ -2,3 +2,5 @@ libbpf/
|
||||
*.gitignore
|
||||
PKGBUILD
|
||||
target
|
||||
*.swp
|
||||
.cache/
|
||||
|
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -1827,6 +1827,7 @@ dependencies = [
|
||||
"crossbeam",
|
||||
"libc",
|
||||
"log",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"scx_stats_derive",
|
||||
"serde",
|
||||
|
@ -12,6 +12,7 @@ anyhow = "1.0.65"
|
||||
crossbeam = "0.8.4"
|
||||
libc = "0.2.137"
|
||||
log = "0.4.17"
|
||||
proc-macro2 = "1.0"
|
||||
quote = "1.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
|
@ -3,6 +3,7 @@ use scx_stats::{StatsData, StatsKind, StatsMetaAux};
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
use syn::parse_macro_input;
|
||||
use syn::spanned::Spanned;
|
||||
use syn::{Attribute, Data, DeriveInput, Fields, Lit};
|
||||
|
||||
static ASSERT_IDX: AtomicU64 = AtomicU64::new(0);
|
||||
|
||||
@ -46,3 +47,99 @@ pub fn stat(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
|
||||
output.into()
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn stat_doc(
|
||||
_attr: proc_macro::TokenStream,
|
||||
item: proc_macro::TokenStream,
|
||||
) -> proc_macro::TokenStream {
|
||||
let input = parse_macro_input!(item as DeriveInput);
|
||||
let ident = input.ident;
|
||||
let vis = input.vis;
|
||||
let attrs = input.attrs;
|
||||
let generics = input.generics;
|
||||
let data = input.data;
|
||||
|
||||
let mut output = proc_macro2::TokenStream::new();
|
||||
|
||||
if let Data::Struct(data_struct) = data {
|
||||
let fields = match data_struct.fields {
|
||||
Fields::Named(fields_named) => fields_named.named,
|
||||
_ => {
|
||||
return syn::Error::new_spanned(
|
||||
ident,
|
||||
"stat attribute can only be used on structs with named fields",
|
||||
)
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
};
|
||||
|
||||
let mut new_fields = Vec::new();
|
||||
|
||||
for mut field in fields {
|
||||
let mut doc_string = None;
|
||||
let mut new_attrs = Vec::new();
|
||||
|
||||
for attr in field.attrs.clone() {
|
||||
if attr.path().is_ident("stat") {
|
||||
// Parse the arguments within #[stat(...)]
|
||||
attr.parse_nested_meta(|meta| {
|
||||
if meta.path.is_ident("desc") {
|
||||
// Extract the literal string value from `desc`
|
||||
let desc_literal: Lit = meta.value()?.parse()?;
|
||||
if let Lit::Str(lit_str) = desc_literal {
|
||||
doc_string = Some(lit_str.value());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
.unwrap_or_else(|err| {
|
||||
panic!("Failed to parse the stat attribute: {}", err);
|
||||
});
|
||||
}
|
||||
new_attrs.push(attr);
|
||||
}
|
||||
|
||||
// If a description string was found, add a #[doc = "..."] attribute
|
||||
if let Some(description) = doc_string {
|
||||
let doc_attr = Attribute {
|
||||
pound_token: syn::token::Pound::default(),
|
||||
style: syn::AttrStyle::Outer,
|
||||
bracket_token: syn::token::Bracket::default(),
|
||||
meta: syn::Meta::NameValue(syn::MetaNameValue {
|
||||
path: syn::Path::from(format_ident!("doc")),
|
||||
eq_token: syn::token::Eq::default(),
|
||||
value: syn::Expr::Lit(syn::ExprLit {
|
||||
lit: Lit::Str(syn::LitStr::new(&description, field.span())),
|
||||
attrs: vec![],
|
||||
}),
|
||||
}),
|
||||
};
|
||||
new_attrs.push(doc_attr);
|
||||
}
|
||||
|
||||
field.attrs = new_attrs;
|
||||
new_fields.push(field);
|
||||
}
|
||||
|
||||
// Rebuild the struct with the modified fields
|
||||
let struct_def = quote! {
|
||||
#(#attrs)*
|
||||
#vis struct #ident #generics {
|
||||
#(#new_fields),*
|
||||
}
|
||||
};
|
||||
|
||||
output.extend(struct_def);
|
||||
return output.into();
|
||||
}
|
||||
|
||||
// If not a struct with named fields, return an error
|
||||
syn::Error::new_spanned(
|
||||
ident,
|
||||
"stat attribute can only be used on structs with named fields",
|
||||
)
|
||||
.to_compile_error()
|
||||
.into()
|
||||
}
|
||||
|
@ -331,6 +331,7 @@ lazy_static::lazy_static! {
|
||||
/// also an scx_stat server listening on /var/run/scx/root/stat that can
|
||||
/// be monitored by running `scx_layered --monitor INTERVAL` separately.
|
||||
///
|
||||
/// ```bash
|
||||
/// $ scx_layered --monitor 1
|
||||
/// tot= 117909 local=86.20 open_idle= 0.21 affn_viol= 1.37 proc=6ms
|
||||
/// busy= 34.2 util= 1733.6 load= 21744.1 fallback_cpu= 1
|
||||
@ -343,45 +344,11 @@ lazy_static::lazy_static! {
|
||||
/// normal : util/frac= 502.9/ 29.0 load/frac= 314.5: 1.4 tasks= 3512
|
||||
/// tot= 45434 local=80.97 open_idle= 0.16 preempt= 0.00 affn_viol= 3.56
|
||||
/// cpus= 50 [ 50, 50] fbfffffe 000fffff
|
||||
/// ```
|
||||
///
|
||||
/// Global statistics:
|
||||
/// Global statistics: see [`SysStats`]
|
||||
///
|
||||
/// - tot: Total scheduling events in the period.
|
||||
///
|
||||
/// - local: % that got scheduled directly into an idle CPU.
|
||||
///
|
||||
/// - open_idle: % of open layer tasks scheduled into occupied idle CPUs.
|
||||
///
|
||||
/// - affn_viol: % which violated configured policies due to CPU affinity
|
||||
/// restrictions.
|
||||
///
|
||||
/// - proc: CPU time this binary consumed during the period.
|
||||
///
|
||||
/// - busy: CPU busy % (100% means all CPUs were fully occupied)
|
||||
///
|
||||
/// - util: CPU utilization % (100% means one CPU was fully occupied)
|
||||
///
|
||||
/// - load: Sum of weight * duty_cycle for all tasks
|
||||
///
|
||||
/// Per-layer statistics:
|
||||
///
|
||||
/// - util/frac: CPU utilization and fraction % (sum of fractions across
|
||||
/// layers is always 100%).
|
||||
///
|
||||
/// - load/frac: Load sum and fraction %.
|
||||
///
|
||||
/// - tasks: Number of tasks.
|
||||
///
|
||||
/// - tot: Total scheduling events.
|
||||
///
|
||||
/// - open_idle: % of tasks scheduled into idle CPUs occupied by other layers.
|
||||
///
|
||||
/// - preempt: % of tasks that preempted other tasks.
|
||||
///
|
||||
/// - affn_viol: % which violated configured policies due to CPU affinity
|
||||
/// restrictions.
|
||||
///
|
||||
/// - cpus: CUR_NR_CPUS [MIN_NR_CPUS, MAX_NR_CPUS] CUR_CPU_MASK
|
||||
/// Per-layer statistics: see [`LayerStats`]
|
||||
///
|
||||
#[derive(Debug, Parser)]
|
||||
#[command(verbatim_doc_comment)]
|
||||
|
@ -16,6 +16,7 @@ use chrono::DateTime;
|
||||
use chrono::Local;
|
||||
use log::warn;
|
||||
use scx_stats::prelude::*;
|
||||
use scx_stats_derive::stat_doc;
|
||||
use scx_stats_derive::Stats;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
@ -44,6 +45,7 @@ fn fmt_num(v: u64) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
#[stat_doc]
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, Stats)]
|
||||
#[stat(_om_prefix = "l_", _om_label = "layer_name")]
|
||||
pub struct LayerStats {
|
||||
@ -55,9 +57,9 @@ pub struct LayerStats {
|
||||
pub load: f64,
|
||||
#[stat(desc = "fraction of total load")]
|
||||
pub load_frac: f64,
|
||||
#[stat(desc = "# tasks")]
|
||||
#[stat(desc = "count of tasks")]
|
||||
pub tasks: u32,
|
||||
#[stat(desc = "# sched events duringg the period")]
|
||||
#[stat(desc = "count of sched events during the period")]
|
||||
pub total: u64,
|
||||
#[stat(desc = "% dispatched into idle CPU")]
|
||||
pub sel_local: f64,
|
||||
@ -67,7 +69,7 @@ pub struct LayerStats {
|
||||
pub enq_expire: f64,
|
||||
#[stat(desc = "% re-enqueued due to RT preemption")]
|
||||
pub enq_reenq: f64,
|
||||
#[stat(desc = "# times exec duration < min_exec_us")]
|
||||
#[stat(desc = "count of times exec duration < min_exec_us")]
|
||||
pub min_exec: f64,
|
||||
#[stat(desc = "total exec durations extended due to min_exec_us")]
|
||||
pub min_exec_us: u64,
|
||||
@ -95,7 +97,7 @@ pub struct LayerStats {
|
||||
pub keep_fail_busy: f64,
|
||||
#[stat(desc = "whether is exclusive", _om_skip)]
|
||||
pub is_excl: u32,
|
||||
#[stat(desc = "# times an excl task skipped a CPU as the sibling was also excl")]
|
||||
#[stat(desc = "count of times an excl task skipped a CPU as the sibling was also excl")]
|
||||
pub excl_collision: f64,
|
||||
#[stat(desc = "% a sibling CPU was preempted for an exclusive task")]
|
||||
pub excl_preempt: f64,
|
||||
@ -103,7 +105,7 @@ pub struct LayerStats {
|
||||
pub kick: f64,
|
||||
#[stat(desc = "% yielded")]
|
||||
pub yielded: f64,
|
||||
#[stat(desc = "# times yield was ignored")]
|
||||
#[stat(desc = "count of times yield was ignored")]
|
||||
pub yield_ignore: u64,
|
||||
#[stat(desc = "% migrated across CPUs")]
|
||||
pub migration: f64,
|
||||
@ -117,7 +119,7 @@ pub struct LayerStats {
|
||||
pub xlayer_rewake: f64,
|
||||
#[stat(desc = "mask of allocated CPUs", _om_skip)]
|
||||
pub cpus: Vec<u32>,
|
||||
#[stat(desc = "# of CPUs assigned")]
|
||||
#[stat(desc = "count of of CPUs assigned")]
|
||||
pub cur_nr_cpus: u32,
|
||||
#[stat(desc = "minimum # of CPUs assigned")]
|
||||
pub min_nr_cpus: u32,
|
||||
@ -326,12 +328,13 @@ impl LayerStats {
|
||||
}
|
||||
}
|
||||
|
||||
#[stat_doc]
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, Stats)]
|
||||
#[stat(top)]
|
||||
pub struct SysStats {
|
||||
#[stat(desc = "timestamp", _om_skip)]
|
||||
pub at: f64,
|
||||
#[stat(desc = "# sched events during the period")]
|
||||
#[stat(desc = "count of sched events during the period")]
|
||||
pub total: u64,
|
||||
#[stat(desc = "% dispatched directly into an idle CPU")]
|
||||
pub local: f64,
|
||||
@ -339,13 +342,15 @@ pub struct SysStats {
|
||||
pub open_idle: f64,
|
||||
#[stat(desc = "% violated config due to CPU affinity")]
|
||||
pub affn_viol: f64,
|
||||
#[stat(desc = "# times an excl task skipped a CPU as the sibling was also excl")]
|
||||
#[stat(desc = "count of times an excl task skipped a CPU as the sibling was also excl")]
|
||||
pub excl_collision: f64,
|
||||
#[stat(desc = "# times a sibling CPU was preempted for an excl task")]
|
||||
#[stat(desc = "count of times a sibling CPU was preempted for an excl task")]
|
||||
pub excl_preempt: f64,
|
||||
#[stat(desc = "# times a CPU skipped dispatching due to an excl task on the sibling")]
|
||||
#[stat(desc = "count of times a CPU skipped dispatching due to an excl task on the sibling")]
|
||||
pub excl_idle: f64,
|
||||
#[stat(desc = "# times an idle sibling CPU was woken up after an excl task is finished")]
|
||||
#[stat(
|
||||
desc = "count of times an idle sibling CPU was woken up after an excl task is finished"
|
||||
)]
|
||||
pub excl_wakeup: f64,
|
||||
#[stat(desc = "CPU time this binary consumed during the period")]
|
||||
pub proc_ms: u64,
|
||||
|
Loading…
Reference in New Issue
Block a user