pub struct PhaseBucket {
pub step_index: u16,
pub label: String,
pub start_ms: u64,
pub end_ms: u64,
pub sample_count: usize,
pub metrics: BTreeMap<String, f64>,
pub per_cgroup: BTreeMap<String, PhaseCgroupStats>,
}Expand description
Per-phase metric bucket — one entry per scenario phase in
ScenarioStats::phases.
A scenario with N Steps yields N + 1 phases: phase 0 is the
BASELINE (pre-first-Step settle window), and phases 1..=N
correspond to Step 0..Step N-1 in scenario order. The
1-indexed Step encoding (instead of 0-indexed) lets BASELINE
own step_index = 0 unambiguously — a step_index = 0 sample
is always settle, not first-Step.
Each bucket carries the metric values reduced over the phase’s
sample window. For crate::stats::MetricKind::Counter
metrics the reduction is last - first across the phase’s
periodic samples (cumulative-counter delta); for Gauge /
Peak / Timestamp it dispatches per the kind via
crate::stats::aggregate_samples. Missing metric keys mean
the phase had no finite samples for that metric.
Metric keys match crate::stats::MetricDef::name — see
crate::stats::METRICS for the canonical list of registered
metric names a get / phase_metric lookup expects.
Fields§
§step_index: u16Phase index. 0 = BASELINE (pre-first-Step settle window).
1..=N align with Step ordinals (1-indexed): Step 0 of the
scenario lives at step_index = 1, Step 1 at
step_index = 2, etc. The encoding avoids the collision
where a 0-indexed Step would share step_index = 0 with
the BASELINE settle window.
label: StringHuman-readable label. "BASELINE" for step_index = 0,
"Step[0]" / "Step[1]" / … for step_index = 1..=N.
Mirrors the formatting used by
crate::timeline::Timeline’s phase rendering so operator
inspection of the formatted diagnostic and the structured
sidecar yield the same phase identifiers.
start_ms: u64Phase window start: the MINIMUM per-sample time anchor in the
phase — each sample’s boundary_offset_ms, falling back to its
elapsed_ms. Samples with neither anchor (both None — a
not-measured timestamp) are excluded from the min.
end_ms: u64Phase window end: the MAXIMUM per-sample time anchor in the
phase (the same boundary_offset_ms-or-elapsed_ms key as
start_ms). A phase whose every sample is unanchored yields the
inverted window (start_ms = u64::MAX, end_ms = 0), which folds
no monitor samples. Downstream renderers should not assume the
value is closed against a stimulus event.
sample_count: usizeNumber of periodic samples bucketed into this phase. Zero
when the phase fired no captures: BASELINE when the settle window
was shorter than the periodic interval, OR a synthesized
capture-free interior step (the
build_phase_buckets_with_stimulus seam — a StepStart-step
whose window held no periodic boundary still gets a bucket so its
capture-independent iteration_rate is not dropped).
metrics: BTreeMap<String, f64>Per-metric phase-aggregated values. See the PhaseBucket
struct doc for the registry key source and per-kind reduction
dispatch; missing keys mean the phase carried no finite
samples for that metric (sentinel-free: None from the
reducer surfaces as “key absent” rather than “value 0.0”).
per_cgroup: BTreeMap<String, PhaseCgroupStats>Per-cgroup raw telemetry components for this phase, keyed by cgroup
name (see PhaseCgroupStats). Empty until a capture path populates
it; the structural carrier for the per-phase per-cgroup distributional
re-pool. Whole-run = aggregate of these per-phase per-cgroup components.
An ORPHAN bucket — a guest carrier whose step_index has NO paired host
bucket (a dropped/absent StepStart frame, or a stimulus-less host/fixture
path; NOT merely a short step, since build_phase_buckets_with_stimulus
synthesizes a bucket for every StepStart so a captured-but-short step
takes the matched arm) — is carried by
fold_guest_per_cgroup_into_host_buckets with the shape
(start_ms, end_ms) == (0, 0) AND empty metrics AND non-empty
per_cgroup (it carries only these components). On every non-zero-duration
window that shape is the orphan arm’s, so the timeline render keys on it
to surface “window not measured” rather than a misleading 0ms (see
crate::timeline::phase_from_bucket): a captured bucket has metrics. A
zero-duration step at scenario start (StepStart==StepEnd==0) can also
produce it via the matched arm, but harmlessly — a zero-duration step has
no window, so “not measured” reads the same as “0ms”.
Implementations§
Source§impl PhaseBucket
impl PhaseBucket
Sourcepub fn get(&self, metric_name: &str) -> Option<f64>
pub fn get(&self, metric_name: &str) -> Option<f64>
Look up the phase-aggregated value for metric_name (see
PhaseBucket::metrics for the registry source). Returns
None when the phase carried no finite samples for that
metric — distinct from Some(0.0) which means the reducer
produced a real zero from finite samples.
Sourcepub fn expect_metric(&self, metric_name: &str) -> f64
pub fn expect_metric(&self, metric_name: &str) -> f64
Like Self::get, but panics with a diagnostic message citing
the bucket’s step_index + label + sample_count + the set
of metric keys actually present when the metric is absent. Use
when the caller knows the metric MUST be in the bucket (the
phase fired samples and the metric is registered — see
PhaseBucket::metrics) — the panic message tells the operator whether the cause is
“phase produced no samples” (sample_count of 0) or “metric key
typo” (positive sample_count but the key isn’t in metrics).
let bucket = r.stats.phase(Phase::step(0)).expect("Step[0] phase");
let throughput = bucket.expect_metric("throughput");Sourcepub fn cgroup_counter_total(&self, name: &str) -> Option<f64>
pub fn cgroup_counter_total(&self, name: &str) -> Option<f64>
Cross-cgroup phase total for a per-cgroup Counter metric that lives
in Self::per_cgroup but never in Self::metrics — currently
"total_migrations", "total_iterations", and "total_cpu_time_ns".
These are registered MetricKind::Counters
(crate::stats::METRICS) whose per-sample source is absent
(crate::stats::MetricDef::read_sample returns None — they are
per-task guest counters not captured per tick), so
crate::assert::build_phase_buckets never folds them into
metrics; only the per-cgroup carrier (PhaseCgroupStats, built
by phase_cgroup_stats) holds them. This sums them across the
phase’s per_cgroup carriers — the SAME cross-cgroup Counter sum
ScenarioStats::total_migrations takes run-level and
merge_matched_phase_buckets takes per key — so the value is the
phase total, not a per-cgroup fragment.
None when the phase has no per_cgroup carriers (NOT measured —
distinct from a measured Some(0.0) when carriers exist but counted
zero) or name is not one of the per-cgroup-sourced counters.
Surfacing them here never double-sources the run-level ext_metrics,
but by two different mechanisms: total_migrations / total_iterations
are in TYPED_FIELD_NAMES (populate_run_ext_metrics_from_phases skips
them; the typed GauntletRow accessor stays the single run-level
authority), while total_cpu_time_ns is NOT in TYPED_FIELD_NAMES —
it is safe because it is never written into any pooled metrics map
(its MetricDef accessor and read_sample both return None, so no
run-level fold ever picks it up; it lives only on the per-cgroup
carrier).
Counterpart to Self::get (which reads metrics only).
crate::vmm::VmResult::phase_metric falls back to this so a
post_vm callback reading phase_metric(phase, "total_migrations")
gets the value instead of a silent None.
Trait Implementations§
Source§impl Clone for PhaseBucket
impl Clone for PhaseBucket
Source§fn clone(&self) -> PhaseBucket
fn clone(&self) -> PhaseBucket
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for PhaseBucket
impl Debug for PhaseBucket
Source§impl Default for PhaseBucket
impl Default for PhaseBucket
Source§fn default() -> PhaseBucket
fn default() -> PhaseBucket
Source§impl<'de> Deserialize<'de> for PhaseBucket
impl<'de> Deserialize<'de> for PhaseBucket
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
Source§impl PartialEq for PhaseBucket
impl PartialEq for PhaseBucket
Source§impl PhaseBucketClaim for PhaseBucket
impl PhaseBucketClaim for PhaseBucket
fn claim_step_index<'a>( &'a self, verdict: &'a mut Verdict, ) -> ClaimBuilder<'a, u16>
fn claim_label<'a>( &'a self, verdict: &'a mut Verdict, ) -> ClaimBuilder<'a, String>
fn claim_start_ms<'a>( &'a self, verdict: &'a mut Verdict, ) -> ClaimBuilder<'a, u64>
fn claim_end_ms<'a>(&'a self, verdict: &'a mut Verdict) -> ClaimBuilder<'a, u64>
fn claim_sample_count<'a>( &'a self, verdict: &'a mut Verdict, ) -> ClaimBuilder<'a, usize>
Source§impl Serialize for PhaseBucket
impl Serialize for PhaseBucket
impl StructuralPartialEq for PhaseBucket
Auto Trait Implementations§
impl Freeze for PhaseBucket
impl RefUnwindSafe for PhaseBucket
impl Send for PhaseBucket
impl Sync for PhaseBucket
impl Unpin for PhaseBucket
impl UnwindSafe for PhaseBucket
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more