//go:build linux && amd64 package intel_pmu import ( "errors" "fmt" "math" "testing" "time" ia "github.com/intel/iaevents" "github.com/stretchr/testify/require" ) type moonClock struct{} func (moonClock) now() time.Time { return time.Date(1969, 7, 20, 20, 17, 0, 0, time.UTC) } type eventWithValues struct { activeEvent *ia.ActiveEvent values ia.CounterValue } func TestReadCoreEvents(t *testing.T) { mReader := &mockValuesReader{} mTimer := &moonClock{} mEntitiesReader := &iaEntitiesValuesReader{mReader, mTimer} t.Run("event reader is nil", func(t *testing.T) { metrics, err := (&iaEntitiesValuesReader{timer: moonClock{}}).readCoreEvents(nil) require.Error(t, err) require.Contains(t, err.Error(), "event values reader or timer is nil") require.Nil(t, metrics) }) t.Run("timer is nil", func(t *testing.T) { metrics, err := (&iaEntitiesValuesReader{eventReader: &iaValuesReader{}}).readCoreEvents(nil) require.Error(t, err) require.Contains(t, err.Error(), "event values reader or timer is nil") require.Nil(t, metrics) }) t.Run("entity is nil", func(t *testing.T) { metrics, err := (&iaEntitiesValuesReader{eventReader: &iaValuesReader{}, timer: moonClock{}}).readCoreEvents(nil) require.Error(t, err) require.Contains(t, err.Error(), "entity is nil") require.Nil(t, metrics) }) t.Run("nil events", func(t *testing.T) { entity := &coreEventEntity{} entity.activeEvents = append(entity.activeEvents, nil) metrics, err := mEntitiesReader.readCoreEvents(entity) require.Error(t, err) require.Contains(t, err.Error(), "active event or corresponding perf event is nil") require.Nil(t, metrics) }) t.Run("reading failed", func(t *testing.T) { errMock := errors.New("mock error") event := &ia.ActiveEvent{PerfEvent: &ia.PerfEvent{Name: "event1"}} entity := &coreEventEntity{} entity.activeEvents = append(entity.activeEvents, event) mReader.On("readValue", event).Return(ia.CounterValue{}, errMock).Once() metrics, err := mEntitiesReader.readCoreEvents(entity) require.Error(t, err) require.Contains(t, err.Error(), fmt.Sprintf("failed to read core event %q values: %v", event, errMock)) require.Nil(t, metrics) mReader.AssertExpectations(t) }) t.Run("read active events values", func(t *testing.T) { entity := &coreEventEntity{} var expected []coreMetric tEvents := []eventWithValues{ {&ia.ActiveEvent{PerfEvent: &ia.PerfEvent{Name: "event1"}}, ia.CounterValue{Raw: 316, Enabled: 182060524, Running: 182060524}}, {&ia.ActiveEvent{PerfEvent: &ia.PerfEvent{Name: "event2"}}, ia.CounterValue{Raw: 1238901, Enabled: 18234123, Running: 18234123}}, {&ia.ActiveEvent{PerfEvent: &ia.PerfEvent{Name: "event3"}}, ia.CounterValue{Raw: 412323, Enabled: 1823132, Running: 1823180}}, } for _, tc := range tEvents { entity.activeEvents = append(entity.activeEvents, tc.activeEvent) cpu, _ := tc.activeEvent.PMUPlacement() newMetric := coreMetric{ values: tc.values, tag: entity.EventsTag, cpu: cpu, name: tc.activeEvent.PerfEvent.Name, time: mTimer.now(), } expected = append(expected, newMetric) mReader.On("readValue", tc.activeEvent).Return(tc.values, nil).Once() } metrics, err := mEntitiesReader.readCoreEvents(entity) require.NoError(t, err) require.Equal(t, expected, metrics) mReader.AssertExpectations(t) }) } func TestReadMultiEventSeparately(t *testing.T) { mReader := &mockValuesReader{} mTimer := &moonClock{} mEntitiesReader := &iaEntitiesValuesReader{mReader, mTimer} t.Run("event reader is nil", func(t *testing.T) { event := multiEvent{} metrics, err := (&iaEntitiesValuesReader{timer: moonClock{}}).readMultiEventSeparately(event) require.Error(t, err) require.Contains(t, err.Error(), "event values reader or timer is nil") require.Nil(t, metrics) }) t.Run("timer is nil", func(t *testing.T) { event := multiEvent{} metrics, err := (&iaEntitiesValuesReader{eventReader: &iaValuesReader{}}).readMultiEventSeparately(event) require.Error(t, err) require.Contains(t, err.Error(), "event values reader or timer is nil") require.Nil(t, metrics) }) t.Run("multi event is nil", func(t *testing.T) { event := multiEvent{} metrics, err := (&iaEntitiesValuesReader{&iaValuesReader{}, moonClock{}}).readMultiEventSeparately(event) require.Error(t, err) require.Contains(t, err.Error(), "no active events or perf event is nil") require.Nil(t, metrics) }) t.Run("reading failed", func(t *testing.T) { errMock := errors.New("mock error") perfEvent := &ia.PerfEvent{Name: "event"} event := &ia.ActiveEvent{PerfEvent: perfEvent} multi := multiEvent{perfEvent: perfEvent, activeEvents: []*ia.ActiveEvent{event}} mReader.On("readValue", event).Return(ia.CounterValue{}, errMock).Once() metrics, err := mEntitiesReader.readMultiEventSeparately(multi) require.Error(t, err) require.Contains(t, err.Error(), fmt.Sprintf("failed to read uncore event %q values: %v", event, errMock)) require.Nil(t, metrics) mReader.AssertExpectations(t) }) t.Run("read active events values", func(t *testing.T) { perfEvent := &ia.PerfEvent{Name: "event", PMUName: "pmu name"} multi := multiEvent{perfEvent: perfEvent} var expected []uncoreMetric tEvents := []eventWithValues{ {&ia.ActiveEvent{PerfEvent: perfEvent}, ia.CounterValue{Raw: 316, Enabled: 182060524, Running: 182060524}}, {&ia.ActiveEvent{PerfEvent: perfEvent}, ia.CounterValue{Raw: 1238901, Enabled: 18234123, Running: 18234123}}, {&ia.ActiveEvent{PerfEvent: perfEvent}, ia.CounterValue{Raw: 412323, Enabled: 1823132, Running: 1823180}}, } for _, tc := range tEvents { multi.activeEvents = append(multi.activeEvents, tc.activeEvent) newMetric := uncoreMetric{ values: tc.values, socket: multi.socket, unitType: multi.perfEvent.PMUName, name: multi.perfEvent.Name, unit: tc.activeEvent.PMUName(), time: mTimer.now(), } expected = append(expected, newMetric) mReader.On("readValue", tc.activeEvent).Return(tc.values, nil).Once() } metrics, err := mEntitiesReader.readMultiEventSeparately(multi) require.NoError(t, err) require.Equal(t, expected, metrics) mReader.AssertExpectations(t) }) } func TestReadMultiEventAgg(t *testing.T) { mReader := &mockValuesReader{} mTimer := &moonClock{} mEntitiesReader := &iaEntitiesValuesReader{mReader, mTimer} errMock := errors.New("mock error") t.Run("event reader is nil", func(t *testing.T) { event := multiEvent{} _, err := (&iaEntitiesValuesReader{timer: moonClock{}}).readMultiEventAgg(event) require.Error(t, err) require.Contains(t, err.Error(), "event values reader or timer is nil") }) t.Run("timer is nil", func(t *testing.T) { event := multiEvent{} _, err := (&iaEntitiesValuesReader{eventReader: &iaValuesReader{}}).readMultiEventAgg(event) require.Error(t, err) require.Contains(t, err.Error(), "event values reader or timer is nil") }) perfEvent := &ia.PerfEvent{Name: "event", PMUName: "pmu name"} tests := []struct { name string multi multiEvent events []eventWithValues result ia.CounterValue readFail bool errMsg string }{ { name: "no events", multi: multiEvent{perfEvent: perfEvent}, events: nil, result: ia.CounterValue{}, errMsg: "no active events or perf event is nil", }, { name: "no perf event", multi: multiEvent{perfEvent: nil, activeEvents: []*ia.ActiveEvent{{}, {}}}, events: nil, result: ia.CounterValue{}, errMsg: "no active events or perf event is nil", }, { name: "successful reading and aggregation", multi: multiEvent{perfEvent: perfEvent}, events: []eventWithValues{ {&ia.ActiveEvent{PerfEvent: perfEvent}, ia.CounterValue{Raw: 5123, Enabled: 1231242, Running: 41123}}, {&ia.ActiveEvent{PerfEvent: perfEvent}, ia.CounterValue{Raw: 4500, Enabled: 1823423, Running: 182343}}, }, result: ia.CounterValue{Raw: 9623, Enabled: 3054665, Running: 223466}, errMsg: "", }, { name: "to big numbers", multi: multiEvent{perfEvent: perfEvent}, events: []eventWithValues{ {&ia.ActiveEvent{PerfEvent: perfEvent}, ia.CounterValue{Raw: math.MaxUint64, Enabled: 0, Running: 0}}, {&ia.ActiveEvent{PerfEvent: perfEvent}, ia.CounterValue{Raw: 1, Enabled: 0, Running: 0}}, }, result: ia.CounterValue{}, errMsg: fmt.Sprintf("cannot aggregate %q values, uint64 exceeding", perfEvent), }, { name: "reading fail", multi: multiEvent{perfEvent: perfEvent}, events: []eventWithValues{ {&ia.ActiveEvent{PerfEvent: perfEvent}, ia.CounterValue{Raw: 0, Enabled: 0, Running: 0}}, }, readFail: true, result: ia.CounterValue{}, errMsg: "failed to read uncore event", }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { for _, eventWithValue := range test.events { test.multi.activeEvents = append(test.multi.activeEvents, eventWithValue.activeEvent) if test.readFail { mReader.On("readValue", eventWithValue.activeEvent).Return(ia.CounterValue{}, errMock).Once() continue } mReader.On("readValue", eventWithValue.activeEvent).Return(eventWithValue.values, nil).Once() } metric, err := mEntitiesReader.readMultiEventAgg(test.multi) mReader.AssertExpectations(t) if len(test.errMsg) > 0 { require.Error(t, err) require.Contains(t, err.Error(), test.errMsg) return } expected := uncoreMetric{ values: test.result, socket: test.multi.socket, unitType: test.multi.perfEvent.PMUName, name: test.multi.perfEvent.Name, time: mTimer.now(), } require.NoError(t, err) require.Equal(t, expected, metric) }) } } func TestReadUncoreEvents(t *testing.T) { errMock := errors.New("mock error") t.Run("entity is nil", func(t *testing.T) { metrics, err := (&iaEntitiesValuesReader{}).readUncoreEvents(nil) require.Error(t, err) require.Contains(t, err.Error(), "entity is nil") require.Nil(t, metrics) }) t.Run("read aggregated entities", func(t *testing.T) { mReader := &mockValuesReader{} mTimer := &moonClock{} mEntitiesReader := &iaEntitiesValuesReader{mReader, mTimer} perfEvent := &ia.PerfEvent{Name: "mock event", PMUName: "cbox", PMUTypes: []ia.NamedPMUType{{Name: "cbox"}}} perfEvent2 := &ia.PerfEvent{Name: "mock event2", PMUName: "rad", PMUTypes: []ia.NamedPMUType{{Name: "rad2"}}} multi := multiEvent{perfEvent: perfEvent} events := []eventWithValues{ {&ia.ActiveEvent{PerfEvent: perfEvent}, ia.CounterValue{Raw: 2003}}, {&ia.ActiveEvent{PerfEvent: perfEvent}, ia.CounterValue{Raw: 4005}}, } multi2 := multiEvent{perfEvent: perfEvent2} events2 := []eventWithValues{ {&ia.ActiveEvent{PerfEvent: perfEvent2}, ia.CounterValue{Raw: 2003}}, {&ia.ActiveEvent{PerfEvent: perfEvent2}, ia.CounterValue{Raw: 123005}}, } for _, event := range events { multi.activeEvents = append(multi.activeEvents, event.activeEvent) mReader.On("readValue", event.activeEvent).Return(event.values, nil).Once() } for _, event := range events2 { multi2.activeEvents = append(multi2.activeEvents, event.activeEvent) mReader.On("readValue", event.activeEvent).Return(event.values, nil).Once() } newMetric := uncoreMetric{ values: ia.CounterValue{Raw: 6008, Enabled: 0, Running: 0}, socket: multi.socket, unitType: perfEvent.PMUName, name: perfEvent.Name, time: mTimer.now(), } newMetric2 := uncoreMetric{ values: ia.CounterValue{Raw: 125008, Enabled: 0, Running: 0}, socket: multi2.socket, unitType: perfEvent2.PMUName, name: perfEvent2.Name, time: mTimer.now(), } expected := []uncoreMetric{newMetric, newMetric2} entityAgg := &uncoreEventEntity{Aggregate: true, activeMultiEvents: []multiEvent{multi, multi2}} metrics, err := mEntitiesReader.readUncoreEvents(entityAgg) require.NoError(t, err) require.Equal(t, expected, metrics) mReader.AssertExpectations(t) t.Run("reading error", func(t *testing.T) { event := &ia.ActiveEvent{PerfEvent: perfEvent} multi := multiEvent{perfEvent: perfEvent, activeEvents: []*ia.ActiveEvent{event}} mReader.On("readValue", event).Return(ia.CounterValue{}, errMock).Once() entityAgg := &uncoreEventEntity{Aggregate: true, activeMultiEvents: []multiEvent{multi}} metrics, err = mEntitiesReader.readUncoreEvents(entityAgg) require.Error(t, err) require.Nil(t, metrics) mReader.AssertExpectations(t) }) }) t.Run("read distributed entities", func(t *testing.T) { mReader := &mockValuesReader{} mTimer := &moonClock{} mEntitiesReader := &iaEntitiesValuesReader{mReader, mTimer} perfEvent := &ia.PerfEvent{Name: "mock event", PMUName: "cbox", PMUTypes: []ia.NamedPMUType{{Name: "cbox"}}} perfEvent2 := &ia.PerfEvent{Name: "mock event2", PMUName: "rad", PMUTypes: []ia.NamedPMUType{{Name: "rad2"}}} multi := multiEvent{perfEvent: perfEvent, socket: 2} events := []eventWithValues{ {&ia.ActiveEvent{PerfEvent: perfEvent}, ia.CounterValue{Raw: 2003}}, {&ia.ActiveEvent{PerfEvent: perfEvent}, ia.CounterValue{Raw: 4005}}, } multi2 := multiEvent{perfEvent: perfEvent2, socket: 1} events2 := []eventWithValues{ {&ia.ActiveEvent{PerfEvent: perfEvent2}, ia.CounterValue{Raw: 2003}}, {&ia.ActiveEvent{PerfEvent: perfEvent2}, ia.CounterValue{Raw: 123005}}, } var expected []uncoreMetric for _, event := range events { multi.activeEvents = append(multi.activeEvents, event.activeEvent) mReader.On("readValue", event.activeEvent).Return(event.values, nil).Once() newMetric := uncoreMetric{ values: event.values, socket: multi.socket, unitType: perfEvent.PMUName, name: perfEvent.Name, unit: event.activeEvent.PMUName(), time: mTimer.now(), } expected = append(expected, newMetric) } for _, event := range events2 { multi2.activeEvents = append(multi2.activeEvents, event.activeEvent) mReader.On("readValue", event.activeEvent).Return(event.values, nil).Once() newMetric := uncoreMetric{ values: event.values, socket: multi2.socket, unitType: perfEvent2.PMUName, name: perfEvent2.Name, unit: event.activeEvent.PMUName(), time: mTimer.now(), } expected = append(expected, newMetric) } entity := &uncoreEventEntity{activeMultiEvents: []multiEvent{multi, multi2}} metrics, err := mEntitiesReader.readUncoreEvents(entity) require.NoError(t, err) require.Equal(t, expected, metrics) mReader.AssertExpectations(t) t.Run("reading error", func(t *testing.T) { event := &ia.ActiveEvent{PerfEvent: perfEvent} multi := multiEvent{perfEvent: perfEvent, activeEvents: []*ia.ActiveEvent{event}} mReader.On("readValue", event).Return(ia.CounterValue{}, errMock).Once() entityAgg := &uncoreEventEntity{activeMultiEvents: []multiEvent{multi}} metrics, err = mEntitiesReader.readUncoreEvents(entityAgg) require.Error(t, err) require.Nil(t, metrics) mReader.AssertExpectations(t) }) }) } func TestReadEntities(t *testing.T) { mReader := &mockValuesReader{} mTimer := &moonClock{} mEntitiesReader := &iaEntitiesValuesReader{mReader, mTimer} t.Run("read entities", func(t *testing.T) { values := ia.CounterValue{} socket := 0 corePerfEvent := &ia.PerfEvent{Name: "core event 1", PMUName: "cpu"} activeCoreEvent := []*ia.ActiveEvent{{PerfEvent: corePerfEvent}} coreMetric1 := coreMetric{values: values, name: corePerfEvent.Name, time: mTimer.now()} corePerfEvent2 := &ia.PerfEvent{Name: "core event 2", PMUName: "cpu"} activeCoreEvent2 := []*ia.ActiveEvent{{PerfEvent: corePerfEvent2}} coreMetric2 := coreMetric{values: values, name: corePerfEvent2.Name, time: mTimer.now()} uncorePerfEvent := &ia.PerfEvent{Name: "uncore event 1", PMUName: "cbox"} activeUncoreEvent := []*ia.ActiveEvent{{PerfEvent: uncorePerfEvent}} uncoreMetric1 := uncoreMetric{ values: values, name: uncorePerfEvent.Name, unitType: uncorePerfEvent.PMUName, socket: socket, time: mTimer.now(), } uncorePerfEvent2 := &ia.PerfEvent{Name: "uncore event 2", PMUName: "rig"} activeUncoreEvent2 := []*ia.ActiveEvent{{PerfEvent: uncorePerfEvent2}} uncoreMetric2 := uncoreMetric{ values: values, name: uncorePerfEvent2.Name, unitType: uncorePerfEvent2.PMUName, socket: socket, time: mTimer.now(), } coreEntities := []*coreEventEntity{{activeEvents: activeCoreEvent}, {activeEvents: activeCoreEvent2}} uncoreEntities := []*uncoreEventEntity{ {activeMultiEvents: []multiEvent{{activeEvents: activeUncoreEvent, perfEvent: uncorePerfEvent, socket: socket}}}, {activeMultiEvents: []multiEvent{{activeEvents: activeUncoreEvent2, perfEvent: uncorePerfEvent2, socket: socket}}}, } expectedCoreMetrics := []coreMetric{coreMetric1, coreMetric2} expectedUncoreMetrics := []uncoreMetric{uncoreMetric1, uncoreMetric2} mReader.On("readValue", activeCoreEvent[0]).Return(values, nil).Once() mReader.On("readValue", activeCoreEvent2[0]).Return(values, nil).Once() mReader.On("readValue", activeUncoreEvent[0]).Return(values, nil).Once() mReader.On("readValue", activeUncoreEvent2[0]).Return(values, nil).Once() coreMetrics, uncoreMetrics, err := mEntitiesReader.readEntities(coreEntities, uncoreEntities) require.NoError(t, err) require.Equal(t, expectedCoreMetrics, coreMetrics) require.NotNil(t, expectedUncoreMetrics, uncoreMetrics) mReader.AssertExpectations(t) }) t.Run("core entity reading failed", func(t *testing.T) { coreEntities := []*coreEventEntity{nil} coreMetrics, uncoreMetrics, err := mEntitiesReader.readEntities(coreEntities, nil) require.Error(t, err) require.Contains(t, err.Error(), "entity is nil") require.Nil(t, coreMetrics) require.Nil(t, uncoreMetrics) }) t.Run("uncore entity reading failed", func(t *testing.T) { uncoreEntities := []*uncoreEventEntity{nil} coreMetrics, uncoreMetrics, err := mEntitiesReader.readEntities(nil, uncoreEntities) require.Error(t, err) require.Contains(t, err.Error(), "entity is nil") require.Nil(t, coreMetrics) require.Nil(t, uncoreMetrics) }) }