fix: measure process CPU concurrently with system CPU

Previously system CPU was measured first (1s window), then processes
were sampled 1+ second later. Short-lived spikes would appear in system
CPU but not in the process list because the offending process had already
finished. Now both run in parallel so their windows overlap — a process
that caused a spike during the CPU measurement window will be captured.
This commit is contained in:
Joel Male
2026-02-25 21:52:00 +10:00
parent b9b31414b0
commit ef6fa1a07f
+32 -10
View File
@@ -15,6 +15,7 @@ import (
"runtime"
"strconv"
"strings"
"sync"
"syscall"
"time"
@@ -908,12 +909,34 @@ func collectMetrics(config *Config) (*Metrics, error) {
}
}
// CPU — single sample with percpu=true, then average for the total.
// Calling cpu.Percent twice with separate 1s windows can produce
// wildly inconsistent totals vs per-core values.
cpuPerCore, err := cpu.Percent(time.Second, true)
if err != nil {
return nil, fmt.Errorf("failed to get CPU percent: %w", err)
// CPU and process collection run concurrently so their measurement
// windows overlap. Previously, processes were sampled 1+ second after
// the CPU window ended, causing spikes to appear in system CPU but not
// in the process list (the offending process had already finished).
var (
cpuPerCore []float64
cpuErr error
processes []ProcessInfo
processErr error
)
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
cpuPerCore, cpuErr = cpu.Percent(time.Second, true)
}()
go func() {
defer wg.Done()
processes, processErr = collectTopProcesses()
}()
wg.Wait()
if cpuErr != nil {
return nil, fmt.Errorf("failed to get CPU percent: %w", cpuErr)
}
metrics.CPU.PerCore = cpuPerCore
if len(cpuPerCore) > 0 {
@@ -1052,10 +1075,9 @@ func collectMetrics(config *Config) (*Metrics, error) {
prevNet = current
}
// Top processes
processes, err := collectTopProcesses()
if err != nil {
log.Printf("Warning: Failed to collect process info: %v", err)
// Top processes (collected concurrently with CPU above)
if processErr != nil {
log.Printf("Warning: Failed to collect process info: %v", processErr)
metrics.Processes = []ProcessInfo{}
} else {
metrics.Processes = processes