#[non_exhaustive]pub struct CtprofParseSummary {
pub tids_walked: u64,
pub read_failures: u64,
pub read_failures_by_file: BTreeMap<String, u64>,
pub dominant_read_failure: Option<String>,
pub kernel_config_dominant: bool,
pub negative_dotted_values: u64,
}Expand description
Per-snapshot procfs read-failure statistics. Curated projection of the capture pipeline’s internal read-tally — exposes per-file counters and a dominant-failure tag a downstream consumer needs to decide whether the snapshot’s procfs-derived fields (CSW, schedstats, IO, etc.) are trustworthy on a given host without scanning every thread for default values.
The read-failure tally (Self::read_failures /
Self::read_failures_by_file) is read-level only — it
counts failures of fs::read_to_string against
/proc/<tgid>/task/<tid>/<file>, not per-field parse failures
inside an otherwise-readable file.
A present-but-malformed file (e.g. a corrupt stat whose
parse_stat returns all-None) does NOT count: the file read
succeeded so the tally stays at zero for that category, even
though the per-field parsers fold every value to its absent-
counter default. Read failures correspond to the kernel never
having written the file (ENOENT / kernel without
CONFIG_SCHED_INFO), the file disappearing mid-capture (race),
or any other I/O-level error from the procfs reader. A snapshot
with 1 K schedstat failures across 1 K tids implies a kernel
build without CONFIG_SCHED_INFO; 47 stat failures across 1 K
tids implies mid-capture races.
One parse-level signal IS surfaced separately:
Self::negative_dotted_values counts the per-line cases in
/proc/<tid>/sched where the kernel’s PN_SCHEDSTAT format
emitted a leading - — a rare but observable clock-skew /
suspend-resume artifact that the parser otherwise folds
silently to zero. Other forms of per-field corruption (
non-numeric fractional, malformed key, …) stay outside this
summary’s scope and surface as zero values on the affected
ThreadState fields.
Per-file tokens in Self::read_failures_by_file are stable
kebab-case identifiers downstream consumers match against. The
recognized set: "stat", "schedstat", "io", "status",
"sched", "cgroup", "smaps_rollup". Adding a new procfs
file to the capture adds a new key; the wire shape carries
any token the capture emitted, so a consumer that only knows
the existing set absorbs new keys without breaking.
Ghost-filtered tids do NOT contribute to read_failures /
read_failures_by_file — their pending failure bumps are
unwound via discard_pending when a thread ends up filtered
out of threads (empty comm + zero start_time), so a busy
host with mid-capture exits doesn’t inflate the failure tallies
with counts that would correspond to threads the snapshot
doesn’t even contain. tids_walked still counts every walk
attempt regardless of the ghost filter outcome.
§Examples
let snap = ktstr::ctprof::capture();
if let Some(ps) = &snap.parse_summary
&& let Some(hint) = ps.kernel_config_hint()
{
eprintln!("{hint}");
}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.tids_walked: u64Total tids the capture pass attempted to read across every tgid. Non-zero whenever the capture walked any tid; the denominator a downstream consumer uses to compute “what fraction of reads failed” without parsing the operator- facing tracing line.
read_failures: u64Total file-level read failures across all categories. Sum
of Self::read_failures_by_file values.
read_failures_by_file: BTreeMap<String, u64>Per-file-kind failure tally, keyed by stable kebab tokens
("stat", "schedstat", "io", "status", "sched",
"cgroup", "smaps_rollup"). Empty map when the capture
saw zero failures. Keys present in the map have non-zero
counts; absent keys imply zero failures for that category,
NOT “category unknown”.
dominant_read_failure: Option<String>Tag string for the file kind with the most read failures
across the snapshot. None when read_failures == 0.
Stable kebab tokens (the same vocabulary
Self::read_failures_by_file keys against). Ties resolve
REVERSE-alphabetically so the output is deterministic — the
alphabetically-earlier tag wins (e.g. "io" beats
"status" when both count equal).
kernel_config_dominant: booltrue when ≥ 50% of read_failures are concentrated in
kernel-config-gated files ("schedstat", "io"). These
two files are absent on kernels built without
CONFIG_SCHED_INFO / CONFIG_TASK_IO_ACCOUNTING
respectively, so a dominance signal here points the
operator at a kernel build/config issue rather than a
transient race or permission problem. false when
read_failures == 0 or when failures are spread across
non-kconfig files.
negative_dotted_values: u64Number of /proc/<tid>/sched PN_SCHEDSTAT dotted-ns
values whose integer part read as negative (kernel emitted
a leading -, e.g. -5.000000). The capture-side parser
(parsed_ns_from_dotted) rejects negative integer parts —
a u64 parse cannot accept the sign — and the call site
then unwrap_or(0)s the resulting None per the
best-effort capture contract. Without this counter the
silent fold to zero leaves operators with no visibility
into the rate at which schedstat values were silently
truncated.
Counts per-field-occurrence, NOT per-thread: a single
tid that exposed five negative dotted fields contributes
5 to this counter (e.g. one tid with negative wait_sum,
sleep_max, block_sum, iowait_sum, and exec_max
adds 5). The denominator for “fraction of tids affected”
is therefore NOT this field — pair with
Self::tids_walked only as an upper bound on
affected-tid count.
Distinct from Self::read_failures: a negative dotted
value comes from a sched file that READ successfully —
it is a parse-level signal, not a read-level signal. The
field stays at zero on a clean host because the kernel
emits non-negative values on every well-behaved schedstat
path; non-zero values are most commonly the result of
clock-skew on suspend/resume where a delta calculation
against a stale baseline lands negative.
Ghost-filter discipline: per-tid bumps are held pending
(alongside the read-failure bumps in
crate::ctprof’s capture-side ParseTally), and
unwound via discard_pending when the surrounding tid is
rejected by the empty-comm + zero-start ghost filter so a
busy host with mid-capture exits doesn’t inflate this
counter with bumps that correspond to threads the snapshot
doesn’t even contain.
Implementations§
Source§impl CtprofParseSummary
impl CtprofParseSummary
Sourcepub fn kernel_config_hint(&self) -> Option<&'static str>
pub fn kernel_config_hint(&self) -> Option<&'static str>
Operator-facing hint when kernel-config-gated file failures
dominate the snapshot. Returns Some(&'static str) naming
the two CONFIG_* knobs that gate the affected files
(CONFIG_SCHED_INFO for schedstat, CONFIG_TASK_IO_ACCOUNTING
for io), or None when Self::kernel_config_dominant
is false. Lets a downstream consumer surface a remediation
pointer without parsing the log line or hand-rolling the
gate, mirroring the CtprofProbeSummary::remediation_hint
pattern.
Trait Implementations§
Source§impl Clone for CtprofParseSummary
impl Clone for CtprofParseSummary
Source§fn clone(&self) -> CtprofParseSummary
fn clone(&self) -> CtprofParseSummary
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for CtprofParseSummary
impl Debug for CtprofParseSummary
Source§impl Default for CtprofParseSummary
impl Default for CtprofParseSummary
Source§fn default() -> CtprofParseSummary
fn default() -> CtprofParseSummary
Source§impl<'de> Deserialize<'de> for CtprofParseSummary
impl<'de> Deserialize<'de> for CtprofParseSummary
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>,
Auto Trait Implementations§
impl Freeze for CtprofParseSummary
impl RefUnwindSafe for CtprofParseSummary
impl Send for CtprofParseSummary
impl Sync for CtprofParseSummary
impl Unpin for CtprofParseSummary
impl UnwindSafe for CtprofParseSummary
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