Skip to content

Commit 707e4b4

Browse files
committed
Read cpu.stat regardless if controller enabled.
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: #347 Signed-off-by: Jackson McKay <[email protected]>
1 parent 8de881b commit 707e4b4

File tree

1 file changed

+109
-89
lines changed

1 file changed

+109
-89
lines changed

cgroup2/manager.go

Lines changed: 109 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -559,114 +559,134 @@ func (c *Manager) MoveTo(destination *Manager) error {
559559
}
560560

561561
func (c *Manager) Stat() (*stats.Metrics, error) {
562-
controllers, err := c.Controllers()
563-
if err != nil {
564-
return nil, err
565-
}
566-
// Sizing this avoids an allocation to increase the map at runtime;
567-
// currently the default bucket size is 8 and we put 40+ elements
568-
// in it so we'd always end up allocating.
569-
out := make(map[string]uint64, 50)
570-
for _, controller := range controllers {
571-
switch controller {
572-
case "cpu", "memory":
573-
if err := readKVStatsFile(c.path, controller+".stat", out); err != nil {
574-
if os.IsNotExist(err) {
575-
continue
576-
}
577-
return nil, err
578-
}
579-
}
580-
}
581-
memoryEvents := make(map[string]uint64)
582-
if err := readKVStatsFile(c.path, "memory.events", memoryEvents); err != nil {
583-
if !os.IsNotExist(err) {
584-
return nil, err
585-
}
586-
}
587-
588562
var metrics stats.Metrics
563+
var err error
564+
589565
metrics.Pids = &stats.PidsStat{
590566
Current: getStatFileContentUint64(filepath.Join(c.path, "pids.current")),
591567
Limit: getStatFileContentUint64(filepath.Join(c.path, "pids.max")),
592568
}
593-
metrics.CPU = &stats.CPUStat{
594-
UsageUsec: out["usage_usec"],
595-
UserUsec: out["user_usec"],
596-
SystemUsec: out["system_usec"],
597-
NrPeriods: out["nr_periods"],
598-
NrThrottled: out["nr_throttled"],
599-
ThrottledUsec: out["throttled_usec"],
600-
PSI: getStatPSIFromFile(filepath.Join(c.path, "cpu.pressure")),
601-
}
602-
if nr_bursts, ok := out["nr_bursts"]; ok {
603-
metrics.CPU.NrBursts = nr_bursts
604-
}
605-
if burst_usec, ok := out["burst_usec"]; ok {
606-
metrics.CPU.BurstUsec = burst_usec
607-
}
608-
metrics.Memory = &stats.MemoryStat{
609-
Anon: out["anon"],
610-
File: out["file"],
611-
KernelStack: out["kernel_stack"],
612-
Slab: out["slab"],
613-
Sock: out["sock"],
614-
Shmem: out["shmem"],
615-
FileMapped: out["file_mapped"],
616-
FileDirty: out["file_dirty"],
617-
FileWriteback: out["file_writeback"],
618-
AnonThp: out["anon_thp"],
619-
InactiveAnon: out["inactive_anon"],
620-
ActiveAnon: out["active_anon"],
621-
InactiveFile: out["inactive_file"],
622-
ActiveFile: out["active_file"],
623-
Unevictable: out["unevictable"],
624-
SlabReclaimable: out["slab_reclaimable"],
625-
SlabUnreclaimable: out["slab_unreclaimable"],
626-
Pgfault: out["pgfault"],
627-
Pgmajfault: out["pgmajfault"],
628-
WorkingsetRefault: out["workingset_refault"],
629-
WorkingsetActivate: out["workingset_activate"],
630-
WorkingsetNodereclaim: out["workingset_nodereclaim"],
631-
Pgrefill: out["pgrefill"],
632-
Pgscan: out["pgscan"],
633-
Pgsteal: out["pgsteal"],
634-
Pgactivate: out["pgactivate"],
635-
Pgdeactivate: out["pgdeactivate"],
636-
Pglazyfree: out["pglazyfree"],
637-
Pglazyfreed: out["pglazyfreed"],
638-
ThpFaultAlloc: out["thp_fault_alloc"],
639-
ThpCollapseAlloc: out["thp_collapse_alloc"],
640-
Usage: getStatFileContentUint64(filepath.Join(c.path, "memory.current")),
641-
UsageLimit: getStatFileContentUint64(filepath.Join(c.path, "memory.max")),
642-
MaxUsage: getStatFileContentUint64(filepath.Join(c.path, "memory.peak")),
643-
SwapUsage: getStatFileContentUint64(filepath.Join(c.path, "memory.swap.current")),
644-
SwapLimit: getStatFileContentUint64(filepath.Join(c.path, "memory.swap.max")),
645-
SwapMaxUsage: getStatFileContentUint64(filepath.Join(c.path, "memory.swap.peak")),
646-
PSI: getStatPSIFromFile(filepath.Join(c.path, "memory.pressure")),
647-
}
648-
if len(memoryEvents) > 0 {
649-
metrics.MemoryEvents = &stats.MemoryEvents{
650-
Low: memoryEvents["low"],
651-
High: memoryEvents["high"],
652-
Max: memoryEvents["max"],
653-
Oom: memoryEvents["oom"],
654-
OomKill: memoryEvents["oom_kill"],
655-
}
569+
570+
metrics.CPU, err = readCPUStats(c.path)
571+
if err != nil {
572+
return nil, err
573+
}
574+
575+
metrics.Memory, err = readMemoryStats(c.path)
576+
if err != nil {
577+
return nil, err
578+
}
579+
580+
metrics.MemoryEvents, err = readMemoryEvents(c.path)
581+
if err != nil {
582+
return nil, err
656583
}
584+
657585
metrics.Io = &stats.IOStat{
658586
Usage: readIoStats(c.path),
659587
PSI: getStatPSIFromFile(filepath.Join(c.path, "io.pressure")),
660588
}
589+
661590
metrics.Rdma = &stats.RdmaStat{
662591
Current: rdmaStats(filepath.Join(c.path, "rdma.current")),
663592
Limit: rdmaStats(filepath.Join(c.path, "rdma.max")),
664593
}
594+
665595
metrics.Hugetlb = readHugeTlbStats(c.path)
666596

667597
return &metrics, nil
668598
}
669599

600+
func readCPUStats(cgroupPath string) (*stats.CPUStat, error) {
601+
cpuStat := make(map[string]uint64)
602+
if err := readKVStatsFile(cgroupPath, "cpu.stat", cpuStat); err != nil {
603+
if os.IsNotExist(err) {
604+
return &stats.CPUStat{}, nil
605+
}
606+
return nil, err
607+
}
608+
return &stats.CPUStat{
609+
UsageUsec: cpuStat["usage_usec"],
610+
UserUsec: cpuStat["user_usec"],
611+
SystemUsec: cpuStat["system_usec"],
612+
NrPeriods: cpuStat["nr_periods"],
613+
NrThrottled: cpuStat["nr_throttled"],
614+
ThrottledUsec: cpuStat["throttled_usec"],
615+
NrBursts: cpuStat["nr_bursts"],
616+
BurstUsec: cpuStat["burst_usec"],
617+
PSI: getStatPSIFromFile(filepath.Join(cgroupPath, "cpu.pressure")),
618+
}, nil
619+
}
620+
621+
func readMemoryStats(cgroupPath string) (*stats.MemoryStat, error) {
622+
memoryStat := make(map[string]uint64, 40)
623+
if err := readKVStatsFile(cgroupPath, "memory.stat", memoryStat); err != nil {
624+
if os.IsNotExist(err) {
625+
return &stats.MemoryStat{}, nil
626+
}
627+
return nil, err
628+
}
629+
return &stats.MemoryStat{
630+
Anon: memoryStat["anon"],
631+
File: memoryStat["file"],
632+
KernelStack: memoryStat["kernel_stack"],
633+
Slab: memoryStat["slab"],
634+
Sock: memoryStat["sock"],
635+
Shmem: memoryStat["shmem"],
636+
FileMapped: memoryStat["file_mapped"],
637+
FileDirty: memoryStat["file_dirty"],
638+
FileWriteback: memoryStat["file_writeback"],
639+
AnonThp: memoryStat["anon_thp"],
640+
InactiveAnon: memoryStat["inactive_anon"],
641+
ActiveAnon: memoryStat["active_anon"],
642+
InactiveFile: memoryStat["inactive_file"],
643+
ActiveFile: memoryStat["active_file"],
644+
Unevictable: memoryStat["unevictable"],
645+
SlabReclaimable: memoryStat["slab_reclaimable"],
646+
SlabUnreclaimable: memoryStat["slab_unreclaimable"],
647+
Pgfault: memoryStat["pgfault"],
648+
Pgmajfault: memoryStat["pgmajfault"],
649+
WorkingsetRefault: memoryStat["workingset_refault"],
650+
WorkingsetActivate: memoryStat["workingset_activate"],
651+
WorkingsetNodereclaim: memoryStat["workingset_nodereclaim"],
652+
Pgrefill: memoryStat["pgrefill"],
653+
Pgscan: memoryStat["pgscan"],
654+
Pgsteal: memoryStat["pgsteal"],
655+
Pgactivate: memoryStat["pgactivate"],
656+
Pgdeactivate: memoryStat["pgdeactivate"],
657+
Pglazyfree: memoryStat["pglazyfree"],
658+
Pglazyfreed: memoryStat["pglazyfreed"],
659+
ThpFaultAlloc: memoryStat["thp_fault_alloc"],
660+
ThpCollapseAlloc: memoryStat["thp_collapse_alloc"],
661+
Usage: getStatFileContentUint64(filepath.Join(cgroupPath, "memory.current")),
662+
UsageLimit: getStatFileContentUint64(filepath.Join(cgroupPath, "memory.max")),
663+
MaxUsage: getStatFileContentUint64(filepath.Join(cgroupPath, "memory.peak")),
664+
SwapUsage: getStatFileContentUint64(filepath.Join(cgroupPath, "memory.swap.current")),
665+
SwapLimit: getStatFileContentUint64(filepath.Join(cgroupPath, "memory.swap.max")),
666+
SwapMaxUsage: getStatFileContentUint64(filepath.Join(cgroupPath, "memory.swap.peak")),
667+
PSI: getStatPSIFromFile(filepath.Join(cgroupPath, "memory.pressure")),
668+
}, nil
669+
}
670+
671+
func readMemoryEvents(cgroupPath string) (*stats.MemoryEvents, error) {
672+
memoryEvents := make(map[string]uint64)
673+
if err := readKVStatsFile(cgroupPath, "memory.events", memoryEvents); err != nil {
674+
if !os.IsNotExist(err) {
675+
return nil, err
676+
}
677+
}
678+
if len(memoryEvents) == 0 {
679+
return nil, nil
680+
}
681+
return &stats.MemoryEvents{
682+
Low: memoryEvents["low"],
683+
High: memoryEvents["high"],
684+
Max: memoryEvents["max"],
685+
Oom: memoryEvents["oom"],
686+
OomKill: memoryEvents["oom_kill"],
687+
}, nil
688+
}
689+
670690
func readKVStatsFile(path string, file string, out map[string]uint64) error {
671691
f, err := os.Open(filepath.Join(path, file))
672692
if err != nil {

0 commit comments

Comments
 (0)