// Collect reads cpu.stat for cgroups and per process cpu.stat // entries for all processes in the cgroup func (s *PerCgroupStat) Collect() { file, err := os.Open(s.path + "/" + "cpu.stat") defer file.Close() if err != nil { return } scanner := bufio.NewScanner(file) for scanner.Scan() { f := regexp.MustCompile("\\s+").Split(scanner.Text(), 2) if f[0] == "nr_periods" { s.NrPeriods.Set(misc.ParseUint(f[1])) } if f[0] == "nr_throttled" { s.NrThrottled.Set(misc.ParseUint(f[1])) } if f[0] == "throttled_time" { s.ThrottledTime.Set(misc.ParseUint(f[1])) } } s.CfsPeriodUs.Set( float64(misc.ReadUintFromFile( s.path + "/" + "cpu.cfs_period_us"))) // negative values of quota indicate no quota. // it is not possible for this variable to be zero s.CfsQuotaUs.Set( float64(misc.ReadUintFromFile( s.path + "/" + "cpu.cfs_quota_us"))) // Calculate approximate cumulative CPU usage for all // processes within this cgroup by calculating difference // between sum number of ticks. // We reset between loops because PIDs within cgroup can // change and sum-counter from previous run can be // unreliable s.getCgroupCPUTimes() time.Sleep(time.Millisecond * 1000) s.getCgroupCPUTimes() // Expose summary metrics for easy json access s.UsageCount.Set(s.usage()) s.UserspaceCount.Set(s.userspace()) s.KernelCount.Set(s.kernel()) s.TotalCount.Set(s.Quota()) s.ThrottleCount.Set(s.Throttle()) // Reset counters s.Utime.Reset() s.Stime.Reset() }
// Unexported functions func parseCPUline(s *PerCPU, f []string) { s.User.Set(misc.ParseUint(f[1])) s.UserLowPrio.Set(misc.ParseUint(f[2])) s.System.Set(misc.ParseUint(f[3])) s.Idle.Set(misc.ParseUint(f[4])) s.Iowait.Set(misc.ParseUint(f[5])) s.Irq.Set(misc.ParseUint(f[6])) s.Softirq.Set(misc.ParseUint(f[7])) s.Steal.Set(misc.ParseUint(f[8])) s.Guest.Set(misc.ParseUint(f[9])) s.Total.Set(s.User.Get() + s.UserLowPrio.Get() + s.System.Get() + s.Idle.Get()) }
func getCPUTimes(pid string) (uint64, uint64) { file, err := os.Open("/proc/" + pid + "/stat") defer file.Close() if err != nil { return 0, 0 } var user, system uint64 scanner := bufio.NewScanner(file) for scanner.Scan() { f := strings.Split(scanner.Text(), " ") user = misc.ParseUint(f[13]) system = misc.ParseUint(f[14]) } return user, system }
// Collect collects per process CPU/Memory/IO metrics func (s *PerProcessStatMetrics) Collect() { file, err := os.Open(root + "proc/" + s.Pid + "/stat") defer file.Close() if err != nil { return } scanner := bufio.NewScanner(file) for scanner.Scan() { f := strings.Split(scanner.Text(), " ") s.Utime.Set(misc.ParseUint(f[13])) s.Stime.Set(misc.ParseUint(f[14])) s.Rss.Set(float64(misc.ParseUint(f[23]))) } // collect IO metrics // only works if we are superuser on Linux file, err = os.Open(root + "proc/" + s.Pid + "/io") defer file.Close() if err != nil { return } scanner = bufio.NewScanner(file) for scanner.Scan() { f := strings.Split(scanner.Text(), " ") switch f[0] { case "read_bytes:": s.IOReadBytes.Set(misc.ParseUint(f[1])) case "write_bytes:": s.IOWriteBytes.Set(misc.ParseUint(f[1])) } } }
// Unexported functions func parseCgroupMemLine(g *metrics.Gauge, f []string) { length := len(f) val := math.NaN() if length < 2 { goto fail } val = float64(misc.ParseUint(f[1])) g.Set(val) return fail: g.Set(math.NaN()) return }
// Unexported functions func parseMemLine(g *metrics.Gauge, f []string) { length := len(f) val := math.NaN() if length < 2 { goto fail } val = float64(misc.ParseUint(f[1])) if length > 2 && f[2] == "kB" { val *= 1024 } g.Set(val) return fail: g.Set(math.NaN()) return }