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