HostContext

Struct HostContext 

Source
#[non_exhaustive]
pub struct HostContext {
Show 19 fields pub cpu_model: Option<String>, pub cpu_vendor: Option<String>, pub total_memory_kib: Option<u64>, pub hugepages_total: Option<u64>, pub hugepages_free: Option<u64>, pub hugepages_size_kib: Option<u64>, pub thp_enabled: Option<String>, pub thp_defrag: Option<String>, pub sched_tunables: Option<BTreeMap<String, String>>, pub online_cpus: Option<usize>, pub numa_nodes: Option<usize>, pub cpufreq_governor: BTreeMap<usize, String>, pub kernel_name: Option<String>, pub kernel_release: Option<String>, pub arch: Option<String>, pub kernel_cmdline: Option<String>, pub task_delayacct: Option<DelayacctState>, pub config_task_xacct: Option<XacctState>, pub heap_state: Option<HostHeapState>,
}
Expand description

Host-level runtime state snapshot attached to each SidecarResult. Every field is optional so a partial read (missing /proc entry, permission denied, parse failure) still records the fields that did succeed instead of dropping the whole snapshot.

§Constructing instances in tests

HostContext is #[non_exhaustive] — see [crate::non_exhaustive] for the cross-crate construction and pattern-match rules shared by every such type in the crate. The concrete pattern for HostContext is to start from a Default instance and mutate fields:

use ktstr::prelude::HostContext;
let mut ctx = HostContext::default();
ctx.cpu_model = Some("Test CPU".to_string());
ctx.numa_nodes = Some(2);

For tests that want a populated baseline (non-trivial defaults for every field) instead of Default’s all-None minimum, start from HostContext::test_fixture and mutate from there.

§Partial-read round-trip

Fields representing producer-time partial-read outcomes use serde(default, skip_serializing_if = ...) so the absent state round-trips through the sidecar JSON — None for the Option<T> fields paired with Option::is_none, empty for the cpufreq_governor BTreeMap<usize, String> paired with BTreeMap::is_empty. A producer-time partial read (missing /proc entry, permission denied, parse failure) lands at the absent state, gets omitted on serialize, and deserializes back to the same absent state. The pattern exists for that producer-side partial-population path, not for cross-binary- version compatibility. Per the pre-1.0 sidecar-disposable rule, a sidecar written by a different binary version may fail to deserialize when the schema has diverged — re-run the test to regenerate it with the current schema.

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.
§cpu_model: Option<String>

CPU model string — the model name line of /proc/cpuinfo. Single value (first processor entry) since heterogeneous CPU models on a single host are rare enough that the extra complexity is not worth carrying.

§cpu_vendor: Option<String>

CPU vendor ID — the vendor_id line of /proc/cpuinfo (e.g. GenuineIntel, AuthenticAMD). On ARM64, /proc/cpuinfo uses CPU implementer instead of vendor_id, so this field is None.

§total_memory_kib: Option<u64>

Total physical memory in KiB — MemTotal: from /proc/meminfo. The kernel labels the value kB but the scale is 1024 bytes (KiB); the field name uses the unambiguous IEC binary unit so the sidecar reader does not need to guess the scale.

§hugepages_total: Option<u64>

Configured huge pages — HugePages_Total from /proc/meminfo.

§hugepages_free: Option<u64>

Free huge pages — HugePages_Free from /proc/meminfo.

§hugepages_size_kib: Option<u64>

Hugepage size in KiB — Hugepagesize: from /proc/meminfo (labeled kB in the file; the scale is 1024 bytes / KiB).

§thp_enabled: Option<String>

Active THP policy — content of /sys/kernel/mm/transparent_hugepage/enabled with the bracketed selection preserved verbatim (e.g. "always [madvise] never"). Trimmed of leading and trailing whitespace by read_trimmed_sysfs, so the trailing newline that sysfs appends does not appear in the captured value. Stored as-read rather than parsed because the bracket is the meaningful part and downstream tooling may want the full menu too.

§thp_defrag: Option<String>

Active THP defrag policy — content of /sys/kernel/mm/transparent_hugepage/defrag, bracket preserved. Trimmed of leading and trailing whitespace by read_trimmed_sysfs.

§sched_tunables: Option<BTreeMap<String, String>>

