1
2
3 package cpu
4
5 import (
6 "bytes"
7 "context"
8 "encoding/binary"
9 "fmt"
10 "runtime"
11 "strconv"
12 "strings"
13 "syscall"
14
15 "github.com/shirou/gopsutil/internal/common"
16 "github.com/tklauser/go-sysconf"
17 "golang.org/x/sys/unix"
18 )
19
20
21 var (
22 CPUser = 0
23 CPNice = 1
24 CPSys = 2
25 CPIntr = 3
26 CPIdle = 4
27 CPUStates = 5
28 )
29
30
31 const (
32 CTLKern = 1
33 CTLHw = 6
34 SMT = 24
35 KernCptime = 40
36 KernCptime2 = 71
37 )
38
39 var ClocksPerSec = float64(128)
40
41 func init() {
42 clkTck, err := sysconf.Sysconf(sysconf.SC_CLK_TCK)
43
44 if err == nil {
45 ClocksPerSec = float64(clkTck)
46 }
47
48 func() {
49 v, err := unix.Sysctl("kern.osrelease")
50 if err != nil {
51 return
52 }
53 v = strings.ToLower(v)
54 version, err := strconv.ParseFloat(v, 64)
55 if err != nil {
56 return
57 }
58 if version >= 6.4 {
59 CPIntr = 4
60 CPIdle = 5
61 CPUStates = 6
62 }
63 }()
64 }
65
66 func smt() (bool, error) {
67 mib := []int32{CTLHw, SMT}
68 buf, _, err := common.CallSyscall(mib)
69 if err != nil {
70 return false, err
71 }
72
73 var ret bool
74 br := bytes.NewReader(buf)
75 if err := binary.Read(br, binary.LittleEndian, &ret); err != nil {
76 return false, err
77 }
78
79 return ret, nil
80 }
81
82 func Times(percpu bool) ([]TimesStat, error) {
83 return TimesWithContext(context.Background(), percpu)
84 }
85
86 func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) {
87 var ret []TimesStat
88
89 var ncpu int
90 if percpu {
91 ncpu, _ = Counts(true)
92 } else {
93 ncpu = 1
94 }
95
96 smt, err := smt()
97 if err == syscall.EOPNOTSUPP {
98
99
100 smt = true
101 } else if err != nil {
102 return nil, err
103 }
104
105 for i := 0; i < ncpu; i++ {
106 j := i
107 if !smt {
108 j *= 2
109 }
110
111 var cpuTimes = make([]int32, CPUStates)
112 var mib []int32
113 if percpu {
114 mib = []int32{CTLKern, KernCptime2, int32(j)}
115 } else {
116 mib = []int32{CTLKern, KernCptime}
117 }
118 buf, _, err := common.CallSyscall(mib)
119 if err != nil {
120 return ret, err
121 }
122
123 br := bytes.NewReader(buf)
124 err = binary.Read(br, binary.LittleEndian, &cpuTimes)
125 if err != nil {
126 return ret, err
127 }
128 c := TimesStat{
129 User: float64(cpuTimes[CPUser]) / ClocksPerSec,
130 Nice: float64(cpuTimes[CPNice]) / ClocksPerSec,
131 System: float64(cpuTimes[CPSys]) / ClocksPerSec,
132 Idle: float64(cpuTimes[CPIdle]) / ClocksPerSec,
133 Irq: float64(cpuTimes[CPIntr]) / ClocksPerSec,
134 }
135 if percpu {
136 c.CPU = fmt.Sprintf("cpu%d", j)
137 } else {
138 c.CPU = "cpu-total"
139 }
140 ret = append(ret, c)
141 }
142
143 return ret, nil
144 }
145
146
147 func Info() ([]InfoStat, error) {
148 return InfoWithContext(context.Background())
149 }
150
151 func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
152 var ret []InfoStat
153 var err error
154
155 c := InfoStat{}
156
157 mhz, err := unix.SysctlUint32("hw.cpuspeed")
158 if err != nil {
159 return nil, err
160 }
161 c.Mhz = float64(mhz)
162
163 ncpu, err := unix.SysctlUint32("hw.ncpuonline")
164 if err != nil {
165 return nil, err
166 }
167 c.Cores = int32(ncpu)
168
169 if c.ModelName, err = unix.Sysctl("hw.model"); err != nil {
170 return nil, err
171 }
172
173 return append(ret, c), nil
174 }
175
176 func CountsWithContext(ctx context.Context, logical bool) (int, error) {
177 return runtime.NumCPU(), nil
178 }
179
View as plain text