Skip to content

Commit

Permalink
Read cpu.stat regardless if controller enabled.
Browse files Browse the repository at this point in the history
The unified hierarchy provides the cpu.stat file for every cgroup,
regardless if the CPU controller is enabled (in fact, setting the
systemd property CPUAccounting=True does not enable this controller
because of this fact). It provides the usage_usec, user_usec,
and system_usec by default. Instead of reading the stat for each
enabled controller (CPU and memory), just attempt to read them
each time the Stat() function is called.

Attempting to read the memory.stat file even if memory accounting
is not enabled seems insignificant (some other files always have
a read attempt, such as memory.current), and eliminates finding and
looping over enabled controllers.

Resolves: containerd#347

Signed-off-by: Jackson McKay  <jackjaymckay@gmail.com>
  • Loading branch information
jay-mckay committed Sep 3, 2024
1 parent 190de3b commit 63aa1c0
Showing 1 changed file with 54 additions and 64 deletions.
118 changes: 54 additions & 64 deletions cgroup2/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -554,78 +554,61 @@ func (c *Manager) MoveTo(destination *Manager) error {
}

func (c *Manager) Stat() (*stats.Metrics, error) {
controllers, err := c.Controllers()
if err != nil {
return nil, err
}
// Sizing this avoids an allocation to increase the map at runtime;
// currently the default bucket size is 8 and we put 40+ elements
// in it so we'd always end up allocating.
out := make(map[string]uint64, 50)
for _, controller := range controllers {
switch controller {
case "cpu", "memory":
if err := readKVStatsFile(c.path, controller+".stat", out); err != nil {
if os.IsNotExist(err) {
continue
}
return nil, err
}
}
}
memoryEvents := make(map[string]uint64)
if err := readKVStatsFile(c.path, "memory.events", memoryEvents); err != nil {
var metrics stats.Metrics

cpuStat := make(map[string]uint64)
if err := readKVStatsFile(c.path, "cpu.stat", cpuStat); err != nil {
if !os.IsNotExist(err) {
return nil, err
}
}

var metrics stats.Metrics
metrics.Pids = &stats.PidsStat{
Current: getStatFileContentUint64(filepath.Join(c.path, "pids.current")),
Limit: getStatFileContentUint64(filepath.Join(c.path, "pids.max")),
}
metrics.CPU = &stats.CPUStat{
UsageUsec: out["usage_usec"],
UserUsec: out["user_usec"],
SystemUsec: out["system_usec"],
NrPeriods: out["nr_periods"],
NrThrottled: out["nr_throttled"],
ThrottledUsec: out["throttled_usec"],
UsageUsec: cpuStat["usage_usec"],
UserUsec: cpuStat["user_usec"],
SystemUsec: cpuStat["system_usec"],
NrPeriods: cpuStat["nr_periods"],
NrThrottled: cpuStat["nr_throttled"],
ThrottledUsec: cpuStat["throttled_usec"],
PSI: getStatPSIFromFile(filepath.Join(c.path, "cpu.pressure")),
}
memoryStat := make(map[string]uint64, 40)
if err := readKVStatsFile(c.path, "memory.stat", memoryStat); err != nil {
if !os.IsNotExist(err) {
return nil, err
}
}
metrics.Memory = &stats.MemoryStat{
Anon: out["anon"],
File: out["file"],
KernelStack: out["kernel_stack"],
Slab: out["slab"],
Sock: out["sock"],
Shmem: out["shmem"],
FileMapped: out["file_mapped"],
FileDirty: out["file_dirty"],
FileWriteback: out["file_writeback"],
AnonThp: out["anon_thp"],
InactiveAnon: out["inactive_anon"],
ActiveAnon: out["active_anon"],
InactiveFile: out["inactive_file"],
ActiveFile: out["active_file"],
Unevictable: out["unevictable"],
SlabReclaimable: out["slab_reclaimable"],
SlabUnreclaimable: out["slab_unreclaimable"],
Pgfault: out["pgfault"],
Pgmajfault: out["pgmajfault"],
WorkingsetRefault: out["workingset_refault"],
WorkingsetActivate: out["workingset_activate"],
WorkingsetNodereclaim: out["workingset_nodereclaim"],
Pgrefill: out["pgrefill"],
Pgscan: out["pgscan"],
Pgsteal: out["pgsteal"],
Pgactivate: out["pgactivate"],
Pgdeactivate: out["pgdeactivate"],
Pglazyfree: out["pglazyfree"],
Pglazyfreed: out["pglazyfreed"],
ThpFaultAlloc: out["thp_fault_alloc"],
ThpCollapseAlloc: out["thp_collapse_alloc"],
Anon: memoryStat["anon"],
File: memoryStat["file"],
KernelStack: memoryStat["kernel_stack"],
Slab: memoryStat["slab"],
Sock: memoryStat["sock"],
Shmem: memoryStat["shmem"],
FileMapped: memoryStat["file_mapped"],
FileDirty: memoryStat["file_dirty"],
FileWriteback: memoryStat["file_writeback"],
AnonThp: memoryStat["anon_thp"],
InactiveAnon: memoryStat["inactive_anon"],
ActiveAnon: memoryStat["active_anon"],
InactiveFile: memoryStat["inactive_file"],
ActiveFile: memoryStat["active_file"],
Unevictable: memoryStat["unevictable"],
SlabReclaimable: memoryStat["slab_reclaimable"],
SlabUnreclaimable: memoryStat["slab_unreclaimable"],
Pgfault: memoryStat["pgfault"],
Pgmajfault: memoryStat["pgmajfault"],
WorkingsetRefault: memoryStat["workingset_refault"],
WorkingsetActivate: memoryStat["workingset_activate"],
WorkingsetNodereclaim: memoryStat["workingset_nodereclaim"],
Pgrefill: memoryStat["pgrefill"],
Pgscan: memoryStat["pgscan"],
Pgsteal: memoryStat["pgsteal"],
Pgactivate: memoryStat["pgactivate"],
Pgdeactivate: memoryStat["pgdeactivate"],
Pglazyfree: memoryStat["pglazyfree"],
Pglazyfreed: memoryStat["pglazyfreed"],
ThpFaultAlloc: memoryStat["thp_fault_alloc"],
ThpCollapseAlloc: memoryStat["thp_collapse_alloc"],
Usage: getStatFileContentUint64(filepath.Join(c.path, "memory.current")),
UsageLimit: getStatFileContentUint64(filepath.Join(c.path, "memory.max")),
MaxUsage: getStatFileContentUint64(filepath.Join(c.path, "memory.peak")),
Expand All @@ -634,6 +617,13 @@ func (c *Manager) Stat() (*stats.Metrics, error) {
SwapMaxUsage: getStatFileContentUint64(filepath.Join(c.path, "memory.swap.peak")),
PSI: getStatPSIFromFile(filepath.Join(c.path, "memory.pressure")),
}

memoryEvents := make(map[string]uint64)
if err := readKVStatsFile(c.path, "memory.stat", memoryStat); err != nil {
if !os.IsNotExist(err) {
return nil, err
}
}
if len(memoryEvents) > 0 {
metrics.MemoryEvents = &stats.MemoryEvents{
Low: memoryEvents["low"],
Expand Down

0 comments on commit 63aa1c0

Please sign in to comment.