Expand description
Pass/fail evaluation of scenario results.
Key types:
AssertResult– pass/fail status with diagnostics and statisticsAssert– composable assertion config (worker + monitor checks)ScenarioStats/CgroupStats– aggregated telemetryNumaMapsEntry– parsed/proc/self/numa_mapsVMA entryVerdict– pointwise-claim accumulator (built viaAssert::verdict/Verdict::new; comparators routed throughClaimBuilder/SetClaim/SeqClaim)
NUMA assertion functions:
parse_numa_maps– parse numa_maps content into per-VMA entriespage_locality– compute page locality fraction from entriesparse_vmstat_numa_pages_migrated– extract vmstat migration counterassert_page_locality/assert_cross_node_migration– threshold checks
Assertion uses a three-layer merge: Assert::default_checks() ->
Scheduler.assert -> per-test assert.
§Statistical conventions
- Percentiles / medians: nearest-rank (see
percentile), value at indexceil(n * p) - 1. Unlike interpolated percentiles, every reported p99 is an actual observed sample, not a synthetic midpoint. Consistent across everyCgroupStatsandScenarioStatslatency field. - CV (coefficient of variation) is stddev/mean computed over
the pooled latency samples, not as a mean of per-worker CVs —
see
CgroupStats::wake_latency_cvfor the masking caveat.
See the Checking chapter of the guide.
Re-exports§
pub use claim::ClaimBuilder;pub use claim::SeqClaim;pub use claim::SetClaim;pub use claim::Verdict;pub use temporal::EachClaim;pub use temporal::FracPair;pub use temporal::PhaseMapExt;pub use temporal::SeriesField;
Modules§
- claim
- Pointwise-claim accumulator API:
Verdict,ClaimBuilder,SetClaim,SeqClaim, and theclaim!macro. - temporal
- Temporal-assertion patterns over a periodic
SampleSeries.
Structs§
- Absolute
Thresholds - Threshold-preset bundle for
assert_thresholds. Captures the guarantees a scheduler-under-test should meet on a healthy run: wake latency stays within bound, per-iteration compute cost stays within bound, CPU migrations stay within bound, and every worker makes some forward progress. - Assert
- Unified assertion configuration. Carries both worker checks and
monitor thresholds as a single composable type. Each
Optionfield acts as an override —Nonemeans “inherit from parent layer”. - Assert
Detail - A single diagnostic message from an assertion, paired with a
structural
DetailKindso filtering is robust to wording changes. - Assert
Detail With Kind Displayadapter returned byAssertDetail::display_with_kind. Renders the detail as[<kind>] <message>. Held by reference so the helper allocates nothing on the formatting path; the lifetime is the borrow of the sourceAssertDetail.- Assert
Result - Verdict for a single test scenario.
- Cgroup
Stats - Per-cgroup statistics from worker telemetry.
- Info
Note - Informational annotation that does NOT contribute to the failure
verdict — the structural counterpart to
AssertDetailfor “context surfaced alongside a result” emissions. Lives in its own type (not as aDetailKindvariant ofAssertDetail) so the “details = failures” mental model holds at the type level:AssertResult::detailsis the failure stream,AssertResult::info_notesis the context stream. Producers can no longer accidentally tag a note as a failure (the priorDetailKind::Notevariant onAssertDetailmade misclassification a one-character bug — every sidecar consumer that readdetailsneeded to remember to filterkind == Noteto count real failures, and forgetting silently misreported failure counts). - Numa
Maps Entry - Per-VMA entry parsed from
/proc/self/numa_maps. - Pass
Detail - Structured record of a single passing claim — the positive
counterpart to
AssertDetail. Populated byVerdict’srecord_pass_unary/record_pass_binaryhelpers at every comparator’s pass arm so the auto-repro renderer (and any other consumer that wants per-claim fidelity) can iterate passes alongside fails. - Phase
- Identifier for a scenario phase. Newtype over
u16carrying the same 1-indexed encoding documented on every other phase-touching site:Phase::BASELINEis the pre-first-Step settle window (u160);Phase::step(k)is scenario Stepkat 1-indexedu16k + 1. The newtype catches the bug class where a rawu16flows between sites that disagree about 0-indexed vs 1-indexed Step encoding, and gives operators readable construction at consumer sites (Phase::BASELINE/Phase::step(2)instead of magic0u16/3u16). - Phase
Bucket - Per-phase metric bucket — one entry per scenario phase in
ScenarioStats::phases. - Phase
Cgroup Stats - Per-phase per-cgroup raw telemetry components — the per-phase analogue of
CgroupStats. Holds RAW components (sample vectors + counters), NOT the reduced ratios/percentilesCgroupStatscomputes, so whole-run and cross-run aggregates RE-POOL from the components at every level (the per-phase telemetry thesis: an aggregate is recomputed over the pooled components, never averaged from ready-made per-phase reductions — a percentile or weighted ratio cannot be recovered from per-phase scalars). Covers every TYPEDCgroupStatsreduction: avg/min/max off-CPU% and spread fromoff_cpu_pcts; p99/median/CV wake latency fromwake_latencies_ns; mean/worst run-delay fromrun_delays_ns; migration_ratio, iterations_per_cpu_sec, iterations_per_worker, page_locality, cross_node_migration_ratio from their counter components; the COUPLED worst gap (ms + the CPU that owned it) frommax_gap_ms/max_gap_cpu; cpus_used / num_cpus fromcpus_used. EXCLUDESCgroupStats::ext_metrics(the generic extensible map — a per-phase per-cgroup custom metric is a future extension, not part of the typed carrier). Lives inPhaseBucket::per_cgroup, keyed by cgroup name. The structural carrier is empty until a capture path populates it per phase. - Phase
Guard - RAII scope guard for the
ACTIVE_PHASEthread-local. Install at scenario-driverrun_stepentry; the guard’sDroprestores the prior phase label, supporting cleanly-nested scenario dispatch (sub-scenarios layer over a parent’s phase context without leaking). - Scenario
Stats - Aggregated statistics across all cgroups in a scenario.
Enums§
- Detail
Kind - Category tag for an
AssertDetail. Enables structural filtering (e.g. byAssertPlan) without matching on substrings of human-readable messages, which is fragile if wording changes. - Note
Value - Result of checking a scenario run.
- Outcome
- Terminal verdict for a single test scenario or merge fold —
strict four-state enum that replaces the
(passed, skipped)bool-pair encoding onAssertResult. - Outcome
Ref - Borrowed view of an
Outcome: same four discriminants but theSkip,Inconclusive, andFailpayloads borrow theirAssertDetailin place. Returned byOutcome::as_refand the zero-clone verdict-read fast pathAssertResult::outcome_ref.
Constants§
- COMPARATOR_
VOCABULARY - Wire-canonical vocabulary of
PassDetail.comparatortokens. - MAX_
RECORDED_ PASSES - Cap on
AssertResult.passes(and the matching truncation sentinel) so a pathological test that fires millions of claims doesn’t balloon the wire-formatted result. Mirrors SnapshotBridge’sMAX_STORED_EVENTStruncation pattern: when the cap is hit, the cap-th record is replaced with a syntheticPassDetail { name: PASSES_TRUNCATION_SENTINEL_NAME, … }carrying the dropped-count, and further pushes are no-ops. - PASSES_
TRUNCATION_ SENTINEL_ COMPARATOR - Comparator-string value used by the truncation sentinel record
alone. Out-of-vocabulary by design — not in
COMPARATOR_VOCABULARY— so the runtime debug_assert inrecord_pass_innerallows it explicitly without polluting the wire-canonical token set. - PASSES_
TRUNCATION_ SENTINEL_ NAME - Sentinel
PassDetail.namevalue used by the truncation record that replaces theMAX_RECORDED_PASSES-th slot when a test over-runs the cap. Consumers (the auto-repro renderer) match on this string to render[N passes truncated]instead of treating it as a real claim.
Traits§
- Cgroup
Stats Claim - Pointwise-claim accessors generated by
#[derive(Claim)]onCgroupStats. Oneclaim_<field>method per public field, taking&mut Verdictas the accumulator; container fields (BTreeSet/Vec) route throughSetClaim/SeqClaim. Method dispatch keys on the stats struct’s type, so identical field names across distinct stats structs do not collide. For prelude-exported stats types the trait is preluded, souse ktstr::prelude::*brings the accessors into scope; otherwise import the trait from the stats type’s module. - Phase
Bucket Claim - Pointwise-claim accessors generated by
#[derive(Claim)]onPhaseBucket. Oneclaim_<field>method per public field, taking&mut Verdictas the accumulator; container fields (BTreeSet/Vec) route throughSetClaim/SeqClaim. Method dispatch keys on the stats struct’s type, so identical field names across distinct stats structs do not collide. For prelude-exported stats types the trait is preluded, souse ktstr::prelude::*brings the accessors into scope; otherwise import the trait from the stats type’s module. - Phase
Cgroup Stats Claim - Pointwise-claim accessors generated by
#[derive(Claim)]onPhaseCgroupStats. Oneclaim_<field>method per public field, taking&mut Verdictas the accumulator; container fields (BTreeSet/Vec) route throughSetClaim/SeqClaim. Method dispatch keys on the stats struct’s type, so identical field names across distinct stats structs do not collide. For prelude-exported stats types the trait is preluded, souse ktstr::prelude::*brings the accessors into scope; otherwise import the trait from the stats type’s module. - Scenario
Stats Claim - Pointwise-claim accessors generated by
#[derive(Claim)]onScenarioStats. Oneclaim_<field>method per public field, taking&mut Verdictas the accumulator; container fields (BTreeSet/Vec) route throughSetClaim/SeqClaim. Method dispatch keys on the stats struct’s type, so identical field names across distinct stats structs do not collide. For prelude-exported stats types the trait is preluded, souse ktstr::prelude::*brings the accessors into scope; otherwise import the trait from the stats type’s module.
Functions§
- assert_
benchmarks - Check benchmarking metrics: p99 wake latency, wake latency CV, and minimum iteration rate.
- assert_
cross_ node_ migration - Check cross-node page migration ratio against threshold.
- assert_
isolation - Check that workers only ran on CPUs in
expected. - assert_
not_ starved - Default fairness check for one cgroup’s worker reports: builds the
per-cgroup telemetry (
cgroup_stats) and records Starved / Unfair / Stuck against the framework default thresholds. Telemetry is ALWAYS populated — including anum_workers == 0entry for empty reports — sor.stats.cgroupsis never empty for a declared cgroup, independent of whether any fail outcome fired. - assert_
page_ locality - Check NUMA page locality against threshold.
- assert_
scx_ events_ clean - Assert that every SCX event counter in
eventsis at or belowmax_count.eventsis a slice of(name, count)pairs sourced from the kernel’s per-taskscx_event_stats(seekernel/sched/ext.c,SCX_EV_*macros) — typically aggregated and surfaced viamonitor::ScxEventDeltasor sidecarGauntletRow.fallback_count/keep_last_countfields. PassNoneformax_countto require zero (the strict default — error-class events should not fire under a healthy scheduler). - assert_
thresholds - Run every check in
thresholdsagainstreports, merging results into a singleAssertResult. ANonefield on the thresholds skips that check. - assert_
throughput_ parity - Check throughput parity across workers: coefficient of variation and minimum work rate.
- build_
phase_ buckets - Build per-phase metric buckets from a sample series.
- build_
phase_ buckets_ with_ stimulus - Phase buckets attributed against the guest stimulus timeline, then
enriched with stimulus-event-derived per-phase
iteration_rate. - cgroup_
stats - Build per-cgroup telemetry (pure measurement, no assertions) from
worker reports. This is the SINGLE telemetry builder on the assertion
path:
AssertPlan::assert_cgroupcalls it unconditionally andassert_not_starvedwraps it with the default fairness checks, so per-cgroupCgroupStatsis never gated behind whether a worker-check assertion was configured. Emptyreportsyield anum_workers == 0CgroupStats(the reduces below collapse to 0.0/0), so a declared cgroup that collected no reports surfaces as a zero-worker entry rather than silently vanishing fromScenarioStats::cgroups. - current_
phase_ label - Snapshot the active phase label installed by the most recent
PhaseGuard::installon this thread.Noneoutside any guarded scope. Construction sites forAssertDetail/PassDetail/InfoNotecall this to auto-stamp thephasefield; the test author can still override via the builderwith_phase(...)chain when an explicit value is preferred. - page_
locality - Compute page locality fraction from parsed numa_maps entries.
- parse_
numa_ maps - Parse
/proc/self/numa_mapscontent into per-VMA entries. - parse_
vmstat_ numa_ pages_ migrated - Extract
numa_pages_migratedfrom/proc/vmstatcontent. - populate_
run_ distribution_ metrics - Populate run-level DERIVED distributional metrics into
stats.ext_metrics: every registeredMetricKind::Distribution,MetricKind::WorstLowest,MetricKind::WakeLatencyTailRatio, andMetricKind::WorstCrossNodeRatio. This is the SOLE within-run producer of those metrics’ values — they carry no per-phase sample slice and no cross-cgroup merge fold, and their registry accessors are|_| None, soMetricDef::readreads the value written here fromext_metrics. - populate_
run_ ext_ all - Run the FULL run-level
ext_metricspopulation sequence intostats— the single source of truth shared by the eval layer (evaluate_vm_result) andcrate::vmm::VmResult::run_metric, so the two produce byte-identical run-level ext maps for the same run. Readssamples+stats.phases+stats.cgroups, writesstats.ext_metrics, in the canonical order: - populate_
run_ ext_ metrics - Populate cross-RUN aggregate entries for every registered
crate::stats::MetricDefwhoseread_samplereturns finite values across the entire sample series. Writes intotarget(typicallyScenarioStats::ext_metrics) under the metric’s registry name — the same key the per-phasePhaseBucket::metricsuses, so cross-RUN and per-phase consumers reference the same name. - populate_
run_ ext_ metrics_ from_ phases - Sibling of
populate_run_ext_metricsthat mines per-phase metrics back into the run-levelext_metricsmap. Closes the gap for registered metrics whose values live inPhaseBucket.metricsbut never reachext_metricsvia the SampleSeries path (theirread_samplereturnsNone):avg_imbalance_ratio(sourced from MonitorSample windowing insidebuild_phase_buckets),iteration_rate(sourced from stimulus event totals insidebuild_phase_buckets_with_stimulus), andsystem_time_ns/user_time_ns(per-thread-group CPU-time deltas injected byphase_group_cpu_deltainsidebuckets_from_grouped). The fold is generic over every key present on any phase, so it carries any such phase-only metric (the ext-metrics-only set whoseread_samplereturnsNone). Keys with a typedGauntletRowfield (TYPED_FIELD_NAMES) are SKIPPED: their run-level value comes from the typed accessor (which wins on read), so re-injecting them here would double-source the run aggregate — the hazard the const’s doc describes. Their per-phasePhaseBucketvalue still feeds per-phase rendering. - populate_
run_ pooled_ iterations_ per_ cpu_ sec - Inject the run-level POOLED
iterations_per_cpu_secRate’s two Counter components intostats.ext_metrics, summed across the cgroups that have measured on-CPU time — the cross-cgroup re-pool axis. Rather than routing the per-cgroup efficiency throughAssertResult::merge’s worst-by-polarityext_metricsfold (which picks the WORST cgroup’s value, not Σ, and has no derive post-pass), this reads the already-mergedstats.cgroupsvec directly:iterations_per_cpu_sec= Σtotal_iterations/ Σ(total_cpu_time_ns/1e9) over cgroups withtotal_cpu_time_ns > 0— the per-cgroupCgroupStats::iterations_per_cpu_secre-pooled, NOT a mean of per-cgroup ratios, NOT the worst single cgroup. - populate_
run_ pooled_ schbench - Inject the schbench whole-run Class-3 metrics — the loop Counter and the
role-separate run-delay gate-Rate components — into
stats.ext_metrics, summed across EVERY phase and EVERY cgroup from the per-phaseSchbenchPhaseStatsraw pairs (stats.phases[].per_cgroup[].schbench). The raw(run_delay_ns, pcount)pairs andloop_countare integer and associative, so summing across phases+cgroups gives the run-level totals; the two*_run_delay_ns_per_schedRates then re-derive Σrun_delay/Σpcount (the sample-weighted per-schedule mean — NOT a mean of per-run means). The MESSAGE and WORKER thread roles pool SEPARATELY (different per-schedule wait populations — never cross-pool). - populate_
run_ pooled_ schbench_ distribution - Inject the schbench whole-run DISTRIBUTIONAL metrics (the wakeup /
request latency percentiles + min/max and the achieved-rps percentiles) into
stats.ext_metricsas the*_wholekeys, re-pooled run-level by UNIONING the per-phase per-cgroupPlatStatshistograms (stats.phases[].per_cgroup[].schbench.{wakeup,request,rps}) across EVERY phase and EVERY cgroup, then re-deriving each percentile / min / max over the merged histogram.PlatStats::combineis an associative bucket-count add, so the merged histogram is the FAITHFUL union and the re-derived percentile is the percentile OF the pooled sample set — NOT a mean of per-phase / per-cgroup percentiles (the percentile operator is non-linear). This is the schbench histogram analog ofpopulate_run_distribution_metrics’s raw-sample union. - populate_
run_ pooled_ taobench - Inject the whole-run taobench engine’s qps + hit Rate components into
stats.ext_metrics, pooled across the run’sWorkType::Taobenchcgroups. Each cgroup carries its workers’ merged whole-run aggregate (crate::assert::CgroupStats::taobench_whole); this folds those across cgroups (Σ ops, MAX wall window — the window is shared by the concurrent cohorts, perTaobenchStats::merge) and writes the sixtotal_taobench_*Counter components (ops,fast_ops,slow_ops,wall_sec, plus the command-timeget_cmds/get_hits), from whichcrate::stats::derive_rate_metricsderivestaobench_total_ops_per_sec,taobench_fast_ops_per_sec,taobench_slow_ops_per_sec, the response-timetaobench_hit_fraction(Σfast/Σcompleted), and the command-timetaobench_command_hit_rate(Σhits/Σcmds). The whole-run Rate keys are registered METRICS, so — unlike the per-phasetaobench_*_qps(MetricKind::PerPhase, invisible to the whole-run cross-run fold) — they reach the perf-delta--noise-adjustspread analysis. The open-loop serve-latency distribution is a SEPARATE pool (populate_run_pooled_taobench_distribution, the*_us_wholekeys). - populate_
run_ pooled_ taobench_ distribution - Inject the taobench WHOLE-RUN open-loop serve-latency percentiles into
stats.ext_metricsas thetaobench_serve_*_us_wholekeys, re-pooled run-level by UNIONING the per-phase per-cgroup servePlatStatshistograms (stats.phases[].per_cgroup[].taobench.serve_lat) across every step-attributed phase and every cgroup, then re-deriving each percentile / min / max over the merged histogram (PlatStats::combineis an associative bucket-count add, so the merged histogram is the faithful pooled sample set and the re-derived percentile is the percentile OF the union — NOT a mean of per-source percentiles). The taobench analog ofpopulate_run_pooled_schbench_distribution, with the same source: the BASELINE (epoch 0) and inter-step-gap (u32::MAX) epochs are excluded (they are dropped fromstats.phasesbyexpand_backdrop_phase_buckets), so this is the steady-state serve distribution over the measured steps — a faithful PerRunDistribution, distinct from the standalone driver’s full-run histogram.