///|
pub fn Snapshot::parse(
identity : String,
time_us : Int64,
cpu_stat~ : String,
memory_current~ : String,
memory_events~ : String,
cpu_pressure? : String,
memory_pressure? : String,
io_pressure? : String,
) -> Snapshot raise ParseError {
guard identity.length() > 0 && identity.length() <= 4096 && time_us >= 0L else {
raise Invalid("invalid snapshot identity or time")
}
let cpu_p = match cpu_pressure {
Some(text) => Some(parse_pressure(text))
None => None
}
let memory_p = match memory_pressure {
Some(text) => Some(parse_pressure(text))
None => None
}
let io_p = match io_pressure {
Some(text) => Some(parse_pressure(text))
None => None
}
{
identity,
time_us,
cpu: parse_cpu_stat(cpu_stat),
memory_current: parse_counter(memory_current),
events: parse_memory_events(memory_events),
cpu_pressure: cpu_p,
memory_pressure: memory_p,
io_pressure: io_p,
}
}
///|
fn difference(old : Int64, now : Int64, key : String) -> Int64 raise DeltaError {
guard now >= old else { raise CounterReset(key) }
now - old
}
///|
fn stall_delta(
old : Pressure?,
now : Pressure?,
elapsed : Int64,
key : String,
) -> StallDelta? raise DeltaError {
match (old, now) {
(None, None) => None
(Some(a), Some(b)) => {
let some = difference(a.some.total_us, b.some.total_us, key)
let full = match (a.full, b.full) {
(None, None) => None
(Some(x), Some(y)) =>
Some(
difference(x.total_us, y.total_us, key).to_double() /
elapsed.to_double() *
100.0,
)
_ => raise AvailabilityChanged(key)
}
// Sequential kernel reads can slightly exceed the timestamp interval.
// Preserve that evidence rather than silently clamping it to 100%.
Some({
some_percent: some.to_double() / elapsed.to_double() * 100.0,
full_percent: full,
})
}
_ => raise AvailabilityChanged(key)
}
}
///|
/// Only compare the same cgroup generation and a positive <=60s interval.
pub fn interval(old : Snapshot, now : Snapshot) -> Interval raise DeltaError {
guard old.identity == now.identity else { raise IdentityChanged }
guard now.time_us > old.time_us else { raise InvalidInterval }
let elapsed = now.time_us - old.time_us
guard elapsed <= 60000000L else { raise InvalidInterval }
let usage = difference(old.cpu.usage_us, now.cpu.usage_us, "usage_usec")
let (throttle, throttled_us) = match (old.cpu.throttle, now.cpu.throttle) {
(None, None) => (None, None)
(Some(a), Some(b)) => {
let periods = difference(a.periods, b.periods, "nr_periods")
let throttled = difference(
a.throttled_periods,
b.throttled_periods,
"nr_throttled",
)
guard throttled <= periods else {
raise CounterReset("inconsistent throttle delta")
}
let duration = difference(
a.throttled_us,
b.throttled_us,
"throttled_usec",
)
let pct = if periods == 0L {
None
} else {
Some(throttled.to_double() / periods.to_double() * 100.0)
}
(pct, Some(duration))
}
_ => raise AvailabilityChanged("cpu.stat throttle")
}
{
identity: now.identity,
end_us: now.time_us,
elapsed_us: elapsed,
cpu_cores_used: usage.to_double() / elapsed.to_double(),
throttled_period_percent: throttle,
throttled_us,
memory_current_bytes: now.memory_current,
memory_high_events: difference(
old.events.high,
now.events.high,
"memory.high",
),
memory_max_events: difference(old.events.max, now.events.max, "memory.max"),
oom_events: difference(old.events.oom, now.events.oom, "memory.oom"),
oom_kills: difference(
old.events.oom_kill,
now.events.oom_kill,
"memory.oom_kill",
),
cpu_stall: stall_delta(
old.cpu_pressure,
now.cpu_pressure,
elapsed,
"cpu PSI",
),
memory_stall: stall_delta(
old.memory_pressure,
now.memory_pressure,
elapsed,
"memory PSI",
),
io_stall: stall_delta(old.io_pressure, now.io_pressure, elapsed, "io PSI"),
}
}