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  }