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