Ctx

Struct Ctx 

Source
#[non_exhaustive]
pub struct Ctx<'a> { pub cgroups: &'a dyn CgroupOps, pub topo: &'a TestTopology, pub duration: Duration, pub workers_per_cgroup: usize, pub sched_pid: Option<pid_t>, pub settle: Duration, pub work_type_override: Option<WorkType>, pub assert: Assert, pub wait_for_map_write: bool, pub current_step: Arc<AtomicU16>, pub entry_name: Option<&'static str>, pub variant_hash: u64, }
Expand description

Runtime context passed to scenario functions.

Provides access to cgroup management, topology information, and test configuration. Custom scenarios are functions receiving this Ctx as their sole parameter (e.g. the custom_* fns in crate::scenario::nested / crate::scenario::stress).

§Method groups

§Time helpers

  • Self::settled_holdHoldSpec::fixed(settle + duration * f) sugar for the dominant Step hold-time pattern.

§Cgroup construction

  • Self::cgroup_defCgroupDef::named(name).workers(workers_per_cgroup) sugar that pins the default-worker-count shape across 40+ call sites.

§Topology accessors

§Constructors

§Field groups

Each pub field’s doc is prefixed with its sub-concern label so the rustdoc table groups visibly. The six groups are:

  • VM environmentcgroups, topo. The host-side filesystem + topology handles the scenario interacts with.
  • Test timingduration, settle. The wall-clock budgets that shape every Step’s hold-time math.
  • Cgroup defaultsworkers_per_cgroup, work_type_override. The merge-time defaults CgroupDef::merged_works applies when a WorkSpec leaves them unset.
  • Scheduler statesched_pid. Liveness-probe target for inter-step scheduler-death detection.
  • Assertion policyassert. The merged default+scheduler+per-test verdict checks run_scenario / execute_steps apply.
  • Runtime coordinationwait_for_map_write. Framework-set gate that custom scenarios typically do not flip.

Fields (Non-exhaustive)§

This struct is marked as non-exhaustive
Non-exhaustive structs could have additional fields added in future. Therefore, non-exhaustive structs cannot be constructed in external crates using the traditional Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.
§cgroups: &'a dyn CgroupOps

VM environment. Cgroup filesystem operations. &dyn CgroupOps (not &CgroupManager) so scenario code can be driven by an in-memory test double without touching /sys/fs/cgroup. Production callers pass &CgroupManager and the auto-coercion is transparent at the call site — ctx.cgroups.set_cpuset(...) works unchanged.

§topo: &'a TestTopology

VM environment. VM CPU topology.

§duration: Duration

Test timing. How long to run the workload.

§workers_per_cgroup: usize

Cgroup defaults. Default number of workers per cgroup.

§sched_pid: Option<pid_t>

Scheduler state. PID of the running scheduler (for liveness checks), or None when no scheduler is attached. Stored as Option<pid_t> so the “no scheduler” state is a distinct variant rather than a 0-sentinel — run_scenario and step-level liveness probes destructure via if let Some(pid) instead of != 0 guards.

§settle: Duration

Test timing. Time to wait after cgroup creation for scheduler stabilization.

§work_type_override: Option<WorkType>

Cgroup defaults. Override work type for scenarios that use SpinWait by default.

§assert: Assert

Assertion policy. Merged assertion config (default_checks + scheduler + per-test). Used by run_scenario for data-driven scenarios and by execute_steps as the default when no explicit checks are passed to execute_steps_with.

§wait_for_map_write: bool

Runtime coordination. When true, execute_steps blocks after writing the scenario start marker until the host confirms its BPF map write is complete — waiting on the bpf_map_write_done latch that hvc0_poll_loop sets when the host pushes SIGNAL_BPF_WRITE_DONE over the virtio-console RX queue. Set automatically by the framework when a KtstrTestEntry declares bpf_map_write; custom scenarios typically do not flip this manually.

§current_step: Arc<AtomicU16>

Phase coordination. Per-VM atomic publishing the current scenario step index. Written by the scenario driver immediately before each run_step call and read by three stamping sites so each captured sample carries the step it belongs to: (1) the host-side freeze-coordinator periodic-capture path stamps at periodic-fire time; (2) the on-demand Op::CaptureSnapshot apply arm stamps at apply time (the apply happens in the same phase as the capture); (3) the host-side user-watchpoint trip handler stamps at TRIP time, not at registration — the user issues Op::WatchSnapshot from some Step k, but the actual write that fires the watchpoint and triggers the snapshot can happen at any later phase, so the trip-time stamp pins the sample to the bucket matching when the observation actually occurred.

