mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-14 18:53:35 +00:00
build: move e2e dependencies into e2e/go.mod
Several packages are only used while running the e2e suite. These packages are less important to update, as the they can not influence the final executable that is part of the Ceph-CSI container-image. By moving these dependencies out of the main Ceph-CSI go.mod, it is easier to identify if a reported CVE affects Ceph-CSI, or only the testing (like most of the Kubernetes CVEs). Signed-off-by: Niels de Vos <ndevos@ibm.com>
This commit is contained in:
committed by
mergify[bot]
parent
15da101b1b
commit
bec6090996
456
e2e/vendor/github.com/google/cadvisor/perf/collector_libpfm.go
generated
vendored
Normal file
456
e2e/vendor/github.com/google/cadvisor/perf/collector_libpfm.go
generated
vendored
Normal file
@ -0,0 +1,456 @@
|
||||
//go:build libpfm && cgo
|
||||
// +build libpfm,cgo
|
||||
|
||||
// Copyright 2020 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Collector of perf events for a container.
|
||||
package perf
|
||||
|
||||
// #cgo CFLAGS: -I/usr/include
|
||||
// #cgo LDFLAGS: -lpfm
|
||||
// #include <perfmon/pfmlib.h>
|
||||
// #include <stdlib.h>
|
||||
// #include <string.h>
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
info "github.com/google/cadvisor/info/v1"
|
||||
"github.com/google/cadvisor/stats"
|
||||
)
|
||||
|
||||
type collector struct {
|
||||
cgroupPath string
|
||||
events PerfEvents
|
||||
cpuFiles map[int]group
|
||||
cpuFilesLock sync.Mutex
|
||||
onlineCPUs []int
|
||||
eventToCustomEvent map[Event]*CustomEvent
|
||||
uncore stats.Collector
|
||||
|
||||
// Handle for mocking purposes.
|
||||
perfEventOpen func(attr *unix.PerfEventAttr, pid int, cpu int, groupFd int, flags int) (fd int, err error)
|
||||
ioctlSetInt func(fd int, req uint, value int) error
|
||||
}
|
||||
|
||||
type group struct {
|
||||
cpuFiles map[string]map[int]readerCloser
|
||||
names []string
|
||||
leaderName string
|
||||
}
|
||||
|
||||
var (
|
||||
isLibpfmInitialized = false
|
||||
libpfmMutex = sync.Mutex{}
|
||||
)
|
||||
|
||||
const (
|
||||
groupLeaderFileDescriptor = -1
|
||||
)
|
||||
|
||||
func init() {
|
||||
libpfmMutex.Lock()
|
||||
defer libpfmMutex.Unlock()
|
||||
pErr := C.pfm_initialize()
|
||||
if pErr != C.PFM_SUCCESS {
|
||||
klog.Errorf("unable to initialize libpfm: %d", int(pErr))
|
||||
return
|
||||
}
|
||||
isLibpfmInitialized = true
|
||||
}
|
||||
|
||||
func newCollector(cgroupPath string, events PerfEvents, onlineCPUs []int, cpuToSocket map[int]int) *collector {
|
||||
collector := &collector{cgroupPath: cgroupPath, events: events, onlineCPUs: onlineCPUs, cpuFiles: map[int]group{}, uncore: NewUncoreCollector(cgroupPath, events, cpuToSocket), perfEventOpen: unix.PerfEventOpen, ioctlSetInt: unix.IoctlSetInt}
|
||||
mapEventsToCustomEvents(collector)
|
||||
return collector
|
||||
}
|
||||
|
||||
func (c *collector) UpdateStats(stats *info.ContainerStats) error {
|
||||
err := c.uncore.UpdateStats(stats)
|
||||
if err != nil {
|
||||
klog.Errorf("Failed to get uncore perf event stats: %v", err)
|
||||
}
|
||||
|
||||
c.cpuFilesLock.Lock()
|
||||
defer c.cpuFilesLock.Unlock()
|
||||
|
||||
stats.PerfStats = []info.PerfStat{}
|
||||
klog.V(5).Infof("Attempting to update perf_event stats from cgroup %q", c.cgroupPath)
|
||||
|
||||
for _, group := range c.cpuFiles {
|
||||
for cpu, file := range group.cpuFiles[group.leaderName] {
|
||||
stat, err := readGroupPerfStat(file, group, cpu, c.cgroupPath)
|
||||
if err != nil {
|
||||
klog.Warningf("Unable to read from perf_event_file (event: %q, CPU: %d) for %q: %q", group.leaderName, cpu, c.cgroupPath, err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
stats.PerfStats = append(stats.PerfStats, stat...)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func readGroupPerfStat(file readerCloser, group group, cpu int, cgroupPath string) ([]info.PerfStat, error) {
|
||||
values, err := getPerfValues(file, group)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
perfStats := make([]info.PerfStat, len(values))
|
||||
for i, value := range values {
|
||||
klog.V(5).Infof("Read metric for event %q for cpu %d from cgroup %q: %d", value.Name, cpu, cgroupPath, value.Value)
|
||||
perfStats[i] = info.PerfStat{
|
||||
PerfValue: value,
|
||||
Cpu: cpu,
|
||||
}
|
||||
}
|
||||
|
||||
return perfStats, nil
|
||||
}
|
||||
|
||||
func getPerfValues(file readerCloser, group group) ([]info.PerfValue, error) {
|
||||
// 24 bytes of GroupReadFormat struct.
|
||||
// 16 bytes of Values struct for each element in group.
|
||||
// See https://man7.org/linux/man-pages/man2/perf_event_open.2.html section "Reading results" with PERF_FORMAT_GROUP specified.
|
||||
buf := make([]byte, 24+16*len(group.names))
|
||||
_, err := file.Read(buf)
|
||||
if err != nil {
|
||||
return []info.PerfValue{}, fmt.Errorf("unable to read perf event group ( leader = %s ): %w", group.leaderName, err)
|
||||
}
|
||||
perfData := &GroupReadFormat{}
|
||||
reader := bytes.NewReader(buf[:24])
|
||||
err = binary.Read(reader, binary.LittleEndian, perfData)
|
||||
if err != nil {
|
||||
return []info.PerfValue{}, fmt.Errorf("unable to decode perf event group ( leader = %s ): %w", group.leaderName, err)
|
||||
}
|
||||
values := make([]Values, perfData.Nr)
|
||||
reader = bytes.NewReader(buf[24:])
|
||||
err = binary.Read(reader, binary.LittleEndian, values)
|
||||
if err != nil {
|
||||
return []info.PerfValue{}, fmt.Errorf("unable to decode perf event group values ( leader = %s ): %w", group.leaderName, err)
|
||||
}
|
||||
|
||||
scalingRatio := 1.0
|
||||
if perfData.TimeRunning != 0 && perfData.TimeEnabled != 0 {
|
||||
scalingRatio = float64(perfData.TimeRunning) / float64(perfData.TimeEnabled)
|
||||
}
|
||||
|
||||
perfValues := make([]info.PerfValue, perfData.Nr)
|
||||
if scalingRatio != float64(0) {
|
||||
for i, name := range group.names {
|
||||
perfValues[i] = info.PerfValue{
|
||||
ScalingRatio: scalingRatio,
|
||||
Value: uint64(float64(values[i].Value) / scalingRatio),
|
||||
Name: name,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for i, name := range group.names {
|
||||
perfValues[i] = info.PerfValue{
|
||||
ScalingRatio: scalingRatio,
|
||||
Value: values[i].Value,
|
||||
Name: name,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return perfValues, nil
|
||||
}
|
||||
|
||||
func (c *collector) setup() error {
|
||||
cgroup, err := os.Open(c.cgroupPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to open cgroup directory %s: %s", c.cgroupPath, err)
|
||||
}
|
||||
defer cgroup.Close()
|
||||
|
||||
c.cpuFilesLock.Lock()
|
||||
defer c.cpuFilesLock.Unlock()
|
||||
cgroupFd := int(cgroup.Fd())
|
||||
groupIndex := 0
|
||||
for _, group := range c.events.Core.Events {
|
||||
// CPUs file descriptors of group leader needed for perf_event_open.
|
||||
leaderFileDescriptors := make(map[int]int, len(c.onlineCPUs))
|
||||
for _, cpu := range c.onlineCPUs {
|
||||
leaderFileDescriptors[cpu] = groupLeaderFileDescriptor
|
||||
}
|
||||
|
||||
leaderFileDescriptors, err := c.createLeaderFileDescriptors(group.events, cgroupFd, groupIndex, leaderFileDescriptors)
|
||||
if err != nil {
|
||||
klog.Errorf("Cannot count perf event group %v: %v", group.events, err)
|
||||
c.deleteGroup(groupIndex)
|
||||
continue
|
||||
} else {
|
||||
groupIndex++
|
||||
}
|
||||
|
||||
// Group is prepared so we should reset and enable counting.
|
||||
for _, fd := range leaderFileDescriptors {
|
||||
err = c.ioctlSetInt(fd, unix.PERF_EVENT_IOC_RESET, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = c.ioctlSetInt(fd, unix.PERF_EVENT_IOC_ENABLE, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *collector) createLeaderFileDescriptors(events []Event, cgroupFd int, groupIndex int, leaderFileDescriptors map[int]int) (map[int]int, error) {
|
||||
for j, event := range events {
|
||||
// First element is group leader.
|
||||
isGroupLeader := j == 0
|
||||
customEvent, ok := c.eventToCustomEvent[event]
|
||||
var err error
|
||||
if ok {
|
||||
config := c.createConfigFromRawEvent(customEvent)
|
||||
leaderFileDescriptors, err = c.registerEvent(eventInfo{string(customEvent.Name), config, cgroupFd, groupIndex, isGroupLeader}, leaderFileDescriptors)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot register perf event: %v", err)
|
||||
}
|
||||
} else {
|
||||
config, err := c.createConfigFromEvent(event)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot create config from perf event: %v", err)
|
||||
|
||||
}
|
||||
leaderFileDescriptors, err = c.registerEvent(eventInfo{string(event), config, cgroupFd, groupIndex, isGroupLeader}, leaderFileDescriptors)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot register perf event: %v", err)
|
||||
}
|
||||
// Clean memory allocated by C code.
|
||||
C.free(unsafe.Pointer(config))
|
||||
}
|
||||
}
|
||||
return leaderFileDescriptors, nil
|
||||
}
|
||||
|
||||
func readPerfEventAttr(name string, pfmGetOsEventEncoding func(string, unsafe.Pointer) error) (*unix.PerfEventAttr, error) {
|
||||
perfEventAttrMemory := C.malloc(C.size_t(unsafe.Sizeof(unix.PerfEventAttr{})))
|
||||
// Fill memory with 0 values.
|
||||
C.memset(perfEventAttrMemory, 0, C.size_t(unsafe.Sizeof(unix.PerfEventAttr{})))
|
||||
err := pfmGetOsEventEncoding(name, unsafe.Pointer(perfEventAttrMemory))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return (*unix.PerfEventAttr)(perfEventAttrMemory), nil
|
||||
}
|
||||
|
||||
func pfmGetOsEventEncoding(name string, perfEventAttrMemory unsafe.Pointer) error {
|
||||
event := pfmPerfEncodeArgT{}
|
||||
fstr := C.CString("")
|
||||
defer C.free(unsafe.Pointer(fstr))
|
||||
event.fstr = unsafe.Pointer(fstr)
|
||||
event.attr = perfEventAttrMemory
|
||||
event.size = C.size_t(unsafe.Sizeof(event))
|
||||
cSafeName := C.CString(name)
|
||||
defer C.free(unsafe.Pointer(cSafeName))
|
||||
pErr := C.pfm_get_os_event_encoding(cSafeName, C.PFM_PLM0|C.PFM_PLM3, C.PFM_OS_PERF_EVENT, unsafe.Pointer(&event))
|
||||
if pErr != C.PFM_SUCCESS {
|
||||
return fmt.Errorf("unable to transform event name %s to perf_event_attr: %d", name, int(pErr))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type eventInfo struct {
|
||||
name string
|
||||
config *unix.PerfEventAttr
|
||||
pid int
|
||||
groupIndex int
|
||||
isGroupLeader bool
|
||||
}
|
||||
|
||||
func (c *collector) registerEvent(event eventInfo, leaderFileDescriptors map[int]int) (map[int]int, error) {
|
||||
newLeaderFileDescriptors := make(map[int]int, len(c.onlineCPUs))
|
||||
var pid, flags int
|
||||
if event.isGroupLeader {
|
||||
pid = event.pid
|
||||
flags = unix.PERF_FLAG_FD_CLOEXEC | unix.PERF_FLAG_PID_CGROUP
|
||||
} else {
|
||||
pid = -1
|
||||
flags = unix.PERF_FLAG_FD_CLOEXEC
|
||||
}
|
||||
|
||||
setAttributes(event.config, event.isGroupLeader)
|
||||
|
||||
for _, cpu := range c.onlineCPUs {
|
||||
fd, err := c.perfEventOpen(event.config, pid, cpu, leaderFileDescriptors[cpu], flags)
|
||||
if err != nil {
|
||||
return leaderFileDescriptors, fmt.Errorf("setting up perf event %#v failed: %q", event.config, err)
|
||||
}
|
||||
perfFile := os.NewFile(uintptr(fd), event.name)
|
||||
if perfFile == nil {
|
||||
return leaderFileDescriptors, fmt.Errorf("unable to create os.File from file descriptor %#v", fd)
|
||||
}
|
||||
|
||||
c.addEventFile(event.groupIndex, event.name, cpu, perfFile)
|
||||
|
||||
// If group leader, save fd for others.
|
||||
if event.isGroupLeader {
|
||||
newLeaderFileDescriptors[cpu] = fd
|
||||
}
|
||||
}
|
||||
|
||||
if event.isGroupLeader {
|
||||
return newLeaderFileDescriptors, nil
|
||||
}
|
||||
return leaderFileDescriptors, nil
|
||||
}
|
||||
|
||||
func (c *collector) addEventFile(index int, name string, cpu int, perfFile *os.File) {
|
||||
_, ok := c.cpuFiles[index]
|
||||
if !ok {
|
||||
c.cpuFiles[index] = group{
|
||||
leaderName: name,
|
||||
cpuFiles: map[string]map[int]readerCloser{},
|
||||
}
|
||||
}
|
||||
|
||||
_, ok = c.cpuFiles[index].cpuFiles[name]
|
||||
if !ok {
|
||||
c.cpuFiles[index].cpuFiles[name] = map[int]readerCloser{}
|
||||
}
|
||||
|
||||
c.cpuFiles[index].cpuFiles[name][cpu] = perfFile
|
||||
|
||||
// Check if name is already stored.
|
||||
for _, have := range c.cpuFiles[index].names {
|
||||
if name == have {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise save it.
|
||||
c.cpuFiles[index] = group{
|
||||
cpuFiles: c.cpuFiles[index].cpuFiles,
|
||||
names: append(c.cpuFiles[index].names, name),
|
||||
leaderName: c.cpuFiles[index].leaderName,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *collector) deleteGroup(index int) {
|
||||
for name, files := range c.cpuFiles[index].cpuFiles {
|
||||
for cpu, file := range files {
|
||||
klog.V(5).Infof("Closing perf event file descriptor for cgroup %q, event %q and CPU %d", c.cgroupPath, name, cpu)
|
||||
err := file.Close()
|
||||
if err != nil {
|
||||
klog.Warningf("Unable to close perf event file descriptor for cgroup %q, event %q and CPU %d", c.cgroupPath, name, cpu)
|
||||
}
|
||||
}
|
||||
}
|
||||
delete(c.cpuFiles, index)
|
||||
}
|
||||
|
||||
func createPerfEventAttr(event CustomEvent) *unix.PerfEventAttr {
|
||||
length := len(event.Config)
|
||||
|
||||
config := &unix.PerfEventAttr{
|
||||
Type: event.Type,
|
||||
Config: event.Config[0],
|
||||
}
|
||||
if length >= 2 {
|
||||
config.Ext1 = event.Config[1]
|
||||
}
|
||||
if length == 3 {
|
||||
config.Ext2 = event.Config[2]
|
||||
}
|
||||
|
||||
klog.V(5).Infof("perf_event_attr struct prepared: %#v", config)
|
||||
return config
|
||||
}
|
||||
|
||||
func setAttributes(config *unix.PerfEventAttr, leader bool) {
|
||||
config.Sample_type = unix.PERF_SAMPLE_IDENTIFIER
|
||||
config.Read_format = unix.PERF_FORMAT_TOTAL_TIME_ENABLED | unix.PERF_FORMAT_TOTAL_TIME_RUNNING | unix.PERF_FORMAT_GROUP | unix.PERF_FORMAT_ID
|
||||
config.Bits = unix.PerfBitInherit
|
||||
|
||||
// Group leader should have this flag set to disable counting until all group would be prepared.
|
||||
if leader {
|
||||
config.Bits |= unix.PerfBitDisabled
|
||||
}
|
||||
|
||||
config.Size = uint32(unsafe.Sizeof(unix.PerfEventAttr{}))
|
||||
}
|
||||
|
||||
func (c *collector) Destroy() {
|
||||
c.uncore.Destroy()
|
||||
c.cpuFilesLock.Lock()
|
||||
defer c.cpuFilesLock.Unlock()
|
||||
|
||||
for i := range c.cpuFiles {
|
||||
c.deleteGroup(i)
|
||||
}
|
||||
}
|
||||
|
||||
// Finalize terminates libpfm4 to free resources.
|
||||
func Finalize() {
|
||||
libpfmMutex.Lock()
|
||||
defer libpfmMutex.Unlock()
|
||||
|
||||
klog.V(1).Info("Attempting to terminate libpfm4")
|
||||
if !isLibpfmInitialized {
|
||||
klog.V(1).Info("libpfm4 has not been initialized; not terminating.")
|
||||
return
|
||||
}
|
||||
|
||||
C.pfm_terminate()
|
||||
isLibpfmInitialized = false
|
||||
}
|
||||
|
||||
func mapEventsToCustomEvents(collector *collector) {
|
||||
collector.eventToCustomEvent = map[Event]*CustomEvent{}
|
||||
for key, event := range collector.events.Core.CustomEvents {
|
||||
collector.eventToCustomEvent[event.Name] = &collector.events.Core.CustomEvents[key]
|
||||
}
|
||||
}
|
||||
|
||||
func (c *collector) createConfigFromRawEvent(event *CustomEvent) *unix.PerfEventAttr {
|
||||
klog.V(5).Infof("Setting up raw perf event %#v", event)
|
||||
|
||||
config := createPerfEventAttr(*event)
|
||||
|
||||
klog.V(5).Infof("perf_event_attr: %#v", config)
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
func (c *collector) createConfigFromEvent(event Event) (*unix.PerfEventAttr, error) {
|
||||
klog.V(5).Infof("Setting up perf event %s", string(event))
|
||||
|
||||
config, err := readPerfEventAttr(string(event), pfmGetOsEventEncoding)
|
||||
if err != nil {
|
||||
C.free((unsafe.Pointer)(config))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
klog.V(5).Infof("perf_event_attr: %#v", config)
|
||||
|
||||
return config, nil
|
||||
}
|
34
e2e/vendor/github.com/google/cadvisor/perf/collector_no_libpfm.go
generated
vendored
Normal file
34
e2e/vendor/github.com/google/cadvisor/perf/collector_no_libpfm.go
generated
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
//go:build !libpfm || !cgo
|
||||
// +build !libpfm !cgo
|
||||
|
||||
// Copyright 2020 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Collector of perf events for a container.
|
||||
package perf
|
||||
|
||||
import (
|
||||
"github.com/google/cadvisor/stats"
|
||||
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
func NewCollector(cgroupPath string, events Events, numCores int) stats.Collector {
|
||||
return &stats.NoopCollector{}
|
||||
}
|
||||
|
||||
// Finalize terminates libpfm4 to free resources.
|
||||
func Finalize() {
|
||||
klog.V(1).Info("cAdvisor is build without cgo and/or libpfm support. Nothing to be finalized")
|
||||
}
|
127
e2e/vendor/github.com/google/cadvisor/perf/config.go
generated
vendored
Normal file
127
e2e/vendor/github.com/google/cadvisor/perf/config.go
generated
vendored
Normal file
@ -0,0 +1,127 @@
|
||||
// Copyright 2020 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Configuration for perf event manager.
|
||||
package perf
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
type PerfEvents struct {
|
||||
// Core perf events to be measured.
|
||||
Core Events `json:"core,omitempty"`
|
||||
|
||||
// Uncore perf events to be measured.
|
||||
Uncore Events `json:"uncore,omitempty"`
|
||||
}
|
||||
|
||||
type Events struct {
|
||||
// List of perf events' names to be measured.
|
||||
Events []Group `json:"events"`
|
||||
|
||||
// List of custom perf events' to be measured. It is impossible to
|
||||
// specify some events using their names and in such case you have
|
||||
// to provide lower level configuration.
|
||||
CustomEvents []CustomEvent `json:"custom_events"`
|
||||
}
|
||||
|
||||
type Event string
|
||||
|
||||
type CustomEvent struct {
|
||||
// Type of the event. See perf_event_attr documentation
|
||||
// at man perf_event_open.
|
||||
Type uint32 `json:"type,omitempty"`
|
||||
|
||||
// Symbolically formed event like:
|
||||
// pmu/config=PerfEvent.Config[0],config1=PerfEvent.Config[1],config2=PerfEvent.Config[2]
|
||||
// as described in man perf-stat.
|
||||
Config Config `json:"config"`
|
||||
|
||||
// Human readable name of metric that will be created from the event.
|
||||
Name Event `json:"name"`
|
||||
}
|
||||
|
||||
type Config []uint64
|
||||
|
||||
func (c *Config) UnmarshalJSON(b []byte) error {
|
||||
config := []string{}
|
||||
err := json.Unmarshal(b, &config)
|
||||
if err != nil {
|
||||
klog.Errorf("Unmarshalling %s into slice of strings failed: %q", b, err)
|
||||
return fmt.Errorf("unmarshalling %s into slice of strings failed: %q", b, err)
|
||||
}
|
||||
intermediate := []uint64{}
|
||||
for _, v := range config {
|
||||
uintValue, err := strconv.ParseUint(v, 0, 64)
|
||||
if err != nil {
|
||||
klog.Errorf("Parsing %#v into uint64 failed: %q", v, err)
|
||||
return fmt.Errorf("parsing %#v into uint64 failed: %q", v, err)
|
||||
}
|
||||
intermediate = append(intermediate, uintValue)
|
||||
}
|
||||
*c = intermediate
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseConfig(file *os.File) (events PerfEvents, err error) {
|
||||
decoder := json.NewDecoder(file)
|
||||
err = decoder.Decode(&events)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("unable to load perf events configuration from %q: %q", file.Name(), err)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type Group struct {
|
||||
events []Event
|
||||
array bool
|
||||
}
|
||||
|
||||
func (g *Group) UnmarshalJSON(b []byte) error {
|
||||
var jsonObj interface{}
|
||||
err := json.Unmarshal(b, &jsonObj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch obj := jsonObj.(type) {
|
||||
case string:
|
||||
*g = Group{
|
||||
events: []Event{Event(obj)},
|
||||
array: false,
|
||||
}
|
||||
return nil
|
||||
case []interface{}:
|
||||
group := Group{
|
||||
events: make([]Event, 0, len(obj)),
|
||||
array: true,
|
||||
}
|
||||
for _, v := range obj {
|
||||
value, ok := v.(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("cannot unmarshal %v", value)
|
||||
}
|
||||
group.events = append(group.events, Event(value))
|
||||
}
|
||||
*g = group
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("unsupported type")
|
||||
}
|
75
e2e/vendor/github.com/google/cadvisor/perf/manager_libpfm.go
generated
vendored
Normal file
75
e2e/vendor/github.com/google/cadvisor/perf/manager_libpfm.go
generated
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
//go:build libpfm && cgo
|
||||
// +build libpfm,cgo
|
||||
|
||||
// Copyright 2020 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Manager of perf events for containers.
|
||||
package perf
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
info "github.com/google/cadvisor/info/v1"
|
||||
"github.com/google/cadvisor/stats"
|
||||
"github.com/google/cadvisor/utils/sysinfo"
|
||||
)
|
||||
|
||||
type manager struct {
|
||||
events PerfEvents
|
||||
onlineCPUs []int
|
||||
cpuToSocket map[int]int
|
||||
stats.NoopDestroy
|
||||
}
|
||||
|
||||
func NewManager(configFile string, topology []info.Node) (stats.Manager, error) {
|
||||
if configFile == "" {
|
||||
return &stats.NoopManager{}, nil
|
||||
}
|
||||
|
||||
file, err := os.Open(configFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to read configuration file %q: %w", configFile, err)
|
||||
}
|
||||
|
||||
config, err := parseConfig(file)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to parse configuration file %q: %w", configFile, err)
|
||||
}
|
||||
|
||||
if len(config.Core.Events) == 0 && len(config.Uncore.Events) == 0 {
|
||||
return nil, fmt.Errorf("there is no events in config file %q", configFile)
|
||||
}
|
||||
|
||||
onlineCPUs := sysinfo.GetOnlineCPUs(topology)
|
||||
|
||||
cpuToSocket := make(map[int]int)
|
||||
|
||||
for _, cpu := range onlineCPUs {
|
||||
cpuToSocket[cpu] = sysinfo.GetSocketFromCPU(topology, cpu)
|
||||
}
|
||||
|
||||
return &manager{events: config, onlineCPUs: onlineCPUs, cpuToSocket: cpuToSocket}, nil
|
||||
}
|
||||
|
||||
func (m *manager) GetCollector(cgroupPath string) (stats.Collector, error) {
|
||||
collector := newCollector(cgroupPath, m.events, m.onlineCPUs, m.cpuToSocket)
|
||||
err := collector.setup()
|
||||
if err != nil {
|
||||
collector.Destroy()
|
||||
return &stats.NoopCollector{}, err
|
||||
}
|
||||
return collector, nil
|
||||
}
|
31
e2e/vendor/github.com/google/cadvisor/perf/manager_no_libpfm.go
generated
vendored
Normal file
31
e2e/vendor/github.com/google/cadvisor/perf/manager_no_libpfm.go
generated
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
//go:build !libpfm || !cgo
|
||||
// +build !libpfm !cgo
|
||||
|
||||
// Copyright 2020 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Manager of perf events for containers.
|
||||
package perf
|
||||
|
||||
import (
|
||||
info "github.com/google/cadvisor/info/v1"
|
||||
"github.com/google/cadvisor/stats"
|
||||
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
func NewManager(configFile string, topology []info.Node) (stats.Manager, error) {
|
||||
klog.V(1).Info("cAdvisor is build without cgo and/or libpfm support. Perf event counters are not available.")
|
||||
return &stats.NoopManager{}, nil
|
||||
}
|
54
e2e/vendor/github.com/google/cadvisor/perf/types_libpfm.go
generated
vendored
Normal file
54
e2e/vendor/github.com/google/cadvisor/perf/types_libpfm.go
generated
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
//go:build libpfm && cgo
|
||||
// +build libpfm,cgo
|
||||
|
||||
// Copyright 2020 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Types related to handling perf events that are missing from unix package.
|
||||
package perf
|
||||
|
||||
import "C"
|
||||
import (
|
||||
"io"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// GroupReadFormat allows to read perf event's values for grouped events.
|
||||
// See https://man7.org/linux/man-pages/man2/perf_event_open.2.html section "Reading results" with PERF_FORMAT_GROUP specified.
|
||||
type GroupReadFormat struct {
|
||||
Nr uint64 /* The number of events */
|
||||
TimeEnabled uint64 /* if PERF_FORMAT_TOTAL_TIME_ENABLED */
|
||||
TimeRunning uint64 /* if PERF_FORMAT_TOTAL_TIME_RUNNING */
|
||||
}
|
||||
|
||||
type Values struct {
|
||||
Value uint64 /* The value of the event */
|
||||
ID uint64 /* if PERF_FORMAT_ID */
|
||||
}
|
||||
|
||||
// pfmPerfEncodeArgT represents structure that is used to parse perf event nam
|
||||
// into perf_event_attr using libpfm.
|
||||
type pfmPerfEncodeArgT struct {
|
||||
attr unsafe.Pointer
|
||||
fstr unsafe.Pointer
|
||||
size C.size_t
|
||||
_ C.int // idx
|
||||
_ C.int // cpu
|
||||
_ C.int // flags
|
||||
}
|
||||
|
||||
type readerCloser interface {
|
||||
io.Reader
|
||||
io.Closer
|
||||
}
|
519
e2e/vendor/github.com/google/cadvisor/perf/uncore_libpfm.go
generated
vendored
Normal file
519
e2e/vendor/github.com/google/cadvisor/perf/uncore_libpfm.go
generated
vendored
Normal file
@ -0,0 +1,519 @@
|
||||
//go:build libpfm && cgo
|
||||
// +build libpfm,cgo
|
||||
|
||||
// Copyright 2020 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Uncore perf events logic.
|
||||
package perf
|
||||
|
||||
// #cgo CFLAGS: -I/usr/include
|
||||
// #cgo LDFLAGS: -lpfm
|
||||
// #include <perfmon/pfmlib.h>
|
||||
// #include <stdlib.h>
|
||||
import "C"
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
info "github.com/google/cadvisor/info/v1"
|
||||
"github.com/google/cadvisor/stats"
|
||||
)
|
||||
|
||||
type pmu struct {
|
||||
name string
|
||||
typeOf uint32
|
||||
cpus []uint32
|
||||
}
|
||||
|
||||
const (
|
||||
uncorePMUPrefix = "uncore"
|
||||
pmuTypeFilename = "type"
|
||||
pmuCpumaskFilename = "cpumask"
|
||||
systemDevicesPath = "/sys/devices"
|
||||
rootPerfEventPath = "/sys/fs/cgroup/perf_event"
|
||||
uncorePID = -1
|
||||
)
|
||||
|
||||
func getPMU(pmus uncorePMUs, gotType uint32) (*pmu, error) {
|
||||
for _, pmu := range pmus {
|
||||
if pmu.typeOf == gotType {
|
||||
return &pmu, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("there is no pmu with event type: %#v", gotType)
|
||||
}
|
||||
|
||||
type uncorePMUs map[string]pmu
|
||||
|
||||
func readUncorePMU(path string, name string, cpumaskRegexp *regexp.Regexp) (*pmu, error) {
|
||||
buf, err := os.ReadFile(filepath.Join(path, pmuTypeFilename))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
typeString := strings.TrimSpace(string(buf))
|
||||
eventType, err := strconv.ParseUint(typeString, 0, 32)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
buf, err = os.ReadFile(filepath.Join(path, pmuCpumaskFilename))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var cpus []uint32
|
||||
cpumask := strings.TrimSpace(string(buf))
|
||||
for _, cpu := range cpumaskRegexp.Split(cpumask, -1) {
|
||||
parsedCPU, err := strconv.ParseUint(cpu, 0, 32)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cpus = append(cpus, uint32(parsedCPU))
|
||||
}
|
||||
|
||||
return &pmu{name: name, typeOf: uint32(eventType), cpus: cpus}, nil
|
||||
}
|
||||
|
||||
func getUncorePMUs(devicesPath string) (uncorePMUs, error) {
|
||||
pmus := make(uncorePMUs)
|
||||
|
||||
// Depends on platform, cpu mask could be for example in form "0-1" or "0,1".
|
||||
cpumaskRegexp := regexp.MustCompile("[-,\n]")
|
||||
err := filepath.Walk(devicesPath, func(path string, info os.FileInfo, err error) error {
|
||||
// Skip root path.
|
||||
if path == devicesPath {
|
||||
return nil
|
||||
}
|
||||
if info.IsDir() {
|
||||
if strings.HasPrefix(info.Name(), uncorePMUPrefix) {
|
||||
pmu, err := readUncorePMU(path, info.Name(), cpumaskRegexp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pmus[info.Name()] = *pmu
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return pmus, nil
|
||||
}
|
||||
|
||||
type uncoreCollector struct {
|
||||
cpuFilesLock sync.Mutex
|
||||
cpuFiles map[int]map[string]group
|
||||
events []Group
|
||||
eventToCustomEvent map[Event]*CustomEvent
|
||||
cpuToSocket map[int]int
|
||||
|
||||
// Handle for mocking purposes.
|
||||
perfEventOpen func(attr *unix.PerfEventAttr, pid int, cpu int, groupFd int, flags int) (fd int, err error)
|
||||
ioctlSetInt func(fd int, req uint, value int) error
|
||||
}
|
||||
|
||||
func NewUncoreCollector(cgroupPath string, events PerfEvents, cpuToSocket map[int]int) stats.Collector {
|
||||
|
||||
if cgroupPath != rootPerfEventPath {
|
||||
// Uncore metric doesn't exists for cgroups, only for entire platform.
|
||||
return &stats.NoopCollector{}
|
||||
}
|
||||
|
||||
collector := &uncoreCollector{
|
||||
cpuToSocket: cpuToSocket,
|
||||
perfEventOpen: unix.PerfEventOpen,
|
||||
ioctlSetInt: unix.IoctlSetInt,
|
||||
}
|
||||
|
||||
err := collector.setup(events, systemDevicesPath)
|
||||
if err != nil {
|
||||
klog.Errorf("Perf uncore metrics will not be available: unable to setup uncore perf event collector: %v", err)
|
||||
return &stats.NoopCollector{}
|
||||
}
|
||||
|
||||
return collector
|
||||
}
|
||||
|
||||
func (c *uncoreCollector) createLeaderFileDescriptors(events []Event, groupIndex int, groupPMUs map[Event]uncorePMUs,
|
||||
leaderFileDescriptors map[string]map[uint32]int) (map[string]map[uint32]int, error) {
|
||||
var err error
|
||||
for _, event := range events {
|
||||
eventName, _ := parseEventName(string(event))
|
||||
customEvent, ok := c.eventToCustomEvent[event]
|
||||
if ok {
|
||||
err = c.setupRawEvent(customEvent, groupPMUs[event], groupIndex, leaderFileDescriptors)
|
||||
} else {
|
||||
err = c.setupEvent(eventName, groupPMUs[event], groupIndex, leaderFileDescriptors)
|
||||
}
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
c.deleteGroup(groupIndex)
|
||||
return nil, fmt.Errorf("cannot create config from perf event: %v", err)
|
||||
}
|
||||
return leaderFileDescriptors, nil
|
||||
}
|
||||
|
||||
func (c *uncoreCollector) setup(events PerfEvents, devicesPath string) error {
|
||||
readUncorePMUs, err := getUncorePMUs(devicesPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.cpuFiles = make(map[int]map[string]group)
|
||||
c.events = events.Uncore.Events
|
||||
c.eventToCustomEvent = parseUncoreEvents(events.Uncore)
|
||||
c.cpuFilesLock.Lock()
|
||||
defer c.cpuFilesLock.Unlock()
|
||||
|
||||
for i, group := range c.events {
|
||||
// Check what PMUs are needed.
|
||||
groupPMUs, err := parsePMUs(group, readUncorePMUs, c.eventToCustomEvent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = checkGroup(group, groupPMUs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// CPUs file descriptors of group leader needed for perf_event_open.
|
||||
leaderFileDescriptors := make(map[string]map[uint32]int)
|
||||
for _, pmu := range readUncorePMUs {
|
||||
leaderFileDescriptors[pmu.name] = make(map[uint32]int)
|
||||
for _, cpu := range pmu.cpus {
|
||||
leaderFileDescriptors[pmu.name][cpu] = groupLeaderFileDescriptor
|
||||
}
|
||||
}
|
||||
leaderFileDescriptors, err = c.createLeaderFileDescriptors(group.events, i, groupPMUs, leaderFileDescriptors)
|
||||
if err != nil {
|
||||
klog.Error(err)
|
||||
continue
|
||||
}
|
||||
// Group is prepared so we should reset and enable counting.
|
||||
for _, pmuCPUs := range leaderFileDescriptors {
|
||||
for _, fd := range pmuCPUs {
|
||||
// Call only for used PMUs.
|
||||
if fd != groupLeaderFileDescriptor {
|
||||
err = c.ioctlSetInt(fd, unix.PERF_EVENT_IOC_RESET, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = c.ioctlSetInt(fd, unix.PERF_EVENT_IOC_ENABLE, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkGroup(group Group, eventPMUs map[Event]uncorePMUs) error {
|
||||
if group.array {
|
||||
var pmu uncorePMUs
|
||||
for _, event := range group.events {
|
||||
if len(eventPMUs[event]) > 1 {
|
||||
return fmt.Errorf("the events in group usually have to be from single PMU, try reorganizing the \"%v\" group", group.events)
|
||||
}
|
||||
if len(eventPMUs[event]) == 1 {
|
||||
if pmu == nil {
|
||||
pmu = eventPMUs[event]
|
||||
continue
|
||||
}
|
||||
|
||||
eq := reflect.DeepEqual(pmu, eventPMUs[event])
|
||||
if !eq {
|
||||
return fmt.Errorf("the events in group usually have to be from the same PMU, try reorganizing the \"%v\" group", group.events)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if len(eventPMUs[group.events[0]]) < 1 {
|
||||
return fmt.Errorf("the event %q don't have any PMU to count with", group.events[0])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseEventName(eventName string) (string, string) {
|
||||
// First "/" separate pmu prefix and event name
|
||||
// ex. "uncore_imc_0/cas_count_read" -> uncore_imc_0 and cas_count_read.
|
||||
splittedEvent := strings.SplitN(eventName, "/", 2)
|
||||
var pmuPrefix = ""
|
||||
if len(splittedEvent) == 2 {
|
||||
pmuPrefix = splittedEvent[0]
|
||||
eventName = splittedEvent[1]
|
||||
}
|
||||
return eventName, pmuPrefix
|
||||
}
|
||||
|
||||
func parsePMUs(group Group, pmus uncorePMUs, customEvents map[Event]*CustomEvent) (map[Event]uncorePMUs, error) {
|
||||
eventPMUs := make(map[Event]uncorePMUs)
|
||||
for _, event := range group.events {
|
||||
_, prefix := parseEventName(string(event))
|
||||
custom, ok := customEvents[event]
|
||||
if ok {
|
||||
if custom.Type != 0 {
|
||||
pmu, err := getPMU(pmus, custom.Type)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
eventPMUs[event] = uncorePMUs{pmu.name: *pmu}
|
||||
continue
|
||||
}
|
||||
}
|
||||
eventPMUs[event] = obtainPMUs(prefix, pmus)
|
||||
}
|
||||
|
||||
return eventPMUs, nil
|
||||
}
|
||||
|
||||
func obtainPMUs(want string, gotPMUs uncorePMUs) uncorePMUs {
|
||||
pmus := make(uncorePMUs)
|
||||
if want == "" {
|
||||
return pmus
|
||||
}
|
||||
for _, pmu := range gotPMUs {
|
||||
if strings.HasPrefix(pmu.name, want) {
|
||||
pmus[pmu.name] = pmu
|
||||
}
|
||||
}
|
||||
|
||||
return pmus
|
||||
}
|
||||
|
||||
func parseUncoreEvents(events Events) map[Event]*CustomEvent {
|
||||
eventToCustomEvent := map[Event]*CustomEvent{}
|
||||
for _, group := range events.Events {
|
||||
for _, uncoreEvent := range group.events {
|
||||
for _, customEvent := range events.CustomEvents {
|
||||
if uncoreEvent == customEvent.Name {
|
||||
eventToCustomEvent[customEvent.Name] = &customEvent
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return eventToCustomEvent
|
||||
}
|
||||
|
||||
func (c *uncoreCollector) Destroy() {
|
||||
c.cpuFilesLock.Lock()
|
||||
defer c.cpuFilesLock.Unlock()
|
||||
|
||||
for groupIndex := range c.cpuFiles {
|
||||
c.deleteGroup(groupIndex)
|
||||
delete(c.cpuFiles, groupIndex)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *uncoreCollector) UpdateStats(stats *info.ContainerStats) error {
|
||||
klog.V(5).Info("Attempting to update uncore perf_event stats")
|
||||
|
||||
for _, groupPMUs := range c.cpuFiles {
|
||||
for pmu, group := range groupPMUs {
|
||||
for cpu, file := range group.cpuFiles[group.leaderName] {
|
||||
stat, err := readPerfUncoreStat(file, group, cpu, pmu, c.cpuToSocket)
|
||||
if err != nil {
|
||||
klog.Warningf("Unable to read from perf_event_file (event: %q, CPU: %d) for %q: %q", group.leaderName, cpu, pmu, err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
stats.PerfUncoreStats = append(stats.PerfUncoreStats, stat...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *uncoreCollector) setupEvent(name string, pmus uncorePMUs, groupIndex int, leaderFileDescriptors map[string]map[uint32]int) error {
|
||||
if !isLibpfmInitialized {
|
||||
return fmt.Errorf("libpfm4 is not initialized, cannot proceed with setting perf events up")
|
||||
}
|
||||
|
||||
klog.V(5).Infof("Setting up uncore perf event %s", name)
|
||||
|
||||
config, err := readPerfEventAttr(name, pfmGetOsEventEncoding)
|
||||
if err != nil {
|
||||
C.free((unsafe.Pointer)(config))
|
||||
return err
|
||||
}
|
||||
|
||||
// Register event for all memory controllers.
|
||||
for _, pmu := range pmus {
|
||||
config.Type = pmu.typeOf
|
||||
isGroupLeader := leaderFileDescriptors[pmu.name][pmu.cpus[0]] == groupLeaderFileDescriptor
|
||||
setAttributes(config, isGroupLeader)
|
||||
leaderFileDescriptors[pmu.name], err = c.registerEvent(eventInfo{name, config, uncorePID, groupIndex, isGroupLeader}, pmu, leaderFileDescriptors[pmu.name])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Clean memory allocated by C code.
|
||||
C.free(unsafe.Pointer(config))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *uncoreCollector) registerEvent(eventInfo eventInfo, pmu pmu, leaderFileDescriptors map[uint32]int) (map[uint32]int, error) {
|
||||
newLeaderFileDescriptors := make(map[uint32]int)
|
||||
isGroupLeader := false
|
||||
for _, cpu := range pmu.cpus {
|
||||
groupFd, flags := leaderFileDescriptors[cpu], 0
|
||||
fd, err := c.perfEventOpen(eventInfo.config, eventInfo.pid, int(cpu), groupFd, flags)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("setting up perf event %#v failed: %q | (pmu: %q, groupFd: %d, cpu: %d)", eventInfo.config, err, pmu, groupFd, cpu)
|
||||
}
|
||||
perfFile := os.NewFile(uintptr(fd), eventInfo.name)
|
||||
if perfFile == nil {
|
||||
return nil, fmt.Errorf("unable to create os.File from file descriptor %#v", fd)
|
||||
}
|
||||
|
||||
c.addEventFile(eventInfo.groupIndex, eventInfo.name, pmu.name, int(cpu), perfFile)
|
||||
|
||||
// If group leader, save fd for others.
|
||||
if leaderFileDescriptors[cpu] == groupLeaderFileDescriptor {
|
||||
newLeaderFileDescriptors[cpu] = fd
|
||||
isGroupLeader = true
|
||||
}
|
||||
}
|
||||
|
||||
if isGroupLeader {
|
||||
return newLeaderFileDescriptors, nil
|
||||
}
|
||||
return leaderFileDescriptors, nil
|
||||
}
|
||||
|
||||
func (c *uncoreCollector) addEventFile(index int, name string, pmu string, cpu int, perfFile *os.File) {
|
||||
_, ok := c.cpuFiles[index]
|
||||
if !ok {
|
||||
c.cpuFiles[index] = map[string]group{}
|
||||
}
|
||||
|
||||
_, ok = c.cpuFiles[index][pmu]
|
||||
if !ok {
|
||||
c.cpuFiles[index][pmu] = group{
|
||||
cpuFiles: map[string]map[int]readerCloser{},
|
||||
leaderName: name,
|
||||
}
|
||||
}
|
||||
|
||||
_, ok = c.cpuFiles[index][pmu].cpuFiles[name]
|
||||
if !ok {
|
||||
c.cpuFiles[index][pmu].cpuFiles[name] = map[int]readerCloser{}
|
||||
}
|
||||
|
||||
c.cpuFiles[index][pmu].cpuFiles[name][cpu] = perfFile
|
||||
|
||||
// Check if name is already stored.
|
||||
for _, have := range c.cpuFiles[index][pmu].names {
|
||||
if name == have {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise save it.
|
||||
c.cpuFiles[index][pmu] = group{
|
||||
cpuFiles: c.cpuFiles[index][pmu].cpuFiles,
|
||||
names: append(c.cpuFiles[index][pmu].names, name),
|
||||
leaderName: c.cpuFiles[index][pmu].leaderName,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *uncoreCollector) setupRawEvent(event *CustomEvent, pmus uncorePMUs, groupIndex int, leaderFileDescriptors map[string]map[uint32]int) error {
|
||||
klog.V(5).Infof("Setting up raw perf uncore event %#v", event)
|
||||
|
||||
for _, pmu := range pmus {
|
||||
newEvent := CustomEvent{
|
||||
Type: pmu.typeOf,
|
||||
Config: event.Config,
|
||||
Name: event.Name,
|
||||
}
|
||||
config := createPerfEventAttr(newEvent)
|
||||
isGroupLeader := leaderFileDescriptors[pmu.name][pmu.cpus[0]] == groupLeaderFileDescriptor
|
||||
setAttributes(config, isGroupLeader)
|
||||
var err error
|
||||
leaderFileDescriptors[pmu.name], err = c.registerEvent(eventInfo{string(newEvent.Name), config, uncorePID, groupIndex, isGroupLeader}, pmu, leaderFileDescriptors[pmu.name])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *uncoreCollector) deleteGroup(groupIndex int) {
|
||||
groupPMUs := c.cpuFiles[groupIndex]
|
||||
for pmu, group := range groupPMUs {
|
||||
for name, cpus := range group.cpuFiles {
|
||||
for cpu, file := range cpus {
|
||||
klog.V(5).Infof("Closing uncore perf event file descriptor for event %q, PMU %s and CPU %d", name, pmu, cpu)
|
||||
err := file.Close()
|
||||
if err != nil {
|
||||
klog.Warningf("Unable to close perf event file descriptor for event %q, PMU %s and CPU %d", name, pmu, cpu)
|
||||
}
|
||||
}
|
||||
delete(group.cpuFiles, name)
|
||||
}
|
||||
delete(groupPMUs, pmu)
|
||||
}
|
||||
delete(c.cpuFiles, groupIndex)
|
||||
}
|
||||
|
||||
func readPerfUncoreStat(file readerCloser, group group, cpu int, pmu string, cpuToSocket map[int]int) ([]info.PerfUncoreStat, error) {
|
||||
values, err := getPerfValues(file, group)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
socket, ok := cpuToSocket[cpu]
|
||||
if !ok {
|
||||
// Socket is unknown.
|
||||
socket = -1
|
||||
}
|
||||
|
||||
perfUncoreStats := make([]info.PerfUncoreStat, len(values))
|
||||
for i, value := range values {
|
||||
klog.V(5).Infof("Read metric for event %q for cpu %d from pmu %q: %d", value.Name, cpu, pmu, value.Value)
|
||||
perfUncoreStats[i] = info.PerfUncoreStat{
|
||||
PerfValue: value,
|
||||
Socket: socket,
|
||||
PMU: pmu,
|
||||
}
|
||||
}
|
||||
|
||||
return perfUncoreStats, nil
|
||||
}
|
Reference in New Issue
Block a user