/proc/sys/kernel/sched_* tunables. Keys are the leaf basename (e.g. sched_migration_cost_ns); values are the file content trimmed of leading and trailing whitespace (internal whitespace preserved — read_trimmed_sysfs uses str::trim, which only strips edges). Every current sched_* tunable is a scalar, but a future kernel that exposes a multi-line tunable would land here as a multi-line String. None when the read_dir of /proc/sys/kernel fails; empty map when the directory is readable but contains no entries starting with sched_ (or all such entries fail the per-file read or trim to empty).

§online_cpus: Option<usize>

Number of online host CPUs — HostTopology::online_cpus.len() from the same from_sysfs probe that drives numa_nodes. None when the topology probe fails. Captured as a discrete field so downstream consumers (sidecar readers, scheduler regression dashboards) don’t need to reconstruct a HostTopology just to learn the CPU count.

§numa_nodes: Option<usize>

Count of NUMA nodes — derived from HostTopology::from_sysfs (the cpu_to_node map’s distinct value count). None when the topology probe itself fails so “unknown” is distinguishable from a populated result. A probe that succeeds but reports no CPU→node entries defaults to Some(1) because every Linux system has at least one NUMA node — see count_numa_nodes_in_topology for the full rationale (in production, empty cpu_to_node from a successful probe cannot happen because TestTopology::from_system bails on zero online CPUs; the .max(1) floor is a guard for synthetic/test topologies).

§cpufreq_governor: BTreeMap<usize, String>

Per-CPU scaling_governor string, keyed by CPU id. Read from /sys/devices/system/cpu/cpu{N}/cpufreq/scaling_governor for every online CPU. Value is the trimmed governor name as written by the kernel (e.g. "performance", "powersave", "schedutil", "ondemand").

Per-CPU granularity matters: heterogeneous hosts (big.LITTLE, P/E cores) can carry different governors on different CPUs, and a scheduler micro-benchmark landing on a powersave CPU sees 2× the latency of one landing on a performance CPU. A run-level single-governor field would average this out and hide the variance.

Empty map when /sys/devices/system/cpu/online is unreadable (sysfs absent, container without it mounted) or when every per-CPU read fails. skip_serializing_if keeps the sidecar compact on hosts without the data.

Cached: the first collect_host_context call populates a process-wide OnceLock with one read per online CPU; subsequent calls clone the cached map. Governor changes after first capture are not reflected — see the “Static-cache staleness under hotplug” section in the module-level docs for the full contract.

§kernel_name: Option<String>

Kernel name — uname.sysname (typically "Linux"). The nodename field is intentionally dropped; it’s a local hostname and has no place in a published sidecar.

§kernel_release: Option<String>

Kernel release — uname.release (e.g. "6.11.0-rc3"). The full /proc/version banner is NOT captured because it embeds the build host + gcc version string, which is environment leakage.

§arch: Option<String>

Machine architecture — uname.machine (e.g. "x86_64", "aarch64").

§kernel_cmdline: Option<String>

/proc/cmdline verbatim (trimmed of leading and trailing whitespace). Captures boot-time parameters that materially affect scheduler behavior — preempt=, isolcpus=, nohz_full=, mitigations=, hugepage reservations, transparent_hugepage=, and others. Stored as a single string because any split-into-pairs parser loses the quoted-value and flag-only variants the kernel accepts.

Named kernel_cmdline rather than cmdline to disambiguate from SidecarResult::kargs: that field carries the extra kargs the ktstr VMM appended when booting the guest, NOT the running host’s boot line. Both are cmdline-shaped strings but describe different systems.

§task_delayacct: Option<DelayacctState>

Kernel delay-accounting state probed from /proc/sys/kernel/task_delayacct. None only in synthetic contexts that did not probe; collect_host_context always populates it. Gates which taskstats delay-family fields are genuinely measured — see DelayacctState.

§config_task_xacct: Option<XacctState>

CONFIG_TASK_XACCT build state probed from /proc/config.gz. Gates the taskstats memory-watermark fields — see XacctState. None only in synthetic contexts.

§heap_state: Option<HostHeapState>

Running process’s jemalloc heap state — active / allocated / resident / mapped bytes and arena count. Populated on jemalloc-linked builds (every ktstr binary), None on downstream consumers that use the library without installing tikv_jemallocator as #[global_allocator]. See HostHeapState for the field-level documentation.

