Skip to content

Commit 3d62d69

Browse files
committed
procent per process
1 parent d87f332 commit 3d62d69

File tree

1 file changed

+51
-4
lines changed

1 file changed

+51
-4
lines changed

internal/metrics/collector.go

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,11 @@ var (
339339
cycleConnections []net.ConnectionStat
340340
cycleCacheMu sync.RWMutex
341341
cycleCacheTime time.Time
342+
343+
// Process CPU tracking for delta-based calculation (like htop does)
344+
prevProcCPUTimes map[int32]float64 // PID -> total CPU time (user + system)
345+
prevProcCPUTime time.Time
346+
prevProcCPUMu sync.RWMutex
342347
)
343348

344349
// OTelConfig holds configuration for OpenTelemetry exporter
@@ -1830,6 +1835,18 @@ func collectProcesses(limit int) ([]ProcessInfo, error) {
18301835
return nil, err
18311836
}
18321837

1838+
// Get timing info for CPU delta calculation
1839+
prevProcCPUMu.RLock()
1840+
prevTimes := prevProcCPUTimes
1841+
prevTime := prevProcCPUTime
1842+
prevProcCPUMu.RUnlock()
1843+
1844+
elapsed := time.Since(prevTime).Seconds()
1845+
numCPU := float64(runtime.NumCPU())
1846+
1847+
// Current CPU times map for next cycle
1848+
currentTimes := make(map[int32]float64)
1849+
18331850
var processes []ProcessInfo
18341851

18351852
for _, p := range procs {
@@ -1838,11 +1855,9 @@ func collectProcesses(limit int) ([]ProcessInfo, error) {
18381855
continue
18391856
}
18401857

1841-
// Only use MemoryPercent for filtering (non-blocking)
1842-
// Skip CPUPercent - it blocks for 100ms per process!
18431858
memPercent, _ := p.MemoryPercent()
18441859

1845-
// Filter by memory only (processes with < 0.1% memory are not interesting)
1860+
// Filter by memory (processes with < 0.1% memory are not interesting)
18461861
if memPercent < 0.1 {
18471862
continue
18481863
}
@@ -1854,6 +1869,26 @@ func collectProcesses(limit int) ([]ProcessInfo, error) {
18541869

18551870
pi.MemoryPercent = float64(memPercent)
18561871

1872+
// Get CPU times for delta calculation (non-blocking, just reads /proc/[pid]/stat)
1873+
if times, err := p.Times(); err == nil && times != nil {
1874+
totalTime := times.User + times.System
1875+
currentTimes[p.Pid] = totalTime
1876+
1877+
// Calculate CPU% from delta if we have previous data
1878+
if prevTimes != nil && elapsed > 0 {
1879+
if prevTotal, ok := prevTimes[p.Pid]; ok {
1880+
// CPU% = (delta CPU time / elapsed time) * 100 / numCPU
1881+
deltaTime := totalTime - prevTotal
1882+
if deltaTime >= 0 {
1883+
pi.CPUPercent = (deltaTime / elapsed) * 100.0 / numCPU
1884+
if pi.CPUPercent > 100 {
1885+
pi.CPUPercent = 100
1886+
}
1887+
}
1888+
}
1889+
}
1890+
}
1891+
18571892
// Minimal syscalls: only cmdline and memory info
18581893
if cmdline, err := p.Cmdline(); err == nil {
18591894
pi.Command = truncateString(cmdline, 200)
@@ -1869,14 +1904,26 @@ func collectProcesses(limit int) ([]ProcessInfo, error) {
18691904
pi.Status = string(status[0])
18701905
}
18711906

1907+
// Legacy fields
1908+
pi.CPUUsage = pi.CPUPercent
18721909
pi.MemoryUsage = pi.MemoryPercent
18731910
pi.MemoryKB = int64(pi.MemoryRSS / 1024)
18741911

18751912
processes = append(processes, pi)
18761913
}
18771914

1878-
// Sort by memory usage (since we don't have CPU anymore)
1915+
// Save current times for next cycle
1916+
prevProcCPUMu.Lock()
1917+
prevProcCPUTimes = currentTimes
1918+
prevProcCPUTime = time.Now()
1919+
prevProcCPUMu.Unlock()
1920+
1921+
// Sort by CPU+Memory combined (prioritize CPU, then memory)
18791922
sort.Slice(processes, func(i, j int) bool {
1923+
// Primary sort by CPU, secondary by memory
1924+
if processes[i].CPUPercent != processes[j].CPUPercent {
1925+
return processes[i].CPUPercent > processes[j].CPUPercent
1926+
}
18801927
return processes[i].MemoryPercent > processes[j].MemoryPercent
18811928
})
18821929

0 commit comments

Comments
 (0)