1 package cpu
2
3 import (
4 "context"
5 "fmt"
6 "reflect"
7 "regexp"
8 "runtime"
9 "strconv"
10 "strings"
11 "unsafe"
12
13 "github.com/shirou/gopsutil/internal/common"
14 "github.com/tklauser/go-sysconf"
15 "golang.org/x/sys/unix"
16 )
17
18 var ClocksPerSec = float64(128)
19 var cpuMatch = regexp.MustCompile(`^CPU:`)
20 var originMatch = regexp.MustCompile(`Origin\s*=\s*"(.+)"\s+Id\s*=\s*(.+)\s+Stepping\s*=\s*(.+)`)
21 var featuresMatch = regexp.MustCompile(`Features=.+<(.+)>`)
22 var featuresMatch2 = regexp.MustCompile(`Features2=[a-f\dx]+<(.+)>`)
23 var cpuEnd = regexp.MustCompile(`^Trying to mount root`)
24 var cpuTimesSize int
25 var emptyTimes cpuTimes
26
27 func init() {
28 clkTck, err := sysconf.Sysconf(sysconf.SC_CLK_TCK)
29
30 if err == nil {
31 ClocksPerSec = float64(clkTck)
32 }
33 }
34
35 func timeStat(name string, t *cpuTimes) *TimesStat {
36 return &TimesStat{
37 User: float64(t.User) / ClocksPerSec,
38 Nice: float64(t.Nice) / ClocksPerSec,
39 System: float64(t.Sys) / ClocksPerSec,
40 Idle: float64(t.Idle) / ClocksPerSec,
41 Irq: float64(t.Intr) / ClocksPerSec,
42 CPU: name,
43 }
44 }
45
46 func Times(percpu bool) ([]TimesStat, error) {
47 return TimesWithContext(context.Background(), percpu)
48 }
49
50 func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) {
51 if percpu {
52 buf, err := unix.SysctlRaw("kern.cp_times")
53 if err != nil {
54 return nil, err
55 }
56
57
58 if cpuTimesSize == 0 {
59 cpuTimesSize = int(reflect.TypeOf(cpuTimes{}).Size())
60 }
61
62 ncpus := len(buf) / cpuTimesSize
63 ret := make([]TimesStat, 0, ncpus)
64 for i := 0; i < ncpus; i++ {
65 times := (*cpuTimes)(unsafe.Pointer(&buf[i*cpuTimesSize]))
66 if *times == emptyTimes {
67
68 continue
69 }
70 ret = append(ret, *timeStat(fmt.Sprintf("cpu%d", len(ret)), times))
71 }
72 return ret, nil
73 }
74
75 buf, err := unix.SysctlRaw("kern.cp_time")
76 if err != nil {
77 return nil, err
78 }
79
80 times := (*cpuTimes)(unsafe.Pointer(&buf[0]))
81 return []TimesStat{*timeStat("cpu-total", times)}, nil
82 }
83
84
85
86
87 func Info() ([]InfoStat, error) {
88 return InfoWithContext(context.Background())
89 }
90
91 func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
92 const dmesgBoot = "/var/run/dmesg.boot"
93
94 c, err := parseDmesgBoot(dmesgBoot)
95 if err != nil {
96 return nil, err
97 }
98
99 var u32 uint32
100 if u32, err = unix.SysctlUint32("hw.clockrate"); err != nil {
101 return nil, err
102 }
103 c.Mhz = float64(u32)
104
105 var num int
106 var buf string
107 if buf, err = unix.Sysctl("hw.cpu_topology.tree"); err != nil {
108 return nil, err
109 }
110 num = strings.Count(buf, "CHIP")
111 c.Cores = int32(strings.Count(string(buf), "CORE") / num)
112
113 if c.ModelName, err = unix.Sysctl("hw.model"); err != nil {
114 return nil, err
115 }
116
117 ret := make([]InfoStat, num)
118 for i := 0; i < num; i++ {
119 ret[i] = c
120 }
121
122 return ret, nil
123 }
124
125 func parseDmesgBoot(fileName string) (InfoStat, error) {
126 c := InfoStat{}
127 lines, _ := common.ReadLines(fileName)
128 for _, line := range lines {
129 if matches := cpuEnd.FindStringSubmatch(line); matches != nil {
130 break
131 } else if matches := originMatch.FindStringSubmatch(line); matches != nil {
132 c.VendorID = matches[1]
133 t, err := strconv.ParseInt(matches[2], 10, 32)
134 if err != nil {
135 return c, fmt.Errorf("unable to parse DragonflyBSD CPU stepping information from %q: %v", line, err)
136 }
137 c.Stepping = int32(t)
138 } else if matches := featuresMatch.FindStringSubmatch(line); matches != nil {
139 for _, v := range strings.Split(matches[1], ",") {
140 c.Flags = append(c.Flags, strings.ToLower(v))
141 }
142 } else if matches := featuresMatch2.FindStringSubmatch(line); matches != nil {
143 for _, v := range strings.Split(matches[1], ",") {
144 c.Flags = append(c.Flags, strings.ToLower(v))
145 }
146 }
147 }
148
149 return c, nil
150 }
151
152 func CountsWithContext(ctx context.Context, logical bool) (int, error) {
153 return runtime.NumCPU(), nil
154 }
155
View as plain text