From 0af8c28bc23ef9c38ee472aa7e0be8b6f5a631d2 Mon Sep 17 00:00:00 2001 From: Manuel Ganter Date: Fri, 6 Feb 2026 15:15:30 +0100 Subject: [PATCH] fix(aggregator): prevent CPU cores overflow when processes restart Guard against unsigned integer underflow in cgroup CPU calculation. When processes exit and new ones start, totalTicks can be less than the previous value, causing the subtraction to wrap around to a huge positive number. Now checks totalTicks >= prev before calculating delta, treating process churn as 0 CPU usage for that sample. Co-Authored-By: Claude Opus 4.5 --- internal/metrics/aggregator.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/internal/metrics/aggregator.go b/internal/metrics/aggregator.go index 78cfc94..d8a22b9 100644 --- a/internal/metrics/aggregator.go +++ b/internal/metrics/aggregator.go @@ -348,9 +348,14 @@ func (a *Aggregator) calculateCgroupMetrics( // Calculate CPU cores used from delta usedCores := 0.0 if prev, ok := a.prevCgroupCPU[containerName]; ok && elapsed > 0 { - deltaTicks := totalTicks - prev - // Convert ticks to cores: deltaTicks / (elapsed_seconds * CLK_TCK) - usedCores = float64(deltaTicks) / (elapsed * float64(proc.DefaultClockTicks)) + // Guard against underflow: if processes exited and new ones started, + // totalTicks could be less than prev. In that case, skip this sample. + if totalTicks >= prev { + deltaTicks := totalTicks - prev + // Convert ticks to cores: deltaTicks / (elapsed_seconds * CLK_TCK) + usedCores = float64(deltaTicks) / (elapsed * float64(proc.DefaultClockTicks)) + } + // If totalTicks < prev, usedCores stays 0 for this sample } a.prevCgroupCPU[containerName] = totalTicks