Implementations§

Source§

impl HostContext

Source

pub fn test_fixture() -> HostContext

Populated HostContext for unit tests. Every field carries a reasonable non-trivial value so call sites only spell out what they want to vary via post-hoc field assignment (#[non_exhaustive] rejects all StructExpression forms cross-crate, including functional update):

use ktstr::prelude::HostContext;
let mut ctx = HostContext::test_fixture();
ctx.numa_nodes = Some(4);

Defaults model a plausible 2-node x86_64 Linux host: Intel CPU identity, 64 GiB memory, 2 NUMA nodes, default THP policies, a minimal sched_* tunable map, and a populated uname triple. Parity with SidecarResult::test_fixture — both fixtures exist so tests don’t re-derive an “everything populated” baseline in every call site.

§Usage guidance

Prefer this fixture over local “populated default” helpers — a local closure duplicates the default set and drifts the moment HostContext grows a field. This is the single place those defaults live. Hash-stability and serialization-pin tests are the one exception: they must NOT rely on these defaults, because any future change to the fixture would silently shift the pinned value. Spell every participating field out explicitly in such tests so the pin is robust against fixture evolution.

Source

pub fn format_human(&self) -> String

Render as a human-readable multi-line report. Each field occupies one line as key: value. Absent fields render as (unknown) rather than being dropped, so operators see which fields failed to populate. The sched_tunables map is expanded one entry per line under the parent key; an empty map renders as (empty) and a None map as (unknown). The output ends with a newline.

This output is for human inspection only. For programmatic access, parse the sidecar JSON directly or drive serde_json against the HostContext struct — the text format here is not a stable serialization contract and may be retuned for readability without notice.

Naming: the name pair (format_human with no format_machine) is intentional rather than accidental asymmetry. The “machine” surface is serde JSON — callers that want a machine-readable rendering use serde_json::to_string(ctx) directly. A dedicated format_machine wrapper around that one line would add no value. format_human stays named as it is (not as impl Display) because it prints a multi-line block with its own newline, which clashes with Display’s implicit one-value-per-formatter convention; embedding this in format!("{ctx}") would surprise callers used to single- line Display output.

Source

pub fn thp_enabled_active(&self) -> Option<&str>

Active THP-enabled policy, extracted from the bracketed [...] token inside Self::thp_enabled. Returns the content between the first [ and subsequent ] (e.g. "madvise" from "always [madvise] never"). None when thp_enabled is None, empty, or carries no bracketed token (kernels that reshape the menu format).

Provided so downstream tooling (cargo ktstr stats, CI regression gates, custom dashboards) can consume the active policy as a bare token without re-implementing the bracket scan in every caller.

Source

pub fn thp_defrag_active(&self) -> Option<&str>

Active THP-defrag policy, extracted the same way as Self::thp_enabled_active. Returns e.g. "madvise" from "always defer defer+madvise [madvise] never".

Source

pub fn diff(&self, other: &HostContext) -> String

Render the differences between two host contexts as indented key: before → after lines. Fields that compare equal are omitted; an empty return value means the two contexts are field-for-field identical (including sched_tunables). None values render as (unknown) and map entries present in one side only render as (absent) so a None → Some(..) transition does not silently look the same as an unchanged absent field. When only one side has a sched_tunables map, the other side renders (unknown); the Some side renders as (empty) for an empty map or (N entries) for a populated one so the cardinality of the new data is visible at a glance.

Trait Implementations§

Source§

impl Clone for HostContext

Source§

fn clone(&self) -> HostContext

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for HostContext

Source§

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

Formats the value using the given formatter. Read more
Source§

impl Default for HostContext

Source§

fn default() -> HostContext

Returns the “default value” for a type. Read more
Source§

impl<'de> Deserialize<'de> for HostContext

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl PartialEq for HostContext

Source§

fn eq(&self, other: &HostContext) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl Serialize for HostContext

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more
Source§

impl Eq for HostContext

Source§

impl StructuralPartialEq for HostContext

Auto Trait Implementations§

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> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. 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> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
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
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

§

impl<T> MaybeSend for T
where T: Send,

§

impl<T> MaybeSend for T
where T: Send,