Encoded per the framework’s 1-indexed phase convention: 0 is the BASELINE settle window (the initial value), 1..=N align with scenario Step ordinals (step_idx + 1). This matches crate::assert::PhaseBucket::step_index so a phase-aware sample drops directly into the correct bucket without a reindex.

Stored as AtomicU16 because the wire StimulusPayload step-index field is also u16, so a single shared width keeps the host-side bridge map and the guest-published wire value type-compatible without narrowing.

Wrapped in Arc so the same per-VM publisher can be cloned into every consumer thread (scenario driver, freeze-coord, on-demand-capture apply arms) without a process-global static — multiple in-process VMs (e.g. parallel gauntlet variants) each get an independent atomic instead of racing on shared global state.

§entry_name: Option<&'static str>

Drift-safe path derivation. The &'static str name of the KtstrTestEntry the running test body was dispatched as, stamped by the guest-side maybe_dispatch_vm_test_with_args and the host-only dispatch path before the test body runs. Drives the body-side path-derivation methods failure_dump_path (and wprof_pb_path / repro_wprof_pb_path when the wprof feature is enabled) — the drift-safe replacement for the legacy pattern of hardcoding the test fn name as a string literal at the callsite. When Some(name), those methods derive the sidecar paths from the macro-stamped value at call time, so a future test rename surfaces the resulting Result<PathBuf> bail at compile-time-equivalent-failure (a deterministic Err) rather than as a runtime ENOENT against a stale literal.

None is the manually-constructed-Ctx escape hatch — ad-hoc scenario tests that build Ctx via CtxBuilder::build without calling CtxBuilder::entry_name get None and a path-derivation method invocation bails with an actionable diagnostic naming the missing-stamp scenario. Sibling to crate::vmm::VmResult::entry_name which carries the same &'static str on the post-VM result struct (the two ends of the test-name chain — pre-VM body context vs post-VM result — store the same shape so the body-side ctx.failure_dump_path() and the host-side result.failure_dump_path() resolve to identical paths).

§variant_hash: u64

The run’s variant hash (see variant_hash_from_parts), stamped at the macro dispatch site alongside Self::entry_name. The body-side failure_dump_path / wprof_pb_path derivations embed it as the -{16-hex} filename suffix so a gauntlet test’s per-preset dumps don’t clobber and each matches its sidecar’s variant hash. 0 on a manually-built fixture (which has entry_name = None and thus bails before reading this).

Implementations§

Source§

impl Ctx<'_>

Source

pub fn current_scheduler(&self) -> Option<&'static SchedulerSpec>

Read the live scheduler identity published by the Op::AttachScheduler / Op::ReplaceScheduler / Op::DetachScheduler dispatch arms. Returns None when no scheduler is currently attached (the pre-attach state at process start and the post-Op::DetachScheduler state).

