From ed2ea8564df605567936921a4a816169b92176bc Mon Sep 17 00:00:00 2001 From: "Maciej \"Iwan\" Iwanowski" Date: Fri, 5 Feb 2021 23:39:08 +0100 Subject: [PATCH] Determining CPU online state correctly using online CPUs list Signed-off-by: Maciej "Iwan" Iwanowski --- machine/machine.go | 47 +---- machine/testdata/cpu0/online | 1 - machine/testdata/cpu2/online | 1 - .../{ => sysfs_cpus}/cpu0/topology/core_id | 0 .../cpu0/topology/physical_package_id | 0 .../testdata/{ => sysfs_cpus}/cpu1/.gitkeep | 0 machine/topology_test.go | 31 +++- utils/sysfs/sysfs.go | 170 +++++++++++++++-- utils/sysfs/sysfs_notx86.go | 19 ++ utils/sysfs/sysfs_test.go | 172 +++++++++++++++++- utils/sysfs/sysfs_x86.go | 19 ++ 11 files changed, 386 insertions(+), 74 deletions(-) delete mode 100644 machine/testdata/cpu0/online delete mode 100644 machine/testdata/cpu2/online rename machine/testdata/{ => sysfs_cpus}/cpu0/topology/core_id (100%) rename machine/testdata/{ => sysfs_cpus}/cpu0/topology/physical_package_id (100%) rename machine/testdata/{ => sysfs_cpus}/cpu1/.gitkeep (100%) create mode 100644 utils/sysfs/sysfs_notx86.go create mode 100644 utils/sysfs/sysfs_x86.go diff --git a/machine/machine.go b/machine/machine.go index 4330d69502..1019fc5a06 100644 --- a/machine/machine.go +++ b/machine/machine.go @@ -16,18 +16,15 @@ package machine import ( - "bytes" "fmt" "io/ioutil" "os" "path" - "path/filepath" "regexp" - "strconv" - "strings" - // s390/s390x changes "runtime" + "strconv" + "strings" info "github.com/google/cadvisor/info/v1" "github.com/google/cadvisor/utils" @@ -54,9 +51,6 @@ var ( maxFreqFile = "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq" ) -const sysFsCPUCoreID = "core_id" -const sysFsCPUPhysicalPackageID = "physical_package_id" -const sysFsCPUTopology = "topology" const memTypeFileName = "dimm_mem_type" const sizeFileName = "size" @@ -66,7 +60,7 @@ func GetPhysicalCores(procInfo []byte) int { if numCores == 0 { // read number of cores from /sys/bus/cpu/devices/cpu*/topology/core_id to deal with processors // for which 'core id' is not available in /proc/cpuinfo - numCores = getUniqueCPUPropertyCount(cpuBusPath, sysFsCPUCoreID) + numCores = sysfs.GetUniqueCPUPropertyCount(cpuBusPath, sysfs.CPUCoreID) } if numCores == 0 { klog.Errorf("Cannot read number of physical cores correctly, number of cores set to %d", numCores) @@ -80,7 +74,7 @@ func GetSockets(procInfo []byte) int { if numSocket == 0 { // read number of sockets from /sys/bus/cpu/devices/cpu*/topology/physical_package_id to deal with processors // for which 'physical id' is not available in /proc/cpuinfo - numSocket = getUniqueCPUPropertyCount(cpuBusPath, sysFsCPUPhysicalPackageID) + numSocket = sysfs.GetUniqueCPUPropertyCount(cpuBusPath, sysfs.CPUPhysicalPackageID) } if numSocket == 0 { klog.Errorf("Cannot read number of sockets correctly, number of sockets set to %d", numSocket) @@ -236,39 +230,6 @@ func parseCapacity(b []byte, r *regexp.Regexp) (uint64, error) { return m * 1024, err } -// Looks for sysfs cpu path containing given CPU property, e.g. core_id or physical_package_id -// and returns number of unique values of given property, exemplary usage: getting number of CPU physical cores -func getUniqueCPUPropertyCount(cpuBusPath string, propertyName string) int { - pathPattern := cpuBusPath + "cpu*[0-9]" - sysCPUPaths, err := filepath.Glob(pathPattern) - if err != nil { - klog.Errorf("Cannot find files matching pattern (pathPattern: %s), number of unique %s set to 0", pathPattern, propertyName) - return 0 - } - uniques := make(map[string]bool) - for _, sysCPUPath := range sysCPUPaths { - onlinePath := filepath.Join(sysCPUPath, "online") - onlineVal, err := ioutil.ReadFile(onlinePath) - if err != nil { - klog.Warningf("Cannot determine CPU %s online state, skipping", sysCPUPath) - continue - } - onlineVal = bytes.TrimSpace(onlineVal) - if len(onlineVal) == 0 || onlineVal[0] != 49 { - klog.Warningf("CPU %s is offline, skipping", sysCPUPath) - continue - } - propertyPath := filepath.Join(sysCPUPath, sysFsCPUTopology, propertyName) - propertyVal, err := ioutil.ReadFile(propertyPath) - if err != nil { - klog.Errorf("Cannot open %s, number of unique %s set to 0", propertyPath, propertyName) - return 0 - } - uniques[string(propertyVal)] = true - } - return len(uniques) -} - // getUniqueMatchesCount returns number of unique matches in given argument using provided regular expression func getUniqueMatchesCount(s string, r *regexp.Regexp) int { matches := r.FindAllString(s, -1) diff --git a/machine/testdata/cpu0/online b/machine/testdata/cpu0/online deleted file mode 100644 index d00491fd7e..0000000000 --- a/machine/testdata/cpu0/online +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/machine/testdata/cpu2/online b/machine/testdata/cpu2/online deleted file mode 100644 index 573541ac97..0000000000 --- a/machine/testdata/cpu2/online +++ /dev/null @@ -1 +0,0 @@ -0 diff --git a/machine/testdata/cpu0/topology/core_id b/machine/testdata/sysfs_cpus/cpu0/topology/core_id similarity index 100% rename from machine/testdata/cpu0/topology/core_id rename to machine/testdata/sysfs_cpus/cpu0/topology/core_id diff --git a/machine/testdata/cpu0/topology/physical_package_id b/machine/testdata/sysfs_cpus/cpu0/topology/physical_package_id similarity index 100% rename from machine/testdata/cpu0/topology/physical_package_id rename to machine/testdata/sysfs_cpus/cpu0/topology/physical_package_id diff --git a/machine/testdata/cpu1/.gitkeep b/machine/testdata/sysfs_cpus/cpu1/.gitkeep similarity index 100% rename from machine/testdata/cpu1/.gitkeep rename to machine/testdata/sysfs_cpus/cpu1/.gitkeep diff --git a/machine/topology_test.go b/machine/topology_test.go index e94030070d..2186e8dd97 100644 --- a/machine/topology_test.go +++ b/machine/topology_test.go @@ -18,14 +18,16 @@ import ( "encoding/json" "io/ioutil" "os" + "path/filepath" "reflect" "sort" "testing" + "github.com/stretchr/testify/assert" + info "github.com/google/cadvisor/info/v1" "github.com/google/cadvisor/utils/sysfs" "github.com/google/cadvisor/utils/sysfs/fakesysfs" - "github.com/stretchr/testify/assert" ) func TestPhysicalCores(t *testing.T) { @@ -40,18 +42,26 @@ func TestPhysicalCores(t *testing.T) { } func TestPhysicalCoresReadingFromCpuBus(t *testing.T) { - cpuBusPath = "./testdata/" // overwriting package variable to mock sysfs - testfile := "./testdata/cpuinfo_arm" // mock cpuinfo without core id + origCPUBusPath := cpuBusPath + defer func() { + cpuBusPath = origCPUBusPath + }() + cpuBusPath = "./testdata/sysfs_cpus/" // overwriting package variable to mock sysfs + testfile := "./testdata/cpuinfo_arm" // mock cpuinfo without core id testcpuinfo, err := ioutil.ReadFile(testfile) assert.Nil(t, err) assert.NotNil(t, testcpuinfo) numPhysicalCores := GetPhysicalCores(testcpuinfo) - assert.Equal(t, 2, numPhysicalCores) + assert.Equal(t, 1, numPhysicalCores) } func TestPhysicalCoresFromWrongSysFs(t *testing.T) { + origCPUBusPath := cpuBusPath + defer func() { + cpuBusPath = origCPUBusPath + }() cpuBusPath = "./testdata/wrongsysfs" // overwriting package variable to mock sysfs testfile := "./testdata/cpuinfo_arm" // mock cpuinfo without core id @@ -75,6 +85,10 @@ func TestSockets(t *testing.T) { } func TestSocketsReadingFromCpuBus(t *testing.T) { + origCPUBusPath := cpuBusPath + defer func() { + cpuBusPath = origCPUBusPath + }() cpuBusPath = "./testdata/wrongsysfs" // overwriting package variable to mock sysfs testfile := "./testdata/cpuinfo_arm" // mock cpuinfo without physical id @@ -87,7 +101,14 @@ func TestSocketsReadingFromCpuBus(t *testing.T) { } func TestSocketsReadingFromWrongSysFs(t *testing.T) { - cpuBusPath = "./testdata/" // overwriting package variable to mock sysfs + path, err := filepath.Abs("./testdata/sysfs_cpus/") + assert.NoError(t, err) + + origCPUBusPath := cpuBusPath + defer func() { + cpuBusPath = origCPUBusPath + }() + cpuBusPath = path // overwriting package variable to mock sysfs testfile := "./testdata/cpuinfo_arm" // mock cpuinfo without physical id testcpuinfo, err := ioutil.ReadFile(testfile) diff --git a/utils/sysfs/sysfs.go b/utils/sysfs/sysfs.go index becc4afc47..ee4c48f48f 100644 --- a/utils/sysfs/sysfs.go +++ b/utils/sysfs/sysfs.go @@ -15,8 +15,6 @@ package sysfs import ( - "bytes" - "errors" "fmt" "io/ioutil" "os" @@ -37,9 +35,21 @@ const ( ppcDevTree = "/proc/device-tree" s390xDevTree = "/etc" // s390/s390x changes - coreIDFilePath = "/topology/core_id" - packageIDFilePath = "/topology/physical_package_id" - meminfoFile = "meminfo" + meminfoFile = "meminfo" + + sysFsCPUTopology = "topology" + + // CPUPhysicalPackageID is a physical package id of cpu#. Typically corresponds to a physical socket number, + // but the actual value is architecture and platform dependent. + CPUPhysicalPackageID = "physical_package_id" + // CPUCoreID is the CPU core ID of cpu#. Typically it is the hardware platform's identifier + // (rather than the kernel's). The actual value is architecture and platform dependent. + CPUCoreID = "core_id" + + coreIDFilePath = "/" + sysFsCPUTopology + "/core_id" + packageIDFilePath = "/" + sysFsCPUTopology + "/physical_package_id" + + // memory size calculations cpuDirPattern = "cpu*[0-9]" nodeDirPattern = "node*[0-9]" @@ -335,25 +345,145 @@ func (fs *realSysFs) GetSystemUUID() (string, error) { } } -func (fs *realSysFs) IsCPUOnline(dir string) bool { - cpuPath := fmt.Sprintf("%s/online", dir) - content, err := ioutil.ReadFile(cpuPath) +func (fs *realSysFs) IsCPUOnline(cpuPath string) bool { + onlinePath, err := filepath.Abs(cpuPath + "/../online") if err != nil { - pathErr, ok := err.(*os.PathError) - if ok { - if errors.Is(pathErr.Unwrap(), os.ErrNotExist) && isZeroCPU(dir) { - return true - } - } - klog.Warningf("unable to read %s: %s", cpuPath, err.Error()) + klog.V(1).Infof("Unable to get absolute path for %s", cpuPath) return false } - trimmed := bytes.TrimSpace(content) - return len(trimmed) == 1 && trimmed[0] == 49 + + // Quick check to determine if file exists: if it does not then kernel CPU hotplug is disabled and all CPUs are online. + _, err = os.Stat(onlinePath) + if err != nil && os.IsNotExist(err) { + return true + } + if err != nil { + klog.V(1).Infof("Unable to stat %s: %s", onlinePath, err) + } + + cpuID, err := getCPUID(cpuPath) + if err != nil { + klog.V(1).Infof("Unable to get CPU ID from path %s: %s", cpuPath, err) + return false + } + + isOnline, err := isCPUOnline(onlinePath, cpuID) + if err != nil { + klog.V(1).Infof("Unable to get online CPUs list: %s", err) + return false + } + return isOnline } -func isZeroCPU(dir string) bool { - regex := regexp.MustCompile("cpu([0-9]*)") +func getCPUID(dir string) (uint16, error) { + regex := regexp.MustCompile("cpu([0-9]+)") matches := regex.FindStringSubmatch(dir) - return len(matches) == 2 && matches[1] == "0" + if len(matches) == 2 { + id, err := strconv.Atoi(matches[1]) + if err != nil { + return 0, err + } + return uint16(id), nil + } + return 0, fmt.Errorf("can't get CPU ID from %s", dir) +} + +// isCPUOnline is copied from github.com/opencontainers/runc/libcontainer/cgroups/fs and modified to suite cAdvisor +// needs as Apache 2.0 license allows. +// It parses CPU list (such as: 0,3-5,10) into a struct that allows to determine quickly if CPU or particular ID is online. +// see: https://github.com/opencontainers/runc/blob/ab27e12cebf148aa5d1ee3ad13d9fc7ae12bf0b6/libcontainer/cgroups/fs/cpuset.go#L45 +func isCPUOnline(path string, cpuID uint16) (bool, error) { + fileContent, err := ioutil.ReadFile(path) + if err != nil { + return false, err + } + if len(fileContent) == 0 { + return false, fmt.Errorf("%s found to be empty", path) + } + + cpuList := strings.TrimSpace(string(fileContent)) + for _, s := range strings.Split(cpuList, ",") { + splitted := strings.SplitN(s, "-", 3) + switch len(splitted) { + case 3: + return false, fmt.Errorf("invalid values in %s", path) + case 2: + min, err := strconv.ParseUint(splitted[0], 10, 16) + if err != nil { + return false, err + } + max, err := strconv.ParseUint(splitted[1], 10, 16) + if err != nil { + return false, err + } + if min > max { + return false, fmt.Errorf("invalid values in %s", path) + } + for i := min; i <= max; i++ { + if uint16(i) == cpuID { + return true, nil + } + } + case 1: + value, err := strconv.ParseUint(s, 10, 16) + if err != nil { + return false, err + } + if uint16(value) == cpuID { + return true, nil + } + } + } + + return false, nil +} + +// Looks for sysfs cpu path containing given CPU property, e.g. core_id or physical_package_id +// and returns number of unique values of given property, exemplary usage: getting number of CPU physical cores +func GetUniqueCPUPropertyCount(cpuBusPath string, propertyName string) int { + absCPUBusPath, err := filepath.Abs(cpuBusPath) + if err != nil { + klog.Errorf("Cannot make %s absolute", cpuBusPath) + return 0 + } + pathPattern := absCPUBusPath + "/cpu*[0-9]" + sysCPUPaths, err := filepath.Glob(pathPattern) + if err != nil { + klog.Errorf("Cannot find files matching pattern (pathPattern: %s), number of unique %s set to 0", pathPattern, propertyName) + return 0 + } + onlinePath, err := filepath.Abs(cpuBusPath + "/online") + if err != nil { + klog.V(1).Infof("Unable to get absolute path for %s", cpuBusPath+"/../online") + return 0 + } + + if err != nil { + klog.V(1).Infof("Unable to get online CPUs list: %s", err) + return 0 + } + uniques := make(map[string]bool) + for _, sysCPUPath := range sysCPUPaths { + cpuID, err := getCPUID(sysCPUPath) + if err != nil { + klog.V(1).Infof("Unable to get CPU ID from path %s: %s", sysCPUPath, err) + return 0 + } + isOnline, err := isCPUOnline(onlinePath, cpuID) + if err != nil && !os.IsNotExist(err) { + klog.V(1).Infof("Unable to determine CPU online state: %s", err) + continue + } + if !isOnline && !os.IsNotExist(err) { + continue + } + propertyPath := filepath.Join(sysCPUPath, sysFsCPUTopology, propertyName) + propertyVal, err := ioutil.ReadFile(propertyPath) + if err != nil { + klog.Warningf("Cannot open %s, assuming 0 for %s of CPU %d", propertyPath, propertyName, cpuID) + propertyVal = []byte("0") + } + uniques[string(propertyVal)] = true + } + return len(uniques) } diff --git a/utils/sysfs/sysfs_notx86.go b/utils/sysfs/sysfs_notx86.go new file mode 100644 index 0000000000..86b8d02467 --- /dev/null +++ b/utils/sysfs/sysfs_notx86.go @@ -0,0 +1,19 @@ +// +build !x86 + +// Copyright 2021 Google Inc. All Rights Reserved. +// +// 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 sysfs + +var isX86 = false diff --git a/utils/sysfs/sysfs_test.go b/utils/sysfs/sysfs_test.go index a14129df91..5369f9995d 100644 --- a/utils/sysfs/sysfs_test.go +++ b/utils/sysfs/sysfs_test.go @@ -126,18 +126,182 @@ func TestGetHugePagesNrWhenFileIsMissing(t *testing.T) { func TestIsCPUOnline(t *testing.T) { sysFs := NewRealSysFs() - online := sysFs.IsCPUOnline("./testdata/node0/cpu0") + online := sysFs.IsCPUOnline("./testdata_epyc7402_nohyperthreading/cpu14") assert.True(t, online) - online = sysFs.IsCPUOnline("./testdata/node0/cpu1") + online = sysFs.IsCPUOnline("./testdata_epyc7402_nohyperthreading/cpu13") assert.False(t, online) } func TestIsCPUOnlineNoFileAndCPU0MustBeOnline(t *testing.T) { + // Test on x86. + origIsX86 := isX86 + defer func() { + isX86 = origIsX86 + }() + isX86 = false + sysFs := NewRealSysFs() - online := sysFs.IsCPUOnline("./testdata/missing_online/node0/cpu0") + online := sysFs.IsCPUOnline("./testdata/missing_online/node0/cpu33") assert.True(t, online) +} + +func TestIsCPU0OnlineOnx86(t *testing.T) { + // Test on x86. + origIsX86 := isX86 + defer func() { + isX86 = origIsX86 + }() + isX86 = true + + sysFs := NewRealSysFs() + online := sysFs.IsCPUOnline("path/is/irrelevant/it/is/zero/that/matters/cpu0") + assert.True(t, online) +} - online = sysFs.IsCPUOnline("./testdata/missing_online/node0/cpu33") +func TestCPU0OfflineOnNotx86(t *testing.T) { + // Test on arch other than x86. + origIsX86 := isX86 + defer func() { + isX86 = origIsX86 + }() + isX86 = false + + sysFs := NewRealSysFs() + online := sysFs.IsCPUOnline("./testdata_graviton2/cpu0") assert.False(t, online) } + +func TestIsCpuOnlineRaspberryPi4(t *testing.T) { + // Test on arch other than x86. + origIsX86 := isX86 + defer func() { + isX86 = origIsX86 + }() + isX86 = false + + sysFS := NewRealSysFs() + online := sysFS.IsCPUOnline("./testdata_rpi4/cpu0") + assert.True(t, online) + + online = sysFS.IsCPUOnline("./testdata_rpi4/cpu1") + assert.True(t, online) + + online = sysFS.IsCPUOnline("./testdata_rpi4/cpu2") + assert.True(t, online) + + online = sysFS.IsCPUOnline("./testdata_rpi4/cpu3") + assert.True(t, online) +} + +func TestIsCpuOnlineGraviton2(t *testing.T) { + // Test on arch other than x86. + origIsX86 := isX86 + defer func() { + isX86 = origIsX86 + }() + isX86 = false + + sysFS := NewRealSysFs() + online := sysFS.IsCPUOnline("./testdata_graviton2/cpu0") + assert.False(t, online) + + online = sysFS.IsCPUOnline("./testdata_graviton2/cpu1") + assert.True(t, online) + + online = sysFS.IsCPUOnline("./testdata_graviton2/cpu2") + assert.True(t, online) + + online = sysFS.IsCPUOnline("./testdata_graviton2/cpu3") + assert.True(t, online) +} + +func TestGetUniqueCPUPropertyCountOnRaspberryPi4(t *testing.T) { + // Test on arch other than x86. + origIsX86 := isX86 + defer func() { + isX86 = origIsX86 + }() + isX86 = false + + count := GetUniqueCPUPropertyCount("./testdata_rpi4/", CPUPhysicalPackageID) + assert.Equal(t, 1, count) + + count = GetUniqueCPUPropertyCount("./testdata_rpi4/", CPUCoreID) + assert.Equal(t, 4, count) +} + +func TestGetUniqueCPUPropertyCountOnEpyc7402(t *testing.T) { + // Test on x86. + origIsX86 := isX86 + defer func() { + isX86 = origIsX86 + }() + isX86 = true + + count := GetUniqueCPUPropertyCount("./testdata_epyc7402/", CPUPhysicalPackageID) + assert.Equal(t, 1, count) + + count = GetUniqueCPUPropertyCount("./testdata_epyc7402/", CPUCoreID) + assert.Equal(t, 24, count) +} + +func TestGetUniqueCPUPropertyCountOnEpyc7402NoHyperThreading(t *testing.T) { + // Test on x86. + origIsX86 := isX86 + defer func() { + isX86 = origIsX86 + }() + isX86 = true + + count := GetUniqueCPUPropertyCount("./testdata_epyc7402_nohyperthreading/", CPUPhysicalPackageID) + assert.Equal(t, 1, count) + + count = GetUniqueCPUPropertyCount("./testdata_epyc7402_nohyperthreading/", CPUCoreID) + assert.Equal(t, 17, count) +} + +func TestGetUniqueCPUPropertyCountOnXeon4214(t *testing.T) { + // Test on x86. + origIsX86 := isX86 + defer func() { + isX86 = origIsX86 + }() + isX86 = true + + count := GetUniqueCPUPropertyCount("./testdata_xeon4214_2socket/", CPUPhysicalPackageID) + assert.Equal(t, 2, count) + + count = GetUniqueCPUPropertyCount("./testdata_xeon4214_2socket/", CPUCoreID) + assert.Equal(t, 24, count) +} + +func TestGetUniqueCPUPropertyCountOnXeon5218NoHyperThreadingNoHotplug(t *testing.T) { + // Test on x86. + origIsX86 := isX86 + defer func() { + isX86 = origIsX86 + }() + isX86 = true + + count := GetUniqueCPUPropertyCount("./testdata_xeon5218_nohyperthread_2socket_nohotplug/", CPUPhysicalPackageID) + assert.Equal(t, 2, count) + + count = GetUniqueCPUPropertyCount("./testdata_xeon5218_nohyperthread_2socket_nohotplug/", CPUCoreID) + assert.Equal(t, 32, count) +} + +func TestUniqueCPUPropertyOnSingleSocketMultipleNUMAsSystem(t *testing.T) { + // Test on x86. + origIsX86 := isX86 + defer func() { + isX86 = origIsX86 + }() + isX86 = true + + count := GetUniqueCPUPropertyCount("./testdata_single_socket_many_NUMAs/", CPUPhysicalPackageID) + assert.Equal(t, 16, count) + + count = GetUniqueCPUPropertyCount("./testdata_single_socket_many_NUMAs/", CPUCoreID) + assert.Equal(t, 16, count) +} diff --git a/utils/sysfs/sysfs_x86.go b/utils/sysfs/sysfs_x86.go new file mode 100644 index 0000000000..fd6ff358f6 --- /dev/null +++ b/utils/sysfs/sysfs_x86.go @@ -0,0 +1,19 @@ +// +build x86 + +// Copyright 2021 Google Inc. All Rights Reserved. +// +// 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 sysfs + +var isX86 = true