@@ -171,7 +171,7 @@ func LoadAverage() (avg LoadAvg, _ error) {
171171// - percentage of wall-clock time the container was CPU-throttled since the last call
172172// or:
173173// - 0 if the prev. sample is old (or first call)
174- func (t * cpuTracker ) get () (util , throttled float64 , _ error ) {
174+ func (t * cpuTracker ) get () (util , throttled int64 , _ error ) {
175175 cur , err := readCPUUsage ()
176176 if err != nil {
177177 return 0 , 0 , err
@@ -190,21 +190,21 @@ func (t *cpuTracker) get() (util, throttled float64, _ error) {
190190 }
191191
192192 wallDt := cur .ts - prev .ts
193- if wallDt <= 0 || wallDt > maxSampleAge {
193+ if wallDt < minWallIval || wallDt > maxSampleAge {
194194 t .mu .Unlock ()
195195 return 0 , 0 , nil
196196 }
197197
198198 if containerized && cur .throttledUsec > 0 {
199199 dtUsec := max (cur .throttledUsec - prev .throttledUsec , 0 )
200- throttled = min (float64 (dtUsec ) * 100_000 / float64 ( wallDt ), 100 )
200+ throttled = min ((dtUsec * 100_000 + wallDt / 2 ) / wallDt , int64 ( 100 ) )
201201 }
202202
203203 t .mu .Unlock ()
204204
205205 cpuDt := max (cur .usage - prev .usage , 0 )
206- ncpu := float64 (NumCPU ())
207- util = float64 (cpuDt ) * 100 / float64 ( wallDt ) / ncpu
206+ denom := wallDt * int64 (NumCPU ())
207+ util = (cpuDt * 100 + denom / 2 ) / denom
208208 return min (util , 100 ), throttled , nil
209209}
210210
@@ -266,9 +266,11 @@ func readCgroupCPUUsage() (usageNs, throttledUsec int64, _ error) {
266266 return ns , 0 , nil
267267}
268268
269- // aggregate "cpu" line from /proc/stat
270- // - sum user+nice+system+irq+softirq+steal
271- // - exclude idle and iowait
269+ // return total cumulative "active" (non-idle) CPU time in nanoseconds:
270+ // - parse the aggregate "cpu" line from /proc/stat
271+ // - require at least 9 fields
272+ // - sum up (user, nice, system, irq, softirq, and steal) jiffies
273+ // - explicitly exclude idle and iowait; ignor guest/guest_nice (already included)
272274func readProcStatCPUUsage () (int64 , error ) {
273275 data , err := os .ReadFile (hostProcStat )
274276 if err != nil {
@@ -281,20 +283,20 @@ func readProcStatCPUUsage() (int64, error) {
281283 }
282284
283285 fields := strings .Fields (line )
284- if len (fields ) < 5 {
285- return 0 , fmt .Errorf ("unexpected %s format: num-fields=%d" , hostProcStat , len (fields ))
286+ if len (fields ) < 9 {
287+ return 0 , fmt .Errorf ("unexpected %s format: num-fields=%d (old kernel?) " , hostProcStat , len (fields ))
286288 }
287289
288- var totalJiffies int64
289- for i , f := range fields [ 1 :] {
290- if i == 3 || i == 4 { // idle, iowait
291- continue
292- }
293- v , e := strconv .ParseInt (f , 10 , 64 )
294- if e != nil {
295- continue
290+ var (
291+ totalJiffies int64
292+ ii = [ ... ] int { 1 , 2 , 3 , 6 , 7 , 8 } // 1:user, 2:nice, 3:system, 6:irq, 7:softirq, 8:steal
293+ )
294+ for _ , i := range ii {
295+ v , err := strconv .ParseInt (fields [ i ] , 10 , 64 )
296+ if err != nil {
297+ return 0 , fmt . Errorf ( "%s: parse field[%d]=%q: %w" , hostProcStat , i , fields [ i ], err )
296298 }
297299 totalJiffies += v
298300 }
299- return int64 ( float64 ( totalJiffies ) * nsPerJiffy ) , nil
301+ return totalJiffies * nsPerJiffy , nil
300302}
0 commit comments