github.com/google/cadvisor@v0.49.1/perf/uncore_libpfm_test.go (about)

     1  //go:build libpfm && cgo
     2  // +build libpfm,cgo
     3  
     4  // Copyright 2020 Google Inc. All Rights Reserved.
     5  //
     6  // Licensed under the Apache License, Version 2.0 (the "License");
     7  // you may not use this file except in compliance with the License.
     8  // You may obtain a copy of the License at
     9  //
    10  //     http://www.apache.org/licenses/LICENSE-2.0
    11  //
    12  // Unless required by applicable law or agreed to in writing, software
    13  // distributed under the License is distributed on an "AS IS" BASIS,
    14  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    15  // See the License for the specific language governing permissions and
    16  // limitations under the License.
    17  
    18  // Uncore perf events logic tests.
    19  package perf
    20  
    21  import (
    22  	"bytes"
    23  	"encoding/binary"
    24  	"os"
    25  	"path/filepath"
    26  	"testing"
    27  
    28  	"golang.org/x/sys/unix"
    29  
    30  	"github.com/stretchr/testify/assert"
    31  
    32  	v1 "github.com/google/cadvisor/info/v1"
    33  )
    34  
    35  func mockSystemDevices() (string, error) {
    36  	testDir, err := os.MkdirTemp("", "uncore_imc_test")
    37  	if err != nil {
    38  		return "", err
    39  	}
    40  
    41  	// First Uncore IMC PMU.
    42  	firstPMUPath := filepath.Join(testDir, "uncore_imc_0")
    43  	err = os.MkdirAll(firstPMUPath, os.ModePerm)
    44  	if err != nil {
    45  		return "", err
    46  	}
    47  	err = os.WriteFile(filepath.Join(firstPMUPath, "cpumask"), []byte("0-1"), 0777)
    48  	if err != nil {
    49  		return "", err
    50  	}
    51  	err = os.WriteFile(filepath.Join(firstPMUPath, "type"), []byte("18"), 0777)
    52  	if err != nil {
    53  		return "", err
    54  	}
    55  
    56  	// Second Uncore IMC PMU.
    57  	secondPMUPath := filepath.Join(testDir, "uncore_imc_1")
    58  	err = os.MkdirAll(secondPMUPath, os.ModePerm)
    59  	if err != nil {
    60  		return "", err
    61  	}
    62  	err = os.WriteFile(filepath.Join(secondPMUPath, "cpumask"), []byte("0,1"), 0777)
    63  	if err != nil {
    64  		return "", err
    65  	}
    66  	err = os.WriteFile(filepath.Join(secondPMUPath, "type"), []byte("19"), 0777)
    67  	if err != nil {
    68  		return "", err
    69  	}
    70  
    71  	return testDir, nil
    72  }
    73  
    74  func TestUncore(t *testing.T) {
    75  	path, err := mockSystemDevices()
    76  	assert.Nil(t, err)
    77  	defer func() {
    78  		err := os.RemoveAll(path)
    79  		assert.Nil(t, err)
    80  	}()
    81  
    82  	actual, err := getUncorePMUs(path)
    83  	assert.Nil(t, err)
    84  	expected := uncorePMUs{
    85  		"uncore_imc_0": {name: "uncore_imc_0", typeOf: 18, cpus: []uint32{0, 1}},
    86  		"uncore_imc_1": {name: "uncore_imc_1", typeOf: 19, cpus: []uint32{0, 1}},
    87  	}
    88  	assert.Equal(t, expected, actual)
    89  
    90  	pmuSet := uncorePMUs{
    91  		"uncore_imc_0": actual["uncore_imc_0"],
    92  		"uncore_imc_1": actual["uncore_imc_1"],
    93  	}
    94  
    95  	actualPMU, err := getPMU(pmuSet, expected["uncore_imc_0"].typeOf)
    96  	assert.Nil(t, err)
    97  	assert.Equal(t, expected["uncore_imc_0"], *actualPMU)
    98  }
    99  
   100  func TestUncoreCollectorSetup(t *testing.T) {
   101  	path, err := mockSystemDevices()
   102  	assert.Nil(t, err)
   103  	defer func() {
   104  		err := os.RemoveAll(path)
   105  		assert.Nil(t, err)
   106  	}()
   107  
   108  	events := PerfEvents{
   109  		Core: Events{
   110  			Events: []Group{
   111  				{[]Event{"cache-misses"}, false},
   112  			},
   113  		},
   114  		Uncore: Events{
   115  			Events: []Group{
   116  				{[]Event{"uncore_imc_1/cas_count_read"}, false},
   117  				{[]Event{"uncore_imc_1/non_existing_event"}, false},
   118  				{[]Event{"uncore_imc_0/cas_count_write", "uncore_imc_0/cas_count_read"}, true},
   119  			},
   120  			CustomEvents: []CustomEvent{
   121  				{19, Config{0x01, 0x02}, "uncore_imc_1/cas_count_read"},
   122  				{0, Config{0x02, 0x03}, "uncore_imc_0/cas_count_write"},
   123  				{18, Config{0x01, 0x02}, "uncore_imc_0/cas_count_read"},
   124  			},
   125  		},
   126  	}
   127  
   128  	collector := &uncoreCollector{}
   129  	collector.perfEventOpen = func(attr *unix.PerfEventAttr, pid int, cpu int, groupFd int, flags int) (fd int, err error) {
   130  		return int(attr.Config), nil
   131  	}
   132  	collector.ioctlSetInt = func(fd int, req uint, value int) error {
   133  		return nil
   134  	}
   135  
   136  	err = collector.setup(events, path)
   137  	assert.Equal(t, []string{"uncore_imc_1/cas_count_read"},
   138  		getMapKeys(collector.cpuFiles[0]["uncore_imc_1"].cpuFiles))
   139  	assert.ElementsMatch(t, []string{"uncore_imc_0/cas_count_write", "uncore_imc_0/cas_count_read"},
   140  		getMapKeys(collector.cpuFiles[2]["uncore_imc_0"].cpuFiles))
   141  
   142  	// There are no errors.
   143  	assert.Nil(t, err)
   144  }
   145  
   146  func TestParseUncoreEvents(t *testing.T) {
   147  	events := PerfEvents{
   148  		Uncore: Events{
   149  			Events: []Group{
   150  				{[]Event{"cas_count_read"}, false},
   151  				{[]Event{"cas_count_write"}, false},
   152  			},
   153  			CustomEvents: []CustomEvent{
   154  				{
   155  					Type:   17,
   156  					Config: Config{0x50, 0x60},
   157  					Name:   "cas_count_read",
   158  				},
   159  			},
   160  		},
   161  	}
   162  	eventToCustomEvent := parseUncoreEvents(events.Uncore)
   163  	assert.Len(t, eventToCustomEvent, 1)
   164  	assert.Equal(t, eventToCustomEvent["cas_count_read"].Name, Event("cas_count_read"))
   165  	assert.Equal(t, eventToCustomEvent["cas_count_read"].Type, uint32(17))
   166  	assert.Equal(t, eventToCustomEvent["cas_count_read"].Config, Config{0x50, 0x60})
   167  }
   168  
   169  func TestObtainPMUs(t *testing.T) {
   170  	got := uncorePMUs{
   171  		"uncore_imc_0": {name: "uncore_imc_0", typeOf: 18, cpus: []uint32{0, 1}},
   172  		"uncore_imc_1": {name: "uncore_imc_1", typeOf: 19, cpus: []uint32{0, 1}},
   173  	}
   174  
   175  	actual := obtainPMUs("uncore_imc_0", got)
   176  	assert.Equal(t, uncorePMUs{"uncore_imc_0": got["uncore_imc_0"]}, actual)
   177  
   178  	actual = obtainPMUs("uncore_imc_1", got)
   179  	assert.Equal(t, uncorePMUs{"uncore_imc_1": got["uncore_imc_1"]}, actual)
   180  
   181  	actual = obtainPMUs("", got)
   182  	assert.Equal(t, uncorePMUs{}, actual)
   183  }
   184  
   185  func TestUncoreParseEventName(t *testing.T) {
   186  	eventName, pmuPrefix := parseEventName("some_event")
   187  	assert.Equal(t, "some_event", eventName)
   188  	assert.Empty(t, pmuPrefix)
   189  
   190  	eventName, pmuPrefix = parseEventName("some_pmu/some_event")
   191  	assert.Equal(t, "some_pmu", pmuPrefix)
   192  	assert.Equal(t, "some_event", eventName)
   193  
   194  	eventName, pmuPrefix = parseEventName("some_pmu/some_event/first_slash/second_slash")
   195  	assert.Equal(t, "some_pmu", pmuPrefix)
   196  	assert.Equal(t, "some_event/first_slash/second_slash", eventName)
   197  }
   198  
   199  func TestCheckGroup(t *testing.T) {
   200  	var testCases = []struct {
   201  		group          Group
   202  		eventPMUs      map[Event]uncorePMUs
   203  		expectedOutput string
   204  	}{
   205  		{
   206  			Group{[]Event{"uncore_imc/cas_count_write"}, false},
   207  			map[Event]uncorePMUs{},
   208  			"the event \"uncore_imc/cas_count_write\" don't have any PMU to count with",
   209  		},
   210  		{
   211  			Group{[]Event{"uncore_imc/cas_count_write", "uncore_imc/cas_count_read"}, true},
   212  			map[Event]uncorePMUs{"uncore_imc/cas_count_write": {
   213  				"uncore_imc_0": {name: "uncore_imc_0", typeOf: 18, cpus: []uint32{0, 1}},
   214  				"uncore_imc_1": {name: "uncore_imc_1", typeOf: 19, cpus: []uint32{0, 1}},
   215  			},
   216  				"uncore_imc/cas_count_read": {
   217  					"uncore_imc_0": {name: "uncore_imc_0", typeOf: 18, cpus: []uint32{0, 1}},
   218  					"uncore_imc_1": {name: "uncore_imc_1", typeOf: 19, cpus: []uint32{0, 1}},
   219  				},
   220  			},
   221  			"the events in group usually have to be from single PMU, try reorganizing the \"[uncore_imc/cas_count_write uncore_imc/cas_count_read]\" group",
   222  		},
   223  		{
   224  			Group{[]Event{"uncore_imc_0/cas_count_write", "uncore_imc_1/cas_count_read"}, true},
   225  			map[Event]uncorePMUs{"uncore_imc_0/cas_count_write": {
   226  				"uncore_imc_0": {name: "uncore_imc_0", typeOf: 18, cpus: []uint32{0, 1}},
   227  			},
   228  				"uncore_imc_1/cas_count_read": {
   229  					"uncore_imc_1": {name: "uncore_imc_1", typeOf: 19, cpus: []uint32{0, 1}},
   230  				},
   231  			},
   232  			"the events in group usually have to be from the same PMU, try reorganizing the \"[uncore_imc_0/cas_count_write uncore_imc_1/cas_count_read]\" group",
   233  		},
   234  		{
   235  			Group{[]Event{"uncore_imc/cas_count_write"}, false},
   236  			map[Event]uncorePMUs{"uncore_imc/cas_count_write": {
   237  				"uncore_imc_0": {name: "uncore_imc_0", typeOf: 18, cpus: []uint32{0, 1}},
   238  				"uncore_imc_1": {name: "uncore_imc_1", typeOf: 19, cpus: []uint32{0, 1}},
   239  			}},
   240  			"",
   241  		},
   242  		{
   243  			Group{[]Event{"uncore_imc_0/cas_count_write", "uncore_imc_0/cas_count_read"}, true},
   244  			map[Event]uncorePMUs{"uncore_imc_0/cas_count_write": {
   245  				"uncore_imc_0": {name: "uncore_imc_0", typeOf: 18, cpus: []uint32{0, 1}},
   246  			},
   247  				"uncore_imc_0/cas_count_read": {
   248  					"uncore_imc_0": {name: "uncore_imc_0", typeOf: 18, cpus: []uint32{0, 1}},
   249  				}},
   250  			"",
   251  		},
   252  	}
   253  
   254  	for _, tc := range testCases {
   255  		err := checkGroup(tc.group, tc.eventPMUs)
   256  		if tc.expectedOutput == "" {
   257  			assert.Nil(t, err)
   258  		} else {
   259  			assert.EqualError(t, err, tc.expectedOutput)
   260  		}
   261  	}
   262  }
   263  
   264  func TestReadPerfUncoreStat(t *testing.T) {
   265  	file := GroupReadFormat{
   266  		TimeEnabled: 0,
   267  		TimeRunning: 1,
   268  		Nr:          1,
   269  	}
   270  
   271  	valuesFile := Values{
   272  		Value: 4,
   273  		ID:    0,
   274  	}
   275  
   276  	expectedStat := []v1.PerfUncoreStat{{
   277  		PerfValue: v1.PerfValue{
   278  			ScalingRatio: 1,
   279  			Value:        4,
   280  			Name:         "foo",
   281  		},
   282  		Socket: 0,
   283  		PMU:    "bar",
   284  	}}
   285  	cpuToSocket := map[int]int{
   286  		1: 0,
   287  		2: 0,
   288  	}
   289  
   290  	buf := &buffer{bytes.NewBuffer([]byte{})}
   291  	err := binary.Write(buf, binary.LittleEndian, file)
   292  	assert.NoError(t, err)
   293  	err = binary.Write(buf, binary.LittleEndian, valuesFile)
   294  	assert.NoError(t, err)
   295  
   296  	stat, err := readPerfUncoreStat(buf, group{
   297  		cpuFiles:   nil,
   298  		names:      []string{"foo"},
   299  		leaderName: "foo",
   300  	}, 1, "bar", cpuToSocket)
   301  	assert.NoError(t, err)
   302  	assert.Equal(t, expectedStat, stat)
   303  }
   304  
   305  func getMapKeys(someMap map[string]map[int]readerCloser) []string {
   306  	var keys []string
   307  	for key := range someMap {
   308  		keys = append(keys, key)
   309  	}
   310  	return keys
   311  }