pub struct Timeline {
pub phases: Vec<Phase>,
}Expand description
Correlated timeline of stimulus events and monitor observations.
Fields§
§phases: Vec<Phase>Implementations§
Source§impl Timeline
impl Timeline
Sourcepub fn build(
stimulus_events: &[StimulusEvent],
monitor_samples: &[MonitorSample],
preemption_threshold_ns: u64,
) -> Self
pub fn build( stimulus_events: &[StimulusEvent], monitor_samples: &[MonitorSample], preemption_threshold_ns: u64, ) -> Self
Build a timeline from stimulus events and monitor samples.
Clock alignment: stimulus events use guest monotonic time (ms since scenario start). Monitor samples use host monotonic time (ms since VM boot). The first stimulus event’s timestamp and the first non-trivial monitor sample (after 500ms warmup) approximately coincide. We compute an offset to align them.
Returns an empty timeline if either input is empty.
Build a Timeline from stimulus events + raw monitor
samples via the per-window compute_metrics reduction.
The production success path uses Self::from_phase_buckets
(which folds pre-bucketed PhaseBuckets); build is the fallback
evaluate_vm_result takes only for a run with an EMPTY PhaseBuckets
vec but monitor samples present — i.e. no periodic captures AND no
stimulus Steps. A monitor-only run that DID run Steps now
synthesizes a capture-free bucket per StepStart (see
crate::assert::build_phase_buckets_with_stimulus), so its vec is
non-empty and it takes the from_phase_buckets path (whose
fold_monitor_into_bucket recovers the same monitor-derived metric
set this path computes). Both entry points produce the same
Timeline field shape; from_phase_buckets is preferred when buckets
are available because it avoids the per-MonitorSample reduction.
preemption_threshold_ns threads the vCPU-preemption exemption
window into the per-phase stall predicate (see compute_metrics);
the production caller passes the run’s
MonitorReport::preemption_threshold_ns. 0 derives it from the
guest kernel CONFIG_HZ.
Sourcepub fn format_with_context(&self, ctx: &TimelineContext) -> String
pub fn format_with_context(&self, ctx: &TimelineContext) -> String
Format the timeline with a system context header.
Tests without a real context pass &TimelineContext::default();
the header lines (kernel:, topology:, etc.) are omitted but
the --- timeline --- prefix is preserved.
Sourcepub fn from_phase_buckets(
phase_buckets: &[PhaseBucket],
stimulus_events: &[StimulusEvent],
_ctx: &TimelineContext,
) -> Self
pub fn from_phase_buckets( phase_buckets: &[PhaseBucket], stimulus_events: &[StimulusEvent], _ctx: &TimelineContext, ) -> Self
Build a Timeline from pre-bucketed
crate::assert::PhaseBuckets emitted by the metric pipeline.
Preferred over Self::build when the caller already has
PhaseBuckets in hand — avoids re-deriving phase boundaries
from stimulus events + monitor samples by walking the buckets
directly.
One Phase is emitted per bucket, in step_index order.
PhaseMetrics fields are populated from the bucket’s
metrics map via a name-keyed mapping:
| PhaseBucket metric key | PhaseMetrics field |
|---|---|
max_imbalance_ratio | max_imbalance |
avg_imbalance_ratio | avg_imbalance |
max_dsq_depth | max_dsq_depth |
avg_dsq_depth | avg_dsq_depth |
avg_nr_running | avg_nr_running |
stuck_count | stall_count |
total_fallback | fallback_rate (rate) |
total_keep_last | keep_last_rate (rate) |
iteration_rate | iteration_rate |
Rate fields (fallback_rate, keep_last_rate) are computed
by dividing the bucket’s reduced counter delta by the
bucket’s window duration in seconds
((end_ms - start_ms) / 1000.0). When the window has zero
duration (degenerate bucket) the rate stays None.
Every PhaseMetrics field has a PhaseBucket source — but
iteration_rate only when build_phase_buckets_with_stimulus
(not the plain build_phase_buckets) produced the bucket.
iteration_rate requires stimulus events that the per-test
scenario produces; the plain bucket-builder used by some
tests doesn’t have access to them. Defaults to None when
PhaseBucket.metrics has no iteration_rate key.
changes (boundary degradation detection) IS computed
here by diffing adjacent PhaseMetrics fields — same
detection logic Self::build uses, applied after the
per-bucket conversion. avg_imbalance + avg_dsq_depth are
supplied by PhaseBucket so the detection runs on the same
fields as the legacy path.
Trait Implementations§
Auto Trait Implementations§
impl Freeze for Timeline
impl RefUnwindSafe for Timeline
impl Send for Timeline
impl Sync for Timeline
impl Unpin for Timeline
impl UnwindSafe for Timeline
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