// +build linux package cpu import ( "bufio" "fmt" "os" "strconv" "strings" "github.com/pkg/errors" ) const nanoSecondsPerSecond = 1e9 // ErrNoCFSLimit is no quota limit var ErrNoCFSLimit = errors.Errorf("no quota limit") var clockTicksPerSecond = uint64(GetClockTicks()) // systemCPUUsage returns the host system's cpu usage in // nanoseconds. An error is returned if the format of the underlying // file does not match. // // Uses /proc/stat defined by POSIX. Looks for the cpu // statistics line and then sums up the first seven fields // provided. See man 5 proc for details on specific field // information. func systemCPUUsage() (usage uint64, err error) { var ( line string f *os.File ) if f, err = os.Open("/proc/stat"); err != nil { return } bufReader := bufio.NewReaderSize(nil, 128) defer func() { bufReader.Reset(nil) f.Close() }() bufReader.Reset(f) for err == nil { if line, err = bufReader.ReadString('\n'); err != nil { err = errors.WithStack(err) return } parts := strings.Fields(line) switch parts[0] { case "cpu": if len(parts) < 8 { err = errors.WithStack(fmt.Errorf("bad format of cpu stats")) return } var totalClockTicks uint64 for _, i := range parts[1:8] { var v uint64 if v, err = strconv.ParseUint(i, 10, 64); err != nil { err = errors.WithStack(fmt.Errorf("error parsing cpu stats")) return } totalClockTicks += v } usage = (totalClockTicks * nanoSecondsPerSecond) / clockTicksPerSecond return } } err = errors.Errorf("bad stats format") return } func totalCPUUsage() (usage uint64, err error) { var cg *cgroup if cg, err = currentcGroup(); err != nil { return } return cg.CPUAcctUsage() } func perCPUUsage() (usage []uint64, err error) { var cg *cgroup if cg, err = currentcGroup(); err != nil { return } return cg.CPUAcctUsagePerCPU() } func cpuSets() (sets []uint64, err error) { var cg *cgroup if cg, err = currentcGroup(); err != nil { return } return cg.CPUSetCPUs() } func cpuQuota() (quota int64, err error) { var cg *cgroup if cg, err = currentcGroup(); err != nil { return } return cg.CPUCFSQuotaUs() } func cpuPeriod() (peroid uint64, err error) { var cg *cgroup if cg, err = currentcGroup(); err != nil { return } return cg.CPUCFSPeriodUs() } func cpuFreq() uint64 { lines, err := readLines("/proc/cpuinfo") if err != nil { return 0 } for _, line := range lines { fields := strings.Split(line, ":") if len(fields) < 2 { continue } key := strings.TrimSpace(fields[0]) value := strings.TrimSpace(fields[1]) if key == "cpu MHz" || key == "clock" { // treat this as the fallback value, thus we ignore error if t, err := strconv.ParseFloat(strings.Replace(value, "MHz", "", 1), 64); err == nil { return uint64(t * 1000.0 * 1000.0) } } } return 0 } func cpuMaxFreq() uint64 { feq := cpuFreq() data, err := readFile("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq") if err != nil { return feq } // override the max freq from /proc/cpuinfo cfeq, err := parseUint(data) if err == nil { feq = cfeq } return feq }