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/shirou/gopsutil/internal/common"
14 )
15
16
17
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
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
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
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
135
136
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
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
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 }
186
View as plain text