|
4 | 4 | package cpu
|
5 | 5 |
|
6 | 6 | import (
|
7 |
| - "bytes" |
8 | 7 | "context"
|
9 |
| - "encoding/binary" |
10 | 8 | "fmt"
|
11 | 9 | "runtime"
|
12 |
| - "strconv" |
13 |
| - "strings" |
14 |
| - "syscall" |
| 10 | + "unsafe" |
15 | 11 |
|
16 | 12 | "github.com/shirou/gopsutil/v3/internal/common"
|
17 | 13 | "github.com/tklauser/go-sysconf"
|
18 | 14 | "golang.org/x/sys/unix"
|
19 | 15 | )
|
20 | 16 |
|
21 |
| -// sys/sched.h |
22 |
| -var ( |
23 |
| - CPUser = 0 |
24 |
| - cpNice = 1 |
25 |
| - cpSys = 2 |
26 |
| - cpIntr = 3 |
27 |
| - cpIdle = 4 |
28 |
| - cpUStates = 5 |
29 |
| -) |
30 |
| - |
31 |
| -// sys/sysctl.h |
32 | 17 | const (
|
33 |
| - ctlKern = 1 // "high kernel": proc, limits |
34 |
| - ctlHw = 6 // CTL_HW |
35 |
| - sMT = 24 // HW_sMT |
36 |
| - kernCptime = 40 // KERN_CPTIME |
37 |
| - kernCptime2 = 71 // KERN_CPTIME2 |
| 18 | + // sys/sched.h |
| 19 | + cpuOnline = 0x0001 // CPUSTATS_ONLINE |
| 20 | + |
| 21 | + // sys/sysctl.h |
| 22 | + ctlKern = 1 // "high kernel": proc, limits |
| 23 | + ctlHw = 6 // CTL_HW |
| 24 | + smt = 24 // HW_SMT |
| 25 | + kernCpTime = 40 // KERN_CPTIME |
| 26 | + kernCPUStats = 85 // KERN_CPUSTATS |
38 | 27 | )
|
39 | 28 |
|
40 | 29 | var ClocksPerSec = float64(128)
|
41 | 30 |
|
| 31 | +type cpuStats struct { |
| 32 | + // cs_time[CPUSTATES] |
| 33 | + User uint64 |
| 34 | + Nice uint64 |
| 35 | + Sys uint64 |
| 36 | + Spin uint64 |
| 37 | + Intr uint64 |
| 38 | + Idle uint64 |
| 39 | + |
| 40 | + // cs_flags |
| 41 | + Flags uint64 |
| 42 | +} |
| 43 | + |
42 | 44 | func init() {
|
43 | 45 | clkTck, err := sysconf.Sysconf(sysconf.SC_CLK_TCK)
|
44 | 46 | // ignore errors
|
45 | 47 | if err == nil {
|
46 | 48 | ClocksPerSec = float64(clkTck)
|
47 | 49 | }
|
48 |
| - |
49 |
| - func() { |
50 |
| - v, err := unix.Sysctl("kern.osrelease") // can't reuse host.PlatformInformation because of circular import |
51 |
| - if err != nil { |
52 |
| - return |
53 |
| - } |
54 |
| - v = strings.ToLower(v) |
55 |
| - version, err := strconv.ParseFloat(v, 64) |
56 |
| - if err != nil { |
57 |
| - return |
58 |
| - } |
59 |
| - if version >= 6.4 { |
60 |
| - cpIntr = 4 |
61 |
| - cpIdle = 5 |
62 |
| - cpUStates = 6 |
63 |
| - } |
64 |
| - }() |
65 |
| -} |
66 |
| - |
67 |
| -func smt() (bool, error) { |
68 |
| - mib := []int32{ctlHw, sMT} |
69 |
| - buf, _, err := common.CallSyscall(mib) |
70 |
| - if err != nil { |
71 |
| - return false, err |
72 |
| - } |
73 |
| - |
74 |
| - var ret bool |
75 |
| - br := bytes.NewReader(buf) |
76 |
| - if err := binary.Read(br, binary.LittleEndian, &ret); err != nil { |
77 |
| - return false, err |
78 |
| - } |
79 |
| - |
80 |
| - return ret, nil |
81 | 50 | }
|
82 | 51 |
|
83 | 52 | func Times(percpu bool) ([]TimesStat, error) {
|
84 | 53 | return TimesWithContext(context.Background(), percpu)
|
85 | 54 | }
|
86 | 55 |
|
87 |
| -func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) { |
88 |
| - var ret []TimesStat |
89 |
| - |
90 |
| - var ncpu int |
91 |
| - if percpu { |
92 |
| - ncpu, _ = Counts(true) |
93 |
| - } else { |
94 |
| - ncpu = 1 |
| 56 | +func TimesWithContext(ctx context.Context, percpu bool) (ret []TimesStat, err error) { |
| 57 | + if !percpu { |
| 58 | + mib := []int32{ctlKern, kernCpTime} |
| 59 | + buf, _, err := common.CallSyscall(mib) |
| 60 | + if err != nil { |
| 61 | + return ret, err |
| 62 | + } |
| 63 | + times := (*cpuTimes)(unsafe.Pointer(&buf[0])) |
| 64 | + stat := TimesStat{ |
| 65 | + CPU: "cpu-total", |
| 66 | + User: float64(times.User) / ClocksPerSec, |
| 67 | + Nice: float64(times.Nice) / ClocksPerSec, |
| 68 | + System: float64(times.Sys) / ClocksPerSec, |
| 69 | + Idle: float64(times.Idle) / ClocksPerSec, |
| 70 | + Irq: float64(times.Intr) / ClocksPerSec, |
| 71 | + } |
| 72 | + return []TimesStat{stat}, nil |
95 | 73 | }
|
96 | 74 |
|
97 |
| - smt, err := smt() |
98 |
| - if err == syscall.EOPNOTSUPP { |
99 |
| - // if hw.smt is not applicable for this platform (e.g. i386), |
100 |
| - // pretend it's enabled |
101 |
| - smt = true |
102 |
| - } else if err != nil { |
103 |
| - return nil, err |
| 75 | + ncpu, err := unix.SysctlUint32("hw.ncpu") |
| 76 | + if err != nil { |
| 77 | + return |
104 | 78 | }
|
105 | 79 |
|
106 |
| - for i := 0; i < ncpu; i++ { |
107 |
| - j := i |
108 |
| - if !smt { |
109 |
| - j *= 2 |
110 |
| - } |
111 |
| - |
112 |
| - cpuTimes := make([]int32, cpUStates) |
113 |
| - var mib []int32 |
114 |
| - if percpu { |
115 |
| - mib = []int32{ctlKern, kernCptime2, int32(j)} |
116 |
| - } else { |
117 |
| - mib = []int32{ctlKern, kernCptime} |
118 |
| - } |
| 80 | + var i uint32 |
| 81 | + for i = 0; i < ncpu; i++ { |
| 82 | + mib := []int32{ctlKern, kernCPUStats, int32(i)} |
119 | 83 | buf, _, err := common.CallSyscall(mib)
|
120 | 84 | if err != nil {
|
121 | 85 | return ret, err
|
122 | 86 | }
|
123 | 87 |
|
124 |
| - br := bytes.NewReader(buf) |
125 |
| - err = binary.Read(br, binary.LittleEndian, &cpuTimes) |
126 |
| - if err != nil { |
127 |
| - return ret, err |
128 |
| - } |
129 |
| - c := TimesStat{ |
130 |
| - User: float64(cpuTimes[CPUser]) / ClocksPerSec, |
131 |
| - Nice: float64(cpuTimes[cpNice]) / ClocksPerSec, |
132 |
| - System: float64(cpuTimes[cpSys]) / ClocksPerSec, |
133 |
| - Idle: float64(cpuTimes[cpIdle]) / ClocksPerSec, |
134 |
| - Irq: float64(cpuTimes[cpIntr]) / ClocksPerSec, |
135 |
| - } |
136 |
| - if percpu { |
137 |
| - c.CPU = fmt.Sprintf("cpu%d", j) |
138 |
| - } else { |
139 |
| - c.CPU = "cpu-total" |
| 88 | + stats := (*cpuStats)(unsafe.Pointer(&buf[0])) |
| 89 | + if (stats.Flags & cpuOnline) == 0 { |
| 90 | + continue |
140 | 91 | }
|
141 |
| - ret = append(ret, c) |
| 92 | + ret = append(ret, TimesStat{ |
| 93 | + CPU: fmt.Sprintf("cpu%d", i), |
| 94 | + User: float64(stats.User) / ClocksPerSec, |
| 95 | + Nice: float64(stats.Nice) / ClocksPerSec, |
| 96 | + System: float64(stats.Sys) / ClocksPerSec, |
| 97 | + Idle: float64(stats.Idle) / ClocksPerSec, |
| 98 | + Irq: float64(stats.Intr) / ClocksPerSec, |
| 99 | + }) |
142 | 100 | }
|
143 | 101 |
|
144 | 102 | return ret, nil
|
|
0 commit comments