github.com/google/cadvisor@v0.49.1/machine/topology_test.go (about) 1 // Copyright 2014 Google Inc. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package machine 16 17 import ( 18 "encoding/json" 19 "os" 20 "path/filepath" 21 "reflect" 22 "sort" 23 "testing" 24 25 "github.com/stretchr/testify/assert" 26 27 info "github.com/google/cadvisor/info/v1" 28 "github.com/google/cadvisor/utils/sysfs" 29 "github.com/google/cadvisor/utils/sysfs/fakesysfs" 30 ) 31 32 func TestPhysicalCores(t *testing.T) { 33 testfile := "./testdata/cpuinfo" 34 35 testcpuinfo, err := os.ReadFile(testfile) 36 assert.Nil(t, err) 37 assert.NotNil(t, testcpuinfo) 38 39 numPhysicalCores := GetPhysicalCores(testcpuinfo) 40 assert.Equal(t, 6, numPhysicalCores) 41 } 42 43 func TestPhysicalCoresReadingFromCpuBus(t *testing.T) { 44 origCPUAttributesPath := cpuAttributesPath 45 defer func() { 46 cpuAttributesPath = origCPUAttributesPath 47 }() 48 cpuAttributesPath = "./testdata/sysfs_cpus/" // overwriting package variable to mock sysfs 49 testfile := "./testdata/cpuinfo_arm" // mock cpuinfo without core id 50 51 testcpuinfo, err := os.ReadFile(testfile) 52 assert.Nil(t, err) 53 assert.NotNil(t, testcpuinfo) 54 55 numPhysicalCores := GetPhysicalCores(testcpuinfo) 56 assert.Equal(t, 1, numPhysicalCores) 57 } 58 59 func TestPhysicalCoresFromWrongSysFs(t *testing.T) { 60 origCPUAttributesPath := cpuAttributesPath 61 defer func() { 62 cpuAttributesPath = origCPUAttributesPath 63 }() 64 cpuAttributesPath = "./testdata/wrongsysfs" // overwriting package variable to mock sysfs 65 testfile := "./testdata/cpuinfo_arm" // mock cpuinfo without core id 66 67 testcpuinfo, err := os.ReadFile(testfile) 68 assert.Nil(t, err) 69 assert.NotNil(t, testcpuinfo) 70 71 numPhysicalCores := GetPhysicalCores(testcpuinfo) 72 assert.Equal(t, 0, numPhysicalCores) 73 } 74 75 func TestSockets(t *testing.T) { 76 testfile := "./testdata/cpuinfo" 77 78 testcpuinfo, err := os.ReadFile(testfile) 79 assert.Nil(t, err) 80 assert.NotNil(t, testcpuinfo) 81 82 numSockets := GetSockets(testcpuinfo) 83 assert.Equal(t, 2, numSockets) 84 } 85 86 func TestSocketsReadingFromCpuBus(t *testing.T) { 87 origCPUAttributesPath := cpuAttributesPath 88 defer func() { 89 cpuAttributesPath = origCPUAttributesPath 90 }() 91 cpuAttributesPath = "./testdata/wrongsysfs" // overwriting package variable to mock sysfs 92 testfile := "./testdata/cpuinfo_arm" // mock cpuinfo without physical id 93 94 testcpuinfo, err := os.ReadFile(testfile) 95 assert.Nil(t, err) 96 assert.NotNil(t, testcpuinfo) 97 98 numSockets := GetSockets(testcpuinfo) 99 assert.Equal(t, 0, numSockets) 100 } 101 102 func TestSocketsReadingFromWrongSysFs(t *testing.T) { 103 path, err := filepath.Abs("./testdata/sysfs_cpus/") 104 assert.NoError(t, err) 105 106 origCPUAttributesPath := cpuAttributesPath 107 defer func() { 108 cpuAttributesPath = origCPUAttributesPath 109 }() 110 cpuAttributesPath = path // overwriting package variable to mock sysfs 111 testfile := "./testdata/cpuinfo_arm" // mock cpuinfo without physical id 112 113 testcpuinfo, err := os.ReadFile(testfile) 114 assert.Nil(t, err) 115 assert.NotNil(t, testcpuinfo) 116 117 numSockets := GetSockets(testcpuinfo) 118 assert.Equal(t, 1, numSockets) 119 } 120 121 func TestTopology(t *testing.T) { 122 machineArch = "" // overwrite package variable 123 sysFs := &fakesysfs.FakeSysFs{} 124 c := sysfs.CacheInfo{ 125 Size: 32 * 1024, 126 Type: "unified", 127 Level: 1, 128 Cpus: 2, 129 } 130 sysFs.SetCacheInfo(c) 131 132 nodesPaths := []string{ 133 "/fakeSysfs/devices/system/node/node0", 134 "/fakeSysfs/devices/system/node/node1", 135 } 136 sysFs.SetNodesPaths(nodesPaths, nil) 137 138 cpusPaths := map[string][]string{ 139 "/fakeSysfs/devices/system/node/node0": { 140 "/fakeSysfs/devices/system/node/node0/cpu0", 141 "/fakeSysfs/devices/system/node/node0/cpu1", 142 "/fakeSysfs/devices/system/node/node0/cpu2", 143 "/fakeSysfs/devices/system/node/node0/cpu6", 144 "/fakeSysfs/devices/system/node/node0/cpu7", 145 "/fakeSysfs/devices/system/node/node0/cpu8", 146 }, 147 "/fakeSysfs/devices/system/node/node1": { 148 "/fakeSysfs/devices/system/node/node0/cpu3", 149 "/fakeSysfs/devices/system/node/node0/cpu4", 150 "/fakeSysfs/devices/system/node/node0/cpu5", 151 "/fakeSysfs/devices/system/node/node0/cpu9", 152 "/fakeSysfs/devices/system/node/node0/cpu10", 153 "/fakeSysfs/devices/system/node/node0/cpu11", 154 }, 155 } 156 sysFs.SetCPUsPaths(cpusPaths, nil) 157 158 coreThread := map[string]string{ 159 "/fakeSysfs/devices/system/node/node0/cpu0": "0", 160 "/fakeSysfs/devices/system/node/node0/cpu1": "1", 161 "/fakeSysfs/devices/system/node/node0/cpu2": "2", 162 "/fakeSysfs/devices/system/node/node0/cpu3": "3", 163 "/fakeSysfs/devices/system/node/node0/cpu4": "4", 164 "/fakeSysfs/devices/system/node/node0/cpu5": "5", 165 "/fakeSysfs/devices/system/node/node0/cpu6": "0", 166 "/fakeSysfs/devices/system/node/node0/cpu7": "1", 167 "/fakeSysfs/devices/system/node/node0/cpu8": "2", 168 "/fakeSysfs/devices/system/node/node0/cpu9": "3", 169 "/fakeSysfs/devices/system/node/node0/cpu10": "4", 170 "/fakeSysfs/devices/system/node/node0/cpu11": "5", 171 } 172 sysFs.SetCoreThreads(coreThread, nil) 173 174 memTotal := "MemTotal: 32817192 kB" 175 sysFs.SetMemory(memTotal, nil) 176 177 hugePages := []os.FileInfo{ 178 &fakesysfs.FileInfo{EntryName: "hugepages-2048kB"}, 179 &fakesysfs.FileInfo{EntryName: "hugepages-1048576kB"}, 180 } 181 sysFs.SetHugePages(hugePages, nil) 182 183 hugePageNr := map[string]string{ 184 "/fakeSysfs/devices/system/node/node0/hugepages/hugepages-2048kB/nr_hugepages": "1", 185 "/fakeSysfs/devices/system/node/node0/hugepages/hugepages-1048576kB/nr_hugepages": "1", 186 "/fakeSysfs/devices/system/node/node1/hugepages/hugepages-2048kB/nr_hugepages": "1", 187 "/fakeSysfs/devices/system/node/node1/hugepages/hugepages-1048576kB/nr_hugepages": "1", 188 } 189 sysFs.SetHugePagesNr(hugePageNr, nil) 190 191 physicalPackageIDs := map[string]string{ 192 "/fakeSysfs/devices/system/node/node0/cpu0": "0", 193 "/fakeSysfs/devices/system/node/node0/cpu1": "0", 194 "/fakeSysfs/devices/system/node/node0/cpu2": "0", 195 "/fakeSysfs/devices/system/node/node0/cpu3": "0", 196 "/fakeSysfs/devices/system/node/node0/cpu4": "0", 197 "/fakeSysfs/devices/system/node/node0/cpu5": "0", 198 "/fakeSysfs/devices/system/node/node0/cpu6": "1", 199 "/fakeSysfs/devices/system/node/node0/cpu7": "1", 200 "/fakeSysfs/devices/system/node/node0/cpu8": "1", 201 "/fakeSysfs/devices/system/node/node0/cpu9": "1", 202 "/fakeSysfs/devices/system/node/node0/cpu10": "1", 203 "/fakeSysfs/devices/system/node/node0/cpu11": "1", 204 } 205 sysFs.SetPhysicalPackageIDs(physicalPackageIDs, nil) 206 207 sysFs.SetDistances("/fakeSysfs/devices/system/node/node0", "10 11", nil) 208 sysFs.SetDistances("/fakeSysfs/devices/system/node/node1", "11 10", nil) 209 210 topology, numCores, err := GetTopology(sysFs) 211 assert.Nil(t, err) 212 assert.Equal(t, 12, numCores) 213 214 expectedTopology := []info.Node{} 215 numNodes := 2 216 numCoresPerNode := 3 217 numThreads := 2 218 cache := info.Cache{ 219 Size: 32 * 1024, 220 Type: "unified", 221 Level: 1, 222 } 223 distances := [][]uint64{ 224 {10, 11}, 225 {11, 10}, 226 } 227 for i := 0; i < numNodes; i++ { 228 node := info.Node{Id: i} 229 // Copy over Memory from result. TODO(rjnagal): Use memory from fake. 230 node.Memory = topology[i].Memory 231 // Copy over HugePagesInfo from result. TODO(ohsewon): Use HugePagesInfo from fake. 232 node.HugePages = topology[i].HugePages 233 node.Distances = distances[i] 234 for j := 0; j < numCoresPerNode; j++ { 235 core := info.Core{Id: i*numCoresPerNode + j} 236 core.Caches = append(core.Caches, cache) 237 for k := 0; k < numThreads; k++ { 238 core.Threads = append(core.Threads, k*numCoresPerNode*numNodes+core.Id) 239 } 240 node.Cores = append(node.Cores, core) 241 } 242 expectedTopology = append(expectedTopology, node) 243 } 244 245 assert.NotNil(t, reflect.DeepEqual(topology, expectedTopology)) 246 } 247 248 func TestTopologyEmptySysFs(t *testing.T) { 249 machineArch = "" // overwrite package variable 250 _, _, err := GetTopology(&fakesysfs.FakeSysFs{}) 251 assert.NotNil(t, err) 252 } 253 254 func TestTopologyWithoutNodes(t *testing.T) { 255 machineArch = "" // overwrite package variable 256 sysFs := &fakesysfs.FakeSysFs{} 257 258 c := sysfs.CacheInfo{ 259 Id: 0, 260 Size: 32 * 1024, 261 Type: "unified", 262 Level: 0, 263 Cpus: 2, 264 } 265 sysFs.SetCacheInfo(c) 266 267 nodesPaths := []string{} 268 sysFs.SetNodesPaths(nodesPaths, nil) 269 270 cpusPaths := map[string][]string{ 271 "/sys/devices/system/cpu": { 272 "/sys/devices/system/cpu/cpu0", 273 "/sys/devices/system/cpu/cpu1", 274 "/sys/devices/system/cpu/cpu2", 275 "/sys/devices/system/cpu/cpu3", 276 }, 277 } 278 sysFs.SetCPUsPaths(cpusPaths, nil) 279 280 coreThread := map[string]string{ 281 "/sys/devices/system/cpu/cpu0": "0", 282 "/sys/devices/system/cpu/cpu1": "1", 283 "/sys/devices/system/cpu/cpu2": "0", 284 "/sys/devices/system/cpu/cpu3": "1", 285 } 286 sysFs.SetCoreThreads(coreThread, nil) 287 288 physicalPackageIDs := map[string]string{ 289 "/sys/devices/system/cpu/cpu0": "0", 290 "/sys/devices/system/cpu/cpu1": "1", 291 "/sys/devices/system/cpu/cpu2": "0", 292 "/sys/devices/system/cpu/cpu3": "1", 293 } 294 sysFs.SetPhysicalPackageIDs(physicalPackageIDs, nil) 295 296 topology, numCores, err := GetTopology(sysFs) 297 sort.SliceStable(topology, func(i, j int) bool { 298 return topology[i].Id < topology[j].Id 299 }) 300 assert.Nil(t, err) 301 assert.Equal(t, 2, len(topology)) 302 assert.Equal(t, 4, numCores) 303 304 topologyJSON1, err := json.Marshal(topology[0]) 305 assert.Nil(t, err) 306 topologyJSON2, err := json.Marshal(topology[1]) 307 assert.Nil(t, err) 308 309 expectedTopology1 := `{"node_id":0,"memory":0,"hugepages":null,"distances":null,"cores":[{"core_id":0,"thread_ids":[0,2],"caches":[{"id":0, "size":32768,"type":"unified","level":0}], "socket_id": 0, "uncore_caches":null}],"caches":null}` 310 expectedTopology2 := ` 311 { 312 "node_id":1, 313 "memory":0, 314 "hugepages":null, 315 "distances": null, 316 "cores":[ 317 { 318 "core_id":1, 319 "thread_ids":[ 320 1, 321 3 322 ], 323 "caches":[ 324 { 325 "id": 0, 326 "size":32768, 327 "type":"unified", 328 "level":0 329 } 330 ], 331 "socket_id": 1, 332 "uncore_caches": null 333 } 334 ], 335 "caches":null 336 }` 337 338 json1 := string(topologyJSON1) 339 json2 := string(topologyJSON2) 340 341 assert.JSONEq(t, expectedTopology1, json1) 342 assert.JSONEq(t, expectedTopology2, json2) 343 } 344 345 func TestTopologyWithNodesWithoutCPU(t *testing.T) { 346 machineArch = "" // overwrite package variable 347 sysFs := &fakesysfs.FakeSysFs{} 348 nodesPaths := []string{ 349 "/fakeSysfs/devices/system/node/node0", 350 "/fakeSysfs/devices/system/node/node1", 351 } 352 sysFs.SetNodesPaths(nodesPaths, nil) 353 354 memTotal := "MemTotal: 32817192 kB" 355 sysFs.SetMemory(memTotal, nil) 356 357 hugePages := []os.FileInfo{ 358 &fakesysfs.FileInfo{EntryName: "hugepages-2048kB"}, 359 &fakesysfs.FileInfo{EntryName: "hugepages-1048576kB"}, 360 } 361 sysFs.SetHugePages(hugePages, nil) 362 363 hugePageNr := map[string]string{ 364 "/fakeSysfs/devices/system/node/node0/hugepages/hugepages-2048kB/nr_hugepages": "1", 365 "/fakeSysfs/devices/system/node/node0/hugepages/hugepages-1048576kB/nr_hugepages": "1", 366 "/fakeSysfs/devices/system/node/node1/hugepages/hugepages-2048kB/nr_hugepages": "1", 367 "/fakeSysfs/devices/system/node/node1/hugepages/hugepages-1048576kB/nr_hugepages": "1", 368 } 369 sysFs.SetHugePagesNr(hugePageNr, nil) 370 371 sysFs.SetDistances("/fakeSysfs/devices/system/node/node0", "10 11", nil) 372 sysFs.SetDistances("/fakeSysfs/devices/system/node/node1", "11 10", nil) 373 374 topology, numCores, err := GetTopology(sysFs) 375 376 assert.Nil(t, err) 377 assert.Equal(t, 0, numCores) 378 379 topologyJSON, err := json.Marshal(topology) 380 assert.Nil(t, err) 381 382 expectedTopology := `[ 383 { 384 "caches": null, 385 "cores": null, 386 "hugepages": [ 387 { 388 "num_pages": 1, 389 "page_size": 2048 390 }, 391 { 392 "num_pages": 1, 393 "page_size": 1048576 394 } 395 ], 396 "distances": [ 397 10, 398 11 399 ], 400 "memory": 33604804608, 401 "node_id": 0 402 }, 403 { 404 "caches": null, 405 "cores": null, 406 "hugepages": [ 407 { 408 "num_pages": 1, 409 "page_size": 2048 410 }, 411 { 412 "num_pages": 1, 413 "page_size": 1048576 414 } 415 ], 416 "distances": [ 417 11, 418 10 419 ], 420 "memory": 33604804608, 421 "node_id": 1 422 } 423 ] 424 ` 425 assert.JSONEq(t, expectedTopology, string(topologyJSON)) 426 } 427 428 func TestTopologyOnSystemZ(t *testing.T) { 429 machineArch = "s390" // overwrite package variable 430 nodes, cores, err := GetTopology(&fakesysfs.FakeSysFs{}) 431 assert.Nil(t, err) 432 assert.Nil(t, nodes) 433 assert.NotNil(t, cores) 434 } 435 436 func TestMemoryInfo(t *testing.T) { 437 testPath := "./testdata/edac/mc" 438 memory, err := GetMachineMemoryByType(testPath) 439 440 assert.Nil(t, err) 441 assert.Len(t, memory, 2) 442 assert.Equal(t, uint64(789*1024*1024), memory["Unbuffered-DDR4"].Capacity) 443 assert.Equal(t, uint64(579*1024*1024), memory["Non-volatile-RAM"].Capacity) 444 assert.Equal(t, uint(1), memory["Unbuffered-DDR4"].DimmCount) 445 assert.Equal(t, uint(2), memory["Non-volatile-RAM"].DimmCount) 446 } 447 448 func TestMemoryInfoOnArchThatDoNotExposeMemoryController(t *testing.T) { 449 testPath := "./there/is/no/spoon" 450 memory, err := GetMachineMemoryByType(testPath) 451 452 assert.Nil(t, err) 453 assert.Len(t, memory, 0) 454 } 455 456 func TestClockSpeedOnCpuUpperCase(t *testing.T) { 457 maxFreqFile = "" // do not read the system max frequency 458 machineArch = "" // overwrite package variable 459 testfile := "./testdata/cpuinfo_upper_case" // mock cpuinfo with CPU MHz 460 461 testcpuinfo, err := os.ReadFile(testfile) 462 assert.Nil(t, err) 463 assert.NotNil(t, testcpuinfo) 464 465 clockSpeed, err := GetClockSpeed(testcpuinfo) 466 assert.Nil(t, err) 467 assert.NotNil(t, clockSpeed) 468 assert.Equal(t, uint64(1800*1000), clockSpeed) 469 } 470 471 func TestClockSpeedOnCpuLowerCase(t *testing.T) { 472 maxFreqFile = "" // do not read the system max frequency 473 machineArch = "" // overwrite package variable 474 testfile := "./testdata/cpuinfo_lower_case" // mock cpuinfo with cpu MHz 475 476 testcpuinfo, err := os.ReadFile(testfile) 477 assert.Nil(t, err) 478 assert.NotNil(t, testcpuinfo) 479 480 clockSpeed, err := GetClockSpeed(testcpuinfo) 481 assert.Nil(t, err) 482 assert.NotNil(t, clockSpeed) 483 assert.Equal(t, uint64(1450*1000), clockSpeed) 484 } 485 486 func TestGetCPUVendorID(t *testing.T) { 487 var testCases = []struct { 488 file string 489 expected string 490 }{ 491 { 492 "./testdata/cpuinfo_onesocket_many_NUMAs", 493 "GenuineIntel", 494 }, 495 { 496 "./testdata/cpuinfo_arm", 497 "", 498 }, 499 } 500 501 for _, test := range testCases { 502 testcpuinfo, err := os.ReadFile(test.file) 503 assert.Nil(t, err) 504 assert.NotNil(t, testcpuinfo) 505 cpuVendorID := GetCPUVendorID(testcpuinfo) 506 assert.Equal(t, test.expected, cpuVendorID) 507 } 508 }