consul/lib/hoststats/cpu.go

89 lines
2.6 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package hoststats
import (
"math"
"github.com/shirou/gopsutil/v3/cpu"
)
// cpuStatsCalculator calculates cpu usage percentages
type cpuStatsCalculator struct {
prev cpu.TimesStat
prevBusy float64
prevTotal float64
}
// calculate the current cpu usage percentages.
// Since the cpu.TimesStat captures the total time a cpu spent in various states
// this function tracks the last seen stat and derives each cpu state's utilization
// as a percentage of the total change in cpu time between calls.
// The first time calculate is called CPUStats will report %100 idle
// usage since there is not a previous value to calculate against
func (h *cpuStatsCalculator) calculate(times cpu.TimesStat) *CPUStats {
// sum all none idle counters to get the total busy cpu time
currentBusy := times.User + times.System + times.Nice + times.Iowait + times.Irq +
times.Softirq + times.Steal + times.Guest + times.GuestNice
// sum of the total cpu time
currentTotal := currentBusy + times.Idle
// calculate how much cpu time has passed since last calculation
deltaTotal := currentTotal - h.prevTotal
stats := &CPUStats{
CPU: times.CPU,
// calculate each percentage as the ratio of the change
// in each state's time to the total change in cpu time
Idle: ((times.Idle - h.prev.Idle) / deltaTotal) * 100,
User: ((times.User - h.prev.User) / deltaTotal) * 100,
System: ((times.System - h.prev.System) / deltaTotal) * 100,
Iowait: ((times.Iowait - h.prev.Iowait) / deltaTotal) * 100,
Total: ((currentBusy - h.prevBusy) / deltaTotal) * 100,
}
// Protect against any invalid values
if math.IsNaN(stats.Idle) || math.IsInf(stats.Idle, 0) {
stats.Idle = 100.0
}
if math.IsNaN(stats.User) || math.IsInf(stats.User, 0) {
stats.User = 0.0
}
if math.IsNaN(stats.System) || math.IsInf(stats.System, 0) {
stats.System = 0.0
}
if math.IsNaN(stats.Iowait) || math.IsInf(stats.Iowait, 0) {
stats.Iowait = 0.0
}
if math.IsNaN(stats.Total) || math.IsInf(stats.Total, 0) {
stats.Total = 0.0
}
h.prev = times
h.prevTotal = currentTotal
h.prevBusy = currentBusy
return stats
}
func (c *Collector) collectCPUStats() (cpus []*CPUStats, err error) {
cpuStats, err := cpu.Times(true)
if err != nil {
return nil, err
}
cs := make([]*CPUStats, len(cpuStats))
for idx, cpuStat := range cpuStats {
percentCalculator, ok := c.cpuCalculator[cpuStat.CPU]
if !ok {
percentCalculator = &cpuStatsCalculator{}
c.cpuCalculator[cpuStat.CPU] = percentCalculator
}
cs[idx] = percentCalculator.calculate(cpuStat)
}
return cs, nil
}