pub enum AggRule {
Show 14 variants
SumCount(fn(&ThreadState) -> MonotonicCount),
SumNs(fn(&ThreadState) -> MonotonicNs),
SumTicks(fn(&ThreadState) -> ClockTicks),
SumBytes(fn(&ThreadState) -> Bytes),
MaxPeak(fn(&ThreadState) -> PeakNs),
MaxPeakBytes(fn(&ThreadState) -> PeakBytes),
MaxGaugeNs(fn(&ThreadState) -> GaugeNs),
MaxGaugeCount(fn(&ThreadState) -> GaugeCount),
RangeI32(fn(&ThreadState) -> OrdinalI32),
RangeU32(fn(&ThreadState) -> OrdinalU32),
Mode(fn(&ThreadState) -> CategoricalString),
ModeChar(fn(&ThreadState) -> char),
ModeBool(fn(&ThreadState) -> bool),
Affinity(fn(&ThreadState) -> CpuSet),
}Expand description
Aggregation rule for a single metric.
Encoded as an enum rather than a trait object so the registry
table (CTPROF_METRICS) can live in static memory. Each
variant’s accessor returns the typed
crate::metric_types newtype that matches the reduction
— the reader and rule are paired by construction so a new
metric cannot register a peak field against a sum reducer
(SumNs(|t| t.wait_max) fails to compile because wait_max
is PeakNs, not MonotonicNs).
Each variant maps 1:1 to a marker trait in
crate::metric_types: Sum* variants take a Summable
type, Max* variants take a Maxable type that is NOT
also Summable (counters use Sum* even though they
implement both — registering a counter as Max* would mask
the sum semantics with the per-contributor maximum), Range*
variants take a Rangeable type, Mode* variants take a
Modeable type or a primitive that the dispatch coerces to
String, and AggRule::Affinity takes the dedicated
crate::metric_types::CpuSet for the affinity-summary
reduction.
Variants§
SumCount(fn(&ThreadState) -> MonotonicCount)
Sum across the group of a MonotonicCount field. Used
for unitless cumulative counters (nr_wakeups,
voluntary_csw, minflt, syscall counts, …). The
dispatch routes through
crate::metric_types::Summable::sum_across which uses
saturating_add per the no-wraparound contract.
SumNs(fn(&ThreadState) -> MonotonicNs)
Sum across the group of a MonotonicNs field. Used for
cumulative-time counters in nanoseconds (run_time_ns,
wait_time_ns, wait_sum, voluntary_sleep_ns,
block_sum, iowait_sum, core_forceidle_sum).
SumTicks(fn(&ThreadState) -> ClockTicks)
Sum across the group of a ClockTicks field. Used for
USER_HZ-scaled cumulative time counters
(utime_clock_ticks, stime_clock_ticks).
SumBytes(fn(&ThreadState) -> Bytes)
Sum across the group of a Bytes field. Used for
IEC-binary-scaled byte counters (allocated_bytes,
deallocated_bytes, rchar, wchar, read_bytes,
write_bytes, cancelled_write_bytes).
MaxPeak(fn(&ThreadState) -> PeakNs)
Maximum across the group of a PeakNs field — the
kernel *_max schedstats (wait_max, sleep_max,
block_max, exec_max, slice_max). Each thread
already carries its own lifetime max-seen value from the
kernel’s scheduler call sites (e.g. update_se in
kernel/sched/fair.c for exec_max; see
struct sched_statistics in include/linux/sched.h).
Group-level reduction takes the largest across members so
a row surfaces the worst single window any thread in the
group has ever experienced. Summing per-thread maxes
would conflate “one thread with a 1s spike” with “1000
threads with 1ms spikes each” — PeakNs therefore does
NOT implement Summable, and trying to register one as
SumNs is a compile error.
MaxPeakBytes(fn(&ThreadState) -> PeakBytes)
Maximum across the group of a PeakBytes field — the
byte-typed twin of MaxPeak. Used for taskstats-sourced
lifetime memory watermarks (hiwater_rss_bytes,
hiwater_vm_bytes).
xacct_add_tsk (kernel/tsacct.c::xacct_add_tsk, lines
99-104) reads the watermark out of the SHARED mm_struct
via get_mm_hiwater_rss(mm) / get_mm_hiwater_vm(mm), so
sibling threads of the same tgid all report the same
value; cross-thread Max within a single process is a no-op.
Cross-PROCESS Max (e.g. under --group-by pcomm when the
bucket spans multiple parent processes) is the meaningful
reduction: it picks the largest watermark any tgid in the
bucket reported. Routes through the IEC binary auto-scale
ladder (crate::metric_types::ScaleLadder::Bytes) so a
7.5 GiB watermark renders as 7.500GiB instead of
dominating the table with raw byte counts. Summing
watermarks would over-count shared address-space mappings
across sibling threads N-fold — PeakBytes does NOT
implement Summable.
MaxGaugeNs(fn(&ThreadState) -> GaugeNs)
Maximum across the group of a GaugeNs field —
instantaneous-time gauges where summing is meaningless.
fair_slice_ns is the per-thread CURRENT scheduler slice
(stale under SCHED_EXT — see field doc) read at capture
time, not a high-water value. Summing instantaneous
gauges produces a number with no physical meaning — N
nearly-identical instantaneous values sum to N * gauge
regardless of group composition, drowning the signal.
Max instead surfaces “the longest current slice any
thread in the bucket is running with”, which IS the
signal a user comparing two snapshots cares about.
MaxGaugeCount(fn(&ThreadState) -> GaugeCount)
Maximum across the group of a GaugeCount field —
leader-deduped structural counts. nr_threads is
populated only on the tgid leader (tid == tgid) and
zero on every non-leader thread of the same process; see
capture_thread_at_with_tally. Sum across a comm- or
cgroup-bucketed group would render 0 for any bucket
whose leader fell elsewhere because non-leader members
each contribute 0. Max instead reads through to the
leader’s value, surfacing “the largest process
represented in this bucket” regardless of which axis the
bucket is built around. The row count already covers
“how many threads are here”, so the structural field’s
value adds new information rather than restating the row
count.
RangeI32(fn(&ThreadState) -> OrdinalI32)
Ordinal i32, aggregated as the observed [min, max] range.
Used for signed-domain ordinals (nice, priority,
processor). Delta math uses the midpoint of each range
as the scalar; output prints both the range and the
delta. The dispatch routes through
crate::metric_types::Rangeable::range_across and
widens to i64 for Aggregated::OrdinalRange.
RangeU32(fn(&ThreadState) -> OrdinalU32)
Ordinal u32, aggregated as the observed [min, max] range.
Used for unsigned-domain ordinals (rt_priority,
kernel-typed unsigned int). Same shape as
AggRule::RangeI32 but the inner width matches the
kernel-side unsigned int declaration; the dispatch
widens the resulting u32 to i64 for
Aggregated::OrdinalRange.
Mode(fn(&ThreadState) -> CategoricalString)
Categorical string, aggregated as the mode (most-frequent
value). Used for policy (string-valued
crate::metric_types::CategoricalString). Delta is
textual: “same” if both modes agree, “differs” otherwise
— there is no arithmetic on a policy name. The dispatch
routes through
crate::metric_types::Modeable::mode_across.
ModeChar(fn(&ThreadState) -> char)
Categorical char, aggregated as the mode. Used for
state (single-letter task state from
/proc/<tid>/status). The dispatch coerces the char
to a String via to_string() before reducing — char
itself is NOT Modeable (only
crate::metric_types::CategoricalString is), so this
variant exists to keep the registry’s accessor type
matching the ThreadState field type without forcing the
field into a wrapper. If a second char-valued metric
appears, promote both fields to a dedicated
CategoricalChar wrapper rather than continuing the
ad-hoc coercion (mirrors the CategoricalBool
promotion guidance on AggRule::ModeBool).
ModeBool(fn(&ThreadState) -> bool)
Categorical bool, aggregated as the mode. Used for
ext_enabled (sched_ext class membership). Same shape as
AggRule::ModeChar: the dispatch coerces via
to_string() so "true"/"false" participate in the
mode reduction. If a second bool-valued metric appears,
promote both fields to a dedicated CategoricalBool
wrapper rather than continuing the ad-hoc coercion.
Tiebreak skew (FA-2): the lex-smallest-wins tiebreak
inside Modeable::mode_across makes "false" ('f',
0x66) win an equal-count tie against "true" ('t',
0x74). This matches the legacy pre-phase-3 behavior —
the old to_string() coercion fed the same string pair
through the same lex-tiebreak — but is worth flagging
explicitly: a 50/50 sched_ext-on/off bucket renders
false as the mode rather than picking the more
“informative” true. Operators reading a false mode
in a heterogeneous bucket should check the count/total
fraction.
Affinity(fn(&ThreadState) -> CpuSet)
CPU affinity set, aggregated as the num_cpus range across
the group plus a uniform-cpuset rendering when every
thread shared the same allowed set. Used for
cpu_affinity. The accessor returns
crate::metric_types::CpuSet; the dispatch unwraps to
Vec<u32> for the AffinitySummary reduction.
Unlike the Sum* / Max* / Range* / Mode* rules,
Affinity does NOT route through a
crate::metric_types trait method — its reduction
produces an AffinitySummary (num_cpus range +
uniform-cpuset flag), not a homogeneous CpuSet, so the
inline aggregator in aggregate walks the per-thread
Vec<u32> directly. A future Affinable trait could
fold the body into crate::metric_types but the
summary type is single-use today.
Type-system bypass caveat (FA-1): the typed AggRule
shape catches “wrong wrapper” mistakes
(SumNs(|t| t.wait_max) fails to compile because
wait_max is PeakNs), but a closure body that
actively MISWRAPS the underlying field — e.g.
SumNs(|t| MonotonicNs(t.wait_max.0)) — laundering a
peak through the sum wrapper still type-checks. Don’t
do that. The wrapper category is load-bearing; the type
system catches the variant mismatch but cannot
inspect the inside of an arbitrary closure.
Implementations§
Source§impl AggRule
impl AggRule
Sourcepub fn ladder(&self) -> ScaleLadder
pub fn ladder(&self) -> ScaleLadder
The auto-scale ladder for this rule’s value cell.
Closed match — adding a new AggRule variant requires
adding the ladder mapping here. The mapping is one-to-one
with the typed accessor newtype: AggRule::SumNs →
ScaleLadder::Ns, AggRule::SumBytes →
ScaleLadder::Bytes, etc.
Trait Implementations§
Auto Trait Implementations§
impl Freeze for AggRule
impl RefUnwindSafe for AggRule
impl Send for AggRule
impl Sync for AggRule
impl Unpin for AggRule
impl UnwindSafe for AggRule
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