github.com/isyscore/isc-gobase@v1.5.3-0.20231218061332-cbc7451899e9/system/cpu/cpu.go (about) 1 package cpu 2 3 import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "github.com/isyscore/isc-gobase/system/common" 8 "math" 9 "strconv" 10 "strings" 11 "sync" 12 "time" 13 ) 14 15 // TimesStat contains the amounts of time the CPU has spent performing different 16 // kinds of work. Time units are in seconds. It is based on linux /proc/stat file. 17 type TimesStat struct { 18 CPU string `json:"cpu"` 19 User float64 `json:"user"` 20 System float64 `json:"system"` 21 Idle float64 `json:"idle"` 22 Nice float64 `json:"nice"` 23 Iowait float64 `json:"iowait"` 24 Irq float64 `json:"irq"` 25 Softirq float64 `json:"softirq"` 26 Steal float64 `json:"steal"` 27 Guest float64 `json:"guest"` 28 GuestNice float64 `json:"guestNice"` 29 } 30 31 type InfoStat struct { 32 CPU int32 `json:"cpu"` 33 VendorID string `json:"vendorId"` 34 Family string `json:"family"` 35 Model string `json:"model"` 36 Stepping int32 `json:"stepping"` 37 PhysicalID string `json:"physicalId"` 38 CoreID string `json:"coreId"` 39 Cores int32 `json:"cores"` 40 ModelName string `json:"modelName"` 41 Mhz float64 `json:"mhz"` 42 CacheSize int32 `json:"cacheSize"` 43 Flags []string `json:"flags"` 44 Microcode string `json:"microcode"` 45 } 46 47 type lastPercent struct { 48 sync.Mutex 49 lastCPUTimes []TimesStat 50 lastPerCPUTimes []TimesStat 51 } 52 53 var lastCPUPercent lastPercent 54 var invoke common.Invoker = common.Invoke{} 55 56 func init() { 57 lastCPUPercent.Lock() 58 lastCPUPercent.lastCPUTimes, _ = Times(false) 59 lastCPUPercent.lastPerCPUTimes, _ = Times(true) 60 lastCPUPercent.Unlock() 61 } 62 63 // Counts returns the number of physical or logical cores in the system 64 func Counts(logical bool) (int, error) { 65 return CountsWithContext(context.Background(), logical) 66 } 67 68 func (c TimesStat) String() string { 69 v := []string{ 70 `"cpu":"` + c.CPU + `"`, 71 `"user":` + strconv.FormatFloat(c.User, 'f', 1, 64), 72 `"system":` + strconv.FormatFloat(c.System, 'f', 1, 64), 73 `"idle":` + strconv.FormatFloat(c.Idle, 'f', 1, 64), 74 `"nice":` + strconv.FormatFloat(c.Nice, 'f', 1, 64), 75 `"iowait":` + strconv.FormatFloat(c.Iowait, 'f', 1, 64), 76 `"irq":` + strconv.FormatFloat(c.Irq, 'f', 1, 64), 77 `"softirq":` + strconv.FormatFloat(c.Softirq, 'f', 1, 64), 78 `"steal":` + strconv.FormatFloat(c.Steal, 'f', 1, 64), 79 `"guest":` + strconv.FormatFloat(c.Guest, 'f', 1, 64), 80 `"guestNice":` + strconv.FormatFloat(c.GuestNice, 'f', 1, 64), 81 } 82 83 return `{` + strings.Join(v, ",") + `}` 84 } 85 86 // Total returns the total number of seconds in a CPUTimesStat 87 func (c TimesStat) Total() float64 { 88 total := c.User + c.System + c.Nice + c.Iowait + c.Irq + c.Softirq + 89 c.Steal + c.Idle 90 return total 91 } 92 93 func (c InfoStat) String() string { 94 s, _ := json.Marshal(c) 95 return string(s) 96 } 97 98 func getAllBusy(t TimesStat) (float64, float64) { 99 busy := t.User + t.System + t.Nice + t.Iowait + t.Irq + 100 t.Softirq + t.Steal 101 return busy + t.Idle, busy 102 } 103 104 func calculateBusy(t1, t2 TimesStat) float64 { 105 t1All, t1Busy := getAllBusy(t1) 106 t2All, t2Busy := getAllBusy(t2) 107 108 if t2Busy <= t1Busy { 109 return 0 110 } 111 if t2All <= t1All { 112 return 100 113 } 114 return math.Min(100, math.Max(0, (t2Busy-t1Busy)/(t2All-t1All)*100)) 115 } 116 117 func calculateAllBusy(t1, t2 []TimesStat) ([]float64, error) { 118 // Make sure the CPU measurements have the same length. 119 if len(t1) != len(t2) { 120 return nil, fmt.Errorf( 121 "received two CPU counts: %d != %d", 122 len(t1), len(t2), 123 ) 124 } 125 126 ret := make([]float64, len(t1)) 127 for i, t := range t2 { 128 ret[i] = calculateBusy(t1[i], t) 129 } 130 return ret, nil 131 } 132 133 // Percent calculates the percentage of cpu used either per CPU or combined. 134 // If an interval of 0 is given it will compare the current cpu times against the last call. 135 // Returns one value per cpu, or a single value if percpu is set to false. 136 func Percent(interval time.Duration, percpu bool) ([]float64, error) { 137 return PercentWithContext(context.Background(), interval, percpu) 138 } 139 140 func PercentWithContext(ctx context.Context, interval time.Duration, percpu bool) ([]float64, error) { 141 if interval <= 0 { 142 return percentUsedFromLastCall(percpu) 143 } 144 145 // Get CPU usage at the start of the interval. 146 cpuTimes1, err := Times(percpu) 147 if err != nil { 148 return nil, err 149 } 150 151 if err := common.Sleep(ctx, interval); err != nil { 152 return nil, err 153 } 154 155 // And at the end of the interval. 156 cpuTimes2, err := Times(percpu) 157 if err != nil { 158 return nil, err 159 } 160 161 return calculateAllBusy(cpuTimes1, cpuTimes2) 162 } 163 164 func percentUsedFromLastCall(percpu bool) ([]float64, error) { 165 cpuTimes, err := Times(percpu) 166 if err != nil { 167 return nil, err 168 } 169 lastCPUPercent.Lock() 170 defer lastCPUPercent.Unlock() 171 var lastTimes []TimesStat 172 if percpu { 173 lastTimes = lastCPUPercent.lastPerCPUTimes 174 lastCPUPercent.lastPerCPUTimes = cpuTimes 175 } else { 176 lastTimes = lastCPUPercent.lastCPUTimes 177 lastCPUPercent.lastCPUTimes = cpuTimes 178 } 179 180 if lastTimes == nil { 181 return nil, fmt.Errorf("error getting times for cpu percent. lastTimes was nil") 182 } 183 return calculateAllBusy(lastTimes, cpuTimes) 184 }