+ // cgroups v1 entries look like
+ // "6:cpu,cpuacct:/user.slice"
+ fields := bytes.SplitN(line, []byte{':'}, 3)
+ if len(fields) != 3 {
+ continue
+ }
+ for _, key := range bytes.Split(fields[1], []byte{','}) {
+ key := string(key)
+ if mounts[key] != "" {
+ paths[key] = mounts[key] + string(fields[2])
+ }
+ }
+ }
+ // In unified mode, /proc/$PID/cgroup doesn't have a cpuset
+ // entry, but we still need it -- there's no cpuset.cpus file
+ // in the cgroup2 subtree indicated by the 0::$PATH entry. We
+ // have to get the right path from /proc/$PID/cpuset.
+ if _, found := paths["cpuset"]; !found && mounts["unified"] != "" {
+ buf, _ := fs.ReadFile(r.FS, procdir+"/cpuset")
+ cpusetPath := string(bytes.TrimRight(buf, "\n"))
+ paths["cpuset"] = mounts["unified"] + cpusetPath
+ }
+ return paths
+}
+
+func (r *Reporter) findStatFiles() {
+ mounts := r.cgroupMounts()
+ paths := r.cgroupPaths(mounts)
+ done := map[*string]bool{}
+ for _, try := range []struct {
+ statFile *string
+ pathkey string
+ file string
+ }{
+ {&r.statFiles.cpuMax, "unified", "cpu.max"},
+ {&r.statFiles.cpusetCpus, "cpuset", "cpuset.cpus.effective"},
+ {&r.statFiles.cpusetCpus, "cpuset", "cpuset.cpus"},
+ {&r.statFiles.cpuacctStat, "cpuacct", "cpuacct.stat"},
+ {&r.statFiles.cpuStat, "unified", "cpu.stat"},
+ // blkio.throttle.io_service_bytes must precede
+ // blkio.io_service_bytes -- on ubuntu1804, the latter
+ // is present but reports 0
+ {&r.statFiles.ioServiceBytes, "blkio", "blkio.throttle.io_service_bytes"},
+ {&r.statFiles.ioServiceBytes, "blkio", "blkio.io_service_bytes"},
+ {&r.statFiles.ioStat, "unified", "io.stat"},
+ {&r.statFiles.memoryStat, "unified", "memory.stat"},
+ {&r.statFiles.memoryStat, "memory", "memory.stat"},
+ {&r.statFiles.memoryCurrent, "unified", "memory.current"},
+ {&r.statFiles.memorySwapCurrent, "unified", "memory.swap.current"},
+ } {
+ startpath, ok := paths[try.pathkey]
+ if !ok || done[try.statFile] {
+ continue
+ }
+ // /proc/$PID/cgroup says cgroup path is
+ // /exa/mple/exa/mple, however, sometimes the file we
+ // need is not under that path, it's only available in
+ // a parent cgroup's dir. So we start at
+ // /sys/fs/cgroup/unified/exa/mple/exa/mple/ and walk
+ // up to /sys/fs/cgroup/unified/ until we find the
+ // desired file.
+ //
+ // This might mean our reported stats include more
+ // cgroups in the cgroup tree, but it's the best we
+ // can do.
+ for path := startpath; path != "" && path != "/" && (path == startpath || strings.HasPrefix(path, mounts[try.pathkey])); path, _ = filepath.Split(strings.TrimRight(path, "/")) {
+ target := strings.TrimLeft(filepath.Join(path, try.file), "/")
+ buf, err := fs.ReadFile(r.FS, target)
+ if err != nil || len(buf) == 0 || bytes.Equal(buf, []byte{'\n'}) {
+ if r.Debug {
+ if os.IsNotExist(err) {
+ // don't stutter
+ err = os.ErrNotExist
+ }
+ r.Logger.Printf("skip /%s: %s", target, err)
+ }
+ continue
+ }
+ *try.statFile = target
+ done[try.statFile] = true
+ r.Logger.Printf("notice: reading stats from /%s", target)