1
0
Fork 0
telegraf/plugins/inputs/intel_pmu/config.go
Daniel Baumann 4978089aab
Adding upstream version 1.34.4.
Signed-off-by: Daniel Baumann <daniel@debian.org>
2025-05-24 07:26:29 +02:00

239 lines
5.9 KiB
Go

//go:build linux && amd64
package intel_pmu
import (
"errors"
"fmt"
"strconv"
"strings"
"github.com/influxdata/telegraf"
)
// Maximum size of core IDs or socket IDs (8192). Based on maximum value of CPUs that linux kernel supports.
const maxIDsSize = 1 << 13
type entitiesParser interface {
parseEntities(coreEntities []*coreEventEntity, uncoreEntities []*uncoreEventEntity) (err error)
}
type configParser struct {
log telegraf.Logger
sys sysInfoProvider
}
func (cp *configParser) parseEntities(coreEntities []*coreEventEntity, uncoreEntities []*uncoreEventEntity) (err error) {
if len(coreEntities) == 0 && len(uncoreEntities) == 0 {
return errors.New("neither core nor uncore entities configured")
}
for _, coreEntity := range coreEntities {
if coreEntity == nil {
return errors.New("core entity is nil")
}
if coreEntity.Events == nil {
if cp.log != nil {
cp.log.Debug("all core events from provided files will be configured")
}
coreEntity.allEvents = true
} else {
events := cp.parseEvents(coreEntity.Events)
if events == nil {
return errors.New("an empty list of core events was provided")
}
coreEntity.parsedEvents = events
}
coreEntity.parsedCores, err = cp.parseCores(coreEntity.Cores)
if err != nil {
return fmt.Errorf("error during cores parsing: %w", err)
}
}
for _, uncoreEntity := range uncoreEntities {
if uncoreEntity == nil {
return errors.New("uncore entity is nil")
}
if uncoreEntity.Events == nil {
if cp.log != nil {
cp.log.Debug("all uncore events from provided files will be configured")
}
uncoreEntity.allEvents = true
} else {
events := cp.parseEvents(uncoreEntity.Events)
if events == nil {
return errors.New("an empty list of uncore events was provided")
}
uncoreEntity.parsedEvents = events
}
uncoreEntity.parsedSockets, err = cp.parseSockets(uncoreEntity.Sockets)
if err != nil {
return fmt.Errorf("error during sockets parsing: %w", err)
}
}
return nil
}
func (cp *configParser) parseEvents(events []string) []*eventWithQuals {
if len(events) == 0 {
return nil
}
events, duplications := removeDuplicateStrings(events)
for _, duplication := range duplications {
if cp.log != nil {
cp.log.Warnf("duplicated event %q will be removed", duplication)
}
}
return parseEventsWithQualifiers(events)
}
func (cp *configParser) parseCores(cores []string) ([]int, error) {
if cores == nil {
if cp.log != nil {
cp.log.Debug("all possible cores will be configured")
}
if cp.sys == nil {
return nil, errors.New("system info provider is nil")
}
cores, err := cp.sys.allCPUs()
if err != nil {
return nil, fmt.Errorf("cannot obtain all cpus: %w", err)
}
return cores, nil
}
if len(cores) == 0 {
return nil, errors.New("an empty list of cores was provided")
}
result, err := cp.parseIntRanges(cores)
if err != nil {
return nil, err
}
return result, nil
}
func (cp *configParser) parseSockets(sockets []string) ([]int, error) {
if sockets == nil {
if cp.log != nil {
cp.log.Debug("all possible sockets will be configured")
}
if cp.sys == nil {
return nil, errors.New("system info provider is nil")
}
sockets, err := cp.sys.allSockets()
if err != nil {
return nil, fmt.Errorf("cannot obtain all sockets: %w", err)
}
return sockets, nil
}
if len(sockets) == 0 {
return nil, errors.New("an empty list of sockets was provided")
}
result, err := cp.parseIntRanges(sockets)
if err != nil {
return nil, err
}
return result, nil
}
func (cp *configParser) parseIntRanges(ranges []string) ([]int, error) {
var ids []int
var duplicatedIDs []int
var err error
ids, err = parseIDs(ranges)
if err != nil {
return nil, err
}
ids, duplicatedIDs = removeDuplicateValues(ids)
for _, duplication := range duplicatedIDs {
if cp.log != nil {
cp.log.Warnf("duplicated id number `%d` will be removed", duplication)
}
}
return ids, nil
}
func parseEventsWithQualifiers(events []string) []*eventWithQuals {
result := make([]*eventWithQuals, 0, len(events))
for _, event := range events {
newEventWithQualifiers := &eventWithQuals{}
split := strings.Split(event, ":")
newEventWithQualifiers.name = split[0]
if len(split) > 1 {
newEventWithQualifiers.qualifiers = split[1:]
}
result = append(result, newEventWithQualifiers)
}
return result
}
func parseIDs(allIDsStrings []string) ([]int, error) {
var result []int
for _, idsString := range allIDsStrings {
ids := strings.Split(idsString, ",")
for _, id := range ids {
id := strings.TrimSpace(id)
// a-b support
var start, end uint
n, err := fmt.Sscanf(id, "%d-%d", &start, &end)
if err == nil && n == 2 {
if start >= end {
return nil, fmt.Errorf("`%d` is equal or greater than `%d`", start, end)
}
for ; start <= end; start++ {
if len(result)+1 > maxIDsSize {
return nil, fmt.Errorf("requested number of IDs exceeds max size `%d`", maxIDsSize)
}
result = append(result, int(start))
}
continue
}
// Single value
num, err := strconv.Atoi(id)
if err != nil {
return nil, fmt.Errorf("wrong format for id number %q: %w", id, err)
}
if len(result)+1 > maxIDsSize {
return nil, fmt.Errorf("requested number of IDs exceeds max size `%d`", maxIDsSize)
}
result = append(result, num)
}
}
return result, nil
}
func removeDuplicateValues(intSlice []int) (result, duplicates []int) {
keys := make(map[int]bool)
for _, entry := range intSlice {
if _, value := keys[entry]; !value {
keys[entry] = true
result = append(result, entry)
} else {
duplicates = append(duplicates, entry)
}
}
return result, duplicates
}
func removeDuplicateStrings(strSlice []string) (result, duplicates []string) {
keys := make(map[string]bool)
for _, entry := range strSlice {
if _, value := keys[entry]; !value {
keys[entry] = true
result = append(result, entry)
} else {
duplicates = append(duplicates, entry)
}
}
return result, duplicates
}