1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package procfs
18
19 import (
20 "bufio"
21 "bytes"
22 "errors"
23 "fmt"
24 "regexp"
25 "strconv"
26 "strings"
27
28 "github.com/prometheus/procfs/internal/util"
29 )
30
31
32 type CPUInfo struct {
33 Processor uint
34 VendorID string
35 CPUFamily string
36 Model string
37 ModelName string
38 Stepping string
39 Microcode string
40 CPUMHz float64
41 CacheSize string
42 PhysicalID string
43 Siblings uint
44 CoreID string
45 CPUCores uint
46 APICID string
47 InitialAPICID string
48 FPU string
49 FPUException string
50 CPUIDLevel uint
51 WP string
52 Flags []string
53 Bugs []string
54 BogoMips float64
55 CLFlushSize uint
56 CacheAlignment uint
57 AddressSizes string
58 PowerManagement string
59 }
60
61 var (
62 cpuinfoClockRegexp = regexp.MustCompile(`([\d.]+)`)
63 cpuinfoS390XProcessorRegexp = regexp.MustCompile(`^processor\s+(\d+):.*`)
64 )
65
66
67
68 func (fs FS) CPUInfo() ([]CPUInfo, error) {
69 data, err := util.ReadFileNoStat(fs.proc.Path("cpuinfo"))
70 if err != nil {
71 return nil, err
72 }
73 return parseCPUInfo(data)
74 }
75
76 func parseCPUInfoX86(info []byte) ([]CPUInfo, error) {
77 scanner := bufio.NewScanner(bytes.NewReader(info))
78
79
80 firstLine := firstNonEmptyLine(scanner)
81 if !strings.HasPrefix(firstLine, "processor") || !strings.Contains(firstLine, ":") {
82 return nil, fmt.Errorf("%w: Cannot parse line: %q", ErrFileParse, firstLine)
83 }
84 field := strings.SplitN(firstLine, ": ", 2)
85 v, err := strconv.ParseUint(field[1], 0, 32)
86 if err != nil {
87 return nil, err
88 }
89 firstcpu := CPUInfo{Processor: uint(v)}
90 cpuinfo := []CPUInfo{firstcpu}
91 i := 0
92
93 for scanner.Scan() {
94 line := scanner.Text()
95 if !strings.Contains(line, ":") {
96 continue
97 }
98 field := strings.SplitN(line, ": ", 2)
99 switch strings.TrimSpace(field[0]) {
100 case "processor":
101 cpuinfo = append(cpuinfo, CPUInfo{})
102 i++
103 v, err := strconv.ParseUint(field[1], 0, 32)
104 if err != nil {
105 return nil, err
106 }
107 cpuinfo[i].Processor = uint(v)
108 case "vendor", "vendor_id":
109 cpuinfo[i].VendorID = field[1]
110 case "cpu family":
111 cpuinfo[i].CPUFamily = field[1]
112 case "model":
113 cpuinfo[i].Model = field[1]
114 case "model name":
115 cpuinfo[i].ModelName = field[1]
116 case "stepping":
117 cpuinfo[i].Stepping = field[1]
118 case "microcode":
119 cpuinfo[i].Microcode = field[1]
120 case "cpu MHz":
121 v, err := strconv.ParseFloat(field[1], 64)
122 if err != nil {
123 return nil, err
124 }
125 cpuinfo[i].CPUMHz = v
126 case "cache size":
127 cpuinfo[i].CacheSize = field[1]
128 case "physical id":
129 cpuinfo[i].PhysicalID = field[1]
130 case "siblings":
131 v, err := strconv.ParseUint(field[1], 0, 32)
132 if err != nil {
133 return nil, err
134 }
135 cpuinfo[i].Siblings = uint(v)
136 case "core id":
137 cpuinfo[i].CoreID = field[1]
138 case "cpu cores":
139 v, err := strconv.ParseUint(field[1], 0, 32)
140 if err != nil {
141 return nil, err
142 }
143 cpuinfo[i].CPUCores = uint(v)
144 case "apicid":
145 cpuinfo[i].APICID = field[1]
146 case "initial apicid":
147 cpuinfo[i].InitialAPICID = field[1]
148 case "fpu":
149 cpuinfo[i].FPU = field[1]
150 case "fpu_exception":
151 cpuinfo[i].FPUException = field[1]
152 case "cpuid level":
153 v, err := strconv.ParseUint(field[1], 0, 32)
154 if err != nil {
155 return nil, err
156 }
157 cpuinfo[i].CPUIDLevel = uint(v)
158 case "wp":
159 cpuinfo[i].WP = field[1]
160 case "flags":
161 cpuinfo[i].Flags = strings.Fields(field[1])
162 case "bugs":
163 cpuinfo[i].Bugs = strings.Fields(field[1])
164 case "bogomips":
165 v, err := strconv.ParseFloat(field[1], 64)
166 if err != nil {
167 return nil, err
168 }
169 cpuinfo[i].BogoMips = v
170 case "clflush size":
171 v, err := strconv.ParseUint(field[1], 0, 32)
172 if err != nil {
173 return nil, err
174 }
175 cpuinfo[i].CLFlushSize = uint(v)
176 case "cache_alignment":
177 v, err := strconv.ParseUint(field[1], 0, 32)
178 if err != nil {
179 return nil, err
180 }
181 cpuinfo[i].CacheAlignment = uint(v)
182 case "address sizes":
183 cpuinfo[i].AddressSizes = field[1]
184 case "power management":
185 cpuinfo[i].PowerManagement = field[1]
186 }
187 }
188 return cpuinfo, nil
189 }
190
191 func parseCPUInfoARM(info []byte) ([]CPUInfo, error) {
192 scanner := bufio.NewScanner(bytes.NewReader(info))
193
194 firstLine := firstNonEmptyLine(scanner)
195 match, err := regexp.MatchString("^[Pp]rocessor", firstLine)
196 if !match || !strings.Contains(firstLine, ":") {
197 return nil, fmt.Errorf("%s: Cannot parse line: %q: %w", ErrFileParse, firstLine, err)
198
199 }
200 field := strings.SplitN(firstLine, ": ", 2)
201 cpuinfo := []CPUInfo{}
202 featuresLine := ""
203 commonCPUInfo := CPUInfo{}
204 i := 0
205 if strings.TrimSpace(field[0]) == "Processor" {
206 commonCPUInfo = CPUInfo{ModelName: field[1]}
207 i = -1
208 } else {
209 v, err := strconv.ParseUint(field[1], 0, 32)
210 if err != nil {
211 return nil, err
212 }
213 firstcpu := CPUInfo{Processor: uint(v)}
214 cpuinfo = []CPUInfo{firstcpu}
215 }
216
217 for scanner.Scan() {
218 line := scanner.Text()
219 if !strings.Contains(line, ":") {
220 continue
221 }
222 field := strings.SplitN(line, ": ", 2)
223 switch strings.TrimSpace(field[0]) {
224 case "processor":
225 cpuinfo = append(cpuinfo, commonCPUInfo)
226 i++
227 v, err := strconv.ParseUint(field[1], 0, 32)
228 if err != nil {
229 return nil, err
230 }
231 cpuinfo[i].Processor = uint(v)
232 case "BogoMIPS":
233 if i == -1 {
234 cpuinfo = append(cpuinfo, commonCPUInfo)
235 i++
236 cpuinfo[i].Processor = 0
237 }
238 v, err := strconv.ParseFloat(field[1], 64)
239 if err != nil {
240 return nil, err
241 }
242 cpuinfo[i].BogoMips = v
243 case "Features":
244 featuresLine = line
245 case "model name":
246 cpuinfo[i].ModelName = field[1]
247 }
248 }
249 fields := strings.SplitN(featuresLine, ": ", 2)
250 for i := range cpuinfo {
251 cpuinfo[i].Flags = strings.Fields(fields[1])
252 }
253 return cpuinfo, nil
254
255 }
256
257 func parseCPUInfoS390X(info []byte) ([]CPUInfo, error) {
258 scanner := bufio.NewScanner(bytes.NewReader(info))
259
260 firstLine := firstNonEmptyLine(scanner)
261 if !strings.HasPrefix(firstLine, "vendor_id") || !strings.Contains(firstLine, ":") {
262 return nil, fmt.Errorf("%w: Cannot parse line: %q", ErrFileParse, firstLine)
263 }
264 field := strings.SplitN(firstLine, ": ", 2)
265 cpuinfo := []CPUInfo{}
266 commonCPUInfo := CPUInfo{VendorID: field[1]}
267
268 for scanner.Scan() {
269 line := scanner.Text()
270 if !strings.Contains(line, ":") {
271 continue
272 }
273 field := strings.SplitN(line, ": ", 2)
274 switch strings.TrimSpace(field[0]) {
275 case "bogomips per cpu":
276 v, err := strconv.ParseFloat(field[1], 64)
277 if err != nil {
278 return nil, err
279 }
280 commonCPUInfo.BogoMips = v
281 case "features":
282 commonCPUInfo.Flags = strings.Fields(field[1])
283 }
284 if strings.HasPrefix(line, "processor") {
285 match := cpuinfoS390XProcessorRegexp.FindStringSubmatch(line)
286 if len(match) < 2 {
287 return nil, fmt.Errorf("%w: %q", ErrFileParse, firstLine)
288 }
289 cpu := commonCPUInfo
290 v, err := strconv.ParseUint(match[1], 0, 32)
291 if err != nil {
292 return nil, err
293 }
294 cpu.Processor = uint(v)
295 cpuinfo = append(cpuinfo, cpu)
296 }
297 if strings.HasPrefix(line, "cpu number") {
298 break
299 }
300 }
301
302 i := 0
303 for scanner.Scan() {
304 line := scanner.Text()
305 if !strings.Contains(line, ":") {
306 continue
307 }
308 field := strings.SplitN(line, ": ", 2)
309 switch strings.TrimSpace(field[0]) {
310 case "cpu number":
311 i++
312 case "cpu MHz dynamic":
313 clock := cpuinfoClockRegexp.FindString(strings.TrimSpace(field[1]))
314 v, err := strconv.ParseFloat(clock, 64)
315 if err != nil {
316 return nil, err
317 }
318 cpuinfo[i].CPUMHz = v
319 case "physical id":
320 cpuinfo[i].PhysicalID = field[1]
321 case "core id":
322 cpuinfo[i].CoreID = field[1]
323 case "cpu cores":
324 v, err := strconv.ParseUint(field[1], 0, 32)
325 if err != nil {
326 return nil, err
327 }
328 cpuinfo[i].CPUCores = uint(v)
329 case "siblings":
330 v, err := strconv.ParseUint(field[1], 0, 32)
331 if err != nil {
332 return nil, err
333 }
334 cpuinfo[i].Siblings = uint(v)
335 }
336 }
337
338 return cpuinfo, nil
339 }
340
341 func parseCPUInfoMips(info []byte) ([]CPUInfo, error) {
342 scanner := bufio.NewScanner(bytes.NewReader(info))
343
344
345 firstLine := firstNonEmptyLine(scanner)
346 if !strings.HasPrefix(firstLine, "system type") || !strings.Contains(firstLine, ":") {
347 return nil, fmt.Errorf("%w: %q", ErrFileParse, firstLine)
348 }
349 field := strings.SplitN(firstLine, ": ", 2)
350 cpuinfo := []CPUInfo{}
351 systemType := field[1]
352
353 i := 0
354
355 for scanner.Scan() {
356 line := scanner.Text()
357 if !strings.Contains(line, ":") {
358 continue
359 }
360 field := strings.SplitN(line, ": ", 2)
361 switch strings.TrimSpace(field[0]) {
362 case "processor":
363 v, err := strconv.ParseUint(field[1], 0, 32)
364 if err != nil {
365 return nil, err
366 }
367 i = int(v)
368 cpuinfo = append(cpuinfo, CPUInfo{})
369 cpuinfo[i].Processor = uint(v)
370 cpuinfo[i].VendorID = systemType
371 case "cpu model":
372 cpuinfo[i].ModelName = field[1]
373 case "BogoMIPS":
374 v, err := strconv.ParseFloat(field[1], 64)
375 if err != nil {
376 return nil, err
377 }
378 cpuinfo[i].BogoMips = v
379 }
380 }
381 return cpuinfo, nil
382 }
383
384 func parseCPUInfoLoong(info []byte) ([]CPUInfo, error) {
385 scanner := bufio.NewScanner(bytes.NewReader(info))
386
387 firstLine := firstNonEmptyLine(scanner)
388 if !strings.HasPrefix(firstLine, "system type") || !strings.Contains(firstLine, ":") {
389 return nil, errors.New("invalid cpuinfo file: " + firstLine)
390 }
391 field := strings.SplitN(firstLine, ": ", 2)
392 cpuinfo := []CPUInfo{}
393 systemType := field[1]
394 i := 0
395 for scanner.Scan() {
396 line := scanner.Text()
397 if !strings.Contains(line, ":") {
398 continue
399 }
400 field := strings.SplitN(line, ": ", 2)
401 switch strings.TrimSpace(field[0]) {
402 case "processor":
403 v, err := strconv.ParseUint(field[1], 0, 32)
404 if err != nil {
405 return nil, err
406 }
407 i = int(v)
408 cpuinfo = append(cpuinfo, CPUInfo{})
409 cpuinfo[i].Processor = uint(v)
410 cpuinfo[i].VendorID = systemType
411 case "CPU Family":
412 cpuinfo[i].CPUFamily = field[1]
413 case "Model Name":
414 cpuinfo[i].ModelName = field[1]
415 }
416 }
417 return cpuinfo, nil
418 }
419
420 func parseCPUInfoPPC(info []byte) ([]CPUInfo, error) {
421 scanner := bufio.NewScanner(bytes.NewReader(info))
422
423 firstLine := firstNonEmptyLine(scanner)
424 if !strings.HasPrefix(firstLine, "processor") || !strings.Contains(firstLine, ":") {
425 return nil, fmt.Errorf("%w: %q", ErrFileParse, firstLine)
426 }
427 field := strings.SplitN(firstLine, ": ", 2)
428 v, err := strconv.ParseUint(field[1], 0, 32)
429 if err != nil {
430 return nil, err
431 }
432 firstcpu := CPUInfo{Processor: uint(v)}
433 cpuinfo := []CPUInfo{firstcpu}
434 i := 0
435
436 for scanner.Scan() {
437 line := scanner.Text()
438 if !strings.Contains(line, ":") {
439 continue
440 }
441 field := strings.SplitN(line, ": ", 2)
442 switch strings.TrimSpace(field[0]) {
443 case "processor":
444 cpuinfo = append(cpuinfo, CPUInfo{})
445 i++
446 v, err := strconv.ParseUint(field[1], 0, 32)
447 if err != nil {
448 return nil, err
449 }
450 cpuinfo[i].Processor = uint(v)
451 case "cpu":
452 cpuinfo[i].VendorID = field[1]
453 case "clock":
454 clock := cpuinfoClockRegexp.FindString(strings.TrimSpace(field[1]))
455 v, err := strconv.ParseFloat(clock, 64)
456 if err != nil {
457 return nil, err
458 }
459 cpuinfo[i].CPUMHz = v
460 }
461 }
462 return cpuinfo, nil
463 }
464
465 func parseCPUInfoRISCV(info []byte) ([]CPUInfo, error) {
466 scanner := bufio.NewScanner(bytes.NewReader(info))
467
468 firstLine := firstNonEmptyLine(scanner)
469 if !strings.HasPrefix(firstLine, "processor") || !strings.Contains(firstLine, ":") {
470 return nil, fmt.Errorf("%w: %q", ErrFileParse, firstLine)
471 }
472 field := strings.SplitN(firstLine, ": ", 2)
473 v, err := strconv.ParseUint(field[1], 0, 32)
474 if err != nil {
475 return nil, err
476 }
477 firstcpu := CPUInfo{Processor: uint(v)}
478 cpuinfo := []CPUInfo{firstcpu}
479 i := 0
480
481 for scanner.Scan() {
482 line := scanner.Text()
483 if !strings.Contains(line, ":") {
484 continue
485 }
486 field := strings.SplitN(line, ": ", 2)
487 switch strings.TrimSpace(field[0]) {
488 case "processor":
489 v, err := strconv.ParseUint(field[1], 0, 32)
490 if err != nil {
491 return nil, err
492 }
493 i = int(v)
494 cpuinfo = append(cpuinfo, CPUInfo{})
495 cpuinfo[i].Processor = uint(v)
496 case "hart":
497 cpuinfo[i].CoreID = field[1]
498 case "isa":
499 cpuinfo[i].ModelName = field[1]
500 }
501 }
502 return cpuinfo, nil
503 }
504
505 func parseCPUInfoDummy(_ []byte) ([]CPUInfo, error) {
506 return nil, errors.New("not implemented")
507 }
508
509
510
511 func firstNonEmptyLine(scanner *bufio.Scanner) string {
512 for scanner.Scan() {
513 line := scanner.Text()
514 if strings.TrimSpace(line) != "" {
515 return line
516 }
517 }
518 return ""
519 }
520
View as plain text