diff --git a/pkg/util/cgroup/common/cgroup_linux.go b/pkg/util/cgroup/common/cgroup_linux.go index 24e7d394b..4a01eb63b 100644 --- a/pkg/util/cgroup/common/cgroup_linux.go +++ b/pkg/util/cgroup/common/cgroup_linux.go @@ -80,16 +80,19 @@ func GetCgroupParamInt(cgroupPath, cgroupFile string) (int64, error) { return res, nil } -// ParseCgroupNumaValue parse cgroup numa stat files like memory.numa_stat, the format is like "anon N0=1686843392 N1=1069957120 N2=316747776 N3=163962880" -func ParseCgroupNumaValue(cgroupPath, cgroupFile string) (map[string]map[int]uint64, error) { - fileName := filepath.Join(cgroupPath, cgroupFile) - content, err := ioutil.ReadFile(fileName) - if err != nil { - return nil, err - } +/* +ParseCgroupNumaValue parse cgroup numa stat files like `memory.numa_stat`. + +cgroup v1 format: += N0= N1= ... +hierarchical_= N0= N1= ... +cgroup v2 format: + N0= N1= ... +*/ +func ParseCgroupNumaValue(content string) (map[string]map[int]uint64, error) { result := make(map[string]map[int]uint64) - lines := strings.Split(string(content), "\n") + lines := strings.Split(content, "\n") for _, line := range lines { cols := strings.Fields(line) if len(cols) <= 1 { @@ -97,6 +100,10 @@ func ParseCgroupNumaValue(cgroupPath, cgroupFile string) (map[string]map[int]uin } key := cols[0] + if index := strings.Index(key, "="); index != -1 { + // For v1 format, remove the suffix after "=" + key = key[:index] + } numaInfo := make(map[int]uint64) for _, pair := range cols[1:] { parts := strings.Split(pair, "=") diff --git a/pkg/util/cgroup/common/cgroup_linux_test.go b/pkg/util/cgroup/common/cgroup_linux_test.go new file mode 100644 index 000000000..10d118018 --- /dev/null +++ b/pkg/util/cgroup/common/cgroup_linux_test.go @@ -0,0 +1,124 @@ +//go:build linux +// +build linux + +/* +Copyright 2022 The Katalyst Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package common + +import ( + "reflect" + "testing" +) + +func TestParseCgroupNumaValue(t *testing.T) { + t.Parallel() + + type args struct { + content string + } + tests := []struct { + name string + args args + want map[string]map[int]uint64 + wantErr bool + }{ + { + name: "cgroupv1 format", + args: args{ + content: `total=7587426 N0=92184 N1=21339 N2=104047 N3=7374122 +file=70686 N0=5353 N1=3096 N2=12817 N3=51844 +anon=7516740 N0=86831 N1=18243 N2=91230 N3=7322278 +unevictable=0 N0=0 N1=0 N2=0 N3=0`, + }, + want: map[string]map[int]uint64{ + "total": { + 0: 92184, + 1: 21339, + 2: 104047, + 3: 7374122, + }, + "file": { + 0: 5353, + 1: 3096, + 2: 12817, + 3: 51844, + }, + "anon": { + 0: 86831, + 1: 18243, + 2: 91230, + 3: 7322278, + }, + "unevictable": { + 0: 0, + 1: 0, + 2: 0, + 3: 0, + }, + }, + wantErr: false, + }, + { + name: "cgroupv2 format", + args: args{ + content: `anon N0=1629990912 N1=65225723904 +file N0=1892352 N1=37441536 +unevictable N0=0 N1=0`, + }, + want: map[string]map[int]uint64{ + "anon": { + 0: 1629990912, + 1: 65225723904, + }, + "file": { + 0: 1892352, + 1: 37441536, + }, + "unevictable": { + 0: 0, + 1: 0, + }, + }, + wantErr: false, + }, + { + name: "wrong separator", + args: args{ + content: `anon N0:1629990912 N1:65225723904 +file N0:1892352 N1:37441536 +unevictable N0:0 N1:0`, + }, + wantErr: true, + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + got, err := ParseCgroupNumaValue(tt.args.content) + if (err != nil) != tt.wantErr { + t.Errorf("ParseCgroupNumaValue() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("ParseCgroupNumaValue() = %v, want %v", got, tt.want) + return + } + }) + } +} diff --git a/pkg/util/cgroup/manager/v1/fs_linux.go b/pkg/util/cgroup/manager/v1/fs_linux.go index d5cc807ee..6cca63b70 100644 --- a/pkg/util/cgroup/manager/v1/fs_linux.go +++ b/pkg/util/cgroup/manager/v1/fs_linux.go @@ -239,9 +239,15 @@ func (m *manager) GetMemory(absCgroupPath string) (*common.MemoryStats, error) { } func (m *manager) GetNumaMemory(absCgroupPath string) (map[int]*common.MemoryNumaMetrics, error) { - numaStat, err := common.ParseCgroupNumaValue(absCgroupPath, "memory.numa_stat") + const fileName = "memory.numa_stat" + content, err := libcgroups.ReadFile(absCgroupPath, fileName) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to read %s: %w", fileName, err) + } + + numaStat, err := common.ParseCgroupNumaValue(content) + if err != nil { + return nil, fmt.Errorf("failed to parse numa stat: %w", err) } pageSize := uint64(syscall.Getpagesize()) diff --git a/pkg/util/cgroup/manager/v2/fs_linux.go b/pkg/util/cgroup/manager/v2/fs_linux.go index c6998cdb3..8519a2c8e 100644 --- a/pkg/util/cgroup/manager/v2/fs_linux.go +++ b/pkg/util/cgroup/manager/v2/fs_linux.go @@ -282,10 +282,15 @@ func (m *manager) GetMemory(absCgroupPath string) (*common.MemoryStats, error) { } func (m *manager) GetNumaMemory(absCgroupPath string) (map[int]*common.MemoryNumaMetrics, error) { - numaStat, err := common.ParseCgroupNumaValue(absCgroupPath, "memory.numa_stat") - general.Infof("get cgroup %+v numa stat %+v", absCgroupPath, numaStat) + const fileName = "memory.numa_stat" + content, err := libcgroups.ReadFile(absCgroupPath, fileName) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to read %s: %w", fileName, err) + } + + numaStat, err := common.ParseCgroupNumaValue(content) + if err != nil { + return nil, fmt.Errorf("failed to parse numa stat: %w", err) } result := make(map[int]*common.MemoryNumaMetrics)