package cpu import ( "context" "encoding/json" "fmt" "math" "strconv" "strings" "sync" "time" "github.com/shirou/gopsutil/v3/internal/common" ) // TimesStat contains the amounts of time the CPU has spent performing different // kinds of work. Time units are in seconds. It is based on linux /proc/stat file. type TimesStat struct { CPU string `json:"cpu"` User float64 `json:"user"` System float64 `json:"system"` Idle float64 `json:"idle"` Nice float64 `json:"nice"` Iowait float64 `json:"iowait"` Irq float64 `json:"irq"` Softirq float64 `json:"softirq"` Steal float64 `json:"steal"` Guest float64 `json:"guest"` GuestNice float64 `json:"guestNice"` } type InfoStat struct { CPU int32 `json:"cpu"` VendorID string `json:"vendorId"` Family string `json:"family"` Model string `json:"model"` Stepping int32 `json:"stepping"` PhysicalID string `json:"physicalId"` CoreID string `json:"coreId"` Cores int32 `json:"cores"` ModelName string `json:"modelName"` Mhz float64 `json:"mhz"` CacheSize int32 `json:"cacheSize"` Flags []string `json:"flags"` Microcode string `json:"microcode"` } type lastPercent struct { sync.Mutex lastCPUTimes []TimesStat lastPerCPUTimes []TimesStat } var ( lastCPUPercent lastPercent invoke common.Invoker = common.Invoke{} ) func init() { lastCPUPercent.Lock() lastCPUPercent.lastCPUTimes, _ = Times(false) lastCPUPercent.lastPerCPUTimes, _ = Times(true) lastCPUPercent.Unlock() } // Counts returns the number of physical or logical cores in the system func Counts(logical bool) (int, error) { return CountsWithContext(context.Background(), logical) } func (c TimesStat) String() string { v := []string{ `"cpu":"` + c.CPU + `"`, `"user":` + strconv.FormatFloat(c.User, 'f', 1, 64), `"system":` + strconv.FormatFloat(c.System, 'f', 1, 64), `"idle":` + strconv.FormatFloat(c.Idle, 'f', 1, 64), `"nice":` + strconv.FormatFloat(c.Nice, 'f', 1, 64), `"iowait":` + strconv.FormatFloat(c.Iowait, 'f', 1, 64), `"irq":` + strconv.FormatFloat(c.Irq, 'f', 1, 64), `"softirq":` + strconv.FormatFloat(c.Softirq, 'f', 1, 64), `"steal":` + strconv.FormatFloat(c.Steal, 'f', 1, 64), `"guest":` + strconv.FormatFloat(c.Guest, 'f', 1, 64), `"guestNice":` + strconv.FormatFloat(c.GuestNice, 'f', 1, 64), } return `{` + strings.Join(v, ",") + `}` } // Total returns the total number of seconds in a CPUTimesStat func (c TimesStat) Total() float64 { total := c.User + c.System + c.Nice + c.Iowait + c.Irq + c.Softirq + c.Steal + c.Idle return total } func (c InfoStat) String() string { s, _ := json.Marshal(c) return string(s) } func getAllBusy(t TimesStat) (float64, float64) { busy := t.User + t.System + t.Nice + t.Iowait + t.Irq + t.Softirq + t.Steal return busy + t.Idle, busy } func calculateBusy(t1, t2 TimesStat) float64 { t1All, t1Busy := getAllBusy(t1) t2All, t2Busy := getAllBusy(t2) if t2Busy <= t1Busy { return 0 } if t2All <= t1All { return 100 } return math.Min(100, math.Max(0, (t2Busy-t1Busy)/(t2All-t1All)*100)) } func calculateAllBusy(t1, t2 []TimesStat) ([]float64, error) { // Make sure the CPU measurements have the same length. if len(t1) != len(t2) { return nil, fmt.Errorf( "received two CPU counts: %d != %d", len(t1), len(t2), ) } ret := make([]float64, len(t1)) for i, t := range t2 { ret[i] = calculateBusy(t1[i], t) } return ret, nil } // Percent calculates the percentage of cpu used either per CPU or combined. // If an interval of 0 is given it will compare the current cpu times against the last call. // Returns one value per cpu, or a single value if percpu is set to false. func Percent(interval time.Duration, percpu bool) ([]float64, error) { return PercentWithContext(context.Background(), interval, percpu) } func PercentWithContext(ctx context.Context, interval time.Duration, percpu bool) ([]float64, error) { if interval <= 0 { return percentUsedFromLastCallWithContext(ctx, percpu) } // Get CPU usage at the start of the interval. cpuTimes1, err := TimesWithContext(ctx, percpu) if err != nil { return nil, err } if err := common.Sleep(ctx, interval); err != nil { return nil, err } // And at the end of the interval. cpuTimes2, err := TimesWithContext(ctx, percpu) if err != nil { return nil, err } return calculateAllBusy(cpuTimes1, cpuTimes2) } func percentUsedFromLastCall(percpu bool) ([]float64, error) { return percentUsedFromLastCallWithContext(context.Background(), percpu) } func percentUsedFromLastCallWithContext(ctx context.Context, percpu bool) ([]float64, error) { cpuTimes, err := TimesWithContext(ctx, percpu) if err != nil { return nil, err } lastCPUPercent.Lock() defer lastCPUPercent.Unlock() var lastTimes []TimesStat if percpu { lastTimes = lastCPUPercent.lastPerCPUTimes lastCPUPercent.lastPerCPUTimes = cpuTimes } else { lastTimes = lastCPUPercent.lastCPUTimes lastCPUPercent.lastCPUTimes = cpuTimes } if lastTimes == nil { return nil, fmt.Errorf("error getting times for cpu percent. lastTimes was nil") } return calculateAllBusy(lastTimes, cpuTimes) }