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 }