@@ -3,11 +3,78 @@ package metrics
33import (
44 "fmt"
55 "strings"
6+ "sync"
7+ "time"
68
7- "github.com/shirou/gopsutil/v3 /net"
8- "github.com/shirou/gopsutil/v3 /process"
9+ "github.com/shirou/gopsutil/v4 /net"
10+ "github.com/shirou/gopsutil/v4 /process"
911)
1012
13+ // Cache for process CPU times (for delta calculation)
14+ var (
15+ procCpuTimesCache = make (map [int32 ]procCpuCacheEntry )
16+ procCpuTimesCacheMu sync.RWMutex
17+ )
18+
19+ type procCpuCacheEntry struct {
20+ userTime float64
21+ sysTime float64
22+ sampleTime time.Time
23+ }
24+
25+ // getProcessCPUPercent calculates CPU% for a process using delta from cached values.
26+ // Non-blocking - uses process.Times() which just reads /proc/[pid]/stat.
27+ func getProcessCPUPercent (proc * process.Process ) float64 {
28+ times , err := proc .Times ()
29+ if err != nil || times == nil {
30+ return 0
31+ }
32+
33+ now := time .Now ()
34+ pid := proc .Pid
35+ currentTotal := times .User + times .System
36+
37+ procCpuTimesCacheMu .Lock ()
38+ defer procCpuTimesCacheMu .Unlock ()
39+
40+ prev , exists := procCpuTimesCache [pid ]
41+ if ! exists {
42+ // First call - store baseline, return 0
43+ procCpuTimesCache [pid ] = procCpuCacheEntry {
44+ userTime : times .User ,
45+ sysTime : times .System ,
46+ sampleTime : now ,
47+ }
48+ return 0
49+ }
50+
51+ elapsed := now .Sub (prev .sampleTime ).Seconds ()
52+ if elapsed <= 0 {
53+ return 0
54+ }
55+
56+ prevTotal := prev .userTime + prev .sysTime
57+ cpuDelta := currentTotal - prevTotal
58+
59+ // Update cache
60+ procCpuTimesCache [pid ] = procCpuCacheEntry {
61+ userTime : times .User ,
62+ sysTime : times .System ,
63+ sampleTime : now ,
64+ }
65+
66+ // CPU% = (cpu time delta / wall time delta) * 100
67+ cpuPercent := (cpuDelta / elapsed ) * 100
68+ if cpuPercent < 0 {
69+ cpuPercent = 0
70+ }
71+ if cpuPercent > 100 {
72+ cpuPercent = 100
73+ }
74+
75+ return cpuPercent
76+ }
77+
1178// ListeningPort represents a port that is being listened on
1279type ListeningPort struct {
1380 Port int `json:"port"`
@@ -61,8 +128,8 @@ func (d *ServiceDetector) DetectServices() ([]ServiceInfo, error) {
61128 continue // Skip unknown services
62129 }
63130
64- // Get process stats
65- cpuPercent , _ := proc . CPUPercent ( )
131+ // Get process stats using non-blocking delta calculation
132+ cpuPercent := getProcessCPUPercent ( proc )
66133 memoryPercent , _ := proc .MemoryPercent ()
67134 memoryInfo , _ := proc .MemoryInfo ()
68135 status , _ := proc .Status ()
0 commit comments