opencontainers / opencontainers/cgroups

Add parsing of memory.events max into Failcnt for cgroup v2

Open
#19 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Go
Stars
29
Forks
32
PR merge metrics
No merged PRs in 30d

Description

In cgroup v2, memory.failcnt is unavailable; memory.events provides the max failures count.
This patch updates getMemoryDataV2 to read the 'max' field from memory.events and populate MemoryData.Failcnt.
Includes TestStatMemoryPodCgroupFailcnt to cover both presence and absence of memory.events.

diff --git a/fs2/memory.go b/fs2/memory.go
index 7613307..47891ef 100644
--- a/fs2/memory.go
+++ b/fs2/memory.go
@@ -170,6 +170,13 @@ func getMemoryDataV2(path, name string) (cgroups.MemoryData, error) {
        }
        memoryData.MaxUsage = value
 
+       // Read failcnt (max events) from memory.events for cgroup v2
+       if failcnt, err := fscommon.GetValueByKey(path, "memory.events", "max"); err == nil {
+               memoryData.Failcnt = failcnt
+       } else if !os.IsNotExist(err) {
+               return cgroups.MemoryData{}, err
+       }
+
        return memoryData, nil
 }
 
diff --git a/fs2/memory_test.go b/fs2/memory_test.go
index e46dbe6..1a7c6c9 100644
--- a/fs2/memory_test.go
+++ b/fs2/memory_test.go
@@ -125,6 +125,62 @@ func TestStatMemoryPodCgroup(t *testing.T) {
        }
 }
 
+// Test that memory.events max field is parsed into Failcnt for cgroup v2
+func TestStatMemoryPodCgroupFailcnt(t *testing.T) {
+       // Use a fake cgroupfs.
+       cgroups.TestMode = true
+       fakeCgroupDir := t.TempDir()
+
+       // Prepare minimal cgroup v2 files.
+       if err := os.WriteFile(filepath.Join(fakeCgroupDir, "memory.stat"), []byte(exampleMemoryStatData), 0o644); err != nil {
+               t.Fatal(err)
+       }
+       if err := os.WriteFile(filepath.Join(fakeCgroupDir, "memory.current"), []byte("1"), 0o644); err != nil {
+               t.Fatal(err)
+       }
+       if err := os.WriteFile(filepath.Join(fakeCgroupDir, "memory.max"), []byte("2"), 0o644); err != nil {
+               t.Fatal(err)
+       }
+       if err := os.WriteFile(filepath.Join(fakeCgroupDir, "memory.peak"), []byte("3"), 0o644); err != nil {
+               t.Fatal(err)
+       }
+       // Write memory.events with max field
+       events := "other_event 5\nmax 42\nsome_event 7\n"
+       if err := os.WriteFile(filepath.Join(fakeCgroupDir, "memory.events"), []byte(events), 0o644); err != nil {
+               t.Fatal(err)
+       }
+
+       gotStats := cgroups.NewStats()
+       if err := statMemory(fakeCgroupDir, gotStats); err != nil {
+               t.Fatalf("unexpected error: %v", err)
+       }
+       if got := gotStats.MemoryStats.Usage.Failcnt; got != 42 {
+               t.Errorf("expected Failcnt 42, got %d", got)
+       }
+
+       // Missing memory.events should not error and Failcnt remains zero
+       fakeCgroupDir = t.TempDir()
+       if err := os.WriteFile(filepath.Join(fakeCgroupDir, "memory.stat"), []byte(exampleMemoryStatData), 0o644); err != nil {
+               t.Fatal(err)
+       }
+       if err := os.WriteFile(filepath.Join(fakeCgroupDir, "memory.current"), []byte("10"), 0o644); err != nil {
+               t.Fatal(err)
+       }
+       if err := os.WriteFile(filepath.Join(fakeCgroupDir, "memory.max"), []byte("20"), 0o644); err != nil {
+               t.Fatal(err)
+       }
+       if err := os.WriteFile(filepath.Join(fakeCgroupDir, "memory.peak"), []byte("30"), 0o644); err != nil {
+               t.Fatal(err)
+       }
+       gotStats = cgroups.NewStats()
+       if err := statMemory(fakeCgroupDir, gotStats); err != nil {
+               t.Fatalf("unexpected error when memory.events missing: %v", err)
+       }
+       if got := gotStats.MemoryStats.Usage.Failcnt; got != 0 {
+               t.Errorf("expected Failcnt 0 when memory.events missing, got %d", got)
+       }
+}
+
 func TestRootStatsFromMeminfo(t *testing.T) {
        stats := &cgroups.Stats{
                MemoryStats: cgroups.MemoryStats{

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in fs2/memory.go at getMemoryDataV2 and review the existing memory.current, memory.max, and memory.peak parsing. Then inspect fs2/memory_test.go, especially TestStatMemoryPodCgroup and the proposed TestStatMemoryPodCgroupFailcnt. Done means the memory.events max value populates Failcnt, while a missing memory.events file remains valid and leaves Failcnt at zero.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
operating-systems
Issue type
Feature
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.