github.com/elastic/gosigar@v0.14.3/cgroup/reader.go (about) 1 package cgroup 2 3 import ( 4 "path/filepath" 5 ) 6 7 // Stats contains metrics and limits from each of the cgroup subsystems. 8 type Stats struct { 9 Metadata 10 CPU *CPUSubsystem `json:"cpu"` 11 CPUAccounting *CPUAccountingSubsystem `json:"cpuacct"` 12 Memory *MemorySubsystem `json:"memory"` 13 BlockIO *BlockIOSubsystem `json:"blkio"` 14 } 15 16 // Metadata contains metadata associated with cgroup stats. 17 type Metadata struct { 18 ID string `json:"id,omitempty"` // ID of the cgroup. 19 Path string `json:"path,omitempty"` // Path to the cgroup relative to the cgroup subsystem's mountpoint. 20 } 21 22 type mount struct { 23 subsystem string // Subsystem name (e.g. cpuacct). 24 mountpoint string // Mountpoint of the subsystem (e.g. /cgroup/cpuacct). 25 path string // Relative path to the cgroup (e.g. /docker/<id>). 26 id string // ID of the cgroup. 27 fullPath string // Absolute path to the cgroup. It's the mountpoint joined with the path. 28 } 29 30 // Reader reads cgroup metrics and limits. 31 type Reader struct { 32 // Mountpoint of the root filesystem. Defaults to / if not set. This can be 33 // useful for example if you mount / as /rootfs inside of a container. 34 rootfsMountpoint string 35 ignoreRootCgroups bool // Ignore a cgroup when its path is "/". 36 cgroupsHierarchyOverride string 37 cgroupMountpoints map[string]string // Mountpoints for each subsystem (e.g. cpu, cpuacct, memory, blkio). 38 } 39 40 // ReaderOptions holds options for NewReaderOptions. 41 type ReaderOptions struct { 42 // RootfsMountpoint holds the mountpoint of the root filesystem. 43 // 44 // If unspecified, "/" is assumed. 45 RootfsMountpoint string 46 47 // IgnoreRootCgroups ignores cgroup subsystem with the path "/". 48 IgnoreRootCgroups bool 49 50 // CgroupsHierarchyOverride is an optional path override for cgroup 51 // subsystem paths. If non-empty, this will be used instead of the 52 // paths specified in /proc/<pid>/cgroup. 53 // 54 // This should be set to "/" when running within a Docker container, 55 // where the paths in /proc/<pid>/cgroup do not correspond to any 56 // paths under /sys/fs/cgroup. 57 CgroupsHierarchyOverride string 58 } 59 60 // NewReader creates and returns a new Reader. 61 func NewReader(rootfsMountpoint string, ignoreRootCgroups bool) (*Reader, error) { 62 return NewReaderOptions(ReaderOptions{ 63 RootfsMountpoint: rootfsMountpoint, 64 IgnoreRootCgroups: ignoreRootCgroups, 65 }) 66 } 67 68 // NewReaderOptions creates and returns a new Reader with the given options. 69 func NewReaderOptions(opts ReaderOptions) (*Reader, error) { 70 if opts.RootfsMountpoint == "" { 71 opts.RootfsMountpoint = "/" 72 } 73 74 // Determine what subsystems are supported by the kernel. 75 subsystems, err := SupportedSubsystems(opts.RootfsMountpoint) 76 if err != nil { 77 return nil, err 78 } 79 80 // Locate the mountpoints of those subsystems. 81 mountpoints, err := SubsystemMountpoints(opts.RootfsMountpoint, subsystems) 82 if err != nil { 83 return nil, err 84 } 85 86 return &Reader{ 87 rootfsMountpoint: opts.RootfsMountpoint, 88 ignoreRootCgroups: opts.IgnoreRootCgroups, 89 cgroupsHierarchyOverride: opts.CgroupsHierarchyOverride, 90 cgroupMountpoints: mountpoints, 91 }, nil 92 } 93 94 // GetStatsForProcess returns cgroup metrics and limits associated with a process. 95 func (r *Reader) GetStatsForProcess(pid int) (*Stats, error) { 96 // Read /proc/[pid]/cgroup to get the paths to the cgroup metrics. 97 paths, err := ProcessCgroupPaths(r.rootfsMountpoint, pid) 98 if err != nil { 99 return nil, err 100 } 101 102 // Build the full path for the subsystems we are interested in. 103 mounts := map[string]mount{} 104 for _, interestedSubsystem := range []string{"blkio", "cpu", "cpuacct", "memory"} { 105 path, found := paths[interestedSubsystem] 106 if !found { 107 continue 108 } 109 110 if path == "/" && r.ignoreRootCgroups { 111 continue 112 } 113 114 subsystemMount, found := r.cgroupMountpoints[interestedSubsystem] 115 if !found { 116 continue 117 } 118 119 id := filepath.Base(path) 120 if r.cgroupsHierarchyOverride != "" { 121 path = r.cgroupsHierarchyOverride 122 } 123 mounts[interestedSubsystem] = mount{ 124 subsystem: interestedSubsystem, 125 mountpoint: subsystemMount, 126 id: id, 127 path: path, 128 fullPath: filepath.Join(subsystemMount, path), 129 } 130 } 131 132 stats := Stats{Metadata: getCommonCgroupMetadata(mounts)} 133 134 // Collect stats from each cgroup subsystem associated with the task. 135 if mount, found := mounts["blkio"]; found { 136 stats.BlockIO = &BlockIOSubsystem{} 137 err := stats.BlockIO.get(mount.fullPath) 138 if err != nil { 139 return nil, err 140 } 141 stats.BlockIO.Metadata.ID = mount.id 142 stats.BlockIO.Metadata.Path = mount.path 143 } 144 if mount, found := mounts["cpu"]; found { 145 stats.CPU = &CPUSubsystem{} 146 err := stats.CPU.get(mount.fullPath) 147 if err != nil { 148 return nil, err 149 } 150 stats.CPU.Metadata.ID = mount.id 151 stats.CPU.Metadata.Path = mount.path 152 } 153 if mount, found := mounts["cpuacct"]; found { 154 stats.CPUAccounting = &CPUAccountingSubsystem{} 155 err := stats.CPUAccounting.get(mount.fullPath) 156 if err != nil { 157 return nil, err 158 } 159 stats.CPUAccounting.Metadata.ID = mount.id 160 stats.CPUAccounting.Metadata.Path = mount.path 161 } 162 if mount, found := mounts["memory"]; found { 163 stats.Memory = &MemorySubsystem{} 164 err := stats.Memory.get(mount.fullPath) 165 if err != nil { 166 return nil, err 167 } 168 stats.Memory.Metadata.ID = mount.id 169 stats.Memory.Metadata.Path = mount.path 170 } 171 172 // Return nil if no metrics were collected. 173 if stats.BlockIO == nil && stats.CPU == nil && stats.CPUAccounting == nil && stats.Memory == nil { 174 return nil, nil 175 } 176 177 return &stats, nil 178 } 179 180 // getCommonCgroupMetadata returns Metadata containing the cgroup path and ID 181 // iff all subsystems share a common path and ID. This is common for 182 // containerized processes. If there is no common path and ID then the returned 183 // values are empty strings. 184 func getCommonCgroupMetadata(mounts map[string]mount) Metadata { 185 var path string 186 for _, m := range mounts { 187 if path == "" { 188 path = m.path 189 } else if path != m.path { 190 // All paths are not the same. 191 return Metadata{} 192 } 193 } 194 195 return Metadata{Path: path, ID: filepath.Base(path)} 196 }