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