#[non_exhaustive]pub struct DualFailureDumpReport {
pub schema: String,
pub early: Option<FailureDumpReport>,
pub late: FailureDumpReport,
pub early_max_age_jiffies: u64,
pub early_threshold_jiffies: u64,
pub early_skipped_reason: Option<String>,
}Expand description
Pair of failure-dump snapshots captured at two points in a stall.
early is taken when the host-side runnable_at scanner observes
any task with jiffies - p->scx.runnable_at > watchdog_timeout/2
(mirrors the kernel’s check_rq_for_timeouts walk over
rq->scx.runnable_list). late is taken at the same trigger as
the single-snapshot path: the BPF probe’s
ktstr_err_exit_detected latch flipping after a sched_ext
error-class exit.
early == None when the watchdog half-way threshold never
triggered before late fired (e.g. an immediate scheduler error
in init_task before any task became runnable). Diffing
late against early shows what BPF state changed during the
stall window — the value-add over the single-snapshot dump.
No user toggle — auto-repro engages this automatically. Only
the auto-repro VM emits this shape;
crate::test_support::probe::attempt_auto_repro is the
single call site flipping the builder’s dual_snapshot flag,
and there is no public ktstr surface for asking for it from a
primary VM. Test authors don’t need to know about it — when an
auto-repro fires, the file at
<test>-<variant_hash>.repro.failure-dump.json changes shape from
FailureDumpReport to this wrapper.
Note: there is no Default impl. The late field is required
by the doc invariant (“the freeze coordinator only writes a
DualFailureDumpReport after the late snapshot has been
captured”); a Default::default() would have produced a wrapper
with an empty late report whose maps/vcpu_regs vectors
silently lie about a successful capture. Construct via the
struct literal with an explicit late: FailureDumpReport.
Fields (Non-exhaustive)§
This struct is marked as non-exhaustive
Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.schema: StringWire-format discriminant. Always "dual" for this variant,
pinning SCHEMA_DUAL. Mirror of FailureDumpReport::schema
— consumers branch on it before deserializing.
early: Option<FailureDumpReport>Snapshot at the watchdog half-way point. None when the
stall fired before the half-way scanner crossed its threshold.
late: FailureDumpReportSnapshot at the error-exit latch trigger. Always present
(the freeze coordinator only writes a DualFailureDumpReport
after the late snapshot has been captured; if the run ends
with only an early snapshot the file is not written at all).
early_max_age_jiffies: u64Maximum jiffies - p->scx.runnable_at observed by the
runnable_at scanner at the moment the early snapshot fired.
Zero when early is None.
To recover the kernel’s full watchdog_timeout, double
Self::early_threshold_jiffies — the scanner trigger
fires at half the watchdog, so the threshold field carries
watchdog_timeout / 2. Diff early_max_age_jiffies against
2 * early_threshold_jiffies to see how close the system
was to the SCX_EXIT_ERROR_STALL emission line at the
early-trigger point.
early_threshold_jiffies: u64The half-way trigger threshold the scanner compared against
when capturing the early snapshot, expressed in guest
jiffies. Equals (watchdog_timeout_ms * CONFIG_HZ) / 1000 / 2
at the moment the snapshot fired. Zero when early is
None.
Surfaced alongside early_max_age_jiffies so a downstream
consumer reading the JSON does not have to recompute the
kernel-internal jiffies arithmetic to reproduce the
trigger condition.
early_skipped_reason: Option<String>Structured reason the early snapshot is absent. None when
the early snapshot was captured (the Self::early field is
Some). When the early field is None, this carries a short
machine-friendly string identifying which of the known
failure modes occurred:
"scan prerequisites unavailable: <prereq>"— the per-CPUrunnable_atscan never resolved its dependencies (most often<prereq>names the missing kernel symbol / BTF entry)."max_age never crossed threshold (peak={peak}j, threshold={threshold}j)"— the scan ran but the maximum observed runnable-age stayed below the half-way mark for the whole VM lifetime. Indicates a non-stall err-class exit (e.g.scx_bpf_error())."scx_tick stall — no per-task runnable_at data"— the stall path that drove the late capture has no per-taskrunnable_atto scan (the kernel’s “watchdog failed to check in” path raisesSCX_EXIT_ERROR_STALLfrom the scx_tick kernel side without any task onrq->scx.runnable_list).
Display rendering at super::display surfaces this string
directly; the previous “stall fired before half-way threshold,
or runnable_at scan setup failed” generic text is replaced
with the structured reason whenever this field is Some.
Trait Implementations§
Source§impl Clone for DualFailureDumpReport
impl Clone for DualFailureDumpReport
Source§fn clone(&self) -> DualFailureDumpReport
fn clone(&self) -> DualFailureDumpReport
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for DualFailureDumpReport
impl Debug for DualFailureDumpReport
Source§impl<'de> Deserialize<'de> for DualFailureDumpReport
impl<'de> Deserialize<'de> for DualFailureDumpReport
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 Display for DualFailureDumpReport
impl Display for DualFailureDumpReport
Auto Trait Implementations§
impl Freeze for DualFailureDumpReport
impl RefUnwindSafe for DualFailureDumpReport
impl Send for DualFailureDumpReport
impl Sync for DualFailureDumpReport
impl Unpin for DualFailureDumpReport
impl UnwindSafe for DualFailureDumpReport
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