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