1
0
Fork 0
telegraf/plugins/inputs/intel_pmu/intel_pmu_test.go

556 lines
18 KiB
Go
Raw Permalink Normal View History

//go:build linux && amd64
package intel_pmu
import (
"errors"
"fmt"
"math"
"os"
"testing"
"time"
ia "github.com/intel/iaevents"
"github.com/stretchr/testify/require"
"github.com/influxdata/telegraf/testutil"
)
func TestInitialization(t *testing.T) {
mError := errors.New("mock error")
mParser := &mockEntitiesParser{}
mResolver := &mockEntitiesResolver{}
mActivator := &mockEntitiesActivator{}
mFileInfo := &mockFileInfoProvider{}
file := "path/to/file"
paths := []string{file}
t.Run("missing parser, resolver or activator", func(t *testing.T) {
err := (&IntelPMU{}).initialization(mParser, nil, nil)
require.Error(t, err)
require.Contains(t, err.Error(), "entities parser and/or resolver and/or activator is nil")
err = (&IntelPMU{}).initialization(nil, mResolver, nil)
require.Error(t, err)
require.Contains(t, err.Error(), "entities parser and/or resolver and/or activator is nil")
err = (&IntelPMU{}).initialization(nil, nil, mActivator)
require.Error(t, err)
require.Contains(t, err.Error(), "entities parser and/or resolver and/or activator is nil")
})
t.Run("parse entities error", func(t *testing.T) {
mIntelPMU := &IntelPMU{EventListPaths: paths, fileInfo: mFileInfo}
mParser.On("parseEntities", mIntelPMU.CoreEntities, mIntelPMU.UncoreEntities).Return(mError).Once()
err := mIntelPMU.initialization(mParser, mResolver, mActivator)
require.Error(t, err)
require.Contains(t, err.Error(), "error during parsing configuration sections")
mParser.AssertExpectations(t)
})
t.Run("resolver error", func(t *testing.T) {
mIntelPMU := &IntelPMU{EventListPaths: paths, fileInfo: mFileInfo}
mParser.On("parseEntities", mIntelPMU.CoreEntities, mIntelPMU.UncoreEntities).Return(nil).Once()
mResolver.On("resolveEntities", mIntelPMU.CoreEntities, mIntelPMU.UncoreEntities).Return(mError).Once()
err := mIntelPMU.initialization(mParser, mResolver, mActivator)
require.Error(t, err)
require.Contains(t, err.Error(), "error during events resolving")
mParser.AssertExpectations(t)
})
t.Run("exceeded file descriptors", func(t *testing.T) {
limit := []byte("10")
uncoreEntities := []*uncoreEventEntity{{parsedEvents: makeEvents(10, 21), parsedSockets: makeIDs(5)}}
estimation := 1050
mIntelPMU := IntelPMU{EventListPaths: paths, Log: testutil.Logger{}, fileInfo: mFileInfo, UncoreEntities: uncoreEntities}
mParser.On("parseEntities", mIntelPMU.CoreEntities, mIntelPMU.UncoreEntities).Return(nil).Once()
mResolver.On("resolveEntities", mIntelPMU.CoreEntities, mIntelPMU.UncoreEntities).Return(nil).Once()
mFileInfo.On("readFile", fileMaxPath).Return(limit, nil).Once()
err := mIntelPMU.initialization(mParser, mResolver, mActivator)
require.Error(t, err)
require.Contains(t, err.Error(), fmt.Sprintf("required file descriptors number `%d` exceeds maximum number of available file descriptors `%d`"+
": consider increasing the maximum number", estimation, 10))
mFileInfo.AssertExpectations(t)
mParser.AssertExpectations(t)
mResolver.AssertExpectations(t)
})
t.Run("failed to activate entities", func(t *testing.T) {
mIntelPMU := IntelPMU{EventListPaths: paths, Log: testutil.Logger{}, fileInfo: mFileInfo}
mParser.On("parseEntities", mIntelPMU.CoreEntities, mIntelPMU.UncoreEntities).Return(nil).Once()
mResolver.On("resolveEntities", mIntelPMU.CoreEntities, mIntelPMU.UncoreEntities).Return(nil).Once()
mActivator.On("activateEntities", mIntelPMU.CoreEntities, mIntelPMU.UncoreEntities).Return(mError).Once()
mFileInfo.On("readFile", fileMaxPath).Return(nil, mError).
On("fileLimit").Return(uint64(0), mError).Once()
err := mIntelPMU.initialization(mParser, mResolver, mActivator)
require.Error(t, err)
require.Contains(t, err.Error(), "error during events activation")
mFileInfo.AssertExpectations(t)
mParser.AssertExpectations(t)
mResolver.AssertExpectations(t)
mActivator.AssertExpectations(t)
})
t.Run("everything all right", func(t *testing.T) {
mIntelPMU := IntelPMU{EventListPaths: paths, Log: testutil.Logger{}, fileInfo: mFileInfo}
mParser.On("parseEntities", mIntelPMU.CoreEntities, mIntelPMU.UncoreEntities).Return(nil).Once()
mResolver.On("resolveEntities", mIntelPMU.CoreEntities, mIntelPMU.UncoreEntities).Return(nil).Once()
mFileInfo.On("readFile", fileMaxPath).Return(nil, mError).
On("fileLimit").Return(uint64(0), mError).Once()
mActivator.On("activateEntities", mIntelPMU.CoreEntities, mIntelPMU.UncoreEntities).Return(nil).Once()
err := mIntelPMU.initialization(mParser, mResolver, mActivator)
require.NoError(t, err)
mFileInfo.AssertExpectations(t)
mParser.AssertExpectations(t)
mResolver.AssertExpectations(t)
mActivator.AssertExpectations(t)
})
}
func TestGather(t *testing.T) {
mEntitiesValuesReader := &mockEntitiesValuesReader{}
mAcc := &testutil.Accumulator{}
mIntelPMU := &IntelPMU{entitiesReader: mEntitiesValuesReader}
type fieldWithTags struct {
fields map[string]interface{}
tags map[string]string
}
t.Run("entities reader is nil", func(t *testing.T) {
err := (&IntelPMU{entitiesReader: nil}).Gather(mAcc)
require.Error(t, err)
require.Contains(t, err.Error(), "entities reader is nil")
})
t.Run("error while reading entities", func(t *testing.T) {
errMock := errors.New("houston we have a problem")
mEntitiesValuesReader.On("readEntities", mIntelPMU.CoreEntities, mIntelPMU.UncoreEntities).
Return(nil, nil, errMock).Once()
err := mIntelPMU.Gather(mAcc)
require.Error(t, err)
require.Contains(t, err.Error(), fmt.Sprintf("failed to read entities events values: %v", errMock))
mEntitiesValuesReader.AssertExpectations(t)
})
tests := []struct {
name string
coreMetrics []coreMetric
uncoreMetrics []uncoreMetric
results []fieldWithTags
errMSg string
}{
{
name: "successful readings",
coreMetrics: []coreMetric{
{
values: ia.CounterValue{Raw: 100, Enabled: 200, Running: 200},
name: "CORE_EVENT_1",
tag: "DOGES",
cpu: 1,
},
{
values: ia.CounterValue{Raw: 2100, Enabled: 400, Running: 200},
name: "CORE_EVENT_2",
cpu: 0,
},
},
uncoreMetrics: []uncoreMetric{
{
values: ia.CounterValue{Raw: 2134562, Enabled: 1000000, Running: 1000000},
name: "UNCORE_EVENT_1",
tag: "SHIBA",
unitType: "cbox",
unit: "cbox_1",
socket: 3,
agg: false,
},
{
values: ia.CounterValue{Raw: 2134562, Enabled: 3222222, Running: 2100000},
name: "UNCORE_EVENT_2",
unitType: "cbox",
socket: 0,
agg: true,
},
},
results: []fieldWithTags{
{
fields: map[string]interface{}{
"raw": uint64(100),
"enabled": uint64(200),
"running": uint64(200),
"scaled": uint64(100),
},
tags: map[string]string{
"event": "CORE_EVENT_1",
"cpu": "1",
"events_tag": "DOGES",
},
},
{
fields: map[string]interface{}{
"raw": uint64(2100),
"enabled": uint64(400),
"running": uint64(200),
"scaled": uint64(4200),
},
tags: map[string]string{
"event": "CORE_EVENT_2",
"cpu": "0",
},
},
{
fields: map[string]interface{}{
"raw": uint64(2134562),
"enabled": uint64(1000000),
"running": uint64(1000000),
"scaled": uint64(2134562),
},
tags: map[string]string{
"event": "UNCORE_EVENT_1",
"events_tag": "SHIBA",
"socket": "3",
"unit_type": "cbox",
"unit": "cbox_1",
},
},
{
fields: map[string]interface{}{
"raw": uint64(2134562),
"enabled": uint64(3222222),
"running": uint64(2100000),
"scaled": uint64(3275253),
},
tags: map[string]string{
"event": "UNCORE_EVENT_2",
"socket": "0",
"unit_type": "cbox",
},
},
},
},
{
name: "core scaled value greater then max uint64",
coreMetrics: []coreMetric{
{
values: ia.CounterValue{Raw: math.MaxUint64, Enabled: 400000, Running: 200000},
name: "I_AM_TOO_BIG",
tag: "BIG_FISH",
},
},
errMSg: `cannot process "I_AM_TOO_BIG" scaled value "36893488147419103230": exceeds uint64`,
},
{
name: "uncore scaled value greater then max uint64",
uncoreMetrics: []uncoreMetric{
{
values: ia.CounterValue{Raw: math.MaxUint64, Enabled: 400000, Running: 200000},
name: "I_AM_TOO_BIG_UNCORE",
tag: "BIG_FISH",
},
},
errMSg: `cannot process "I_AM_TOO_BIG_UNCORE" scaled value "36893488147419103230": exceeds uint64`,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
mEntitiesValuesReader.On("readEntities", mIntelPMU.CoreEntities, mIntelPMU.UncoreEntities).
Return(test.coreMetrics, test.uncoreMetrics, nil).Once()
err := mIntelPMU.Gather(mAcc)
mEntitiesValuesReader.AssertExpectations(t)
if len(test.errMSg) > 0 {
require.Error(t, err)
require.Contains(t, err.Error(), test.errMSg)
return
}
require.NoError(t, err)
for _, result := range test.results {
mAcc.AssertContainsTaggedFields(t, "pmu_metric", result.fields, result.tags)
}
})
}
}
func TestCheckFileDescriptors(t *testing.T) {
tests := []struct {
name string
uncores []*uncoreEventEntity
cores []*coreEventEntity
estimation uint64
maxFD []byte
fileLimit uint64
errMsg string
}{
{"exceed maximum file descriptors number", []*uncoreEventEntity{
{parsedEvents: makeEvents(100, 21), parsedSockets: makeIDs(5)},
{parsedEvents: makeEvents(25, 3), parsedSockets: makeIDs(7)},
{parsedEvents: makeEvents(2, 7), parsedSockets: makeIDs(20)}},
[]*coreEventEntity{
{parsedEvents: makeEvents(100, 1), parsedCores: makeIDs(5)},
{parsedEvents: makeEvents(25, 1), parsedCores: makeIDs(7)},
{parsedEvents: makeEvents(2, 1), parsedCores: makeIDs(20)}},
12020, []byte("11000"), 8000, fmt.Sprintf("required file descriptors number `%d` exceeds maximum number of available file descriptors `%d`"+
": consider increasing the maximum number", 12020, 11000),
},
{"exceed soft file limit", []*uncoreEventEntity{{parsedEvents: makeEvents(100, 21), parsedSockets: makeIDs(5)}}, []*coreEventEntity{
{parsedEvents: makeEvents(100, 1), parsedCores: makeIDs(5)}},
11000, []byte("2515357"), 800, fmt.Sprintf("required file descriptors number `%d` exceeds soft limit of open files `%d`"+
": consider increasing the limit", 11000, 800),
},
{"no exceeds", []*uncoreEventEntity{{parsedEvents: makeEvents(100, 21), parsedSockets: makeIDs(5)}},
[]*coreEventEntity{{parsedEvents: makeEvents(100, 1), parsedCores: makeIDs(5)}},
11000, []byte("2515357"), 13000, "",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
mFileInfo := &mockFileInfoProvider{}
mIntelPMU := IntelPMU{
CoreEntities: test.cores,
UncoreEntities: test.uncores,
fileInfo: mFileInfo,
Log: testutil.Logger{},
}
mFileInfo.On("readFile", fileMaxPath).Return(test.maxFD, nil).
On("fileLimit").Return(test.fileLimit, nil).Once()
err := mIntelPMU.checkFileDescriptors()
if len(test.errMsg) > 0 {
require.Error(t, err)
require.Contains(t, err.Error(), test.errMsg)
return
}
require.NoError(t, err)
mFileInfo.AssertExpectations(t)
})
}
}
func TestEstimateUncoreFd(t *testing.T) {
tests := []struct {
name string
entities []*uncoreEventEntity
result uint64
}{
{"nil entities", nil, 0},
{"nil perf event", []*uncoreEventEntity{{parsedEvents: []*eventWithQuals{{"", nil, ia.CustomizableEvent{}}}, parsedSockets: makeIDs(0)}}, 0},
{"one uncore entity", []*uncoreEventEntity{{parsedEvents: makeEvents(10, 10), parsedSockets: makeIDs(20)}}, 2000},
{"nil entity", []*uncoreEventEntity{nil, {parsedEvents: makeEvents(1, 8), parsedSockets: makeIDs(1)}}, 8},
{"many core entities", []*uncoreEventEntity{
{parsedEvents: makeEvents(100, 21), parsedSockets: makeIDs(5)},
{parsedEvents: makeEvents(25, 3), parsedSockets: makeIDs(7)},
{parsedEvents: makeEvents(2, 7), parsedSockets: makeIDs(20)},
}, 11305},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
mIntelPMU := IntelPMU{UncoreEntities: test.entities}
result, err := estimateUncoreFd(mIntelPMU.UncoreEntities)
require.Equal(t, test.result, result)
require.NoError(t, err)
})
}
}
func TestEstimateCoresFd(t *testing.T) {
tests := []struct {
name string
entities []*coreEventEntity
result uint64
}{
{"nil entities", nil, 0},
{"one core entity", []*coreEventEntity{{parsedEvents: makeEvents(10, 1), parsedCores: makeIDs(20)}}, 200},
{"nil entity", []*coreEventEntity{nil, {parsedEvents: makeEvents(10, 1), parsedCores: makeIDs(20)}}, 200},
{"many core entities", []*coreEventEntity{
{parsedEvents: makeEvents(100, 1), parsedCores: makeIDs(5)},
{parsedEvents: makeEvents(25, 1), parsedCores: makeIDs(7)},
{parsedEvents: makeEvents(2, 1), parsedCores: makeIDs(20)},
}, 715},
{"1024 events", []*coreEventEntity{{parsedEvents: makeEvents(1024, 1), parsedCores: makeIDs(12)}}, 12288},
{"big number", []*coreEventEntity{{parsedEvents: makeEvents(1024, 1), parsedCores: makeIDs(1048576)}}, 1073741824},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
mIntelPMU := IntelPMU{CoreEntities: test.entities}
result, err := estimateCoresFd(mIntelPMU.CoreEntities)
require.NoError(t, err)
require.Equal(t, test.result, result)
})
}
}
func makeEvents(number, pmusNumber int) []*eventWithQuals {
a := make([]*eventWithQuals, number)
for i := range a {
b := make([]ia.NamedPMUType, pmusNumber)
for j := range b {
b[j] = ia.NamedPMUType{}
}
a[i] = &eventWithQuals{fmt.Sprintf("EVENT.%d", i), nil,
ia.CustomizableEvent{Event: &ia.PerfEvent{PMUTypes: b}},
}
}
return a
}
func makeIDs(number int) []int {
a := make([]int, number)
for i := range a {
a[i] = i
}
return a
}
func TestReadMaxFD(t *testing.T) {
mFileReader := &mockFileInfoProvider{}
t.Run("reader is nil", func(t *testing.T) {
result, err := readMaxFD(nil)
require.Error(t, err)
require.Contains(t, err.Error(), "file reader is nil")
require.Zero(t, result)
})
openErrorMsg := fmt.Sprintf("cannot open file %q", fileMaxPath)
parseErrorMsg := fmt.Sprintf("cannot parse file content of %q", fileMaxPath)
tests := []struct {
name string
err error
content []byte
maxFD uint64
failMsg string
}{
{"read file error", errors.New("mock error"), nil, 0, openErrorMsg},
{"file content parse error", nil, []byte("wrong format"), 0, parseErrorMsg},
{"negative value reading", nil, []byte("-10000"), 0, parseErrorMsg},
{"max uint exceeded", nil, []byte("18446744073709551616"), 0, parseErrorMsg},
{"reading succeeded", nil, []byte("12343122"), 12343122, ""},
{"min value reading", nil, []byte("0"), 0, ""},
{"max uint 64 reading", nil, []byte("18446744073709551615"), math.MaxUint64, ""},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
mFileReader.On("readFile", fileMaxPath).Return(test.content, test.err).Once()
result, err := readMaxFD(mFileReader)
if len(test.failMsg) > 0 {
require.Error(t, err)
require.Contains(t, err.Error(), test.failMsg)
} else {
require.NoError(t, err)
}
require.Equal(t, test.maxFD, result)
mFileReader.AssertExpectations(t)
})
}
}
func TestAddFiles(t *testing.T) {
mFileInfo := &mockFileInfoProvider{}
mError := errors.New("mock error")
t.Run("no paths", func(t *testing.T) {
err := checkFiles(nil, mFileInfo)
require.Error(t, err)
require.Contains(t, err.Error(), "no paths were given")
})
t.Run("no file info provider", func(t *testing.T) {
err := checkFiles([]string{"path/1, path/2"}, nil)
require.Error(t, err)
require.Contains(t, err.Error(), "file info provider is nil")
})
t.Run("stat error", func(t *testing.T) {
file := "path/to/file"
paths := []string{file}
mFileInfo.On("lstat", file).Return(nil, mError).Once()
err := checkFiles(paths, mFileInfo)
require.Error(t, err)
require.Contains(t, err.Error(), fmt.Sprintf("cannot obtain file info of %q", file))
mFileInfo.AssertExpectations(t)
})
t.Run("file does not exist", func(t *testing.T) {
file := "path/to/file"
paths := []string{file}
mFileInfo.On("lstat", file).Return(nil, os.ErrNotExist).Once()
err := checkFiles(paths, mFileInfo)
require.Error(t, err)
require.Contains(t, err.Error(), fmt.Sprintf("file %q doesn't exist", file))
mFileInfo.AssertExpectations(t)
})
t.Run("file is symlink", func(t *testing.T) {
file := "path/to/symlink"
paths := []string{file}
fileInfo := fakeFileInfo{fileMode: os.ModeSymlink}
mFileInfo.On("lstat", file).Return(fileInfo, nil).Once()
err := checkFiles(paths, mFileInfo)
require.Error(t, err)
require.Contains(t, err.Error(), fmt.Sprintf("file %q is a symlink", file))
mFileInfo.AssertExpectations(t)
})
t.Run("file doesn't point to a regular file", func(t *testing.T) {
file := "path/to/file"
paths := []string{file}
fileInfo := fakeFileInfo{fileMode: os.ModeDir}
mFileInfo.On("lstat", file).Return(fileInfo, nil).Once()
err := checkFiles(paths, mFileInfo)
require.Error(t, err)
require.Contains(t, err.Error(), fmt.Sprintf("file %q doesn't point to a reagular file", file))
mFileInfo.AssertExpectations(t)
})
t.Run("checking succeeded", func(t *testing.T) {
paths := []string{"path/to/file1", "path/to/file2", "path/to/file3"}
fileInfo := fakeFileInfo{}
for _, file := range paths {
mFileInfo.On("lstat", file).Return(fileInfo, nil).Once()
}
err := checkFiles(paths, mFileInfo)
require.NoError(t, err)
mFileInfo.AssertExpectations(t)
})
}
type fakeFileInfo struct {
fileMode os.FileMode
}
func (fakeFileInfo) Name() string { return "" }
func (fakeFileInfo) Size() int64 { return 0 }
func (f fakeFileInfo) Mode() os.FileMode { return f.fileMode }
func (fakeFileInfo) ModTime() time.Time { return time.Time{} }
func (fakeFileInfo) IsDir() bool { return false }
func (fakeFileInfo) Sys() interface{} { return nil }