Distinct from entry.scheduler (the boot-time descriptor from the #[ktstr_test] macro): entry.scheduler stays the same across Op::ReplaceScheduler swaps, while ctx.current_scheduler() reflects the LIVE identity after any runtime swap. Consumer sites that care about the currently-attached BPF binary (verifier_stats wiring, monitor thresholds, auto-repro probe gates) want this method; sites that care about the test’s declared scheduler (test-runner skip/include filtering, sidecar scheduler_name metadata) want entry.scheduler.

v0 limitation: the boot path does not publish the boot scheduler into the side channel, so the first observable Some arrives after the first Op::AttachScheduler / Op::ReplaceScheduler runs. Consumer sites that want a fallback should call .unwrap_or(&entry.scheduler.binary) to combine the live view with the boot descriptor.

Source

pub fn cpuset_cpus(&self, spec: &CpusetSpec) -> usize

Resolve a CpusetSpec against this context’s topology and return the CPU count. Convenience accessor for tests that need to size work counts proportional to a cpuset without computing the topology denominator by hand. Mirrors the framework’s own resolution: the count is exactly the size of the BTreeSet spec.resolve(self) returns, so any CpusetSpec-aware code path (cgroup cpuset assignment, affinity intent resolution, WorkSpec::workers_pct) sees the same denominator. Uses the TOPOLOGY-level cpuset, not the currently-effective cgroup cpuset — narrowing via mid-scenario Op::SetCpuset does not change the value this returns.

Source

pub fn settled_hold(&self, fraction_of_duration: f64) -> HoldSpec

HoldSpec::fixed(settle + duration * fraction_of_duration) — the dominant Step hold-time pattern across scenarios. A Step typically holds for the settle window (so the scheduler can reach steady state) plus some fraction of the workload duration (often 1.0 for whole-test Steps, or 0.5/1.0/3.0 for multi-Step scenarios that subdivide the duration budget).

The multiplication routes through Duration::mul_f64, so a fraction like 1.0 / 3.0 may yield a Duration that differs from an integer-division formulation by ≤1 nanosecond — below Linux thread sleep granularity and so unobservable at the hold-evaluation boundary, but worth noting if a test ever byte-pins a Duration value.

§Panics

When fraction_of_duration is NaN, infinite, or negative (per the Duration::mul_f64 contract).

§Examples
Step::new(vec![], ctx.settled_hold(0.5));  // settle + half duration
Step::new(vec![], ctx.settled_hold(1.0));  // settle + full duration
Source

pub fn cgroup_def(&self, name: impl Into<Cow<'static, str>>) -> CgroupDef

Construct a CgroupDef with self.workers_per_cgroup workers — the most common scenario shape, dedupe of 40+ CgroupDef::named(name).workers(ctx.workers_per_cgroup) call sites across src/scenario/ and tests/.

Equivalent to:

CgroupDef::named(name).workers(ctx.workers_per_cgroup)

Returns a fresh CgroupDef so the test author can chain further builders (.cpuset, .work, etc.) on the result. For non-default worker counts call CgroupDef::named(name).workers(N) directly — the helper pins ONLY the ctx.workers_per_cgroup default path.

§Examples
// Before (42+ sites):
vec![CgroupDef::named("cg_0").workers(ctx.workers_per_cgroup)].into()
// After:
vec![ctx.cgroup_def("cg_0")].into()

// With additional builders:
ctx.cgroup_def("cg_0").cpuset(...)
Source

pub fn failure_dump_path(&self) -> Result<PathBuf>

Per-test failure-dump sidecar path. Derives {sidecar_dir()}/{entry_name}-{variant_hash:016x}.failure-dump.json from the macro-stamped Self::entry_name — the drift-safe replacement for the legacy pattern of hardcoding the test fn name as a string literal at the callsite.

§Sibling to crate::vmm::VmResult::failure_dump_path

The post-VM result struct carries its own copy of the macro-stamped entry name + computes the same path string. A test body invocation ctx.failure_dump_path() and a post-VM result.failure_dump_path() resolve to identical paths because both stamp from the same entry.name: &'static str source — proc-macro emission at the #[ktstr_test] site.

§Errors

Bails when self.entry_name is None. The None shape is the manually-constructed-Ctx escape hatch — ad-hoc scenario unit tests build Ctx via CtxBuilder::build without calling CtxBuilder::entry_name; such a context cannot compute a drift-safe path. The bail diagnostic names the missing-stamp scenario explicitly so test authors who hit it know exactly which builder method to call.

Source

pub fn wprof_pb_path(&self) -> Result<PathBuf>

Per-test wprof Perfetto-trace sidecar path. Mirror of Self::failure_dump_path for the wprof artifact — derives {sidecar_dir()}/{entry_name}-{variant_hash:016x}.wprof.pb from the macro-stamped Self::entry_name.

Sibling to crate::vmm::VmResult::wprof_pb_path — the post-VM and pre-VM derivations produce identical paths. See Self::failure_dump_path for the broader contract + the manually-constructed-Ctx None-bail diagnostic.

§Errors

Bails when self.entry_name is None per the same shape as Self::failure_dump_path.

Source

pub fn repro_wprof_pb_path(&self) -> Result<PathBuf>

Source§

impl<'a> Ctx<'a>

Source

pub fn builder( cgroups: &'a dyn CgroupOps, topo: &'a TestTopology, ) -> CtxBuilder<'a>

Start a new CtxBuilder with required cgroups and topo borrows and sane defaults for every other field. See CtxBuilder for the full default set.

Source

pub fn payload(&'a self, p: &'static Payload) -> PayloadRun<'a>

Start a PayloadRun builder for the given Payload.

The builder inherits payload.default_args and payload.default_checks; chained .arg(...) / .check(...) calls extend them; .clear_args() / .clear_checks() wipe both defaults and prior appends. Terminal .run() blocks and returns Result<(AssertResult, PayloadMetrics)>.

Only PayloadKind::Binary payloads are runnable here; .run() on a PayloadKind::Scheduler payload returns Err.

Trait Implementations§

Source§

impl Debug for Ctx<'_>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl<'a> Freeze for Ctx<'a>

§

impl<'a> !RefUnwindSafe for Ctx<'a>

§

impl<'a> !Send for Ctx<'a>

§

impl<'a> !Sync for Ctx<'a>

§

impl<'a> Unpin for Ctx<'a>

§

impl<'a> !UnwindSafe for Ctx<'a>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts 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
§

impl<T> Pointable for T

§

const ALIGN: usize

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
§

impl<T> PolicyExt for T
where T: ?Sized,

§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns [Action::Follow] only if self and other return Action::Follow. Read more
§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns [Action::Follow] if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more