#![cfg(test)]
use super::*;
#[test]
fn empty_samples_default_summary() {
let summary = MonitorSummary::from_samples(&[]);
assert_eq!(summary.total_samples, 0);
assert_eq!(summary.max_imbalance_ratio, 0.0);
assert_eq!(summary.max_local_dsq_depth, 0);
assert_eq!(summary.stuck_count, 0);
assert_eq!(summary.avg_imbalance_ratio, 0.0);
assert_eq!(summary.avg_nr_running, 0.0);
assert_eq!(summary.avg_local_dsq_depth, 0.0);
}
#[test]
fn single_sample_imbalanced_cpus() {
let sample = MonitorSample {
bpf_map_fields: Vec::new(),
prog_stats: None,
psi_irq: None,
elapsed_ms: 100,
cpus: vec![
CpuSnapshot {
nr_running: 1,
local_dsq_depth: 3,
rq_clock: 1000,
..Default::default()
},
CpuSnapshot {
nr_running: 4,
local_dsq_depth: 1,
rq_clock: 2000,
..Default::default()
},
],
};
let summary = MonitorSummary::from_samples(&[sample]);
assert_eq!(summary.total_samples, 1);
assert!((summary.max_imbalance_ratio - 4.0).abs() < f64::EPSILON);
assert_eq!(summary.max_local_dsq_depth, 3);
assert_eq!(summary.stuck_count, 0);
assert!((summary.avg_imbalance_ratio - 4.0).abs() < f64::EPSILON);
assert!((summary.avg_nr_running - 2.5).abs() < f64::EPSILON);
assert!((summary.avg_local_dsq_depth - 2.0).abs() < f64::EPSILON);
}
#[test]
fn stuck_count_when_clock_stuck() {
let s1 = MonitorSample {
bpf_map_fields: Vec::new(),
prog_stats: None,
psi_irq: None,
elapsed_ms: 100,
cpus: vec![
CpuSnapshot {
nr_running: 1,
rq_clock: 5000,
..Default::default()
},
CpuSnapshot {
nr_running: 1,
rq_clock: 6000,
..Default::default()
},
],
};
let s2 = MonitorSample {
bpf_map_fields: Vec::new(),
prog_stats: None,
psi_irq: None,
elapsed_ms: 200,
cpus: vec![
CpuSnapshot {
nr_running: 1,
rq_clock: 5000, ..Default::default()
},
CpuSnapshot {
nr_running: 1,
rq_clock: 7000,
..Default::default()
},
],
};
let summary = MonitorSummary::from_samples(&[s1, s2]);
assert_eq!(summary.stuck_count, 1);
}
#[test]
fn balanced_cpus_ratio_one() {
let sample = MonitorSample {
bpf_map_fields: Vec::new(),
prog_stats: None,
psi_irq: None,
elapsed_ms: 50,
cpus: vec![
CpuSnapshot {
nr_running: 3,
rq_clock: 100,
..Default::default()
},
CpuSnapshot {
nr_running: 3,
rq_clock: 200,
..Default::default()
},
],
};
let summary = MonitorSummary::from_samples(&[sample]);
assert!((summary.max_imbalance_ratio - 1.0).abs() < f64::EPSILON);
assert_eq!(summary.stuck_count, 0);
assert!((summary.avg_imbalance_ratio - 1.0).abs() < f64::EPSILON);
assert!((summary.avg_nr_running - 3.0).abs() < f64::EPSILON);
assert!((summary.avg_local_dsq_depth - 0.0).abs() < f64::EPSILON);
}
#[test]
fn single_cpu_no_division_by_zero() {
let sample = MonitorSample {
bpf_map_fields: Vec::new(),
prog_stats: None,
psi_irq: None,
elapsed_ms: 10,
cpus: vec![CpuSnapshot {
nr_running: 5,
local_dsq_depth: 2,
rq_clock: 1000,
..Default::default()
}],
};
let summary = MonitorSummary::from_samples(&[sample]);
assert_eq!(summary.total_samples, 1);
assert!((summary.max_imbalance_ratio - 1.0).abs() < f64::EPSILON);
assert_eq!(summary.max_local_dsq_depth, 2);
assert_eq!(summary.stuck_count, 0);
}
#[test]
fn all_zero_snapshots() {
let sample = MonitorSample {
bpf_map_fields: Vec::new(),
prog_stats: None,
psi_irq: None,
elapsed_ms: 0,
cpus: vec![CpuSnapshot::default(), CpuSnapshot::default()],
};
let summary = MonitorSummary::from_samples(&[sample]);
assert_eq!(summary.total_samples, 1);
assert!((summary.max_imbalance_ratio - 1.0).abs() < f64::EPSILON);
assert_eq!(summary.max_local_dsq_depth, 0);
assert_eq!(summary.stuck_count, 0);
assert_eq!(summary.avg_imbalance_ratio, 0.0);
assert_eq!(summary.avg_nr_running, 0.0);
assert_eq!(summary.avg_local_dsq_depth, 0.0);
}
#[test]
fn empty_cpus_in_sample() {
let sample = MonitorSample {
bpf_map_fields: Vec::new(),
prog_stats: None,
psi_irq: None,
elapsed_ms: 10,
cpus: vec![],
};
let summary = MonitorSummary::from_samples(&[sample]);
assert_eq!(summary.total_samples, 1);
assert!((summary.max_imbalance_ratio - 1.0).abs() < f64::EPSILON);
assert_eq!(summary.avg_imbalance_ratio, 0.0);
assert_eq!(summary.avg_nr_running, 0.0);
assert_eq!(summary.avg_local_dsq_depth, 0.0);
}
#[test]
fn min_nr_zero_division_guard() {
let sample = MonitorSample {
bpf_map_fields: Vec::new(),
prog_stats: None,
psi_irq: None,
elapsed_ms: 10,
cpus: vec![
CpuSnapshot {
nr_running: 0,
rq_clock: 100,
..Default::default()
},
CpuSnapshot {
nr_running: 0,
rq_clock: 200,
..Default::default()
},
],
};
let summary = MonitorSummary::from_samples(&[sample]);
assert!((summary.max_imbalance_ratio - 1.0).abs() < f64::EPSILON);
}
#[test]
fn min_nr_zero_max_nr_nonzero() {
let sample = MonitorSample {
bpf_map_fields: Vec::new(),
prog_stats: None,
psi_irq: None,
elapsed_ms: 10,
cpus: vec![
CpuSnapshot {
nr_running: 0,
rq_clock: 100,
..Default::default()
},
CpuSnapshot {
nr_running: 5,
rq_clock: 200,
..Default::default()
},
],
};
let summary = MonitorSummary::from_samples(&[sample]);
assert!((summary.max_imbalance_ratio - 5.0).abs() < f64::EPSILON);
}
#[test]
fn advancing_clocks_no_stuck() {
let s1 = MonitorSample {
bpf_map_fields: Vec::new(),
prog_stats: None,
psi_irq: None,
elapsed_ms: 100,
cpus: vec![
CpuSnapshot {
nr_running: 1,
rq_clock: 1000,
..Default::default()
},
CpuSnapshot {
nr_running: 1,
rq_clock: 2000,
..Default::default()
},
],
};
let s2 = MonitorSample {
bpf_map_fields: Vec::new(),
prog_stats: None,
psi_irq: None,
elapsed_ms: 200,
cpus: vec![
CpuSnapshot {
nr_running: 1,
rq_clock: 1500,
..Default::default()
},
CpuSnapshot {
nr_running: 1,
rq_clock: 2500,
..Default::default()
},
],
};
let s3 = MonitorSample {
bpf_map_fields: Vec::new(),
prog_stats: None,
psi_irq: None,
elapsed_ms: 300,
cpus: vec![
CpuSnapshot {
nr_running: 1,
rq_clock: 2000,
..Default::default()
},
CpuSnapshot {
nr_running: 1,
rq_clock: 3000,
..Default::default()
},
],
};
let summary = MonitorSummary::from_samples(&[s1, s2, s3]);
assert_eq!(summary.stuck_count, 0);
assert_eq!(summary.total_samples, 3);
}
#[test]
fn different_length_cpu_vecs() {
let s1 = MonitorSample {
bpf_map_fields: Vec::new(),
prog_stats: None,
psi_irq: None,
elapsed_ms: 100,
cpus: vec![
CpuSnapshot {
nr_running: 1,
rq_clock: 1000,
..Default::default()
},
CpuSnapshot {
nr_running: 1,
rq_clock: 2000,
..Default::default()
},
],
};
let s2 = MonitorSample {
bpf_map_fields: Vec::new(),
prog_stats: None,
psi_irq: None,
elapsed_ms: 200,
cpus: vec![
CpuSnapshot {
nr_running: 1,
rq_clock: 1500,
..Default::default()
},
CpuSnapshot {
nr_running: 1,
rq_clock: 2500,
..Default::default()
},
CpuSnapshot {
nr_running: 1,
rq_clock: 3000,
..Default::default()
},
],
};
let summary = MonitorSummary::from_samples(&[s1, s2]);
assert_eq!(summary.stuck_count, 0);
assert_eq!(summary.total_samples, 2);
assert_eq!(summary.max_local_dsq_depth, 0);
}
#[test]
fn from_samples_fields_sane_values() {
let samples: Vec<_> = (0..5u64)
.map(|i| MonitorSample {
bpf_map_fields: Vec::new(),
prog_stats: None,
psi_irq: None,
elapsed_ms: i * 100,
cpus: vec![
CpuSnapshot {
nr_running: (i as u32 + 1),
scx_nr_running: i as u32,
local_dsq_depth: (i as u32) % 3,
rq_clock: 1000 + i * 500,
scx_flags: 0,
event_counters: Some(ScxEventCounters {
select_cpu_fallback: i as i64 * 2,
dispatch_keep_last: i as i64,
..Default::default()
}),
schedstat: None,
vcpu_cpu_time_ns: None,
vcpu_perf: None,
avg_irq_util: None,
sched_domains: None,
},
CpuSnapshot {
nr_running: (i as u32 + 2),
scx_nr_running: i as u32 + 1,
local_dsq_depth: 0,
rq_clock: 1100 + i * 600,
scx_flags: 0,
event_counters: Some(ScxEventCounters {
select_cpu_fallback: i as i64 * 3,
dispatch_keep_last: i as i64 * 2,
..Default::default()
}),
schedstat: None,
vcpu_cpu_time_ns: None,
vcpu_perf: None,
avg_irq_util: None,
sched_domains: None,
},
],
})
.collect();
let summary = MonitorSummary::from_samples(&samples);
assert_eq!(summary.total_samples, 5);
assert!(
(summary.max_imbalance_ratio - 2.0).abs() < f64::EPSILON,
"peak ratio is sample 0's 2/1: {}",
summary.max_imbalance_ratio
);
assert_eq!(
summary.max_local_dsq_depth, 2,
"peak local_dsq_depth is i=2's (2 % 3) = 2"
);
assert!(
summary.max_local_dsq_depth <= DSQ_PLAUSIBILITY_CEILING,
"must stay below the plausibility ceiling that gates validity",
);
assert_eq!(
summary.stuck_count, 0,
"no stuck expected with advancing rq_clock"
);
let deltas = summary
.event_deltas
.as_ref()
.expect("event deltas must be present");
assert_eq!(
deltas.total_fallback, 20,
"total_fallback = last_sum(20) - first_sum(0)"
);
assert_eq!(
deltas.total_dispatch_keep_last, 12,
"total_dispatch_keep_last = last_sum(12) - first_sum(0)"
);
assert!(
(deltas.fallback_rate - 50.0).abs() < f64::EPSILON,
"fallback_rate = 20 / 0.4s = 50.0: {}",
deltas.fallback_rate
);
assert!(
(deltas.keep_last_rate - 30.0).abs() < f64::EPSILON,
"keep_last_rate = 12 / 0.4s = 30.0: {}",
deltas.keep_last_rate
);
assert_eq!(
deltas.max_fallback_burst, 5,
"each consecutive fallback delta is 5i - 5(i-1) = 5"
);
assert!(
(summary.avg_nr_running - 3.5).abs() < f64::EPSILON,
"avg_nr_running = 35 / 10 readings: {}",
summary.avg_nr_running,
);
assert!(
(summary.avg_local_dsq_depth - 0.4).abs() < f64::EPSILON,
"avg_local_dsq_depth = 4 / 10 readings: {}",
summary.avg_local_dsq_depth,
);
let expected_avg_imbalance = (2.0 + 1.5 + 4.0 / 3.0 + 1.25 + 1.2) / 5.0;
assert!(
(summary.avg_imbalance_ratio - expected_avg_imbalance).abs() < 1e-12,
"avg_imbalance = mean of (i+2)/(i+1): got {} want {}",
summary.avg_imbalance_ratio,
expected_avg_imbalance,
);
}
#[test]
fn from_samples_empty_all_defaults() {
let summary = MonitorSummary::from_samples(&[]);
assert_eq!(summary.total_samples, 0);
assert_eq!(summary.max_imbalance_ratio, 0.0);
assert_eq!(summary.max_local_dsq_depth, 0);
assert_eq!(summary.stuck_count, 0);
assert_eq!(summary.avg_imbalance_ratio, 0.0);
assert_eq!(summary.avg_nr_running, 0.0);
assert_eq!(summary.avg_local_dsq_depth, 0.0);
assert_eq!(summary.psi_irq_full_avg10, None);
assert_eq!(summary.total_irq_pressure_us, None);
assert!(
summary.event_deltas.is_none(),
"empty input must not produce event deltas"
);
}
#[test]
fn avg_irq_util_means_reporting_cpus_and_skips_none() {
let s1 = MonitorSample {
bpf_map_fields: Vec::new(),
prog_stats: None,
psi_irq: None,
elapsed_ms: 100,
cpus: vec![
CpuSnapshot {
avg_irq_util: Some(40),
..Default::default()
},
CpuSnapshot {
avg_irq_util: Some(60),
..Default::default()
},
],
};
let s2 = MonitorSample {
bpf_map_fields: Vec::new(),
prog_stats: None,
psi_irq: None,
elapsed_ms: 200,
cpus: vec![
CpuSnapshot {
avg_irq_util: Some(80),
..Default::default()
},
CpuSnapshot {
avg_irq_util: None,
..Default::default()
},
],
};
let summary = MonitorSummary::from_samples(&[s1, s2]);
let avg = summary
.avg_irq_util
.expect("some CPU reported avg_irq_util");
assert!(
(avg - 60.0).abs() < f64::EPSILON,
"mean over reporting CPUs only (None skipped): {avg}",
);
assert_eq!(
summary.max_avg_irq_util,
Some(80.0),
"peak across reporting CPUs/samples",
);
}
#[test]
fn avg_irq_util_none_when_no_cpu_reports() {
let sample = MonitorSample {
bpf_map_fields: Vec::new(),
prog_stats: None,
psi_irq: None,
elapsed_ms: 100,
cpus: vec![
CpuSnapshot {
nr_running: 2,
rq_clock: 1000,
avg_irq_util: None,
..Default::default()
},
CpuSnapshot {
nr_running: 2,
rq_clock: 2000,
avg_irq_util: None,
..Default::default()
},
],
};
let summary = MonitorSummary::from_samples(&[sample]);
assert_eq!(
summary.avg_irq_util, None,
"no reporting CPU -> None, not 0.0",
);
assert_eq!(
summary.max_avg_irq_util, None,
"no reporting CPU -> None, not 0.0",
);
assert!((summary.avg_nr_running - 2.0).abs() < f64::EPSILON);
}
#[test]
fn psi_irq_folds_avg10_mean_and_total_delta() {
let s1 = MonitorSample {
bpf_map_fields: Vec::new(),
elapsed_ms: 100,
cpus: vec![CpuSnapshot {
nr_running: 1,
rq_clock: 1000,
..Default::default()
}],
prog_stats: None,
psi_irq: Some(PsiIrqSample {
avg10_raw: 51_200,
total_ns: 1_000_000,
}),
};
let s2 = MonitorSample {
bpf_map_fields: Vec::new(),
elapsed_ms: 200,
cpus: vec![CpuSnapshot {
nr_running: 1,
rq_clock: 2000,
..Default::default()
}],
prog_stats: None,
psi_irq: Some(PsiIrqSample {
avg10_raw: 153_600,
total_ns: 3_500_000,
}),
};
let summary = MonitorSummary::from_samples(&[s1, s2]);
let avg10 = summary
.psi_irq_full_avg10
.expect("samples reported PSI-irq");
assert!(
(avg10 - 50.0).abs() < f64::EPSILON,
"avg10 mean = (25 + 75) / 2 = 50.0: {avg10}",
);
let total = summary
.total_irq_pressure_us
.expect("samples reported PSI-irq");
assert!(
(total - 2500.0).abs() < f64::EPSILON,
"total delta = (3_500_000 - 1_000_000) ns / 1000 = 2500 µs: {total}",
);
}
#[test]
fn psi_irq_none_when_no_sample_reports() {
let sample = MonitorSample {
bpf_map_fields: Vec::new(),
elapsed_ms: 100,
cpus: vec![CpuSnapshot {
nr_running: 2,
rq_clock: 1000,
..Default::default()
}],
prog_stats: None,
psi_irq: None,
};
let summary = MonitorSummary::from_samples(&[sample]);
assert_eq!(
summary.psi_irq_full_avg10, None,
"no PSI reading -> None, not 0.0",
);
assert_eq!(
summary.total_irq_pressure_us, None,
"no PSI reading -> None, not 0.0",
);
assert!((summary.avg_nr_running - 2.0).abs() < f64::EPSILON);
}
#[test]
fn psi_irq_total_saturates_on_counter_reset() {
let s1 = MonitorSample {
bpf_map_fields: Vec::new(),
elapsed_ms: 100,
cpus: vec![CpuSnapshot {
nr_running: 1,
rq_clock: 1000,
..Default::default()
}],
prog_stats: None,
psi_irq: Some(PsiIrqSample {
avg10_raw: 0,
total_ns: 5_000_000,
}),
};
let s2 = MonitorSample {
bpf_map_fields: Vec::new(),
elapsed_ms: 200,
cpus: vec![CpuSnapshot {
nr_running: 1,
rq_clock: 2000,
..Default::default()
}],
prog_stats: None,
psi_irq: Some(PsiIrqSample {
avg10_raw: 0,
total_ns: 1_000_000, }),
};
let summary = MonitorSummary::from_samples(&[s1, s2]);
assert_eq!(
summary.total_irq_pressure_us,
Some(0.0),
"counter reset must clamp the delta to 0, not underflow",
);
}
#[test]
fn psi_irq_single_sample_zero_total_delta() {
let sample = MonitorSample {
bpf_map_fields: Vec::new(),
elapsed_ms: 100,
cpus: vec![CpuSnapshot {
nr_running: 1,
rq_clock: 1000,
..Default::default()
}],
prog_stats: None,
psi_irq: Some(PsiIrqSample {
avg10_raw: 102_400, total_ns: 7_000_000,
}),
};
let summary = MonitorSummary::from_samples(&[sample]);
assert!(
(summary.psi_irq_full_avg10.unwrap() - 50.0).abs() < f64::EPSILON,
"single-sample avg10 = the lone decoded value",
);
assert_eq!(
summary.total_irq_pressure_us,
Some(0.0),
"single sample: first == last ⇒ delta 0",
);
}
#[test]
fn psi_irq_interleaved_none_is_filtered_not_zeroed() {
let mk = |elapsed_ms: u64, psi: Option<PsiIrqSample>| MonitorSample {
bpf_map_fields: Vec::new(),
elapsed_ms,
cpus: vec![CpuSnapshot {
nr_running: 1,
rq_clock: elapsed_ms * 10,
..Default::default()
}],
prog_stats: None,
psi_irq: psi,
};
let samples = vec![
mk(
100,
Some(PsiIrqSample {
avg10_raw: 102_400, total_ns: 1_000_000,
}),
),
mk(200, None),
mk(
300,
Some(PsiIrqSample {
avg10_raw: 102_400, total_ns: 3_000_000,
}),
),
];
let summary = MonitorSummary::from_samples(&samples);
assert!(
(summary.psi_irq_full_avg10.unwrap() - 50.0).abs() < f64::EPSILON,
"interleaved None must be filtered from the mean, not counted as 0%",
);
assert_eq!(
summary.total_irq_pressure_us,
Some(2000.0),
"delta spans first→last reporting sample across the None gap",
);
}
#[test]
fn fold_run_level_ext_folds_the_five_monitor_metrics() {
use std::collections::BTreeMap;
let mut s = MonitorSummary {
total_samples: 5,
avg_nr_running: 2.5,
avg_irq_util: Some(40.0),
max_avg_irq_util: Some(80.0),
psi_irq_full_avg10: Some(12.5),
total_irq_pressure_us: Some(3000.0),
..Default::default()
};
let mut ext = BTreeMap::new();
s.fold_run_level_ext(&mut ext);
assert_eq!(ext.get("avg_nr_running"), Some(&2.5));
assert_eq!(ext.get("avg_irq_util"), Some(&40.0));
assert_eq!(ext.get("max_avg_irq_util"), Some(&80.0));
assert_eq!(ext.get("psi_irq_full_avg10"), Some(&12.5));
assert_eq!(ext.get("total_irq_pressure_us"), Some(&3000.0));
s.avg_irq_util = None;
s.psi_irq_full_avg10 = None;
let mut ext2 = BTreeMap::new();
s.fold_run_level_ext(&mut ext2);
assert_eq!(ext2.get("avg_nr_running"), Some(&2.5));
assert_eq!(ext2.get("avg_irq_util"), None, "None → absent, not 0.0");
assert_eq!(
ext2.get("psi_irq_full_avg10"),
None,
"None → absent, not 0.0"
);
assert_eq!(ext2.get("max_avg_irq_util"), Some(&80.0));
assert_eq!(ext2.get("total_irq_pressure_us"), Some(&3000.0));
let empty = MonitorSummary {
total_samples: 0,
avg_nr_running: 9.9,
avg_irq_util: Some(50.0),
..Default::default()
};
let mut ext3 = BTreeMap::new();
empty.fold_run_level_ext(&mut ext3);
assert!(ext3.is_empty(), "0-sample summary folds nothing");
let mut ext4 = BTreeMap::new();
ext4.insert("avg_nr_running".to_string(), 1.0);
s.fold_run_level_ext(&mut ext4);
assert_eq!(ext4.get("avg_nr_running"), Some(&1.0), "pre-set value wins");
}
#[test]
fn fold_run_level_ext_tags_dynamic_counter_keys() {
use super::BpfMapFieldValue;
use std::collections::{BTreeMap, BTreeSet};
let s = MonitorSummary {
total_samples: 3,
avg_nr_running: 1.5,
sched_domain_lb: Some(vec![SchedDomainLbDelta {
level: "MC".into(),
lb_count: 20,
lb_failed: 6,
lb_gained: 12,
lb_imbalance_load: 100,
lb_imbalance_util: 200,
lb_imbalance_task: 3,
lb_imbalance_misfit: 0,
alb_count: 1,
alb_pushed: 2,
}]),
bpf_map_fields: Some(vec![
BpfMapFieldValue {
key: "bpf_x_allocs".into(),
value: 42.0,
is_counter: true,
},
BpfMapFieldValue {
key: "bpf_x_lat".into(),
value: 7.0,
is_counter: false,
},
]),
..Default::default()
};
let mut ext = BTreeMap::new();
let mut counter_keys = BTreeSet::new();
s.fold_run_level_ext_with_counter_keys(&mut ext, &mut counter_keys);
for k in [
"lb_count_mc",
"lb_failed_mc",
"lb_gained_mc",
"lb_imbalance_load_mc",
"lb_imbalance_util_mc",
"lb_imbalance_task_mc",
"lb_imbalance_misfit_mc",
"alb_count_mc",
"alb_pushed_mc",
"bpf_x_allocs",
] {
assert!(
counter_keys.contains(k),
"{k} should be tagged as a counter"
);
}
assert!(!counter_keys.contains("avg_nr_running"));
assert!(!counter_keys.contains("bpf_x_lat"));
let mut ext_via_wrapper = BTreeMap::new();
s.fold_run_level_ext(&mut ext_via_wrapper);
assert_eq!(ext, ext_via_wrapper);
}
#[test]
fn bpf_map_field_value_is_counter_serde_default() {
use super::BpfMapFieldValue;
let v = BpfMapFieldValue {
key: "k".into(),
value: 9.0,
is_counter: true,
};
let json = serde_json::to_string(&v).unwrap();
let back: BpfMapFieldValue = serde_json::from_str(&json).unwrap();
assert_eq!(back.key, "k");
assert_eq!(back.value, 9.0);
assert!(back.is_counter, "is_counter roundtrips");
let stale: BpfMapFieldValue = serde_json::from_str(r#"{"key":"k","value":9.0}"#).unwrap();
assert!(!stale.is_counter, "missing is_counter defaults to false");
}
#[test]
fn bpf_map_field_sample_per_cpu_counter_serde_default() {
use super::BpfMapFieldSample;
let s = BpfMapFieldSample {
key_base: "k".into(),
scalar: None,
per_cpu: Some(vec![1.0, 2.0]),
scalar_counter: false,
per_cpu_counter: true,
};
let json = serde_json::to_string(&s).unwrap();
let back: BpfMapFieldSample = serde_json::from_str(&json).unwrap();
assert!(back.per_cpu_counter, "per_cpu_counter roundtrips");
assert_eq!(back.per_cpu, Some(vec![1.0, 2.0]));
let stale: BpfMapFieldSample =
serde_json::from_str(r#"{"key_base":"k","per_cpu":[1.0,2.0]}"#).unwrap();
assert!(
!stale.per_cpu_counter,
"missing per_cpu_counter defaults to false",
);
}
#[test]
fn fold_run_level_ext_folds_per_domain_lb_keys() {
use std::collections::BTreeMap;
let s = MonitorSummary {
total_samples: 3,
sched_domain_lb: Some(vec![SchedDomainLbDelta {
level: "MC".into(),
lb_count: 20,
lb_failed: 6,
lb_gained: 12,
lb_imbalance_load: 100,
lb_imbalance_util: 200,
lb_imbalance_task: 3,
lb_imbalance_misfit: 0,
alb_count: 1,
alb_pushed: 2,
}]),
..Default::default()
};
let mut ext = BTreeMap::new();
s.fold_run_level_ext(&mut ext);
assert_eq!(ext.get("lb_count_mc"), Some(&20.0));
assert_eq!(ext.get("lb_failed_mc"), Some(&6.0));
assert_eq!(ext.get("lb_gained_mc"), Some(&12.0));
assert_eq!(ext.get("lb_imbalance_load_mc"), Some(&100.0));
assert_eq!(ext.get("lb_imbalance_util_mc"), Some(&200.0));
assert_eq!(ext.get("lb_imbalance_task_mc"), Some(&3.0));
assert_eq!(
ext.get("lb_imbalance_misfit_mc"),
Some(&0.0),
"a present level's zero counter is a measured 0, emitted (not absent)"
);
assert_eq!(ext.get("alb_count_mc"), Some(&1.0));
assert_eq!(ext.get("alb_pushed_mc"), Some(&2.0));
let empty = MonitorSummary {
total_samples: 3,
..Default::default()
};
let mut ext2 = BTreeMap::new();
empty.fold_run_level_ext(&mut ext2);
assert!(
!ext2
.keys()
.any(|k| k.starts_with("lb_") || k.starts_with("alb_")),
"no per-domain keys when sched_domain_lb is None"
);
}
#[test]
fn bpf_map_fields_fold_scalar_and_per_cpu() {
use super::BpfMapFieldSample;
let mk = |scalar: f64, pc: Vec<f64>| MonitorSample {
bpf_map_fields: vec![
BpfMapFieldSample {
key_base: "scx_lavd_avg_lat_cri".into(),
scalar: Some(scalar),
per_cpu: None,
scalar_counter: false,
per_cpu_counter: false,
},
BpfMapFieldSample {
key_base: "scx_lavd_lat_headroom".into(),
scalar: None,
per_cpu: Some(pc),
scalar_counter: false,
per_cpu_counter: false,
},
],
prog_stats: None,
psi_irq: None,
elapsed_ms: 0,
cpus: vec![CpuSnapshot {
nr_running: 1,
rq_clock: 1,
..Default::default()
}],
};
let summary =
MonitorSummary::from_samples(&[mk(10.0, vec![2.0, 6.0]), mk(20.0, vec![4.0, 8.0])]);
let folded = summary
.bpf_map_fields
.as_ref()
.expect("watched fields folded");
let by: std::collections::BTreeMap<&str, f64> =
folded.iter().map(|f| (f.key.as_str(), f.value)).collect();
assert_eq!(by.get("scx_lavd_avg_lat_cri"), Some(&15.0));
assert_eq!(by.get("scx_lavd_lat_headroom_avg"), Some(&5.0));
assert_eq!(by.get("scx_lavd_lat_headroom_max"), Some(&8.0));
let mut ext = std::collections::BTreeMap::new();
summary.fold_run_level_ext(&mut ext);
assert_eq!(ext.get("scx_lavd_avg_lat_cri"), Some(&15.0));
assert_eq!(ext.get("scx_lavd_lat_headroom_avg"), Some(&5.0));
assert_eq!(ext.get("scx_lavd_lat_headroom_max"), Some(&8.0));
}
#[test]
fn bpf_map_fields_fold_scalar_counter_takes_last() {
use super::BpfMapFieldSample;
let mk = |gauge: f64, counter: f64| MonitorSample {
bpf_map_fields: vec![
BpfMapFieldSample {
key_base: "bpf_lat".into(),
scalar: Some(gauge),
per_cpu: None,
scalar_counter: false,
per_cpu_counter: false,
},
BpfMapFieldSample {
key_base: "bpf_alloc_count".into(),
scalar: Some(counter),
per_cpu: None,
scalar_counter: true,
per_cpu_counter: false,
},
],
prog_stats: None,
psi_irq: None,
elapsed_ms: 0,
cpus: vec![CpuSnapshot {
nr_running: 1,
rq_clock: 1,
..Default::default()
}],
};
let summary = MonitorSummary::from_samples(&[mk(4.0, 10.0), mk(8.0, 30.0), mk(3.0, 100.0)]);
let by: std::collections::BTreeMap<&str, f64> = summary
.bpf_map_fields
.as_ref()
.expect("watched fields folded")
.iter()
.map(|f| (f.key.as_str(), f.value))
.collect();
assert_eq!(by.get("bpf_alloc_count"), Some(&100.0));
assert_eq!(by.get("bpf_lat"), Some(&5.0));
}
#[test]
fn bpf_map_fields_counter_holds_last_successful_when_tail_unreported() {
use super::BpfMapFieldSample;
let counter = |v: f64| MonitorSample {
bpf_map_fields: vec![BpfMapFieldSample {
key_base: "bpf_alloc_count".into(),
scalar: Some(v),
per_cpu: None,
scalar_counter: true,
per_cpu_counter: false,
}],
prog_stats: None,
psi_irq: None,
elapsed_ms: 0,
cpus: vec![CpuSnapshot {
nr_running: 1,
rq_clock: 1,
..Default::default()
}],
};
let tail_unreported = MonitorSample {
bpf_map_fields: Vec::new(),
prog_stats: None,
psi_irq: None,
elapsed_ms: 0,
cpus: vec![CpuSnapshot {
nr_running: 1,
rq_clock: 1,
..Default::default()
}],
};
let summary = MonitorSummary::from_samples(&[counter(10.0), counter(50.0), tail_unreported]);
let v = summary
.bpf_map_fields
.as_ref()
.and_then(|f| f.iter().find(|x| x.key == "bpf_alloc_count"))
.map(|x| x.value);
assert_eq!(v, Some(50.0));
}
#[test]
fn bpf_map_fields_single_sample_counter_folds_to_value() {
use super::BpfMapFieldSample;
let summary = MonitorSummary::from_samples(&[MonitorSample {
bpf_map_fields: vec![BpfMapFieldSample {
key_base: "bpf_alloc_count".into(),
scalar: Some(42.0),
per_cpu: None,
scalar_counter: true,
per_cpu_counter: false,
}],
prog_stats: None,
psi_irq: None,
elapsed_ms: 0,
cpus: vec![CpuSnapshot {
nr_running: 1,
rq_clock: 1,
..Default::default()
}],
}]);
let v = summary
.bpf_map_fields
.as_ref()
.and_then(|f| f.iter().find(|x| x.key == "bpf_alloc_count"))
.map(|x| x.value);
assert_eq!(v, Some(42.0));
}
#[test]
fn bpf_map_fields_counter_and_per_cpu_coexist() {
use super::BpfMapFieldSample;
let mk = |counter: f64, pc: Vec<f64>| MonitorSample {
bpf_map_fields: vec![
BpfMapFieldSample {
key_base: "bpf_alloc_count".into(),
scalar: Some(counter),
per_cpu: None,
scalar_counter: true,
per_cpu_counter: false,
},
BpfMapFieldSample {
key_base: "bpf_headroom".into(),
scalar: None,
per_cpu: Some(pc),
scalar_counter: false,
per_cpu_counter: false,
},
],
prog_stats: None,
psi_irq: None,
elapsed_ms: 0,
cpus: vec![CpuSnapshot {
nr_running: 1,
rq_clock: 1,
..Default::default()
}],
};
let summary =
MonitorSummary::from_samples(&[mk(10.0, vec![2.0, 6.0]), mk(40.0, vec![4.0, 8.0])]);
let by: std::collections::BTreeMap<&str, f64> = summary
.bpf_map_fields
.as_ref()
.expect("watched fields folded")
.iter()
.map(|f| (f.key.as_str(), f.value))
.collect();
assert_eq!(by.get("bpf_alloc_count"), Some(&40.0));
assert_eq!(by.get("bpf_headroom_avg"), Some(&5.0));
assert_eq!(by.get("bpf_headroom_max"), Some(&8.0));
}
#[test]
fn bpf_map_fields_fold_per_cpu_counter_sums_cross_cpu_last() {
use super::BpfMapFieldSample;
let mk = |pc: Vec<f64>| MonitorSample {
bpf_map_fields: vec![BpfMapFieldSample {
key_base: "bpf_evt_count".into(),
scalar: None,
per_cpu: Some(pc),
scalar_counter: false,
per_cpu_counter: true,
}],
prog_stats: None,
psi_irq: None,
elapsed_ms: 0,
cpus: vec![CpuSnapshot {
nr_running: 1,
rq_clock: 1,
..Default::default()
}],
};
let summary = MonitorSummary::from_samples(&[
mk(vec![10.0, 20.0]),
mk(vec![40.0, 80.0]),
mk(vec![100.0, 200.0]),
]);
let folded = summary
.bpf_map_fields
.as_ref()
.expect("watched fields folded");
let f = folded
.iter()
.find(|x| x.key == "bpf_evt_count")
.expect("per-cpu-counter total key");
assert_eq!(f.value, 300.0, "cross-CPU sum at the LAST sample");
assert!(f.is_counter, "is_counter so it SUM-folds across runs");
assert_eq!(
folded
.iter()
.filter(|x| x.key.starts_with("bpf_evt_count"))
.count(),
1,
"PerCpuCounter emits ONE key (the total), not _avg/_max",
);
}
#[test]
fn bpf_map_fields_per_cpu_counter_last_sample_reporting_cpus() {
use super::BpfMapFieldSample;
let mk = |pc: Vec<f64>| MonitorSample {
bpf_map_fields: vec![BpfMapFieldSample {
key_base: "bpf_evt_count".into(),
scalar: None,
per_cpu: Some(pc),
scalar_counter: false,
per_cpu_counter: true,
}],
prog_stats: None,
psi_irq: None,
elapsed_ms: 0,
cpus: vec![CpuSnapshot {
nr_running: 1,
rq_clock: 1,
..Default::default()
}],
};
let summary = MonitorSummary::from_samples(&[mk(vec![10.0, 20.0]), mk(vec![70.0])]);
assert_eq!(
summary
.bpf_map_fields
.as_ref()
.and_then(|f| f.iter().find(|x| x.key == "bpf_evt_count"))
.map(|x| x.value),
Some(70.0),
"sum over the CPUs that reported in the last sample",
);
let one = MonitorSummary::from_samples(&[mk(vec![5.0, 6.0, 7.0])]);
assert_eq!(
one.bpf_map_fields
.as_ref()
.and_then(|f| f.iter().find(|x| x.key == "bpf_evt_count"))
.map(|x| x.value),
Some(18.0),
"single-sample cross-CPU sum",
);
}
#[test]
fn bpf_map_fields_per_cpu_counter_empty_sample_loud_absent() {
use super::BpfMapFieldSample;
let mk = |pc: Vec<f64>| MonitorSample {
bpf_map_fields: vec![BpfMapFieldSample {
key_base: "bpf_evt_count".into(),
scalar: None,
per_cpu: Some(pc),
scalar_counter: false,
per_cpu_counter: true,
}],
prog_stats: None,
psi_irq: None,
elapsed_ms: 0,
cpus: vec![CpuSnapshot {
nr_running: 1,
rq_clock: 1,
..Default::default()
}],
};
let empty = MonitorSummary::from_samples(&[mk(vec![])]);
assert!(
empty
.bpf_map_fields
.as_ref()
.map(|f| f.iter().all(|x| x.key != "bpf_evt_count"))
.unwrap_or(true),
"empty per-CPU counter sample emits no key (no phantom 0.0)",
);
let tail_empty = MonitorSummary::from_samples(&[mk(vec![10.0, 20.0]), mk(vec![])]);
assert_eq!(
tail_empty
.bpf_map_fields
.as_ref()
.and_then(|f| f.iter().find(|x| x.key == "bpf_evt_count"))
.map(|x| x.value),
Some(30.0),
"empty last sample falls back to the prior non-empty cross-CPU sum",
);
}
#[test]
fn bpf_map_fields_absent_when_unreported() {
let summary = MonitorSummary::from_samples(&[MonitorSample {
bpf_map_fields: Vec::new(),
prog_stats: None,
psi_irq: None,
elapsed_ms: 0,
cpus: vec![CpuSnapshot {
nr_running: 1,
rq_clock: 1,
..Default::default()
}],
}]);
assert!(summary.bpf_map_fields.is_none());
}