source: code/trunk/vendor/github.com/prometheus/procfs/cpuinfo.go@ 822

Last change on this file since 822 was 822, checked in by yakumo.izuru, 22 months ago

Prefer immortal.run over runit and rc.d, use vendored modules
for convenience.

Signed-off-by: Izuru Yakumo <yakumo.izuru@…>

File size: 12.2 KB
Line 
1// Copyright 2019 The Prometheus Authors
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14//go:build linux
15// +build linux
16
17package procfs
18
19import (
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// CPUInfo contains general information about a system CPU found in /proc/cpuinfo.
32type 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
61var (
62 cpuinfoClockRegexp = regexp.MustCompile(`([\d.]+)`)
63 cpuinfoS390XProcessorRegexp = regexp.MustCompile(`^processor\s+(\d+):.*`)
64)
65
66// CPUInfo returns information about current system CPUs.
67// See https://www.kernel.org/doc/Documentation/filesystems/proc.txt
68func (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
76func parseCPUInfoX86(info []byte) ([]CPUInfo, error) {
77 scanner := bufio.NewScanner(bytes.NewReader(info))
78
79 // find the first "processor" line
80 firstLine := firstNonEmptyLine(scanner)
81 if !strings.HasPrefix(firstLine, "processor") || !strings.Contains(firstLine, ":") {
82 return nil, fmt.Errorf("invalid cpuinfo file: %q", 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{}) // start of the next processor
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
191func parseCPUInfoARM(info []byte) ([]CPUInfo, error) {
192 scanner := bufio.NewScanner(bytes.NewReader(info))
193
194 firstLine := firstNonEmptyLine(scanner)
195 match, _ := regexp.MatchString("^[Pp]rocessor", firstLine)
196 if !match || !strings.Contains(firstLine, ":") {
197 return nil, fmt.Errorf("invalid cpuinfo file: %q", firstLine)
198 }
199 field := strings.SplitN(firstLine, ": ", 2)
200 cpuinfo := []CPUInfo{}
201 featuresLine := ""
202 commonCPUInfo := CPUInfo{}
203 i := 0
204 if strings.TrimSpace(field[0]) == "Processor" {
205 commonCPUInfo = CPUInfo{ModelName: field[1]}
206 i = -1
207 } else {
208 v, err := strconv.ParseUint(field[1], 0, 32)
209 if err != nil {
210 return nil, err
211 }
212 firstcpu := CPUInfo{Processor: uint(v)}
213 cpuinfo = []CPUInfo{firstcpu}
214 }
215
216 for scanner.Scan() {
217 line := scanner.Text()
218 if !strings.Contains(line, ":") {
219 continue
220 }
221 field := strings.SplitN(line, ": ", 2)
222 switch strings.TrimSpace(field[0]) {
223 case "processor":
224 cpuinfo = append(cpuinfo, commonCPUInfo) // start of the next processor
225 i++
226 v, err := strconv.ParseUint(field[1], 0, 32)
227 if err != nil {
228 return nil, err
229 }
230 cpuinfo[i].Processor = uint(v)
231 case "BogoMIPS":
232 if i == -1 {
233 cpuinfo = append(cpuinfo, commonCPUInfo) // There is only one processor
234 i++
235 cpuinfo[i].Processor = 0
236 }
237 v, err := strconv.ParseFloat(field[1], 64)
238 if err != nil {
239 return nil, err
240 }
241 cpuinfo[i].BogoMips = v
242 case "Features":
243 featuresLine = line
244 case "model name":
245 cpuinfo[i].ModelName = field[1]
246 }
247 }
248 fields := strings.SplitN(featuresLine, ": ", 2)
249 for i := range cpuinfo {
250 cpuinfo[i].Flags = strings.Fields(fields[1])
251 }
252 return cpuinfo, nil
253
254}
255
256func parseCPUInfoS390X(info []byte) ([]CPUInfo, error) {
257 scanner := bufio.NewScanner(bytes.NewReader(info))
258
259 firstLine := firstNonEmptyLine(scanner)
260 if !strings.HasPrefix(firstLine, "vendor_id") || !strings.Contains(firstLine, ":") {
261 return nil, fmt.Errorf("invalid cpuinfo file: %q", firstLine)
262 }
263 field := strings.SplitN(firstLine, ": ", 2)
264 cpuinfo := []CPUInfo{}
265 commonCPUInfo := CPUInfo{VendorID: field[1]}
266
267 for scanner.Scan() {
268 line := scanner.Text()
269 if !strings.Contains(line, ":") {
270 continue
271 }
272 field := strings.SplitN(line, ": ", 2)
273 switch strings.TrimSpace(field[0]) {
274 case "bogomips per cpu":
275 v, err := strconv.ParseFloat(field[1], 64)
276 if err != nil {
277 return nil, err
278 }
279 commonCPUInfo.BogoMips = v
280 case "features":
281 commonCPUInfo.Flags = strings.Fields(field[1])
282 }
283 if strings.HasPrefix(line, "processor") {
284 match := cpuinfoS390XProcessorRegexp.FindStringSubmatch(line)
285 if len(match) < 2 {
286 return nil, fmt.Errorf("invalid cpuinfo file: %q", firstLine)
287 }
288 cpu := commonCPUInfo
289 v, err := strconv.ParseUint(match[1], 0, 32)
290 if err != nil {
291 return nil, err
292 }
293 cpu.Processor = uint(v)
294 cpuinfo = append(cpuinfo, cpu)
295 }
296 if strings.HasPrefix(line, "cpu number") {
297 break
298 }
299 }
300
301 i := 0
302 for scanner.Scan() {
303 line := scanner.Text()
304 if !strings.Contains(line, ":") {
305 continue
306 }
307 field := strings.SplitN(line, ": ", 2)
308 switch strings.TrimSpace(field[0]) {
309 case "cpu number":
310 i++
311 case "cpu MHz dynamic":
312 clock := cpuinfoClockRegexp.FindString(strings.TrimSpace(field[1]))
313 v, err := strconv.ParseFloat(clock, 64)
314 if err != nil {
315 return nil, err
316 }
317 cpuinfo[i].CPUMHz = v
318 case "physical id":
319 cpuinfo[i].PhysicalID = field[1]
320 case "core id":
321 cpuinfo[i].CoreID = field[1]
322 case "cpu cores":
323 v, err := strconv.ParseUint(field[1], 0, 32)
324 if err != nil {
325 return nil, err
326 }
327 cpuinfo[i].CPUCores = uint(v)
328 case "siblings":
329 v, err := strconv.ParseUint(field[1], 0, 32)
330 if err != nil {
331 return nil, err
332 }
333 cpuinfo[i].Siblings = uint(v)
334 }
335 }
336
337 return cpuinfo, nil
338}
339
340func parseCPUInfoMips(info []byte) ([]CPUInfo, error) {
341 scanner := bufio.NewScanner(bytes.NewReader(info))
342
343 // find the first "processor" line
344 firstLine := firstNonEmptyLine(scanner)
345 if !strings.HasPrefix(firstLine, "system type") || !strings.Contains(firstLine, ":") {
346 return nil, fmt.Errorf("invalid cpuinfo file: %q", firstLine)
347 }
348 field := strings.SplitN(firstLine, ": ", 2)
349 cpuinfo := []CPUInfo{}
350 systemType := field[1]
351
352 i := 0
353
354 for scanner.Scan() {
355 line := scanner.Text()
356 if !strings.Contains(line, ":") {
357 continue
358 }
359 field := strings.SplitN(line, ": ", 2)
360 switch strings.TrimSpace(field[0]) {
361 case "processor":
362 v, err := strconv.ParseUint(field[1], 0, 32)
363 if err != nil {
364 return nil, err
365 }
366 i = int(v)
367 cpuinfo = append(cpuinfo, CPUInfo{}) // start of the next processor
368 cpuinfo[i].Processor = uint(v)
369 cpuinfo[i].VendorID = systemType
370 case "cpu model":
371 cpuinfo[i].ModelName = field[1]
372 case "BogoMIPS":
373 v, err := strconv.ParseFloat(field[1], 64)
374 if err != nil {
375 return nil, err
376 }
377 cpuinfo[i].BogoMips = v
378 }
379 }
380 return cpuinfo, nil
381}
382
383func parseCPUInfoPPC(info []byte) ([]CPUInfo, error) {
384 scanner := bufio.NewScanner(bytes.NewReader(info))
385
386 firstLine := firstNonEmptyLine(scanner)
387 if !strings.HasPrefix(firstLine, "processor") || !strings.Contains(firstLine, ":") {
388 return nil, fmt.Errorf("invalid cpuinfo file: %q", firstLine)
389 }
390 field := strings.SplitN(firstLine, ": ", 2)
391 v, err := strconv.ParseUint(field[1], 0, 32)
392 if err != nil {
393 return nil, err
394 }
395 firstcpu := CPUInfo{Processor: uint(v)}
396 cpuinfo := []CPUInfo{firstcpu}
397 i := 0
398
399 for scanner.Scan() {
400 line := scanner.Text()
401 if !strings.Contains(line, ":") {
402 continue
403 }
404 field := strings.SplitN(line, ": ", 2)
405 switch strings.TrimSpace(field[0]) {
406 case "processor":
407 cpuinfo = append(cpuinfo, CPUInfo{}) // start of the next processor
408 i++
409 v, err := strconv.ParseUint(field[1], 0, 32)
410 if err != nil {
411 return nil, err
412 }
413 cpuinfo[i].Processor = uint(v)
414 case "cpu":
415 cpuinfo[i].VendorID = field[1]
416 case "clock":
417 clock := cpuinfoClockRegexp.FindString(strings.TrimSpace(field[1]))
418 v, err := strconv.ParseFloat(clock, 64)
419 if err != nil {
420 return nil, err
421 }
422 cpuinfo[i].CPUMHz = v
423 }
424 }
425 return cpuinfo, nil
426}
427
428func parseCPUInfoRISCV(info []byte) ([]CPUInfo, error) {
429 scanner := bufio.NewScanner(bytes.NewReader(info))
430
431 firstLine := firstNonEmptyLine(scanner)
432 if !strings.HasPrefix(firstLine, "processor") || !strings.Contains(firstLine, ":") {
433 return nil, fmt.Errorf("invalid cpuinfo file: %q", firstLine)
434 }
435 field := strings.SplitN(firstLine, ": ", 2)
436 v, err := strconv.ParseUint(field[1], 0, 32)
437 if err != nil {
438 return nil, err
439 }
440 firstcpu := CPUInfo{Processor: uint(v)}
441 cpuinfo := []CPUInfo{firstcpu}
442 i := 0
443
444 for scanner.Scan() {
445 line := scanner.Text()
446 if !strings.Contains(line, ":") {
447 continue
448 }
449 field := strings.SplitN(line, ": ", 2)
450 switch strings.TrimSpace(field[0]) {
451 case "processor":
452 v, err := strconv.ParseUint(field[1], 0, 32)
453 if err != nil {
454 return nil, err
455 }
456 i = int(v)
457 cpuinfo = append(cpuinfo, CPUInfo{}) // start of the next processor
458 cpuinfo[i].Processor = uint(v)
459 case "hart":
460 cpuinfo[i].CoreID = field[1]
461 case "isa":
462 cpuinfo[i].ModelName = field[1]
463 }
464 }
465 return cpuinfo, nil
466}
467
468func parseCPUInfoDummy(_ []byte) ([]CPUInfo, error) { // nolint:unused,deadcode
469 return nil, errors.New("not implemented")
470}
471
472// firstNonEmptyLine advances the scanner to the first non-empty line
473// and returns the contents of that line.
474func firstNonEmptyLine(scanner *bufio.Scanner) string {
475 for scanner.Scan() {
476 line := scanner.Text()
477 if strings.TrimSpace(line) != "" {
478 return line
479 }
480 }
481 return ""
482}
Note: See TracBrowser for help on using the repository browser.