1// +build linux
2
3package cpu
4
5import (
6	"context"
7	"errors"
8	"fmt"
9	"os/exec"
10	"strconv"
11	"strings"
12
13	"github.com/shirou/gopsutil/internal/common"
14)
15
16var cpu_tick = float64(100)
17
18func init() {
19	getconf, err := exec.LookPath("/usr/bin/getconf")
20	if err != nil {
21		return
22	}
23	out, err := invoke.CommandWithContext(context.Background(), getconf, "CLK_TCK")
24	// ignore errors
25	if err == nil {
26		i, err := strconv.ParseFloat(strings.TrimSpace(string(out)), 64)
27		if err == nil {
28			cpu_tick = float64(i)
29		}
30	}
31}
32
33func Times(percpu bool) ([]TimesStat, error) {
34	return TimesWithContext(context.Background(), percpu)
35}
36
37func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) {
38	filename := common.HostProc("stat")
39	var lines = []string{}
40	if percpu {
41		statlines, err := common.ReadLines(filename)
42		if err != nil || len(statlines) < 2 {
43			return []TimesStat{}, nil
44		}
45		for _, line := range statlines[1:] {
46			if !strings.HasPrefix(line, "cpu") {
47				break
48			}
49			lines = append(lines, line)
50		}
51	} else {
52		lines, _ = common.ReadLinesOffsetN(filename, 0, 1)
53	}
54
55	ret := make([]TimesStat, 0, len(lines))
56
57	for _, line := range lines {
58		ct, err := parseStatLine(line)
59		if err != nil {
60			continue
61		}
62		ret = append(ret, *ct)
63
64	}
65	return ret, nil
66}
67
68func sysCPUPath(cpu int32, relPath string) string {
69	return common.HostSys(fmt.Sprintf("devices/system/cpu/cpu%d", cpu), relPath)
70}
71
72func finishCPUInfo(c *InfoStat) error {
73	var lines []string
74	var err error
75	var value float64
76
77	if len(c.CoreID) == 0 {
78		lines, err = common.ReadLines(sysCPUPath(c.CPU, "topology/core_id"))
79		if err == nil {
80			c.CoreID = lines[0]
81		}
82	}
83
84	// override the value of c.Mhz with cpufreq/cpuinfo_max_freq regardless
85	// of the value from /proc/cpuinfo because we want to report the maximum
86	// clock-speed of the CPU for c.Mhz, matching the behaviour of Windows
87	lines, err = common.ReadLines(sysCPUPath(c.CPU, "cpufreq/cpuinfo_max_freq"))
88	// if we encounter errors below such as there are no cpuinfo_max_freq file,
89	// we just ignore. so let Mhz is 0.
90	if err != nil {
91		return nil
92	}
93	value, err = strconv.ParseFloat(lines[0], 64)
94	if err != nil {
95		return nil
96	}
97	c.Mhz = value / 1000.0 // value is in kHz
98	if c.Mhz > 9999 {
99		c.Mhz = c.Mhz / 1000.0 // value in Hz
100	}
101	return nil
102}
103
104// CPUInfo on linux will return 1 item per physical thread.
105//
106// CPUs have three levels of counting: sockets, cores, threads.
107// Cores with HyperThreading count as having 2 threads per core.
108// Sockets often come with many physical CPU cores.
109// For example a single socket board with two cores each with HT will
110// return 4 CPUInfoStat structs on Linux and the "Cores" field set to 1.
111func Info() ([]InfoStat, error) {
112	return InfoWithContext(context.Background())
113}
114
115func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
116	filename := common.HostProc("cpuinfo")
117	lines, _ := common.ReadLines(filename)
118
119	var ret []InfoStat
120	var processorName string
121
122	c := InfoStat{CPU: -1, Cores: 1}
123	for _, line := range lines {
124		fields := strings.Split(line, ":")
125		if len(fields) < 2 {
126			continue
127		}
128		key := strings.TrimSpace(fields[0])
129		value := strings.TrimSpace(fields[1])
130
131		switch key {
132		case "Processor":
133			processorName = value
134		case "processor":
135			if c.CPU >= 0 {
136				err := finishCPUInfo(&c)
137				if err != nil {
138					return ret, err
139				}
140				ret = append(ret, c)
141			}
142			c = InfoStat{Cores: 1, ModelName: processorName}
143			t, err := strconv.ParseInt(value, 10, 64)
144			if err != nil {
145				return ret, err
146			}
147			c.CPU = int32(t)
148		case "vendorId", "vendor_id":
149			c.VendorID = value
150		case "cpu family":
151			c.Family = value
152		case "model":
153			c.Model = value
154		case "model name", "cpu":
155			c.ModelName = value
156			if strings.Contains(value, "POWER8") ||
157				strings.Contains(value, "POWER7") {
158				c.Model = strings.Split(value, " ")[0]
159				c.Family = "POWER"
160				c.VendorID = "IBM"
161			}
162		case "stepping", "revision":
163			val := value
164
165			if key == "revision" {
166				val = strings.Split(value, ".")[0]
167			}
168
169			t, err := strconv.ParseInt(val, 10, 64)
170			if err != nil {
171				return ret, err
172			}
173			c.Stepping = int32(t)
174		case "cpu MHz", "clock":
175			// treat this as the fallback value, thus we ignore error
176			if t, err := strconv.ParseFloat(strings.Replace(value, "MHz", "", 1), 64); err == nil {
177				c.Mhz = t
178			}
179		case "cache size":
180			t, err := strconv.ParseInt(strings.Replace(value, " KB", "", 1), 10, 64)
181			if err != nil {
182				return ret, err
183			}
184			c.CacheSize = int32(t)
185		case "physical id":
186			c.PhysicalID = value
187		case "core id":
188			c.CoreID = value
189		case "flags", "Features":
190			c.Flags = strings.FieldsFunc(value, func(r rune) bool {
191				return r == ',' || r == ' '
192			})
193		case "microcode":
194			c.Microcode = value
195		}
196	}
197	if c.CPU >= 0 {
198		err := finishCPUInfo(&c)
199		if err != nil {
200			return ret, err
201		}
202		ret = append(ret, c)
203	}
204	return ret, nil
205}
206
207func parseStatLine(line string) (*TimesStat, error) {
208	fields := strings.Fields(line)
209
210	if len(fields) == 0 {
211		return nil, errors.New("stat does not contain cpu info")
212	}
213
214	if strings.HasPrefix(fields[0], "cpu") == false {
215		//		return CPUTimesStat{}, e
216		return nil, errors.New("not contain cpu")
217	}
218
219	cpu := fields[0]
220	if cpu == "cpu" {
221		cpu = "cpu-total"
222	}
223	user, err := strconv.ParseFloat(fields[1], 64)
224	if err != nil {
225		return nil, err
226	}
227	nice, err := strconv.ParseFloat(fields[2], 64)
228	if err != nil {
229		return nil, err
230	}
231	system, err := strconv.ParseFloat(fields[3], 64)
232	if err != nil {
233		return nil, err
234	}
235	idle, err := strconv.ParseFloat(fields[4], 64)
236	if err != nil {
237		return nil, err
238	}
239	iowait, err := strconv.ParseFloat(fields[5], 64)
240	if err != nil {
241		return nil, err
242	}
243	irq, err := strconv.ParseFloat(fields[6], 64)
244	if err != nil {
245		return nil, err
246	}
247	softirq, err := strconv.ParseFloat(fields[7], 64)
248	if err != nil {
249		return nil, err
250	}
251
252	ct := &TimesStat{
253		CPU:     cpu,
254		User:    float64(user) / cpu_tick,
255		Nice:    float64(nice) / cpu_tick,
256		System:  float64(system) / cpu_tick,
257		Idle:    float64(idle) / cpu_tick,
258		Iowait:  float64(iowait) / cpu_tick,
259		Irq:     float64(irq) / cpu_tick,
260		Softirq: float64(softirq) / cpu_tick,
261	}
262	if len(fields) > 8 { // Linux >= 2.6.11
263		steal, err := strconv.ParseFloat(fields[8], 64)
264		if err != nil {
265			return nil, err
266		}
267		ct.Steal = float64(steal) / cpu_tick
268	}
269	if len(fields) > 9 { // Linux >= 2.6.24
270		guest, err := strconv.ParseFloat(fields[9], 64)
271		if err != nil {
272			return nil, err
273		}
274		ct.Guest = float64(guest) / cpu_tick
275	}
276	if len(fields) > 10 { // Linux >= 3.2.0
277		guestNice, err := strconv.ParseFloat(fields[10], 64)
278		if err != nil {
279			return nil, err
280		}
281		ct.GuestNice = float64(guestNice) / cpu_tick
282	}
283
284	return ct, nil
285}
286