github.com/google/cadvisor@v0.49.1/perf/collector_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  // Collector of perf events for a container.
    19  package perf
    20  
    21  import (
    22  	"bytes"
    23  	"encoding/binary"
    24  	"os"
    25  	"testing"
    26  	"unsafe"
    27  
    28  	"golang.org/x/sys/unix"
    29  
    30  	"github.com/stretchr/testify/assert"
    31  
    32  	info "github.com/google/cadvisor/info/v1"
    33  	"github.com/google/cadvisor/stats"
    34  )
    35  
    36  type buffer struct {
    37  	*bytes.Buffer
    38  }
    39  
    40  func (b buffer) Close() error {
    41  	return nil
    42  }
    43  
    44  func TestCollector_UpdateStats(t *testing.T) {
    45  	collector := collector{uncore: &stats.NoopCollector{}}
    46  	notScaledBuffer := buffer{bytes.NewBuffer([]byte{})}
    47  	scaledBuffer := buffer{bytes.NewBuffer([]byte{})}
    48  	groupedBuffer := buffer{bytes.NewBuffer([]byte{})}
    49  	err := binary.Write(notScaledBuffer, binary.LittleEndian, GroupReadFormat{
    50  		Nr:          1,
    51  		TimeEnabled: 100,
    52  		TimeRunning: 100,
    53  	})
    54  	assert.NoError(t, err)
    55  	err = binary.Write(notScaledBuffer, binary.LittleEndian, Values{
    56  		Value: 123456789,
    57  		ID:    0,
    58  	})
    59  	assert.NoError(t, err)
    60  	err = binary.Write(scaledBuffer, binary.LittleEndian, GroupReadFormat{
    61  		Nr:          1,
    62  		TimeEnabled: 3,
    63  		TimeRunning: 1,
    64  	})
    65  	assert.NoError(t, err)
    66  	err = binary.Write(scaledBuffer, binary.LittleEndian, Values{
    67  		Value: 333333333,
    68  		ID:    2,
    69  	})
    70  	assert.NoError(t, err)
    71  	err = binary.Write(groupedBuffer, binary.LittleEndian, GroupReadFormat{
    72  		Nr:          2,
    73  		TimeEnabled: 100,
    74  		TimeRunning: 100,
    75  	})
    76  	assert.NoError(t, err)
    77  	err = binary.Write(groupedBuffer, binary.LittleEndian, Values{
    78  		Value: 123456,
    79  		ID:    0,
    80  	})
    81  	assert.NoError(t, err)
    82  	err = binary.Write(groupedBuffer, binary.LittleEndian, Values{
    83  		Value: 654321,
    84  		ID:    1,
    85  	})
    86  	assert.NoError(t, err)
    87  
    88  	collector.cpuFiles = map[int]group{
    89  		1: {
    90  			cpuFiles: map[string]map[int]readerCloser{
    91  				"instructions": {0: notScaledBuffer},
    92  			},
    93  			names:      []string{"instructions"},
    94  			leaderName: "instructions",
    95  		},
    96  		2: {
    97  			cpuFiles: map[string]map[int]readerCloser{
    98  				"cycles": {11: scaledBuffer},
    99  			},
   100  			names:      []string{"cycles"},
   101  			leaderName: "cycles",
   102  		},
   103  		3: {
   104  			cpuFiles: map[string]map[int]readerCloser{
   105  				"cache-misses": {
   106  					0: groupedBuffer,
   107  				},
   108  			},
   109  			names:      []string{"cache-misses", "cache-references"},
   110  			leaderName: "cache-misses",
   111  		},
   112  	}
   113  
   114  	stats := &info.ContainerStats{}
   115  	err = collector.UpdateStats(stats)
   116  
   117  	assert.NoError(t, err)
   118  	assert.Len(t, stats.PerfStats, 4)
   119  
   120  	assert.Contains(t, stats.PerfStats, info.PerfStat{
   121  		PerfValue: info.PerfValue{
   122  			ScalingRatio: 0.3333333333333333,
   123  			Value:        999999999,
   124  			Name:         "cycles",
   125  		},
   126  		Cpu: 11,
   127  	})
   128  	assert.Contains(t, stats.PerfStats, info.PerfStat{
   129  		PerfValue: info.PerfValue{
   130  			ScalingRatio: 1,
   131  			Value:        123456789,
   132  			Name:         "instructions",
   133  		},
   134  		Cpu: 0,
   135  	})
   136  	assert.Contains(t, stats.PerfStats, info.PerfStat{
   137  		PerfValue: info.PerfValue{
   138  			ScalingRatio: 1.0,
   139  			Value:        123456,
   140  			Name:         "cache-misses",
   141  		},
   142  		Cpu: 0,
   143  	})
   144  	assert.Contains(t, stats.PerfStats, info.PerfStat{
   145  		PerfValue: info.PerfValue{
   146  			ScalingRatio: 1.0,
   147  			Value:        654321,
   148  			Name:         "cache-references",
   149  		},
   150  		Cpu: 0,
   151  	})
   152  }
   153  
   154  func TestCreatePerfEventAttr(t *testing.T) {
   155  	event := CustomEvent{
   156  		Type:   0x1,
   157  		Config: Config{uint64(0x2), uint64(0x3), uint64(0x4)},
   158  		Name:   "fake_event",
   159  	}
   160  
   161  	attributes := createPerfEventAttr(event)
   162  
   163  	assert.Equal(t, uint32(1), attributes.Type)
   164  	assert.Equal(t, uint64(2), attributes.Config)
   165  	assert.Equal(t, uint64(3), attributes.Ext1)
   166  	assert.Equal(t, uint64(4), attributes.Ext2)
   167  }
   168  
   169  func TestSetGroupAttributes(t *testing.T) {
   170  	event := CustomEvent{
   171  		Type:   0x1,
   172  		Config: Config{uint64(0x2), uint64(0x3), uint64(0x4)},
   173  		Name:   "fake_event",
   174  	}
   175  
   176  	attributes := createPerfEventAttr(event)
   177  	setAttributes(attributes, true)
   178  	assert.Equal(t, uint64(65536), attributes.Sample_type)
   179  	assert.Equal(t, uint64(0xf), attributes.Read_format)
   180  	assert.Equal(t, uint64(0x3), attributes.Bits)
   181  
   182  	attributes = createPerfEventAttr(event)
   183  	setAttributes(attributes, false)
   184  	assert.Equal(t, uint64(65536), attributes.Sample_type)
   185  	assert.Equal(t, uint64(0xf), attributes.Read_format)
   186  	assert.Equal(t, uint64(0x2), attributes.Bits)
   187  }
   188  
   189  func TestNewCollector(t *testing.T) {
   190  	perfCollector := newCollector("cgroup", PerfEvents{
   191  		Core: Events{
   192  			Events: []Group{{[]Event{"event_1"}, false}, {[]Event{"event_2"}, false}},
   193  			CustomEvents: []CustomEvent{{
   194  				Type:   0,
   195  				Config: []uint64{1, 2, 3},
   196  				Name:   "event_2",
   197  			}},
   198  		},
   199  	}, []int{0, 1, 2, 3}, map[int]int{})
   200  	assert.Len(t, perfCollector.eventToCustomEvent, 1)
   201  	assert.Nil(t, perfCollector.eventToCustomEvent[Event("event_1")])
   202  	assert.Same(t, &perfCollector.events.Core.CustomEvents[0], perfCollector.eventToCustomEvent[Event("event_2")])
   203  }
   204  
   205  func TestCollectorSetup(t *testing.T) {
   206  	path, err := os.MkdirTemp("", "cgroup")
   207  	assert.Nil(t, err)
   208  	defer func() {
   209  		err := os.RemoveAll(path)
   210  		assert.Nil(t, err)
   211  	}()
   212  	events := PerfEvents{
   213  		Core: Events{
   214  			Events: []Group{
   215  				{[]Event{"cache-misses"}, false},
   216  				{[]Event{"non-existing-event"}, false},
   217  			},
   218  		},
   219  	}
   220  	c := newCollector(path, events, []int{0}, map[int]int{0: 0})
   221  	c.perfEventOpen = func(attr *unix.PerfEventAttr, pid int, cpu int, groupFd int, flags int) (fd int, err error) {
   222  		return int(attr.Config), nil
   223  	}
   224  	c.ioctlSetInt = func(fd int, req uint, value int) error {
   225  		return nil
   226  	}
   227  	err = c.setup()
   228  	assert.Nil(t, err)
   229  	assert.Equal(t, 1, len(c.cpuFiles))
   230  	assert.Equal(t, []string{"cache-misses"}, c.cpuFiles[0].names)
   231  }
   232  
   233  var readGroupPerfStatCases = []struct {
   234  	test       string
   235  	file       GroupReadFormat
   236  	valuesFile Values
   237  	name       string
   238  	cpu        int
   239  	perfStat   []info.PerfStat
   240  	err        error
   241  }{
   242  	{
   243  		test: "no scaling",
   244  		file: GroupReadFormat{
   245  			TimeEnabled: 0,
   246  			TimeRunning: 0,
   247  			Nr:          1,
   248  		},
   249  		valuesFile: Values{
   250  			Value: 5,
   251  			ID:    0,
   252  		},
   253  		name: "some metric",
   254  		cpu:  1,
   255  		perfStat: []info.PerfStat{{
   256  			PerfValue: info.PerfValue{
   257  				ScalingRatio: 1,
   258  				Value:        5,
   259  				Name:         "some metric",
   260  			},
   261  			Cpu: 1,
   262  		}},
   263  		err: nil,
   264  	},
   265  	{
   266  		test: "no scaling - TimeEnabled = 0",
   267  		file: GroupReadFormat{
   268  			TimeEnabled: 0,
   269  			TimeRunning: 1,
   270  			Nr:          1,
   271  		},
   272  		valuesFile: Values{
   273  			Value: 5,
   274  			ID:    0,
   275  		},
   276  		name: "some metric",
   277  		cpu:  1,
   278  		perfStat: []info.PerfStat{{
   279  			PerfValue: info.PerfValue{
   280  				ScalingRatio: 1,
   281  				Value:        5,
   282  				Name:         "some metric",
   283  			},
   284  			Cpu: 1,
   285  		}},
   286  		err: nil,
   287  	},
   288  	{
   289  		test: "scaling - 0.5",
   290  		file: GroupReadFormat{
   291  			TimeEnabled: 4,
   292  			TimeRunning: 2,
   293  			Nr:          1,
   294  		},
   295  		valuesFile: Values{
   296  			Value: 4,
   297  			ID:    0,
   298  		},
   299  		name: "some metric",
   300  		cpu:  2,
   301  		perfStat: []info.PerfStat{{
   302  			PerfValue: info.PerfValue{
   303  				ScalingRatio: 0.5,
   304  				Value:        8,
   305  				Name:         "some metric",
   306  			},
   307  			Cpu: 2,
   308  		}},
   309  		err: nil,
   310  	},
   311  	{
   312  		test: "scaling - 0 (TimeEnabled = 1, TimeRunning = 0)",
   313  		file: GroupReadFormat{
   314  			TimeEnabled: 1,
   315  			TimeRunning: 0,
   316  			Nr:          1,
   317  		},
   318  		valuesFile: Values{
   319  			Value: 4,
   320  			ID:    0,
   321  		},
   322  		name: "some metric",
   323  		cpu:  3,
   324  		perfStat: []info.PerfStat{{
   325  			PerfValue: info.PerfValue{
   326  				ScalingRatio: 1.0,
   327  				Value:        4,
   328  				Name:         "some metric",
   329  			},
   330  			Cpu: 3,
   331  		}},
   332  		err: nil,
   333  	},
   334  	{
   335  		test: "scaling - 0 (TimeEnabled = 0, TimeRunning = 1)",
   336  		file: GroupReadFormat{
   337  			TimeEnabled: 0,
   338  			TimeRunning: 1,
   339  			Nr:          1,
   340  		},
   341  		valuesFile: Values{
   342  			Value: 4,
   343  			ID:    0,
   344  		},
   345  		name: "some metric",
   346  		cpu:  3,
   347  		perfStat: []info.PerfStat{{
   348  			PerfValue: info.PerfValue{
   349  				ScalingRatio: 1.0,
   350  				Value:        4,
   351  				Name:         "some metric",
   352  			},
   353  			Cpu: 3,
   354  		}},
   355  		err: nil,
   356  	},
   357  	{
   358  		test: "zeros, zeros everywhere",
   359  		file: GroupReadFormat{
   360  			TimeEnabled: 0,
   361  			TimeRunning: 0,
   362  			Nr:          1,
   363  		},
   364  		valuesFile: Values{
   365  			Value: 0,
   366  			ID:    0,
   367  		},
   368  		name: "some metric",
   369  		cpu:  4,
   370  		perfStat: []info.PerfStat{{
   371  			PerfValue: info.PerfValue{
   372  				ScalingRatio: 1.0,
   373  				Value:        0,
   374  				Name:         "some metric",
   375  			},
   376  			Cpu: 4,
   377  		}},
   378  		err: nil,
   379  	},
   380  	{
   381  		test: "non-zero TimeRunning",
   382  		file: GroupReadFormat{
   383  			TimeEnabled: 0,
   384  			TimeRunning: 3,
   385  			Nr:          1,
   386  		},
   387  		valuesFile: Values{
   388  			Value: 0,
   389  			ID:    0,
   390  		},
   391  		name: "some metric",
   392  		cpu:  4,
   393  		perfStat: []info.PerfStat{{
   394  			PerfValue: info.PerfValue{
   395  				ScalingRatio: 1.0,
   396  				Value:        0,
   397  				Name:         "some metric",
   398  			},
   399  			Cpu: 4,
   400  		}},
   401  		err: nil,
   402  	},
   403  }
   404  
   405  func TestReadPerfStat(t *testing.T) {
   406  	for _, test := range readGroupPerfStatCases {
   407  		t.Run(test.test, func(tt *testing.T) {
   408  			buf := &buffer{bytes.NewBuffer([]byte{})}
   409  			err := binary.Write(buf, binary.LittleEndian, test.file)
   410  			assert.NoError(tt, err)
   411  			err = binary.Write(buf, binary.LittleEndian, test.valuesFile)
   412  			assert.NoError(tt, err)
   413  			stat, err := readGroupPerfStat(buf, group{
   414  				cpuFiles:   nil,
   415  				names:      []string{test.name},
   416  				leaderName: test.name,
   417  			}, test.cpu, "/")
   418  			assert.Equal(tt, test.perfStat, stat)
   419  			assert.Equal(tt, test.err, err)
   420  		})
   421  	}
   422  }
   423  
   424  func TestReadPerfEventAttr(t *testing.T) {
   425  	var testCases = []struct {
   426  		expected      *unix.PerfEventAttr
   427  		pfmMockedFunc func(string, unsafe.Pointer) error
   428  	}{
   429  		{
   430  			&unix.PerfEventAttr{
   431  				Type:               0,
   432  				Size:               0,
   433  				Config:             0,
   434  				Sample:             0,
   435  				Sample_type:        0,
   436  				Read_format:        0,
   437  				Bits:               0,
   438  				Wakeup:             0,
   439  				Bp_type:            0,
   440  				Ext1:               0,
   441  				Ext2:               0,
   442  				Branch_sample_type: 0,
   443  				Sample_regs_user:   0,
   444  				Sample_stack_user:  0,
   445  				Clockid:            0,
   446  				Sample_regs_intr:   0,
   447  				Aux_watermark:      0,
   448  				Sample_max_stack:   0,
   449  			},
   450  			func(s string, pointer unsafe.Pointer) error {
   451  				return nil
   452  			},
   453  		},
   454  	}
   455  
   456  	for _, test := range testCases {
   457  		got, err := readPerfEventAttr("event_name", test.pfmMockedFunc)
   458  		assert.NoError(t, err)
   459  		assert.Equal(t, test.expected, got)
   460  	}
   461  }