You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
189 lines
5.0 KiB
189 lines
5.0 KiB
package cpu |
|
|
|
import ( |
|
"context" |
|
"encoding/json" |
|
"fmt" |
|
"runtime" |
|
"strconv" |
|
"strings" |
|
"sync" |
|
"time" |
|
|
|
"github.com/shirou/gopsutil/internal/common" |
|
) |
|
|
|
// TimesStat contains the amounts of time the CPU has spent performing different |
|
// kinds of work. Time units are in USER_HZ or Jiffies (typically hundredths of |
|
// a second). 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"` |
|
Stolen float64 `json:"stolen"` |
|
} |
|
|
|
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 |
|
var invoke common.Invoker = common.Invoke{} |
|
|
|
func init() { |
|
lastCPUPercent.Lock() |
|
lastCPUPercent.lastCPUTimes, _ = Times(false) |
|
lastCPUPercent.lastPerCPUTimes, _ = Times(true) |
|
lastCPUPercent.Unlock() |
|
} |
|
|
|
func Counts(logical bool) (int, error) { |
|
return CountsWithContext(context.Background(), logical) |
|
} |
|
|
|
func CountsWithContext(ctx context.Context, logical bool) (int, error) { |
|
return runtime.NumCPU(), nil |
|
} |
|
|
|
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), |
|
`"stolen":` + strconv.FormatFloat(c.Stolen, '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.Guest + c.GuestNice + c.Idle + c.Stolen |
|
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 + t.Guest + t.GuestNice + t.Stolen |
|
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 1 |
|
} |
|
return (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 percentUsedFromLastCall(percpu) |
|
} |
|
|
|
// Get CPU usage at the start of the interval. |
|
cpuTimes1, err := Times(percpu) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
time.Sleep(interval) |
|
|
|
// And at the end of the interval. |
|
cpuTimes2, err := Times(percpu) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
return calculateAllBusy(cpuTimes1, cpuTimes2) |
|
} |
|
|
|
func percentUsedFromLastCall(percpu bool) ([]float64, error) { |
|
cpuTimes, err := Times(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) |
|
}
|
|
|