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
14package procfs
15
16import (
17	"bufio"
18	"bytes"
19	"io/ioutil"
20	"strconv"
21	"strings"
22)
23
24// CPUInfo contains general information about a system CPU found in /proc/cpuinfo
25type CPUInfo struct {
26	Processor       uint
27	VendorID        string
28	CPUFamily       string
29	Model           string
30	ModelName       string
31	Stepping        string
32	Microcode       string
33	CPUMHz          float64
34	CacheSize       string
35	PhysicalID      string
36	Siblings        uint
37	CoreID          string
38	CPUCores        uint
39	APICID          string
40	InitialAPICID   string
41	FPU             string
42	FPUException    string
43	CPUIDLevel      uint
44	WP              string
45	Flags           []string
46	Bugs            []string
47	BogoMips        float64
48	CLFlushSize     uint
49	CacheAlignment  uint
50	AddressSizes    string
51	PowerManagement string
52}
53
54// CPUInfo returns information about current system CPUs.
55// See https://www.kernel.org/doc/Documentation/filesystems/proc.txt
56func (fs FS) CPUInfo() ([]CPUInfo, error) {
57	data, err := ioutil.ReadFile(fs.proc.Path("cpuinfo"))
58	if err != nil {
59		return nil, err
60	}
61	return parseCPUInfo(data)
62}
63
64// parseCPUInfo parses data from /proc/cpuinfo
65func parseCPUInfo(info []byte) ([]CPUInfo, error) {
66	cpuinfo := []CPUInfo{}
67	i := -1
68	scanner := bufio.NewScanner(bytes.NewReader(info))
69	for scanner.Scan() {
70		line := scanner.Text()
71		if strings.TrimSpace(line) == "" {
72			continue
73		}
74		field := strings.SplitN(line, ": ", 2)
75		switch strings.TrimSpace(field[0]) {
76		case "processor":
77			cpuinfo = append(cpuinfo, CPUInfo{}) // start of the next processor
78			i++
79			v, err := strconv.ParseUint(field[1], 0, 32)
80			if err != nil {
81				return nil, err
82			}
83			cpuinfo[i].Processor = uint(v)
84		case "vendor_id":
85			cpuinfo[i].VendorID = field[1]
86		case "cpu family":
87			cpuinfo[i].CPUFamily = field[1]
88		case "model":
89			cpuinfo[i].Model = field[1]
90		case "model name":
91			cpuinfo[i].ModelName = field[1]
92		case "stepping":
93			cpuinfo[i].Stepping = field[1]
94		case "microcode":
95			cpuinfo[i].Microcode = field[1]
96		case "cpu MHz":
97			v, err := strconv.ParseFloat(field[1], 64)
98			if err != nil {
99				return nil, err
100			}
101			cpuinfo[i].CPUMHz = v
102		case "cache size":
103			cpuinfo[i].CacheSize = field[1]
104		case "physical id":
105			cpuinfo[i].PhysicalID = field[1]
106		case "siblings":
107			v, err := strconv.ParseUint(field[1], 0, 32)
108			if err != nil {
109				return nil, err
110			}
111			cpuinfo[i].Siblings = uint(v)
112		case "core id":
113			cpuinfo[i].CoreID = field[1]
114		case "cpu cores":
115			v, err := strconv.ParseUint(field[1], 0, 32)
116			if err != nil {
117				return nil, err
118			}
119			cpuinfo[i].CPUCores = uint(v)
120		case "apicid":
121			cpuinfo[i].APICID = field[1]
122		case "initial apicid":
123			cpuinfo[i].InitialAPICID = field[1]
124		case "fpu":
125			cpuinfo[i].FPU = field[1]
126		case "fpu_exception":
127			cpuinfo[i].FPUException = field[1]
128		case "cpuid level":
129			v, err := strconv.ParseUint(field[1], 0, 32)
130			if err != nil {
131				return nil, err
132			}
133			cpuinfo[i].CPUIDLevel = uint(v)
134		case "wp":
135			cpuinfo[i].WP = field[1]
136		case "flags":
137			cpuinfo[i].Flags = strings.Fields(field[1])
138		case "bugs":
139			cpuinfo[i].Bugs = strings.Fields(field[1])
140		case "bogomips":
141			v, err := strconv.ParseFloat(field[1], 64)
142			if err != nil {
143				return nil, err
144			}
145			cpuinfo[i].BogoMips = v
146		case "clflush size":
147			v, err := strconv.ParseUint(field[1], 0, 32)
148			if err != nil {
149				return nil, err
150			}
151			cpuinfo[i].CLFlushSize = uint(v)
152		case "cache_alignment":
153			v, err := strconv.ParseUint(field[1], 0, 32)
154			if err != nil {
155				return nil, err
156			}
157			cpuinfo[i].CacheAlignment = uint(v)
158		case "address sizes":
159			cpuinfo[i].AddressSizes = field[1]
160		case "power management":
161			cpuinfo[i].PowerManagement = field[1]
162		}
163	}
164	return cpuinfo, nil
165
166}
167