#[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
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
impl HostContext
Sourcepub fn test_fixture() -> HostContext
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.
Sourcepub fn format_human(&self) -> String
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.
Sourcepub fn thp_enabled_active(&self) -> Option<&str>
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.
Sourcepub fn thp_defrag_active(&self) -> Option<&str>
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".
Sourcepub fn diff(&self, other: &HostContext) -> String
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
impl Clone for HostContext
Source§fn clone(&self) -> HostContext
fn clone(&self) -> HostContext
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for HostContext
impl Debug for HostContext
Source§impl Default for HostContext
impl Default for HostContext
Source§fn default() -> HostContext
fn default() -> HostContext
Source§impl<'de> Deserialize<'de> for HostContext
impl<'de> Deserialize<'de> for HostContext
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 HostContext
impl PartialEq for HostContext
Source§impl Serialize for HostContext
impl Serialize for HostContext
impl Eq for HostContext
impl StructuralPartialEq for HostContext
Auto Trait Implementations§
impl Freeze for HostContext
impl RefUnwindSafe for HostContext
impl Send for HostContext
impl Sync for HostContext
impl Unpin for HostContext
impl UnwindSafe for HostContext
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<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key and return true if they are equal.§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
§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