5223 lines
134 KiB
Go
5223 lines
134 KiB
Go
//go:build linux && amd64
|
|
|
|
package intel_powerstat
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strconv"
|
|
"testing"
|
|
|
|
ptel "github.com/intel/powertelemetry"
|
|
"github.com/stretchr/testify/mock"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/influxdata/telegraf/testutil"
|
|
)
|
|
|
|
type parsePackageMetricTestCase struct {
|
|
name string
|
|
metrics []packageMetricType
|
|
parsed []packageMetricType
|
|
err error
|
|
}
|
|
|
|
type parseCPUMetricTestCase struct {
|
|
name string
|
|
metrics []cpuMetricType
|
|
parsed []cpuMetricType
|
|
err error
|
|
}
|
|
|
|
func TestParsePackageMetrics(t *testing.T) {
|
|
testCases := []parsePackageMetricTestCase{
|
|
{
|
|
name: "NilSlice",
|
|
metrics: nil,
|
|
parsed: []packageMetricType{
|
|
packageCurrentPowerConsumption,
|
|
packageCurrentDramPowerConsumption,
|
|
packageThermalDesignPower,
|
|
},
|
|
},
|
|
{
|
|
name: "EmptySlice",
|
|
metrics: make([]packageMetricType, 0),
|
|
parsed: make([]packageMetricType, 0),
|
|
},
|
|
{
|
|
name: "HasDuplicates",
|
|
metrics: []packageMetricType{
|
|
packageCurrentPowerConsumption,
|
|
packageThermalDesignPower,
|
|
packageCurrentDramPowerConsumption,
|
|
packageThermalDesignPower, // duplicate
|
|
},
|
|
err: errors.New("package metrics contains duplicates"),
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
p := &PowerStat{
|
|
PackageMetrics: tc.metrics,
|
|
}
|
|
|
|
err := p.parsePackageMetrics()
|
|
|
|
if tc.err != nil {
|
|
require.ErrorContains(t, err, tc.err.Error())
|
|
} else {
|
|
require.NoError(t, err)
|
|
require.Equal(t, tc.parsed, p.PackageMetrics)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestParseCPUMetrics(t *testing.T) {
|
|
testCases := []parseCPUMetricTestCase{
|
|
{
|
|
name: "NilSlice",
|
|
metrics: nil,
|
|
parsed: nil,
|
|
},
|
|
{
|
|
name: "EmptySlice",
|
|
metrics: make([]cpuMetricType, 0),
|
|
parsed: make([]cpuMetricType, 0),
|
|
},
|
|
{
|
|
name: "HasDuplicates",
|
|
metrics: []cpuMetricType{
|
|
cpuC0StateResidency,
|
|
cpuC1StateResidency,
|
|
cpuTemperature,
|
|
cpuC0StateResidency, // duplicate
|
|
},
|
|
err: errors.New("cpu metrics contains duplicates"),
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
p := &PowerStat{
|
|
CPUMetrics: tc.metrics,
|
|
}
|
|
|
|
err := p.parseCPUMetrics()
|
|
|
|
if tc.err != nil {
|
|
require.ErrorContains(t, err, tc.err.Error())
|
|
} else {
|
|
require.NoError(t, err)
|
|
require.Equal(t, tc.parsed, p.CPUMetrics)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestParseCPUTimeRelatedMsrMetrics(t *testing.T) {
|
|
testCases := []parseCPUMetricTestCase{
|
|
{
|
|
name: "EmptySlice",
|
|
metrics: make([]cpuMetricType, 0),
|
|
parsed: make([]cpuMetricType, 0),
|
|
},
|
|
{
|
|
name: "NotFound",
|
|
metrics: []cpuMetricType{
|
|
// Metric not relying on MSR.
|
|
cpuFrequency,
|
|
|
|
// Metric relying on single MSR read.
|
|
cpuTemperature,
|
|
|
|
// Metrics relying on perf events.
|
|
cpuC0SubstateC01Percent,
|
|
cpuC0SubstateC02Percent,
|
|
cpuC0SubstateC0WaitPercent,
|
|
},
|
|
parsed: make([]cpuMetricType, 0),
|
|
},
|
|
{
|
|
name: "Found",
|
|
metrics: []cpuMetricType{
|
|
// Metric not relying on MSR.
|
|
cpuFrequency,
|
|
|
|
// Metric relying on single MSR read.
|
|
cpuTemperature,
|
|
|
|
// Metrics relying on time-related MSR offset reads.
|
|
cpuC0StateResidency,
|
|
cpuC1StateResidency,
|
|
cpuC3StateResidency,
|
|
cpuC6StateResidency,
|
|
cpuC7StateResidency,
|
|
cpuBusyCycles,
|
|
cpuBusyFrequency,
|
|
|
|
// Metrics relying on perf events.
|
|
cpuC0SubstateC01Percent,
|
|
cpuC0SubstateC02Percent,
|
|
},
|
|
parsed: []cpuMetricType{
|
|
cpuC0StateResidency,
|
|
cpuC1StateResidency,
|
|
cpuC3StateResidency,
|
|
cpuC6StateResidency,
|
|
cpuC7StateResidency,
|
|
cpuBusyCycles,
|
|
cpuBusyFrequency,
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
p := &PowerStat{
|
|
CPUMetrics: tc.metrics,
|
|
}
|
|
|
|
p.parseCPUTimeRelatedMsrMetrics()
|
|
require.Equal(t, tc.parsed, p.parsedCPUTimedMsrMetrics)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestParseCPUPerfMetrics(t *testing.T) {
|
|
testCases := []parseCPUMetricTestCase{
|
|
{
|
|
name: "EmptySlice",
|
|
metrics: make([]cpuMetricType, 0),
|
|
parsed: make([]cpuMetricType, 0),
|
|
},
|
|
{
|
|
name: "NotFound",
|
|
metrics: []cpuMetricType{
|
|
// Metric not relying on MSR.
|
|
cpuFrequency,
|
|
|
|
// Metric relying on single MSR read.
|
|
cpuTemperature,
|
|
|
|
// Metrics relying on time-related MSR offset reads.
|
|
cpuC3StateResidency,
|
|
cpuC6StateResidency,
|
|
cpuBusyFrequency,
|
|
},
|
|
parsed: make([]cpuMetricType, 0),
|
|
},
|
|
{
|
|
name: "Found",
|
|
metrics: []cpuMetricType{
|
|
// Metric not relying on MSR.
|
|
cpuFrequency,
|
|
|
|
// Metric relying on single MSR read.
|
|
cpuTemperature,
|
|
|
|
// Metrics relying on perf events.
|
|
cpuC0SubstateC01Percent,
|
|
cpuC0SubstateC02Percent,
|
|
cpuC0SubstateC0WaitPercent,
|
|
|
|
// Metrics relying on time-related MSR offset reads.
|
|
cpuC3StateResidency,
|
|
cpuC6StateResidency,
|
|
cpuBusyFrequency,
|
|
},
|
|
parsed: []cpuMetricType{
|
|
cpuC0SubstateC01Percent,
|
|
cpuC0SubstateC02Percent,
|
|
cpuC0SubstateC0WaitPercent,
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
p := &PowerStat{
|
|
CPUMetrics: tc.metrics,
|
|
}
|
|
|
|
p.parseCPUPerfMetrics()
|
|
require.Equal(t, tc.parsed, p.parsedCPUPerfMetrics)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestParsePackageRaplMetrics(t *testing.T) {
|
|
testCases := []parsePackageMetricTestCase{
|
|
{
|
|
name: "EmptySlice",
|
|
metrics: make([]packageMetricType, 0),
|
|
parsed: make([]packageMetricType, 0),
|
|
},
|
|
{
|
|
name: "NotFound",
|
|
metrics: []packageMetricType{
|
|
// Metrics not relying on rapl.
|
|
packageTurboLimit,
|
|
packageCPUBaseFrequency,
|
|
packageUncoreFrequency,
|
|
},
|
|
parsed: make([]packageMetricType, 0),
|
|
},
|
|
{
|
|
name: "Found",
|
|
metrics: []packageMetricType{
|
|
// Metrics not relying on rapl.
|
|
packageTurboLimit,
|
|
packageCPUBaseFrequency,
|
|
packageUncoreFrequency,
|
|
|
|
// Metrics relying on rapl.
|
|
packageCurrentPowerConsumption,
|
|
packageCurrentDramPowerConsumption,
|
|
packageThermalDesignPower,
|
|
},
|
|
parsed: []packageMetricType{
|
|
packageCurrentPowerConsumption,
|
|
packageCurrentDramPowerConsumption,
|
|
packageThermalDesignPower,
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
p := &PowerStat{
|
|
PackageMetrics: tc.metrics,
|
|
}
|
|
|
|
p.parsePackageRaplMetrics()
|
|
require.Equal(t, tc.parsed, p.parsedPackageRaplMetrics)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestParsePackageMsrMetrics(t *testing.T) {
|
|
testCases := []parsePackageMetricTestCase{
|
|
{
|
|
name: "EmptySlice",
|
|
metrics: make([]packageMetricType, 0),
|
|
parsed: make([]packageMetricType, 0),
|
|
},
|
|
{
|
|
name: "NotFound",
|
|
metrics: []packageMetricType{
|
|
// Metrics not relying on msr.
|
|
packageCurrentPowerConsumption,
|
|
packageCurrentDramPowerConsumption,
|
|
packageThermalDesignPower,
|
|
},
|
|
parsed: make([]packageMetricType, 0),
|
|
},
|
|
{
|
|
name: "Found",
|
|
metrics: []packageMetricType{
|
|
// Metrics not relying on msr.
|
|
packageCurrentPowerConsumption,
|
|
packageCurrentDramPowerConsumption,
|
|
packageThermalDesignPower,
|
|
|
|
// Metrics relying uniquely on msr.
|
|
packageTurboLimit,
|
|
packageCPUBaseFrequency,
|
|
},
|
|
parsed: []packageMetricType{
|
|
packageTurboLimit,
|
|
packageCPUBaseFrequency,
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
p := &PowerStat{
|
|
PackageMetrics: tc.metrics,
|
|
}
|
|
|
|
p.parsePackageMsrMetrics()
|
|
require.Equal(t, tc.parsed, p.parsedPackageMsrMetrics)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestParseCoreRange(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
coreRange string
|
|
cores []int
|
|
err error
|
|
}{
|
|
{
|
|
name: "InvalidFormat",
|
|
coreRange: "1,3",
|
|
cores: nil,
|
|
err: errors.New("invalid core range format"),
|
|
},
|
|
{
|
|
name: "LowerBoundNonNumeric",
|
|
coreRange: "a-10",
|
|
cores: nil,
|
|
err: errors.New("failed to parse low bounds' core range"),
|
|
},
|
|
{
|
|
name: "MissingLowerBound",
|
|
coreRange: "-10",
|
|
cores: nil,
|
|
err: errors.New("failed to parse low bounds' core range"),
|
|
},
|
|
{
|
|
name: "HigherBoundNonNumeric",
|
|
coreRange: "0-a",
|
|
cores: nil,
|
|
err: errors.New("failed to parse high bounds' core range"),
|
|
},
|
|
{
|
|
name: "MissingHigherBound",
|
|
coreRange: "0-",
|
|
cores: nil,
|
|
err: errors.New("failed to parse high bounds' core range"),
|
|
},
|
|
{
|
|
name: "InvalidBounds",
|
|
coreRange: "10-1",
|
|
cores: nil,
|
|
err: errors.New("high bound of core range cannot be less than low bound"),
|
|
},
|
|
{
|
|
name: "SingleCore",
|
|
coreRange: "1-1",
|
|
cores: []int{1},
|
|
},
|
|
{
|
|
name: "CoreRange",
|
|
coreRange: "5-10",
|
|
cores: []int{5, 6, 7, 8, 9, 10},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
coresOut, err := parseCoreRange(tc.coreRange)
|
|
|
|
require.Equal(t, tc.cores, coresOut)
|
|
if tc.err != nil {
|
|
require.ErrorContains(t, err, tc.err.Error())
|
|
} else {
|
|
require.NoError(t, err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestParseGroupCores(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
coreGroup string
|
|
cores []int
|
|
err error
|
|
}{
|
|
{
|
|
name: "FailedToParseCoreRange",
|
|
coreGroup: "1-a,7,9,11",
|
|
cores: nil,
|
|
err: errors.New("failed to parse core range \"1-a\""),
|
|
},
|
|
{
|
|
name: "FailedToParseSingleCore",
|
|
coreGroup: "1-5,7,b,11",
|
|
cores: nil,
|
|
err: errors.New("failed to parse single core"),
|
|
},
|
|
{
|
|
name: "Ok",
|
|
coreGroup: "1-5,7,9,11",
|
|
cores: []int{1, 2, 3, 4, 5, 7, 9, 11},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
coresOut, err := parseGroupCores(tc.coreGroup)
|
|
|
|
require.Equal(t, tc.cores, coresOut)
|
|
if tc.err != nil {
|
|
require.ErrorContains(t, err, tc.err.Error())
|
|
} else {
|
|
require.NoError(t, err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestHasDuplicate(t *testing.T) {
|
|
t.Run("Int", func(t *testing.T) {
|
|
t.Run("False", func(t *testing.T) {
|
|
nums := []int{0, 1, 2, 3}
|
|
require.False(t, hasDuplicate(nums))
|
|
})
|
|
|
|
t.Run("True", func(t *testing.T) {
|
|
nums := []uint32{0, 1, 2, 3, 4, 5, 6, 1}
|
|
require.True(t, hasDuplicate(nums))
|
|
})
|
|
})
|
|
|
|
t.Run("String", func(t *testing.T) {
|
|
t.Run("False", func(t *testing.T) {
|
|
strs := []string{"1", "2", "3", "4"}
|
|
require.False(t, hasDuplicate(strs))
|
|
})
|
|
|
|
t.Run("True", func(t *testing.T) {
|
|
strs := []string{"1", "2", "3", "1"}
|
|
require.True(t, hasDuplicate(strs))
|
|
})
|
|
})
|
|
}
|
|
|
|
func TestParseCores(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
coreGroups []string
|
|
cores []int
|
|
err error
|
|
}{
|
|
{
|
|
name: "InvalidCoreGroup",
|
|
coreGroups: []string{"1-4,11", "10-b"},
|
|
cores: nil,
|
|
err: errors.New("failed to parse core group"),
|
|
},
|
|
{
|
|
name: "FoundDuplicates",
|
|
coreGroups: []string{"1-4,11", "10-12"},
|
|
cores: nil,
|
|
err: errors.New("core values cannot be duplicated"),
|
|
},
|
|
{
|
|
name: "CoresIsNil",
|
|
coreGroups: nil,
|
|
cores: make([]int, 0),
|
|
},
|
|
{
|
|
name: "CoresIsEmpty",
|
|
coreGroups: make([]string, 0),
|
|
cores: make([]int, 0),
|
|
},
|
|
{
|
|
name: "Ok",
|
|
coreGroups: []string{"1-4,6", "8", "10-12", "15,20"},
|
|
cores: []int{1, 2, 3, 4, 6, 8, 10, 11, 12, 15, 20},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
coresOut, err := parseCores(tc.coreGroups)
|
|
|
|
require.Equal(t, tc.cores, coresOut)
|
|
if tc.err != nil {
|
|
require.ErrorContains(t, err, tc.err.Error())
|
|
} else {
|
|
require.NoError(t, err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestParseConfig(t *testing.T) {
|
|
t.Run("BothCPUOptionsProvided", func(t *testing.T) {
|
|
p := &PowerStat{
|
|
IncludedCPUs: []string{"0-10,20-22"},
|
|
ExcludedCPUs: []string{"0"},
|
|
}
|
|
|
|
require.ErrorContains(t, p.parseConfig(), "both 'included_cpus' and 'excluded_cpus' configured; provide only one or none of the two")
|
|
})
|
|
|
|
t.Run("FailedToParseIncludedCPUs", func(t *testing.T) {
|
|
p := &PowerStat{
|
|
// has duplicates
|
|
IncludedCPUs: []string{"1-4,11", "10-12"},
|
|
}
|
|
|
|
require.ErrorContains(t, p.parseConfig(), "failed to parse included CPUs")
|
|
})
|
|
|
|
t.Run("FailedToParseExcludedCPUs", func(t *testing.T) {
|
|
p := &PowerStat{
|
|
// has non-numeric CPU ID
|
|
ExcludedCPUs: []string{"1-4,b"},
|
|
}
|
|
|
|
require.ErrorContains(t, p.parseConfig(), "failed to parse excluded CPUs")
|
|
})
|
|
|
|
t.Run("FailedToParseCPUMetrics", func(t *testing.T) {
|
|
p := &PowerStat{
|
|
// has duplicates
|
|
CPUMetrics: []cpuMetricType{
|
|
cpuFrequency,
|
|
cpuTemperature,
|
|
cpuFrequency, // duplicate
|
|
},
|
|
}
|
|
|
|
require.ErrorContains(t, p.parseConfig(), "failed to parse cpu metrics")
|
|
})
|
|
|
|
t.Run("EventDefinitionsNotProvidedForPerf", func(t *testing.T) {
|
|
p := &PowerStat{
|
|
// has duplicates
|
|
CPUMetrics: []cpuMetricType{
|
|
cpuC0SubstateC01Percent,
|
|
},
|
|
}
|
|
|
|
require.ErrorContains(t, p.parseConfig(), "'event_definitions' contains an empty path")
|
|
})
|
|
|
|
t.Run("EventDefinitionsDoesNotExist", func(t *testing.T) {
|
|
p := &PowerStat{
|
|
// has duplicates
|
|
CPUMetrics: []cpuMetricType{
|
|
cpuC0SubstateC02Percent,
|
|
},
|
|
EventDefinitions: "./testdata/doesNotExist.json",
|
|
}
|
|
|
|
require.ErrorContains(t, p.parseConfig(), "'event_definitions' file \"./testdata/doesNotExist.json\" does not exist")
|
|
})
|
|
|
|
t.Run("NoMetricsProvided", func(t *testing.T) {
|
|
p := &PowerStat{
|
|
// Disable default package metrics.
|
|
PackageMetrics: make([]packageMetricType, 0),
|
|
}
|
|
|
|
require.ErrorContains(t, p.parseConfig(), "no metrics were found in the configuration file")
|
|
})
|
|
|
|
t.Run("DisablePackageMetrics", func(t *testing.T) {
|
|
p := &PowerStat{
|
|
CPUMetrics: []cpuMetricType{
|
|
cpuBusyFrequency,
|
|
},
|
|
// Disable default package metrics.
|
|
PackageMetrics: make([]packageMetricType, 0),
|
|
}
|
|
|
|
require.NoError(t, p.parseConfig())
|
|
require.Empty(t, p.PackageMetrics)
|
|
require.Len(t, p.CPUMetrics, 1)
|
|
})
|
|
|
|
t.Run("DefaultPackageMetrics", func(t *testing.T) {
|
|
p := &PowerStat{
|
|
PackageMetrics: nil, // default package metrics
|
|
}
|
|
|
|
require.NoError(t, p.parseConfig())
|
|
require.Equal(t,
|
|
[]packageMetricType{
|
|
packageCurrentPowerConsumption,
|
|
packageCurrentDramPowerConsumption,
|
|
packageThermalDesignPower,
|
|
},
|
|
p.PackageMetrics)
|
|
})
|
|
|
|
t.Run("IncludedCPUs", func(t *testing.T) {
|
|
p := &PowerStat{
|
|
IncludedCPUs: []string{"0-5"},
|
|
}
|
|
|
|
require.NoError(t, p.parseConfig())
|
|
require.Equal(t, []int{0, 1, 2, 3, 4, 5}, p.parsedIncludedCores)
|
|
require.Nil(t, p.parsedExcludedCores)
|
|
})
|
|
|
|
t.Run("ExcludedCPUs", func(t *testing.T) {
|
|
p := &PowerStat{
|
|
ExcludedCPUs: []string{"2-6", "8", "10"},
|
|
}
|
|
|
|
require.NoError(t, p.parseConfig())
|
|
require.Equal(t, []int{2, 3, 4, 5, 6, 8, 10}, p.parsedExcludedCores)
|
|
require.Nil(t, p.parsedIncludedCores)
|
|
})
|
|
|
|
t.Run("MetricsWithIncludedCPUs", func(t *testing.T) {
|
|
p := &PowerStat{
|
|
IncludedCPUs: []string{"0-3,6"},
|
|
CPUMetrics: []cpuMetricType{
|
|
cpuC0StateResidency,
|
|
cpuC1StateResidency,
|
|
cpuC3StateResidency,
|
|
},
|
|
PackageMetrics: []packageMetricType{
|
|
packageUncoreFrequency,
|
|
packageTurboLimit,
|
|
},
|
|
}
|
|
|
|
require.NoError(t, p.parseConfig())
|
|
require.Equal(t, []int{0, 1, 2, 3, 6}, p.parsedIncludedCores)
|
|
|
|
// Check flags
|
|
require.True(t, p.needsMsrCPU)
|
|
require.True(t, p.needsTimeRelatedMsr)
|
|
require.False(t, p.needsCoreFreq)
|
|
require.False(t, p.needsPerf)
|
|
})
|
|
}
|
|
|
|
type mockOptGenerator struct {
|
|
mock.Mock
|
|
}
|
|
|
|
func (m *mockOptGenerator) generate(cfg optConfig) []ptel.Option {
|
|
args := m.Called(cfg)
|
|
return args.Get(0).([]ptel.Option)
|
|
}
|
|
|
|
func TestSampleConfig(t *testing.T) {
|
|
p := &PowerStat{}
|
|
require.NotEmpty(t, p.SampleConfig())
|
|
}
|
|
|
|
func TestInit(t *testing.T) {
|
|
t.Run("FailedToDisableUnsupportedMetrics", func(t *testing.T) {
|
|
t.Setenv("HOST_PROC", "./testdata/cpu_model_missing")
|
|
|
|
p := &PowerStat{}
|
|
|
|
require.ErrorContains(t, p.Init(), "error occurred while parsing CPU model")
|
|
})
|
|
|
|
t.Run("FailedToParseConfigWithDuplicates", func(t *testing.T) {
|
|
t.Setenv("HOST_PROC", "./testdata")
|
|
|
|
logger := &testutil.CaptureLogger{}
|
|
|
|
p := &PowerStat{
|
|
// has duplicates
|
|
IncludedCPUs: []string{"1-4,11", "10-12"},
|
|
Log: logger,
|
|
}
|
|
|
|
require.ErrorContains(t, p.Init(), "failed to parse included CPUs")
|
|
require.Empty(t, logger.Warnings())
|
|
})
|
|
|
|
t.Run("FailedToParseConfigWithNegativeTimeout", func(t *testing.T) {
|
|
t.Setenv("HOST_PROC", "./testdata")
|
|
|
|
logger := &testutil.CaptureLogger{}
|
|
|
|
p := &PowerStat{
|
|
// negative value
|
|
MsrReadTimeout: -2,
|
|
Log: logger,
|
|
}
|
|
|
|
require.ErrorContains(t, p.Init(), "msr_read_timeout should be positive number or equal to 0 (to disable timeouts)")
|
|
require.Empty(t, logger.Warnings())
|
|
})
|
|
}
|
|
|
|
func TestStart(t *testing.T) {
|
|
t.Run("FailedToStartCPUDoesNotExist", func(t *testing.T) {
|
|
t.Setenv("HOST_PROC", "./testdata")
|
|
|
|
acc := &testutil.Accumulator{}
|
|
logger := &testutil.CaptureLogger{}
|
|
|
|
p := &PowerStat{
|
|
// has CPU ID out of bounds
|
|
parsedIncludedCores: []int{0, 9},
|
|
Log: logger,
|
|
|
|
option: &optGenerator{},
|
|
}
|
|
|
|
require.ErrorContains(t, p.Start(acc), "failed to initialize metric fetcher interface")
|
|
require.Empty(t, logger.Warnings())
|
|
})
|
|
|
|
t.Run("FailedToStartNotSupportedCPUVendor", func(t *testing.T) {
|
|
t.Setenv("HOST_PROC", "./testdata/vendor_not_supported")
|
|
|
|
acc := &testutil.Accumulator{}
|
|
logger := &testutil.CaptureLogger{}
|
|
|
|
p := &PowerStat{
|
|
Log: logger,
|
|
option: &optGenerator{},
|
|
}
|
|
|
|
require.ErrorContains(t, p.Start(acc), "host processor is not supported")
|
|
})
|
|
|
|
t.Run("WithWarning", func(t *testing.T) {
|
|
t.Setenv("HOST_PROC", "./testdata")
|
|
|
|
acc := &testutil.Accumulator{}
|
|
logger := &testutil.CaptureLogger{}
|
|
|
|
mOptGenerator := &mockOptGenerator{}
|
|
mOptGenerator.On("generate", mock.AnythingOfType("optConfig")).Return(
|
|
[]ptel.Option{
|
|
ptel.WithRapl("/dummy/path"),
|
|
},
|
|
)
|
|
|
|
p := &PowerStat{
|
|
PackageMetrics: []packageMetricType{
|
|
packageCurrentPowerConsumption, // needs rapl
|
|
},
|
|
Log: logger,
|
|
|
|
option: mOptGenerator,
|
|
}
|
|
|
|
require.NoError(t, p.Start(acc))
|
|
require.Len(t, logger.Warnings(), 1)
|
|
require.Contains(t, logger.Warnings()[0], "Plugin started with errors")
|
|
})
|
|
}
|
|
|
|
func TestGather(t *testing.T) {
|
|
t.Run("WithoutMetrics", func(t *testing.T) {
|
|
acc := &testutil.Accumulator{}
|
|
|
|
p := &PowerStat{
|
|
PackageMetrics: make([]packageMetricType, 0),
|
|
}
|
|
|
|
require.NoError(t, p.Gather(acc))
|
|
|
|
require.Empty(t, acc.Errors)
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
})
|
|
|
|
t.Run("WithDefaultPackageMetrics", func(t *testing.T) {
|
|
packageID := 0
|
|
|
|
packagePower := 10.0
|
|
dramPower := 5.0
|
|
thermalDesignPower := 20.0
|
|
|
|
acc := &testutil.Accumulator{}
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting available package IDs.
|
|
mFetcher.On("GetPackageIDs").Return([]int{packageID}).Once()
|
|
|
|
// mock getting current package power consumption metric.
|
|
mFetcher.On("GetCurrentPackagePowerConsumptionWatts", packageID).Return(packagePower, nil).Once()
|
|
|
|
// mock getting current dram power consumption metric.
|
|
mFetcher.On("GetCurrentDramPowerConsumptionWatts", packageID).Return(dramPower, nil).Once()
|
|
|
|
// mock getting package thermal design power metric.
|
|
mFetcher.On("GetPackageThermalDesignPowerWatts", packageID).Return(thermalDesignPower, nil).Once()
|
|
|
|
p := &PowerStat{
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.NoError(t, p.parseConfig())
|
|
|
|
require.NoError(t, p.Gather(acc))
|
|
|
|
require.Empty(t, acc.Errors)
|
|
require.Len(t, acc.GetTelegrafMetrics(), 3)
|
|
require.True(t, acc.HasField("powerstat_package", "current_power_consumption_watts"))
|
|
require.True(t, acc.HasField("powerstat_package", "current_dram_power_consumption_watts"))
|
|
require.True(t, acc.HasField("powerstat_package", "thermal_design_power_watts"))
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("WithPackageMetrics", func(t *testing.T) {
|
|
packageIDs := []int{0, 1, 2, 3}
|
|
|
|
baseFreq := uint64(200)
|
|
packagePower := 30.0
|
|
|
|
acc := &testutil.Accumulator{}
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting available package IDs.
|
|
mFetcher.On("GetPackageIDs").Return(packageIDs).Once()
|
|
|
|
// mock getting CPU base frequency metric.
|
|
mFetcher.On("GetCPUBaseFrequency", mock.AnythingOfType("int")).Return(baseFreq, nil).Times(len(packageIDs))
|
|
|
|
// mock getting current package power consumption metric.
|
|
mFetcher.On("GetCurrentPackagePowerConsumptionWatts", mock.AnythingOfType("int")).Return(packagePower, nil).Times(len(packageIDs))
|
|
|
|
p := &PowerStat{
|
|
PackageMetrics: []packageMetricType{
|
|
packageCurrentPowerConsumption,
|
|
packageCPUBaseFrequency,
|
|
},
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.NoError(t, p.parseConfig())
|
|
|
|
require.NoError(t, p.Gather(acc))
|
|
|
|
require.Empty(t, acc.Errors)
|
|
require.Len(t, acc.GetTelegrafMetrics(), 8)
|
|
require.True(t, acc.HasField("powerstat_package", "cpu_base_frequency_mhz"))
|
|
require.True(t, acc.HasField("powerstat_package", "current_power_consumption_watts"))
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("WithCPUMetrics", func(t *testing.T) {
|
|
cpuIDs := []int{0, 1, 2, 3}
|
|
|
|
cpuFreq := 123.5
|
|
cpuTemp := uint64(20)
|
|
cpuBusyFreq := 456.7
|
|
|
|
acc := &testutil.Accumulator{}
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting available CPU IDs with access to msr registers and coreFreq.
|
|
mFetcher.On("GetMsrCPUIDs").Return(cpuIDs).Once()
|
|
|
|
// mock getting core ID for CPU IDs.
|
|
mFetcher.On("GetCPUCoreID", mock.AnythingOfType("int")).Return(1, nil).Times(len(cpuIDs))
|
|
|
|
// mock getting package ID for CPU IDs.
|
|
mFetcher.On("GetCPUPackageID", mock.AnythingOfType("int")).Return(1, nil).Times(len(cpuIDs))
|
|
|
|
// mock updating msr time-related metrics for CPU IDs.
|
|
mFetcher.On("UpdatePerCPUMetrics", mock.AnythingOfType("int")).Return(nil).Times(len(cpuIDs))
|
|
|
|
// mock getting CPU frequency for CPU IDs.
|
|
mFetcher.On("GetCPUFrequency", mock.AnythingOfType("int")).Return(cpuFreq, nil).Times(len(cpuIDs))
|
|
|
|
// mock getting CPU temperature metric for CPU IDs.
|
|
mFetcher.On("GetCPUTemperature", mock.AnythingOfType("int")).Return(cpuTemp, nil).Times(len(cpuIDs))
|
|
|
|
// mock getting CPU busy frequency metric for CPU IDs.
|
|
mFetcher.On("GetCPUBusyFrequencyMhz", mock.AnythingOfType("int")).Return(cpuBusyFreq, nil).Times(len(cpuIDs))
|
|
|
|
p := &PowerStat{
|
|
// Disables package metrics
|
|
PackageMetrics: make([]packageMetricType, 0),
|
|
CPUMetrics: []cpuMetricType{
|
|
cpuFrequency,
|
|
cpuTemperature,
|
|
cpuBusyFrequency,
|
|
},
|
|
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.NoError(t, p.parseConfig())
|
|
|
|
require.NoError(t, p.Gather(acc))
|
|
|
|
require.Empty(t, acc.Errors)
|
|
require.Len(t, acc.GetTelegrafMetrics(), 12)
|
|
require.True(t, acc.HasField("powerstat_core", "cpu_frequency_mhz"))
|
|
require.True(t, acc.HasField("powerstat_core", "cpu_temperature_celsius"))
|
|
require.True(t, acc.HasField("powerstat_core", "cpu_busy_frequency_mhz"))
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("WithPerfMetrics", func(t *testing.T) {
|
|
cpuIDs := []int{0, 1, 2}
|
|
|
|
c01 := 0.1
|
|
c02 := 1.2
|
|
c0Wait := 2.3
|
|
|
|
acc := &testutil.Accumulator{}
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock reading perf events.
|
|
mFetcher.On("ReadPerfEvents").Return(nil).Once()
|
|
|
|
// mock getting available CPU IDs with access to msr registers and coreFreq.
|
|
mFetcher.On("GetPerfCPUIDs").Return(cpuIDs).Once()
|
|
|
|
// mock getting core ID for CPU IDs.
|
|
mFetcher.On("GetCPUCoreID", mock.AnythingOfType("int")).Return(1, nil).Times(len(cpuIDs))
|
|
|
|
// mock getting package ID for CPU IDs.
|
|
mFetcher.On("GetCPUPackageID", mock.AnythingOfType("int")).Return(1, nil).Times(len(cpuIDs))
|
|
|
|
// mock getting CPU C01 metric.
|
|
mFetcher.On("GetCPUC0SubstateC01Percent", mock.AnythingOfType("int")).Return(c01, nil).Times(len(cpuIDs))
|
|
|
|
// mock getting CPU C02 metric.
|
|
mFetcher.On("GetCPUC0SubstateC02Percent", mock.AnythingOfType("int")).Return(c02, nil).Times(len(cpuIDs))
|
|
|
|
// mock getting CPU C0Wait metric.
|
|
mFetcher.On("GetCPUC0SubstateC0WaitPercent", mock.AnythingOfType("int")).Return(c0Wait, nil).Times(len(cpuIDs))
|
|
|
|
p := &PowerStat{
|
|
// Disables package metrics
|
|
PackageMetrics: make([]packageMetricType, 0),
|
|
CPUMetrics: []cpuMetricType{
|
|
cpuC0SubstateC01Percent,
|
|
cpuC0SubstateC02Percent,
|
|
cpuC0SubstateC0WaitPercent,
|
|
},
|
|
EventDefinitions: "./testdata/sapphirerapids_core.json",
|
|
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.NoError(t, p.parseConfig())
|
|
|
|
require.NoError(t, p.Gather(acc))
|
|
|
|
require.Empty(t, acc.Errors)
|
|
require.Len(t, acc.GetTelegrafMetrics(), 9)
|
|
require.True(t, acc.HasField("powerstat_core", "cpu_c0_substate_c01_percent"))
|
|
require.True(t, acc.HasField("powerstat_core", "cpu_c0_substate_c02_percent"))
|
|
require.True(t, acc.HasField("powerstat_core", "cpu_c0_substate_c0_wait_percent"))
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("WithPerfAndMsrCPUMetrics", func(t *testing.T) {
|
|
cpuIDsMsr := []int{0, 1, 2, 3}
|
|
cpuIDsPerf := []int{0, 1}
|
|
|
|
c1 := 0.5
|
|
c6 := 1.5
|
|
c01 := 0.1
|
|
c02 := 1.2
|
|
|
|
acc := &testutil.Accumulator{}
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting available CPU IDs with access to msr registers.
|
|
mFetcher.On("GetMsrCPUIDs").Return(cpuIDsMsr).Once()
|
|
|
|
// mock getting core ID for CPU IDs.
|
|
mFetcher.On("GetCPUCoreID", mock.AnythingOfType("int")).Return(1, nil).Times(len(cpuIDsMsr) + len(cpuIDsPerf))
|
|
|
|
// mock getting package ID for CPU IDs.
|
|
mFetcher.On("GetCPUPackageID", mock.AnythingOfType("int")).Return(1, nil).Times(len(cpuIDsMsr) + len(cpuIDsPerf))
|
|
|
|
// mock updating msr time-related metrics for CPU IDs.
|
|
mFetcher.On("UpdatePerCPUMetrics", mock.AnythingOfType("int")).Return(nil).Times(len(cpuIDsMsr))
|
|
|
|
// mock getting CPU C1 state residency metric for CPU IDs.
|
|
mFetcher.On("GetCPUC1StateResidency", mock.AnythingOfType("int")).Return(c1, nil).Times(len(cpuIDsMsr))
|
|
|
|
// mock getting CPU C6 state residency metric for CPU IDs.
|
|
mFetcher.On("GetCPUC6StateResidency", mock.AnythingOfType("int")).Return(c6, nil).Times(len(cpuIDsMsr))
|
|
|
|
// mock reading perf events.
|
|
mFetcher.On("ReadPerfEvents").Return(nil).Once()
|
|
|
|
// mock getting available CPU IDs with access to msr registers and coreFreq.
|
|
mFetcher.On("GetPerfCPUIDs").Return(cpuIDsPerf).Once()
|
|
|
|
// mock getting CPU C01 metric.
|
|
mFetcher.On("GetCPUC0SubstateC01Percent", mock.AnythingOfType("int")).Return(c01, nil).Times(len(cpuIDsPerf))
|
|
|
|
// mock getting CPU C02 metric.
|
|
mFetcher.On("GetCPUC0SubstateC02Percent", mock.AnythingOfType("int")).Return(c02, nil).Times(len(cpuIDsPerf))
|
|
|
|
p := &PowerStat{
|
|
// Disables package metrics
|
|
PackageMetrics: make([]packageMetricType, 0),
|
|
CPUMetrics: []cpuMetricType{
|
|
cpuC0SubstateC01Percent,
|
|
cpuC0SubstateC02Percent,
|
|
cpuC1StateResidency,
|
|
cpuC6StateResidency,
|
|
},
|
|
EventDefinitions: "./testdata/sapphirerapids_core.json",
|
|
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.NoError(t, p.parseConfig())
|
|
|
|
require.NoError(t, p.Gather(acc))
|
|
|
|
require.Empty(t, acc.Errors)
|
|
require.Len(t, acc.GetTelegrafMetrics(), 12)
|
|
require.True(t, acc.HasField("powerstat_core", "cpu_c0_substate_c01_percent"))
|
|
require.True(t, acc.HasField("powerstat_core", "cpu_c0_substate_c02_percent"))
|
|
require.True(t, acc.HasField("powerstat_core", "cpu_c1_state_residency_percent"))
|
|
require.True(t, acc.HasField("powerstat_core", "cpu_c6_state_residency_percent"))
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("WithCPUAndPackageMetrics", func(t *testing.T) {
|
|
cpuIDs := []int{10, 12}
|
|
packageIDs := []int{0, 1, 2, 3}
|
|
dieIDs := []int{0, 1}
|
|
|
|
c7 := 0.12
|
|
|
|
initMin := 200.0
|
|
initMax := 1200.0
|
|
currMin := 300.0
|
|
currMax := 1300.0
|
|
curr := 800.0
|
|
|
|
acc := &testutil.Accumulator{}
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting available CPU IDs with access to msr registers and coreFreq.
|
|
mFetcher.On("GetMsrCPUIDs").Return(cpuIDs).Once()
|
|
|
|
// mock getting core ID for CPU IDs.
|
|
mFetcher.On("GetCPUCoreID", mock.AnythingOfType("int")).Return(1, nil).Times(len(cpuIDs))
|
|
|
|
// mock getting package ID for CPU IDs.
|
|
mFetcher.On("GetCPUPackageID", mock.AnythingOfType("int")).Return(1, nil).Times(len(cpuIDs))
|
|
|
|
// mock updating msr time-related metrics for CPU IDs.
|
|
mFetcher.On("UpdatePerCPUMetrics", mock.AnythingOfType("int")).Return(nil).Times(len(cpuIDs))
|
|
|
|
// mock getting C7 state residency metric for CPU IDs.
|
|
mFetcher.On("GetCPUC7StateResidency", mock.AnythingOfType("int")).Return(c7, nil).Times(len(cpuIDs))
|
|
|
|
// mock getting available package IDs.
|
|
mFetcher.On("GetPackageIDs").Return(packageIDs).Once()
|
|
|
|
// mock getting die IDs for package ID.
|
|
mFetcher.On("GetPackageDieIDs", mock.AnythingOfType("int")).Return(dieIDs, nil).Times(len(packageIDs))
|
|
|
|
// mock getting initial minimum uncore frequency limit.
|
|
mFetcher.On("GetInitialUncoreFrequencyMin", mock.AnythingOfType("int"), mock.AnythingOfType("int")).Return(initMin, nil).
|
|
Times(len(packageIDs) * len(dieIDs))
|
|
|
|
// mock getting initial maximum uncore frequency limit.
|
|
mFetcher.On("GetInitialUncoreFrequencyMax", mock.AnythingOfType("int"), mock.AnythingOfType("int")).Return(initMax, nil).
|
|
Times(len(packageIDs) * len(dieIDs))
|
|
|
|
// mock getting custom minimum uncore frequency limit.
|
|
mFetcher.On("GetCustomizedUncoreFrequencyMin", mock.AnythingOfType("int"), mock.AnythingOfType("int")).Return(currMin, nil).
|
|
Times(len(packageIDs) * len(dieIDs))
|
|
|
|
// mock getting custom maximum uncore frequency limit.
|
|
mFetcher.On("GetCustomizedUncoreFrequencyMax", mock.AnythingOfType("int"), mock.AnythingOfType("int")).Return(currMax, nil).
|
|
Times(len(packageIDs) * len(dieIDs))
|
|
|
|
// mock getting current uncore frequency value.
|
|
mFetcher.On("GetCurrentUncoreFrequency", mock.AnythingOfType("int"), mock.AnythingOfType("int")).Return(curr, nil).Times(len(packageIDs) * len(dieIDs))
|
|
|
|
p := &PowerStat{
|
|
PackageMetrics: []packageMetricType{
|
|
packageUncoreFrequency,
|
|
},
|
|
CPUMetrics: []cpuMetricType{
|
|
cpuC7StateResidency,
|
|
},
|
|
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.NoError(t, p.parseConfig())
|
|
|
|
require.NoError(t, p.Gather(acc))
|
|
|
|
require.Empty(t, acc.Errors)
|
|
require.Len(t, acc.GetTelegrafMetrics(), 18)
|
|
require.True(t, acc.HasField("powerstat_core", "cpu_c7_state_residency_percent"))
|
|
require.True(t, acc.HasField("powerstat_package", "uncore_frequency_limit_mhz_min"))
|
|
require.True(t, acc.HasField("powerstat_package", "uncore_frequency_limit_mhz_max"))
|
|
require.True(t, acc.HasField("powerstat_package", "uncore_frequency_mhz_cur"))
|
|
require.True(t, acc.HasTag("powerstat_package", "type"))
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
}
|
|
|
|
func TestStop(t *testing.T) {
|
|
t.Run("NoErrorWithoutPerf", func(t *testing.T) {
|
|
logger := &testutil.CaptureLogger{}
|
|
|
|
p := &PowerStat{
|
|
Log: logger,
|
|
|
|
needsPerf: false,
|
|
}
|
|
|
|
p.Stop()
|
|
|
|
require.Empty(t, logger.Errors())
|
|
})
|
|
|
|
t.Run("FailedToDeactivatePerfEvents", func(t *testing.T) {
|
|
logger := &testutil.CaptureLogger{}
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock deactivating perf events.
|
|
mFetcher.On("DeactivatePerfEvents").Return(errors.New("mock error")).Once()
|
|
|
|
p := &PowerStat{
|
|
Log: logger,
|
|
|
|
fetcher: mFetcher,
|
|
|
|
needsPerf: true,
|
|
}
|
|
|
|
p.Stop()
|
|
|
|
require.Len(t, logger.Errors(), 1)
|
|
require.Contains(t, logger.Errors()[0], "Failed to deactivate perf events")
|
|
})
|
|
|
|
t.Run("NoErrorWithPerf", func(t *testing.T) {
|
|
logger := &testutil.CaptureLogger{}
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock deactivating perf events.
|
|
mFetcher.On("DeactivatePerfEvents").Return(nil).Once()
|
|
|
|
p := &PowerStat{
|
|
Log: logger,
|
|
|
|
fetcher: mFetcher,
|
|
|
|
needsPerf: true,
|
|
}
|
|
|
|
p.Stop()
|
|
|
|
require.Empty(t, logger.Errors())
|
|
})
|
|
}
|
|
|
|
func TestDisableUnsupportedMetrics(t *testing.T) {
|
|
t.Run("ModelMissing", func(t *testing.T) {
|
|
t.Setenv("HOST_PROC", "./testdata/cpu_model_missing")
|
|
|
|
p := &PowerStat{}
|
|
|
|
err := p.disableUnsupportedMetrics()
|
|
|
|
require.Error(t, err, "error occurred while parsing CPU model")
|
|
})
|
|
|
|
t.Run("MsrFlagNotFound", func(t *testing.T) {
|
|
t.Setenv("HOST_PROC", "./testdata/msr_flag_not_found")
|
|
|
|
logger := &testutil.CaptureLogger{}
|
|
|
|
p := &PowerStat{
|
|
CPUMetrics: []cpuMetricType{
|
|
// Metrics relying on msr flag
|
|
cpuC0StateResidency,
|
|
cpuC1StateResidency,
|
|
cpuC6StateResidency,
|
|
cpuBusyFrequency,
|
|
cpuBusyCycles,
|
|
cpuTemperature,
|
|
},
|
|
PackageMetrics: []packageMetricType{
|
|
// Metrics relying on msr flag
|
|
packageCPUBaseFrequency,
|
|
packageTurboLimit,
|
|
|
|
// Metrics not relying on msr flag
|
|
packageCurrentPowerConsumption,
|
|
packageThermalDesignPower,
|
|
},
|
|
|
|
Log: logger,
|
|
}
|
|
|
|
err := p.disableUnsupportedMetrics()
|
|
|
|
require.NoError(t, err)
|
|
require.Empty(t, p.CPUMetrics)
|
|
require.Len(t, p.PackageMetrics, 2)
|
|
require.Contains(t, p.PackageMetrics, packageCurrentPowerConsumption)
|
|
require.Contains(t, p.PackageMetrics, packageThermalDesignPower)
|
|
require.Len(t, logger.Warnings(), 8)
|
|
})
|
|
|
|
t.Run("AperfMperfFlagNotFound", func(t *testing.T) {
|
|
t.Setenv("HOST_PROC", "./testdata/aperfmperf_flag_not_found")
|
|
|
|
logger := &testutil.CaptureLogger{}
|
|
|
|
p := &PowerStat{
|
|
CPUMetrics: []cpuMetricType{
|
|
// Metrics relying on aperfmperf flag
|
|
cpuC0StateResidency,
|
|
cpuC1StateResidency,
|
|
cpuBusyFrequency,
|
|
cpuBusyCycles,
|
|
|
|
// Metrics not relying on aperfmperf flag
|
|
cpuTemperature,
|
|
},
|
|
|
|
Log: logger,
|
|
}
|
|
|
|
err := p.disableUnsupportedMetrics()
|
|
|
|
require.NoError(t, err)
|
|
require.Len(t, p.CPUMetrics, 1)
|
|
require.Contains(t, p.CPUMetrics, cpuTemperature)
|
|
require.Len(t, logger.Warnings(), 4)
|
|
})
|
|
|
|
t.Run("DtsFlagNotFound", func(t *testing.T) {
|
|
t.Setenv("HOST_PROC", "./testdata/dts_flag_not_found")
|
|
|
|
logger := &testutil.CaptureLogger{}
|
|
|
|
p := &PowerStat{
|
|
CPUMetrics: []cpuMetricType{
|
|
// Metrics relying on dts flag
|
|
cpuTemperature,
|
|
|
|
// Metrics not relying on dts flag
|
|
cpuBusyFrequency,
|
|
},
|
|
PackageMetrics: make([]packageMetricType, 0),
|
|
|
|
Log: logger,
|
|
}
|
|
|
|
err := p.disableUnsupportedMetrics()
|
|
|
|
require.NoError(t, err)
|
|
require.Len(t, p.CPUMetrics, 1)
|
|
require.Contains(t, p.CPUMetrics, cpuBusyFrequency)
|
|
require.Len(t, logger.Warnings(), 1)
|
|
})
|
|
|
|
t.Run("ModelNotSupported", func(t *testing.T) {
|
|
t.Setenv("HOST_PROC", "./testdata/model_not_supported")
|
|
|
|
logger := &testutil.CaptureLogger{}
|
|
|
|
p := &PowerStat{
|
|
CPUMetrics: []cpuMetricType{
|
|
// Metrics not supported by CPU
|
|
cpuTemperature,
|
|
cpuC1StateResidency,
|
|
cpuC3StateResidency,
|
|
cpuC6StateResidency,
|
|
cpuC7StateResidency,
|
|
},
|
|
PackageMetrics: []packageMetricType{
|
|
// Metrics not supported by CPU
|
|
packageCPUBaseFrequency,
|
|
|
|
packageUncoreFrequency,
|
|
},
|
|
|
|
Log: logger,
|
|
}
|
|
|
|
err := p.disableUnsupportedMetrics()
|
|
|
|
require.NoError(t, err)
|
|
require.Empty(t, p.CPUMetrics)
|
|
require.Contains(t, p.PackageMetrics, packageUncoreFrequency)
|
|
require.Len(t, logger.Warnings(), 6)
|
|
})
|
|
}
|
|
|
|
func TestDisableCPUMetric(t *testing.T) {
|
|
t.Run("NoMetricsRemoved", func(t *testing.T) {
|
|
expStartLen := 1
|
|
expEndLen := 1
|
|
logger := &testutil.CaptureLogger{}
|
|
|
|
p := &PowerStat{
|
|
CPUMetrics: []cpuMetricType{cpuC1StateResidency},
|
|
Log: logger,
|
|
}
|
|
|
|
require.Len(t, p.CPUMetrics, expStartLen)
|
|
p.disableCPUMetric(cpuC3StateResidency)
|
|
require.Len(t, p.CPUMetrics, expEndLen)
|
|
|
|
require.Empty(t, logger.Warnings())
|
|
})
|
|
t.Run("TwoMetricsRemoved", func(t *testing.T) {
|
|
expStartLen := 3
|
|
expEndLen := 1
|
|
logger := &testutil.CaptureLogger{}
|
|
|
|
p := &PowerStat{
|
|
CPUMetrics: []cpuMetricType{cpuC1StateResidency, cpuC3StateResidency, cpuC6StateResidency},
|
|
Log: logger,
|
|
}
|
|
|
|
require.Len(t, p.CPUMetrics, expStartLen)
|
|
p.disableCPUMetric(cpuC3StateResidency)
|
|
p.disableCPUMetric(cpuC1StateResidency)
|
|
require.Len(t, p.CPUMetrics, expEndLen)
|
|
|
|
require.Len(t, logger.Warnings(), 2)
|
|
require.Contains(t, logger.Warnings()[0], "\"cpu_c3_state_residency\" is not supported by CPU, metric will not be gathered")
|
|
require.Contains(t, logger.Warnings()[1], "\"cpu_c1_state_residency\" is not supported by CPU, metric will not be gathered")
|
|
})
|
|
}
|
|
|
|
func TestDisablePackageMetric(t *testing.T) {
|
|
t.Run("NoMetricsRemoved", func(t *testing.T) {
|
|
expStartLen := 1
|
|
expEndLen := 1
|
|
logger := &testutil.CaptureLogger{}
|
|
|
|
p := &PowerStat{
|
|
PackageMetrics: []packageMetricType{packageCurrentPowerConsumption},
|
|
Log: logger,
|
|
}
|
|
|
|
require.Len(t, p.PackageMetrics, expStartLen)
|
|
p.disablePackageMetric(packageCPUBaseFrequency)
|
|
require.Len(t, p.PackageMetrics, expEndLen)
|
|
|
|
require.Empty(t, logger.Warnings())
|
|
})
|
|
t.Run("TwoMetricsRemoved", func(t *testing.T) {
|
|
expStartLen := 3
|
|
expEndLen := 1
|
|
logger := &testutil.CaptureLogger{}
|
|
|
|
p := &PowerStat{
|
|
PackageMetrics: []packageMetricType{packageCurrentPowerConsumption, packageTurboLimit, packageCPUBaseFrequency},
|
|
Log: logger,
|
|
}
|
|
|
|
require.Len(t, p.PackageMetrics, expStartLen)
|
|
p.disablePackageMetric(packageCPUBaseFrequency)
|
|
p.disablePackageMetric(packageTurboLimit)
|
|
require.Len(t, p.PackageMetrics, expEndLen)
|
|
|
|
require.Len(t, logger.Warnings(), 2)
|
|
require.Contains(t, logger.Warnings()[0], "\"cpu_base_frequency\" is not supported by CPU, metric will not be gathered")
|
|
require.Contains(t, logger.Warnings()[1], "\"max_turbo_frequency\" is not supported by CPU, metric will not be gathered")
|
|
})
|
|
}
|
|
|
|
type fetcherMock struct {
|
|
mock.Mock
|
|
}
|
|
|
|
func (m *fetcherMock) GetMsrCPUIDs() []int {
|
|
args := m.Called()
|
|
if args.Get(0) == nil {
|
|
return nil
|
|
}
|
|
return args.Get(0).([]int)
|
|
}
|
|
|
|
func (m *fetcherMock) GetPerfCPUIDs() []int {
|
|
args := m.Called()
|
|
if args.Get(0) == nil {
|
|
return nil
|
|
}
|
|
return args.Get(0).([]int)
|
|
}
|
|
|
|
func (m *fetcherMock) GetPackageIDs() []int {
|
|
args := m.Called()
|
|
if args.Get(0) == nil {
|
|
return nil
|
|
}
|
|
return args.Get(0).([]int)
|
|
}
|
|
|
|
func (m *fetcherMock) GetCPUPackageID(cpuID int) (int, error) {
|
|
args := m.Called(cpuID)
|
|
return args.Int(0), args.Error(1)
|
|
}
|
|
|
|
func (m *fetcherMock) GetCPUCoreID(cpuID int) (int, error) {
|
|
args := m.Called(cpuID)
|
|
return args.Int(0), args.Error(1)
|
|
}
|
|
|
|
func (m *fetcherMock) GetPackageDieIDs(packageID int) ([]int, error) {
|
|
args := m.Called(packageID)
|
|
if args.Get(0) == nil {
|
|
return nil, args.Error(1)
|
|
}
|
|
return args.Get(0).([]int), args.Error(1)
|
|
}
|
|
|
|
func (m *fetcherMock) GetCPUFrequency(cpuID int) (float64, error) {
|
|
args := m.Called(cpuID)
|
|
return args.Get(0).(float64), args.Error(1)
|
|
}
|
|
|
|
func (m *fetcherMock) UpdatePerCPUMetrics(cpuID int) error {
|
|
args := m.Called(cpuID)
|
|
return args.Error(0)
|
|
}
|
|
|
|
func (m *fetcherMock) GetCPUTemperature(cpuID int) (uint64, error) {
|
|
args := m.Called(cpuID)
|
|
return args.Get(0).(uint64), args.Error(1)
|
|
}
|
|
|
|
func (m *fetcherMock) GetCPUC0StateResidency(cpuID int) (float64, error) {
|
|
args := m.Called(cpuID)
|
|
return args.Get(0).(float64), args.Error(1)
|
|
}
|
|
|
|
func (m *fetcherMock) GetCPUC1StateResidency(cpuID int) (float64, error) {
|
|
args := m.Called(cpuID)
|
|
return args.Get(0).(float64), args.Error(1)
|
|
}
|
|
|
|
func (m *fetcherMock) GetCPUC3StateResidency(cpuID int) (float64, error) {
|
|
args := m.Called(cpuID)
|
|
return args.Get(0).(float64), args.Error(1)
|
|
}
|
|
|
|
func (m *fetcherMock) GetCPUC6StateResidency(cpuID int) (float64, error) {
|
|
args := m.Called(cpuID)
|
|
return args.Get(0).(float64), args.Error(1)
|
|
}
|
|
|
|
func (m *fetcherMock) GetCPUC7StateResidency(cpuID int) (float64, error) {
|
|
args := m.Called(cpuID)
|
|
return args.Get(0).(float64), args.Error(1)
|
|
}
|
|
|
|
func (m *fetcherMock) GetCPUBusyFrequencyMhz(cpuID int) (float64, error) {
|
|
args := m.Called(cpuID)
|
|
return args.Get(0).(float64), args.Error(1)
|
|
}
|
|
|
|
func (m *fetcherMock) ReadPerfEvents() error {
|
|
args := m.Called()
|
|
return args.Error(0)
|
|
}
|
|
|
|
func (m *fetcherMock) DeactivatePerfEvents() error {
|
|
args := m.Called()
|
|
return args.Error(0)
|
|
}
|
|
|
|
func (m *fetcherMock) GetCPUC0SubstateC01Percent(cpuID int) (float64, error) {
|
|
args := m.Called(cpuID)
|
|
return args.Get(0).(float64), args.Error(1)
|
|
}
|
|
|
|
func (m *fetcherMock) GetCPUC0SubstateC02Percent(cpuID int) (float64, error) {
|
|
args := m.Called(cpuID)
|
|
return args.Get(0).(float64), args.Error(1)
|
|
}
|
|
|
|
func (m *fetcherMock) GetCPUC0SubstateC0WaitPercent(cpuID int) (float64, error) {
|
|
args := m.Called(cpuID)
|
|
return args.Get(0).(float64), args.Error(1)
|
|
}
|
|
|
|
func (m *fetcherMock) GetCPUBaseFrequency(packageID int) (uint64, error) {
|
|
args := m.Called(packageID)
|
|
return args.Get(0).(uint64), args.Error(1)
|
|
}
|
|
|
|
func (m *fetcherMock) GetInitialUncoreFrequencyMin(packageID, dieID int) (float64, error) {
|
|
args := m.Called(packageID, dieID)
|
|
return args.Get(0).(float64), args.Error(1)
|
|
}
|
|
|
|
func (m *fetcherMock) GetCustomizedUncoreFrequencyMin(packageID, dieID int) (float64, error) {
|
|
args := m.Called(packageID, dieID)
|
|
return args.Get(0).(float64), args.Error(1)
|
|
}
|
|
|
|
func (m *fetcherMock) GetInitialUncoreFrequencyMax(packageID, dieID int) (float64, error) {
|
|
args := m.Called(packageID, dieID)
|
|
return args.Get(0).(float64), args.Error(1)
|
|
}
|
|
|
|
func (m *fetcherMock) GetCustomizedUncoreFrequencyMax(packageID, dieID int) (float64, error) {
|
|
args := m.Called(packageID, dieID)
|
|
return args.Get(0).(float64), args.Error(1)
|
|
}
|
|
|
|
func (m *fetcherMock) GetCurrentUncoreFrequency(packageID, dieID int) (float64, error) {
|
|
args := m.Called(packageID, dieID)
|
|
return args.Get(0).(float64), args.Error(1)
|
|
}
|
|
|
|
func (m *fetcherMock) GetCurrentPackagePowerConsumptionWatts(packageID int) (float64, error) {
|
|
args := m.Called(packageID)
|
|
return args.Get(0).(float64), args.Error(1)
|
|
}
|
|
|
|
func (m *fetcherMock) GetCurrentDramPowerConsumptionWatts(packageID int) (float64, error) {
|
|
args := m.Called(packageID)
|
|
return args.Get(0).(float64), args.Error(1)
|
|
}
|
|
|
|
func (m *fetcherMock) GetPackageThermalDesignPowerWatts(packageID int) (float64, error) {
|
|
args := m.Called(packageID)
|
|
return args.Get(0).(float64), args.Error(1)
|
|
}
|
|
|
|
func (m *fetcherMock) GetMaxTurboFreqList(packageID int) ([]ptel.MaxTurboFreq, error) {
|
|
args := m.Called(packageID)
|
|
if args.Get(0) == nil {
|
|
return nil, args.Error(1)
|
|
}
|
|
return args.Get(0).([]ptel.MaxTurboFreq), args.Error(1)
|
|
}
|
|
|
|
func TestAddCPUMetrics(t *testing.T) {
|
|
// Disable package metrics when parseConfig method is called.
|
|
packageMetrics := make([]packageMetricType, 0)
|
|
|
|
t.Run("NoAvailableCPUs", func(t *testing.T) {
|
|
acc := &testutil.Accumulator{}
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting available CPU IDs with access to msr registers.
|
|
mFetcher.On("GetMsrCPUIDs").Return(nil).Once()
|
|
|
|
p := &PowerStat{
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
p.addCPUMetrics(acc)
|
|
|
|
require.Empty(t, acc.Errors)
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("WithDataCPUIDErrors", func(t *testing.T) {
|
|
t.Run("SingleCPUID", func(t *testing.T) {
|
|
cpuID := 0
|
|
|
|
acc := &testutil.Accumulator{}
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting available CPU IDs with access to msr registers and coreFreq.
|
|
mFetcher.On("GetMsrCPUIDs").Return([]int{cpuID}).Once()
|
|
|
|
// mock getting core ID for CPU ID.
|
|
mFetcher.On("GetCPUCoreID", cpuID).Return(0, errors.New("mock error")).Once()
|
|
|
|
p := &PowerStat{
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
p.addCPUMetrics(acc)
|
|
|
|
require.Len(t, acc.Errors, 1)
|
|
require.ErrorContains(t, acc.FirstError(), fmt.Sprintf("failed to get coreFreq and/or msr metrics for CPU ID %v", cpuID))
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("MultipleCPUIDs", func(t *testing.T) {
|
|
cpuID := 1
|
|
coreID := 2
|
|
packageID := 3
|
|
cpuFreq := 500.0
|
|
|
|
acc := &testutil.Accumulator{}
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting available CPU IDs with access to msr registers and coreFreq.
|
|
mFetcher.On("GetMsrCPUIDs").Return([]int{0, cpuID}).Once()
|
|
|
|
// mock getting core ID for CPU ID 0.
|
|
mFetcher.On("GetCPUCoreID", 0).Return(0, errors.New("mock error")).Once()
|
|
|
|
// mock getting core ID for CPU ID 1.
|
|
mFetcher.On("GetCPUCoreID", cpuID).Return(coreID, nil).Once()
|
|
|
|
// mock getting package ID for CPU ID 1.
|
|
mFetcher.On("GetCPUPackageID", cpuID).Return(packageID, nil).Once()
|
|
|
|
// mock getting CPU frequency for CPU ID 1.
|
|
mFetcher.On("GetCPUFrequency", cpuID).Return(cpuFreq, nil).Once()
|
|
|
|
p := &PowerStat{
|
|
CPUMetrics: []cpuMetricType{
|
|
// Metric which relies on coreFreq.
|
|
cpuFrequency,
|
|
|
|
// Metrics which do not rely on coreFreq nor msr.
|
|
cpuC0SubstateC01Percent,
|
|
cpuC0SubstateC02Percent,
|
|
},
|
|
PackageMetrics: packageMetrics,
|
|
EventDefinitions: "./testdata/sapphirerapids_core.json",
|
|
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.NoError(t, p.parseConfig())
|
|
|
|
p.addCPUMetrics(acc)
|
|
|
|
require.Len(t, acc.Errors, 1)
|
|
require.ErrorContains(t, acc.FirstError(), "failed to get coreFreq and/or msr metrics for CPU ID 0")
|
|
require.Len(t, acc.GetTelegrafMetrics(), 1)
|
|
acc.AssertContainsTaggedFields(
|
|
t,
|
|
// measurement
|
|
"powerstat_core",
|
|
// fields
|
|
map[string]interface{}{
|
|
"cpu_frequency_mhz": cpuFreq,
|
|
},
|
|
// tags
|
|
map[string]string{
|
|
"cpu_id": strconv.Itoa(cpuID),
|
|
"core_id": strconv.Itoa(coreID),
|
|
"package_id": strconv.Itoa(packageID),
|
|
},
|
|
)
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
})
|
|
|
|
t.Run("WithCoreFreqMetrics", func(t *testing.T) {
|
|
cpuFreq := 500.0
|
|
|
|
acc := &testutil.Accumulator{}
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting available CPU IDs with access to coreFreq.
|
|
mFetcher.On("GetMsrCPUIDs").Return([]int{0, 1}).Once()
|
|
|
|
// mock getting corresponding core ID to CPU IDs 0 and 1.
|
|
mFetcher.On("GetCPUCoreID", mock.AnythingOfType("int")).Return(0, nil).Twice()
|
|
|
|
// mock getting corresponding package ID to CPU IDs 0 and 1.
|
|
mFetcher.On("GetCPUPackageID", mock.AnythingOfType("int")).Return(1, nil).Twice()
|
|
|
|
// mock getting CPU frequency for CPU ID 0.
|
|
mFetcher.On("GetCPUFrequency", 0).Return(cpuFreq, nil).Once()
|
|
|
|
// mock getting CPU frequency for CPU ID 1.
|
|
mFetcher.On("GetCPUFrequency", 1).Return(0.0, errors.New("mock error")).Once()
|
|
|
|
p := &PowerStat{
|
|
CPUMetrics: []cpuMetricType{
|
|
// Metric which relies on coreFreq.
|
|
cpuFrequency,
|
|
|
|
// Metrics which do not rely on coreFreq nor msr
|
|
cpuC0SubstateC01Percent,
|
|
cpuC0SubstateC02Percent,
|
|
},
|
|
PackageMetrics: packageMetrics,
|
|
EventDefinitions: "./testdata/sapphirerapids_core.json",
|
|
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.NoError(t, p.parseConfig())
|
|
|
|
p.addCPUMetrics(acc)
|
|
|
|
require.Len(t, acc.Errors, 1)
|
|
require.ErrorContains(t, acc.FirstError(), fmt.Sprintf("failed to get %q for CPU ID 1", cpuFrequency))
|
|
require.Len(t, acc.GetTelegrafMetrics(), 1)
|
|
acc.AssertContainsTaggedFields(
|
|
t,
|
|
// measurement
|
|
"powerstat_core",
|
|
// fields
|
|
map[string]interface{}{
|
|
"cpu_frequency_mhz": cpuFreq,
|
|
},
|
|
// tags
|
|
map[string]string{
|
|
"cpu_id": "0",
|
|
"core_id": "0",
|
|
"package_id": "1",
|
|
},
|
|
)
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("WithMsrMetrics", func(t *testing.T) {
|
|
t.Run("SingleRead", func(t *testing.T) {
|
|
cpuTemp := uint64(18)
|
|
|
|
acc := &testutil.Accumulator{}
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting available CPU IDs with access to msr registers.
|
|
mFetcher.On("GetMsrCPUIDs").Return([]int{0, 1}).Once()
|
|
|
|
// mock getting corresponding core ID to CPU IDs 0 and 1.
|
|
mFetcher.On("GetCPUCoreID", mock.AnythingOfType("int")).Return(0, nil).Twice()
|
|
|
|
// mock getting corresponding package ID to CPU IDs 0 and 1.
|
|
mFetcher.On("GetCPUPackageID", mock.AnythingOfType("int")).Return(1, nil).Twice()
|
|
|
|
// mock getting CPU temperature metric for CPU ID 0.
|
|
mFetcher.On("GetCPUTemperature", 0).Return(cpuTemp, nil).Once()
|
|
|
|
// mock getting CPU temperature metric for CPU ID 1.
|
|
mFetcher.On("GetCPUTemperature", 1).Return(uint64(0), errors.New("mock error")).Once()
|
|
|
|
p := &PowerStat{
|
|
CPUMetrics: []cpuMetricType{
|
|
// Metrics which rely on single-read msr registers.
|
|
cpuTemperature,
|
|
|
|
// Metrics which do not rely on coreFreq nor msr
|
|
cpuC0SubstateC01Percent,
|
|
cpuC0SubstateC02Percent,
|
|
},
|
|
PackageMetrics: packageMetrics,
|
|
EventDefinitions: "./testdata/sapphirerapids_core.json",
|
|
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.NoError(t, p.parseConfig())
|
|
|
|
p.addCPUMetrics(acc)
|
|
|
|
require.Len(t, acc.Errors, 1)
|
|
require.ErrorContains(t, acc.FirstError(), fmt.Sprintf("failed to get %q for CPU ID 1", cpuTemperature))
|
|
require.Len(t, acc.GetTelegrafMetrics(), 1)
|
|
acc.AssertContainsTaggedFields(
|
|
t,
|
|
// measurement
|
|
"powerstat_core",
|
|
// fields
|
|
map[string]interface{}{
|
|
"cpu_temperature_celsius": cpuTemp,
|
|
},
|
|
// tags
|
|
map[string]string{
|
|
"cpu_id": "0",
|
|
"core_id": "0",
|
|
"package_id": "1",
|
|
},
|
|
)
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("TimeRelated", func(t *testing.T) {
|
|
cpuBusyFreq := 750.0
|
|
|
|
acc := &testutil.Accumulator{}
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting available CPU IDs with access to msr registers.
|
|
mFetcher.On("GetMsrCPUIDs").Return([]int{0, 1}).Once()
|
|
|
|
// mock getting corresponding core ID to CPU IDs 0 and 1.
|
|
mFetcher.On("GetCPUCoreID", mock.AnythingOfType("int")).Return(0, nil).Twice()
|
|
|
|
// mock getting corresponding package ID to CPU IDs 0 and 1.
|
|
mFetcher.On("GetCPUPackageID", mock.AnythingOfType("int")).Return(1, nil).Twice()
|
|
|
|
// mock updating msr time-related metrics for CPU ID 0.
|
|
mFetcher.On("UpdatePerCPUMetrics", 0).Return(errors.New("mock error")).Once()
|
|
|
|
// mock updating msr time-related metrics for CPU ID 1.
|
|
mFetcher.On("UpdatePerCPUMetrics", 1).Return(nil).Once()
|
|
|
|
// mock getting CPU busy frequency metric for CPU ID 1.
|
|
mFetcher.On("GetCPUBusyFrequencyMhz", 1).Return(cpuBusyFreq, nil).Once()
|
|
|
|
p := &PowerStat{
|
|
CPUMetrics: []cpuMetricType{
|
|
// Metrics which rely on time-related msr reads.
|
|
cpuBusyFrequency,
|
|
|
|
// Metrics which do not rely on coreFreq nor msr
|
|
cpuC0SubstateC01Percent,
|
|
cpuC0SubstateC02Percent,
|
|
cpuC0SubstateC0WaitPercent,
|
|
},
|
|
PackageMetrics: packageMetrics,
|
|
EventDefinitions: "./testdata/sapphirerapids_core.json",
|
|
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.NoError(t, p.parseConfig())
|
|
|
|
p.addCPUMetrics(acc)
|
|
|
|
require.Len(t, acc.Errors, 1)
|
|
require.ErrorContains(t, acc.FirstError(), "failed to update MSR time-related metrics for CPU ID 0")
|
|
require.Len(t, acc.GetTelegrafMetrics(), 1)
|
|
acc.AssertContainsTaggedFields(
|
|
t,
|
|
// measurement
|
|
"powerstat_core",
|
|
// fields
|
|
map[string]interface{}{
|
|
"cpu_busy_frequency_mhz": cpuBusyFreq,
|
|
},
|
|
// tags
|
|
map[string]string{
|
|
"cpu_id": "1",
|
|
"core_id": "0",
|
|
"package_id": "1",
|
|
},
|
|
)
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
})
|
|
}
|
|
|
|
func TestAddPerCPUMsrMetrics(t *testing.T) {
|
|
// Disable package metrics when parseConfig method is called.
|
|
packageMetrics := make([]packageMetricType, 0)
|
|
|
|
t.Run("WithoutMsrMetrics", func(t *testing.T) {
|
|
cpuID := 0
|
|
coreID := 1
|
|
packageID := 0
|
|
|
|
acc := &testutil.Accumulator{}
|
|
|
|
p := &PowerStat{
|
|
CPUMetrics: []cpuMetricType{
|
|
// metrics which do not rely on msr
|
|
cpuFrequency,
|
|
cpuC0SubstateC01Percent,
|
|
},
|
|
}
|
|
|
|
p.addPerCPUMsrMetrics(acc, cpuID, coreID, packageID)
|
|
|
|
require.Empty(t, acc.Errors)
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
})
|
|
|
|
t.Run("WithSingleMsrReadMetrics", func(t *testing.T) {
|
|
cpuID := 0
|
|
coreID := 1
|
|
packageID := 0
|
|
cpuMetrics := []cpuMetricType{
|
|
// metric that relies on a single msr read.
|
|
cpuTemperature,
|
|
|
|
// metrics that do not rely on msr.
|
|
cpuFrequency,
|
|
cpuC0SubstateC01Percent,
|
|
}
|
|
|
|
t.Run("WithError", func(t *testing.T) {
|
|
acc := &testutil.Accumulator{}
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting CPU temperature metric.
|
|
mFetcher.On("GetCPUTemperature", cpuID).Return(uint64(0), errors.New("mock error")).Once()
|
|
|
|
p := &PowerStat{
|
|
CPUMetrics: cpuMetrics,
|
|
PackageMetrics: packageMetrics,
|
|
EventDefinitions: "./testdata/sapphirerapids_core.json",
|
|
|
|
fetcher: mFetcher,
|
|
|
|
logOnce: map[string]struct{}{},
|
|
}
|
|
|
|
require.NoError(t, p.parseConfig())
|
|
|
|
p.addPerCPUMsrMetrics(acc, cpuID, coreID, packageID)
|
|
|
|
require.Len(t, acc.Errors, 1)
|
|
require.ErrorContains(t, acc.FirstError(), fmt.Sprintf("failed to get %q for CPU ID %v", cpuTemperature, cpuID))
|
|
require.Empty(t, p.logOnce)
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("WithModuleNotInitializedError", func(t *testing.T) {
|
|
acc := &testutil.Accumulator{}
|
|
|
|
mErr := &ptel.ModuleNotInitializedError{Name: "msr"}
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting CPU temperature metric.
|
|
mFetcher.On("GetCPUTemperature", cpuID).Return(uint64(0), mErr).Twice()
|
|
|
|
p := &PowerStat{
|
|
CPUMetrics: cpuMetrics,
|
|
PackageMetrics: packageMetrics,
|
|
EventDefinitions: "./testdata/sapphirerapids_core.json",
|
|
|
|
fetcher: mFetcher,
|
|
|
|
logOnce: map[string]struct{}{},
|
|
}
|
|
|
|
require.NoError(t, p.parseConfig())
|
|
|
|
// First call adds the error to the accumulator and logOnce map.
|
|
p.addPerCPUMsrMetrics(acc, cpuID, coreID, packageID)
|
|
|
|
// Second call detects previous error in logOnce map and skips adding it to the accumulator.
|
|
p.addPerCPUMsrMetrics(acc, cpuID, coreID, packageID)
|
|
|
|
require.Len(t, acc.Errors, 1)
|
|
require.ErrorContains(t, acc.FirstError(), fmt.Sprintf("failed to get %q: %v", cpuTemperature, mErr))
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
|
|
require.Len(t, p.logOnce, 1)
|
|
require.Contains(t, p.logOnce, "msr_cpu_temperature")
|
|
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("WithoutErrors", func(t *testing.T) {
|
|
cpuTemp := uint64(20)
|
|
|
|
acc := &testutil.Accumulator{}
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting CPU temperature metric.
|
|
mFetcher.On("GetCPUTemperature", cpuID).Return(cpuTemp, nil).Once()
|
|
|
|
p := &PowerStat{
|
|
CPUMetrics: cpuMetrics,
|
|
PackageMetrics: packageMetrics,
|
|
EventDefinitions: "./testdata/sapphirerapids_core.json",
|
|
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.NoError(t, p.parseConfig())
|
|
|
|
p.addPerCPUMsrMetrics(acc, cpuID, coreID, packageID)
|
|
|
|
require.Empty(t, acc.Errors)
|
|
require.Len(t, acc.GetTelegrafMetrics(), 1)
|
|
acc.AssertContainsTaggedFields(
|
|
t,
|
|
// measurement
|
|
"powerstat_core",
|
|
// fields
|
|
map[string]interface{}{
|
|
"cpu_temperature_celsius": cpuTemp,
|
|
},
|
|
// tags
|
|
map[string]string{
|
|
"cpu_id": strconv.Itoa(cpuID),
|
|
"core_id": strconv.Itoa(coreID),
|
|
"package_id": strconv.Itoa(packageID),
|
|
},
|
|
)
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
})
|
|
|
|
t.Run("WithTimeRelatedMsrMetrics", func(t *testing.T) {
|
|
cpuID := 0
|
|
coreID := 1
|
|
packageID := 0
|
|
|
|
c1State := 5.15
|
|
c6State := 8.10
|
|
|
|
cpuMetrics := []cpuMetricType{
|
|
// metrics that rely on a time-related msr.
|
|
cpuC1StateResidency,
|
|
cpuC6StateResidency,
|
|
|
|
// metrics which do not rely on msr.
|
|
cpuFrequency,
|
|
cpuC0SubstateC01Percent,
|
|
}
|
|
|
|
t.Run("FailedToUpdate", func(t *testing.T) {
|
|
acc := &testutil.Accumulator{}
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock updating msr time-related metrics.
|
|
mFetcher.On("UpdatePerCPUMetrics", cpuID).Return(errors.New("mock error")).Once()
|
|
|
|
p := &PowerStat{
|
|
CPUMetrics: cpuMetrics,
|
|
PackageMetrics: packageMetrics,
|
|
EventDefinitions: "./testdata/sapphirerapids_core.json",
|
|
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.NoError(t, p.parseConfig())
|
|
|
|
p.addPerCPUMsrMetrics(acc, cpuID, coreID, packageID)
|
|
|
|
require.Len(t, acc.Errors, 1)
|
|
require.ErrorContains(t, acc.FirstError(), fmt.Sprintf("failed to update MSR time-related metrics for CPU ID %v", cpuID))
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("FailedToUpdateModuleNotInitializedError", func(t *testing.T) {
|
|
acc := &testutil.Accumulator{}
|
|
|
|
mErr := &ptel.ModuleNotInitializedError{Name: "msr"}
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock updating msr time-related metrics.
|
|
mFetcher.On("UpdatePerCPUMetrics", cpuID).Return(mErr).Twice()
|
|
|
|
p := &PowerStat{
|
|
CPUMetrics: cpuMetrics,
|
|
PackageMetrics: packageMetrics,
|
|
EventDefinitions: "./testdata/sapphirerapids_core.json",
|
|
|
|
fetcher: mFetcher,
|
|
|
|
logOnce: map[string]struct{}{},
|
|
}
|
|
|
|
require.NoError(t, p.parseConfig())
|
|
|
|
// First call adds the error to the accumulator and key to logOnce map.
|
|
p.addPerCPUMsrMetrics(acc, cpuID, coreID, packageID)
|
|
|
|
// Second call detects previous error in logOnce map and skips adding it to the accumulator.
|
|
p.addPerCPUMsrMetrics(acc, cpuID, coreID, packageID)
|
|
|
|
require.Len(t, acc.Errors, 1)
|
|
require.ErrorContains(t, acc.FirstError(), fmt.Sprintf("failed to update MSR time-related metrics: %v", mErr))
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
|
|
require.Len(t, p.logOnce, 1)
|
|
require.Contains(t, p.logOnce, "msr_time_related")
|
|
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("WithoutErrors", func(t *testing.T) {
|
|
acc := &testutil.Accumulator{}
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock updating msr time-related metrics.
|
|
mFetcher.On("UpdatePerCPUMetrics", cpuID).Return(nil).Once()
|
|
|
|
// mock getting C1 state residency.
|
|
mFetcher.On("GetCPUC1StateResidency", cpuID).Return(c1State, nil).Once()
|
|
|
|
// mock getting C6 state residency.
|
|
mFetcher.On("GetCPUC6StateResidency", cpuID).Return(c6State, nil).Once()
|
|
|
|
p := &PowerStat{
|
|
CPUMetrics: cpuMetrics,
|
|
PackageMetrics: packageMetrics,
|
|
EventDefinitions: "./testdata/sapphirerapids_core.json",
|
|
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.NoError(t, p.parseConfig())
|
|
|
|
p.addPerCPUMsrMetrics(acc, cpuID, coreID, packageID)
|
|
|
|
require.Empty(t, acc.Errors)
|
|
require.Len(t, acc.GetTelegrafMetrics(), 2)
|
|
acc.AssertContainsTaggedFields(
|
|
t,
|
|
// measurement
|
|
"powerstat_core",
|
|
// fields
|
|
map[string]interface{}{
|
|
"cpu_c1_state_residency_percent": c1State,
|
|
},
|
|
// flags
|
|
map[string]string{
|
|
"cpu_id": strconv.Itoa(cpuID),
|
|
"core_id": strconv.Itoa(coreID),
|
|
"package_id": strconv.Itoa(packageID),
|
|
},
|
|
)
|
|
acc.AssertContainsTaggedFields(
|
|
t,
|
|
// measurement
|
|
"powerstat_core",
|
|
// fields
|
|
map[string]interface{}{
|
|
"cpu_c6_state_residency_percent": c6State,
|
|
},
|
|
// flags
|
|
map[string]string{
|
|
"cpu_id": strconv.Itoa(cpuID),
|
|
"core_id": strconv.Itoa(coreID),
|
|
"package_id": strconv.Itoa(packageID),
|
|
},
|
|
)
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
})
|
|
}
|
|
|
|
func TestAddCPUTimeRelatedMsrMetrics(t *testing.T) {
|
|
cpuID := 0
|
|
coreID := 1
|
|
packageID := 0
|
|
|
|
c0State := 3.0
|
|
c1State := 2.0
|
|
c3State := 1.5
|
|
c6State := 1.0
|
|
busyCycles := 0.5
|
|
|
|
acc := &testutil.Accumulator{}
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting CPU C0 state residency value.
|
|
mFetcher.On("GetCPUC0StateResidency", cpuID).Return(c0State, nil).Once()
|
|
|
|
// mock getting CPU C1 state residency value.
|
|
mFetcher.On("GetCPUC1StateResidency", cpuID).Return(c1State, nil).Once()
|
|
|
|
// mock getting CPU C3 state residency value.
|
|
mFetcher.On("GetCPUC3StateResidency", cpuID).Return(c3State, nil).Once()
|
|
|
|
// mock getting CPU C6 state residency value.
|
|
mFetcher.On("GetCPUC6StateResidency", cpuID).Return(c6State, nil).Once()
|
|
|
|
// mock getting CPU C0 state residency value, triggered when calling add CPU busy cycle metric.
|
|
mFetcher.On("GetCPUC0StateResidency", cpuID).Return(busyCycles, nil).Once()
|
|
|
|
p := &PowerStat{
|
|
CPUMetrics: []cpuMetricType{
|
|
// Metrics which are not time-related MSR.
|
|
cpuFrequency,
|
|
cpuTemperature,
|
|
cpuC0SubstateC01Percent,
|
|
|
|
// Time-related MSR metrics.
|
|
cpuC0StateResidency,
|
|
cpuC1StateResidency,
|
|
cpuC3StateResidency,
|
|
cpuC6StateResidency,
|
|
cpuBusyCycles,
|
|
},
|
|
PackageMetrics: make([]packageMetricType, 0),
|
|
EventDefinitions: "./testdata/sapphirerapids_core.json",
|
|
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.NoError(t, p.parseConfig())
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
|
|
p.addCPUTimeRelatedMsrMetrics(acc, cpuID, coreID, packageID)
|
|
|
|
require.Empty(t, acc.Errors)
|
|
require.Len(t, acc.GetTelegrafMetrics(), 5)
|
|
acc.AssertContainsTaggedFields(
|
|
t,
|
|
// measurement
|
|
"powerstat_core",
|
|
// fields
|
|
map[string]interface{}{
|
|
"cpu_c0_state_residency_percent": c0State,
|
|
},
|
|
// tags
|
|
map[string]string{
|
|
"cpu_id": strconv.Itoa(cpuID),
|
|
"core_id": strconv.Itoa(coreID),
|
|
"package_id": strconv.Itoa(packageID),
|
|
},
|
|
)
|
|
acc.AssertContainsTaggedFields(
|
|
t,
|
|
// measurement
|
|
"powerstat_core",
|
|
// fields
|
|
map[string]interface{}{
|
|
"cpu_c1_state_residency_percent": c1State,
|
|
},
|
|
// tags
|
|
map[string]string{
|
|
"cpu_id": strconv.Itoa(cpuID),
|
|
"core_id": strconv.Itoa(coreID),
|
|
"package_id": strconv.Itoa(packageID),
|
|
},
|
|
)
|
|
acc.AssertContainsTaggedFields(
|
|
t,
|
|
// measurement
|
|
"powerstat_core",
|
|
// fields
|
|
map[string]interface{}{
|
|
"cpu_c6_state_residency_percent": c6State,
|
|
},
|
|
// tags
|
|
map[string]string{
|
|
"cpu_id": strconv.Itoa(cpuID),
|
|
"core_id": strconv.Itoa(coreID),
|
|
"package_id": strconv.Itoa(packageID),
|
|
},
|
|
)
|
|
acc.AssertContainsTaggedFields(
|
|
t,
|
|
// measurement
|
|
"powerstat_core",
|
|
// fields
|
|
map[string]interface{}{
|
|
"cpu_c3_state_residency_percent": c3State,
|
|
},
|
|
// tags
|
|
map[string]string{
|
|
"cpu_id": strconv.Itoa(cpuID),
|
|
"core_id": strconv.Itoa(coreID),
|
|
"package_id": strconv.Itoa(packageID),
|
|
},
|
|
)
|
|
acc.AssertContainsTaggedFields(
|
|
t,
|
|
// measurement
|
|
"powerstat_core",
|
|
// fields
|
|
map[string]interface{}{
|
|
"cpu_busy_cycles_percent": busyCycles,
|
|
},
|
|
// tags
|
|
map[string]string{
|
|
"cpu_id": strconv.Itoa(cpuID),
|
|
"core_id": strconv.Itoa(coreID),
|
|
"package_id": strconv.Itoa(packageID),
|
|
},
|
|
)
|
|
mFetcher.AssertExpectations(t)
|
|
}
|
|
|
|
func TestAddCPUPerfMetrics(t *testing.T) {
|
|
// Disable package metrics when parseConfig method is called.
|
|
packageMetrics := make([]packageMetricType, 0)
|
|
|
|
t.Run("FailedToReadPerfEvents", func(t *testing.T) {
|
|
acc := &testutil.Accumulator{}
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock reading perf events.
|
|
mFetcher.On("ReadPerfEvents").Return(errors.New("mock error")).Once()
|
|
|
|
p := &PowerStat{
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
p.addCPUPerfMetrics(acc)
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
require.Len(t, acc.Errors, 1)
|
|
require.ErrorContains(t, acc.FirstError(), "failed to read perf events")
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("FailedToReadPerfEventsModuleNotInitializedError", func(t *testing.T) {
|
|
acc := &testutil.Accumulator{}
|
|
|
|
mErr := &ptel.ModuleNotInitializedError{Name: "perf"}
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock reading perf events.
|
|
mFetcher.On("ReadPerfEvents").Return(mErr).Twice()
|
|
|
|
p := &PowerStat{
|
|
fetcher: mFetcher,
|
|
|
|
logOnce: map[string]struct{}{},
|
|
}
|
|
|
|
// First call adds the error to the accumulator and key to logOnce map.
|
|
p.addCPUPerfMetrics(acc)
|
|
|
|
// Second call detects previous error in logOnce map and skips adding it to the accumulator.
|
|
p.addCPUPerfMetrics(acc)
|
|
|
|
require.Len(t, acc.Errors, 1)
|
|
require.ErrorContains(t, acc.FirstError(), fmt.Sprintf("failed to read perf events: %v", mErr))
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
|
|
require.Len(t, p.logOnce, 1)
|
|
require.Contains(t, p.logOnce, "perf_read")
|
|
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("NoAvailableCPUs", func(t *testing.T) {
|
|
acc := &testutil.Accumulator{}
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock reading perf events.
|
|
mFetcher.On("ReadPerfEvents").Return(nil).Once()
|
|
|
|
// mock getting available CPU IDs for perf events.
|
|
mFetcher.On("GetPerfCPUIDs").Return(nil).Once()
|
|
|
|
p := &PowerStat{
|
|
CPUMetrics: []cpuMetricType{
|
|
// Metrics which do not rely on perf.
|
|
cpuFrequency,
|
|
cpuTemperature,
|
|
cpuBusyCycles,
|
|
|
|
// Metrics which rely on perf.
|
|
cpuC0SubstateC01Percent,
|
|
cpuC0SubstateC02Percent,
|
|
cpuC0SubstateC0WaitPercent,
|
|
},
|
|
PackageMetrics: make([]packageMetricType, 0),
|
|
EventDefinitions: "./testdata/sapphirerapids_core.json",
|
|
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.NoError(t, p.parseConfig())
|
|
|
|
p.addCPUPerfMetrics(acc)
|
|
|
|
require.Empty(t, acc.Errors)
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("FailedToGetDataCPUID", func(t *testing.T) {
|
|
t.Run("SingleCPUID", func(t *testing.T) {
|
|
cpuID := 0
|
|
|
|
acc := &testutil.Accumulator{}
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock reading perf events.
|
|
mFetcher.On("ReadPerfEvents").Return(nil).Once()
|
|
|
|
// mock getting available CPU IDs for perf events.
|
|
mFetcher.On("GetPerfCPUIDs").Return([]int{cpuID}).Once()
|
|
|
|
// mock getting corresponding core ID to CPU ID 0.
|
|
mFetcher.On("GetCPUCoreID", cpuID).Return(1, nil).Once()
|
|
|
|
// mock getting corresponding package ID to CPU ID 0.
|
|
mFetcher.On("GetCPUPackageID", cpuID).Return(0, errors.New("mock error")).Once()
|
|
|
|
p := &PowerStat{
|
|
CPUMetrics: []cpuMetricType{
|
|
// Metrics which do not rely on perf.
|
|
cpuFrequency,
|
|
cpuTemperature,
|
|
cpuBusyCycles,
|
|
|
|
// Metrics which rely on perf.
|
|
cpuC0SubstateC01Percent,
|
|
cpuC0SubstateC02Percent,
|
|
cpuC0SubstateC0WaitPercent,
|
|
},
|
|
EventDefinitions: "./testdata/sapphirerapids_core.json",
|
|
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.NoError(t, p.parseConfig())
|
|
|
|
p.addCPUPerfMetrics(acc)
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
require.Len(t, acc.Errors, 1)
|
|
require.ErrorContains(t, acc.FirstError(), fmt.Sprintf("failed to get perf metrics for CPU ID %v", cpuID))
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("MultipleCPUIDs", func(t *testing.T) {
|
|
cpuID := 0
|
|
coreID := 2
|
|
packageID := 3
|
|
|
|
c01Percent := 0.2
|
|
|
|
acc := &testutil.Accumulator{}
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock reading perf events.
|
|
mFetcher.On("ReadPerfEvents").Return(nil).Once()
|
|
|
|
// mock getting available CPU IDs for perf events.
|
|
mFetcher.On("GetPerfCPUIDs").Return([]int{cpuID, 1}).Once()
|
|
|
|
// mock getting corresponding core ID to CPU ID 0.
|
|
mFetcher.On("GetCPUCoreID", cpuID).Return(coreID, nil).Once()
|
|
|
|
// mock getting corresponding package ID to CPU ID 0.
|
|
mFetcher.On("GetCPUPackageID", cpuID).Return(packageID, nil).Once()
|
|
|
|
// mock getting CPU C01 metric.
|
|
mFetcher.On("GetCPUC0SubstateC01Percent", cpuID).Return(c01Percent, nil).Once()
|
|
|
|
// mock getting corresponding core ID to CPU ID 1.
|
|
mFetcher.On("GetCPUCoreID", 1).Return(5, nil).Once()
|
|
|
|
// mock getting corresponding package ID to CPU ID 1.
|
|
mFetcher.On("GetCPUPackageID", 1).Return(0, errors.New("mock error")).Once()
|
|
|
|
p := &PowerStat{
|
|
CPUMetrics: []cpuMetricType{
|
|
// Metrics which do not rely on perf.
|
|
cpuFrequency,
|
|
cpuTemperature,
|
|
cpuC6StateResidency,
|
|
|
|
// Metrics which rely on perf.
|
|
cpuC0SubstateC01Percent,
|
|
},
|
|
PackageMetrics: packageMetrics,
|
|
EventDefinitions: "./testdata/sapphirerapids_core.json",
|
|
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.NoError(t, p.parseConfig())
|
|
|
|
p.addCPUPerfMetrics(acc)
|
|
|
|
require.Len(t, acc.Errors, 1)
|
|
require.ErrorContains(t, acc.FirstError(), "failed to get perf metrics for CPU ID 1")
|
|
require.Len(t, acc.GetTelegrafMetrics(), 1)
|
|
acc.AssertContainsTaggedFields(
|
|
t,
|
|
// measurement
|
|
"powerstat_core",
|
|
// fields
|
|
map[string]interface{}{
|
|
"cpu_c0_substate_c01_percent": c01Percent,
|
|
},
|
|
// tags
|
|
map[string]string{
|
|
"cpu_id": strconv.Itoa(cpuID),
|
|
"core_id": strconv.Itoa(coreID),
|
|
"package_id": strconv.Itoa(packageID),
|
|
},
|
|
)
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
})
|
|
|
|
t.Run("WithError", func(t *testing.T) {
|
|
cpuID := 0
|
|
coreID := 1
|
|
packageID := 0
|
|
|
|
c01Percent := 0.5
|
|
c0Wait := 2.5
|
|
|
|
acc := &testutil.Accumulator{}
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock reading perf events.
|
|
mFetcher.On("ReadPerfEvents").Return(nil).Once()
|
|
|
|
// mock getting available CPU IDs for perf events.
|
|
mFetcher.On("GetPerfCPUIDs").Return([]int{cpuID}).Once()
|
|
|
|
// mock getting corresponding core ID to CPU ID 0.
|
|
mFetcher.On("GetCPUCoreID", cpuID).Return(coreID, nil).Once()
|
|
|
|
// mock getting corresponding package ID to CPU ID 0.
|
|
mFetcher.On("GetCPUPackageID", cpuID).Return(packageID, nil).Once()
|
|
|
|
// mock getting CPU C01 metric.
|
|
mFetcher.On("GetCPUC0SubstateC01Percent", cpuID).Return(c01Percent, nil).Once()
|
|
|
|
// mock getting CPU C02 metric.
|
|
mFetcher.On("GetCPUC0SubstateC02Percent", cpuID).Return(0.0, errors.New("mock error")).Once()
|
|
|
|
// mock getting CPU C0Wait metric.
|
|
mFetcher.On("GetCPUC0SubstateC0WaitPercent", cpuID).Return(c0Wait, nil).Once()
|
|
|
|
p := &PowerStat{
|
|
CPUMetrics: []cpuMetricType{
|
|
// Metrics which do not rely on perf.
|
|
cpuFrequency,
|
|
cpuTemperature,
|
|
cpuC6StateResidency,
|
|
|
|
// Metrics which rely on perf.
|
|
cpuC0SubstateC01Percent,
|
|
cpuC0SubstateC02Percent,
|
|
cpuC0SubstateC0WaitPercent,
|
|
},
|
|
PackageMetrics: packageMetrics,
|
|
EventDefinitions: "./testdata/sapphirerapids_core.json",
|
|
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.NoError(t, p.parseConfig())
|
|
|
|
p.addCPUPerfMetrics(acc)
|
|
|
|
require.Len(t, acc.Errors, 1)
|
|
require.ErrorContains(t, acc.FirstError(), fmt.Sprintf("failed to get %q for CPU ID %v", cpuC0SubstateC02Percent, cpuID))
|
|
require.Len(t, acc.GetTelegrafMetrics(), 2)
|
|
acc.AssertContainsTaggedFields(
|
|
t,
|
|
// measurement
|
|
"powerstat_core",
|
|
// fields
|
|
map[string]interface{}{
|
|
"cpu_c0_substate_c01_percent": c01Percent,
|
|
},
|
|
// tags
|
|
map[string]string{
|
|
"cpu_id": strconv.Itoa(cpuID),
|
|
"core_id": strconv.Itoa(coreID),
|
|
"package_id": strconv.Itoa(packageID),
|
|
},
|
|
)
|
|
acc.AssertContainsTaggedFields(
|
|
t,
|
|
// measurement
|
|
"powerstat_core",
|
|
// fields
|
|
map[string]interface{}{
|
|
"cpu_c0_substate_c0_wait_percent": c0Wait,
|
|
},
|
|
// tags
|
|
map[string]string{
|
|
"cpu_id": strconv.Itoa(cpuID),
|
|
"core_id": strconv.Itoa(coreID),
|
|
"package_id": strconv.Itoa(packageID),
|
|
},
|
|
)
|
|
})
|
|
}
|
|
|
|
func TestAddPerCPUPerfMetrics(t *testing.T) {
|
|
cpuID := 0
|
|
coreID := 1
|
|
packageID := 0
|
|
|
|
c01Percent := 1.09
|
|
c02Percent := 2.12
|
|
|
|
acc := &testutil.Accumulator{}
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting CPU C01 metric.
|
|
mFetcher.On("GetCPUC0SubstateC01Percent", cpuID).Return(c01Percent, nil).Once()
|
|
|
|
// mock getting CPU C02 metric.
|
|
mFetcher.On("GetCPUC0SubstateC02Percent", cpuID).Return(c02Percent, nil).Once()
|
|
|
|
p := &PowerStat{
|
|
CPUMetrics: []cpuMetricType{
|
|
// Metrics which do not rely on perf.
|
|
cpuFrequency,
|
|
cpuTemperature,
|
|
cpuC6StateResidency,
|
|
|
|
// Metrics which rely on perf.
|
|
cpuC0SubstateC01Percent,
|
|
cpuC0SubstateC02Percent,
|
|
},
|
|
PackageMetrics: make([]packageMetricType, 0),
|
|
EventDefinitions: "./testdata/sapphirerapids_core.json",
|
|
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.NoError(t, p.parseConfig())
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
|
|
p.addPerCPUPerfMetrics(acc, cpuID, coreID, packageID)
|
|
|
|
require.Len(t, acc.GetTelegrafMetrics(), 2)
|
|
acc.AssertContainsTaggedFields(
|
|
t,
|
|
// measurement
|
|
"powerstat_core",
|
|
// fields
|
|
map[string]interface{}{
|
|
"cpu_c0_substate_c01_percent": c01Percent,
|
|
},
|
|
// tags
|
|
map[string]string{
|
|
"cpu_id": strconv.Itoa(cpuID),
|
|
"core_id": strconv.Itoa(coreID),
|
|
"package_id": strconv.Itoa(packageID),
|
|
},
|
|
)
|
|
acc.AssertContainsTaggedFields(
|
|
t,
|
|
// measurement
|
|
"powerstat_core",
|
|
// fields
|
|
map[string]interface{}{
|
|
"cpu_c0_substate_c02_percent": c02Percent,
|
|
},
|
|
// tags
|
|
map[string]string{
|
|
"cpu_id": strconv.Itoa(cpuID),
|
|
"core_id": strconv.Itoa(coreID),
|
|
"package_id": strconv.Itoa(packageID),
|
|
},
|
|
)
|
|
mFetcher.AssertExpectations(t)
|
|
}
|
|
|
|
func TestGetDataCPUID(t *testing.T) {
|
|
t.Run("FailedToGetCoreID", func(t *testing.T) {
|
|
cpuID := 1
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting core ID corresponding to the CPU ID.
|
|
mFetcher.On("GetCPUCoreID", cpuID).Return(0, errors.New("mock error")).Once()
|
|
|
|
coreID, packageID, err := getDataCPUID(mFetcher, cpuID)
|
|
|
|
require.Equal(t, 0, coreID)
|
|
require.Equal(t, 0, packageID)
|
|
require.ErrorContains(t, err, fmt.Sprintf("failed to get core ID from CPU ID %v", cpuID))
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("FailedToGetPackageID", func(t *testing.T) {
|
|
cpuID := 1
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting core ID corresponding to the CPU ID.
|
|
mFetcher.On("GetCPUCoreID", cpuID).Return(1, nil).Once()
|
|
|
|
// mock getting package ID corresponding to the CPU ID.
|
|
mFetcher.On("GetCPUPackageID", cpuID).Return(0, errors.New("mock error")).Once()
|
|
|
|
coreID, packageID, err := getDataCPUID(mFetcher, cpuID)
|
|
|
|
require.Equal(t, 0, coreID)
|
|
require.Equal(t, 0, packageID)
|
|
require.ErrorContains(t, err, fmt.Sprintf("failed to get package ID from CPU ID %v", cpuID))
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("Ok", func(t *testing.T) {
|
|
cpuID := 1
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting core ID corresponding to the CPU ID.
|
|
mFetcher.On("GetCPUCoreID", cpuID).Return(1, nil).Once()
|
|
|
|
// mock getting package ID corresponding to the CPU ID.
|
|
mFetcher.On("GetCPUPackageID", cpuID).Return(2, nil).Once()
|
|
|
|
coreID, packageID, err := getDataCPUID(mFetcher, cpuID)
|
|
|
|
require.Equal(t, 1, coreID)
|
|
require.Equal(t, 2, packageID)
|
|
require.NoError(t, err)
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
}
|
|
|
|
func TestAddPackageMetrics(t *testing.T) {
|
|
t.Run("NoPackageIDs", func(t *testing.T) {
|
|
acc := &testutil.Accumulator{}
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting available package IDs.
|
|
mFetcher.On("GetPackageIDs").Return(nil).Once()
|
|
|
|
p := &PowerStat{
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
p.addPackageMetrics(acc)
|
|
|
|
require.Empty(t, acc.Errors)
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
})
|
|
|
|
t.Run("WithRaplMetrics", func(t *testing.T) {
|
|
tdp := 80.0
|
|
|
|
acc := &testutil.Accumulator{}
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting available package IDs.
|
|
mFetcher.On("GetPackageIDs").Return([]int{0, 1}).Once()
|
|
|
|
// mock getting package thermal design power metric for CPU ID 0.
|
|
mFetcher.On("GetPackageThermalDesignPowerWatts", 0).Return(tdp, nil).Once()
|
|
|
|
// mock getting package thermal design power metric for CPU ID 1.
|
|
mFetcher.On("GetPackageThermalDesignPowerWatts", 1).Return(0.0, errors.New("mock error")).Once()
|
|
|
|
p := &PowerStat{
|
|
PackageMetrics: []packageMetricType{
|
|
// metrics which rely on rapl
|
|
packageThermalDesignPower,
|
|
},
|
|
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.NoError(t, p.parseConfig())
|
|
|
|
p.addPackageMetrics(acc)
|
|
|
|
require.Len(t, acc.Errors, 1)
|
|
require.ErrorContains(t, acc.FirstError(), fmt.Sprintf("failed to get %q for package ID 1", packageThermalDesignPower))
|
|
require.Len(t, acc.GetTelegrafMetrics(), 1)
|
|
acc.AssertContainsTaggedFields(
|
|
t,
|
|
// measurement
|
|
"powerstat_package",
|
|
// fields
|
|
map[string]interface{}{
|
|
"thermal_design_power_watts": tdp,
|
|
},
|
|
// tags
|
|
map[string]string{
|
|
"package_id": "0",
|
|
},
|
|
)
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("WithMsrMetrics", func(t *testing.T) {
|
|
baseFreq := uint64(400)
|
|
|
|
acc := &testutil.Accumulator{}
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting available package IDs.
|
|
mFetcher.On("GetPackageIDs").Return([]int{0, 1}).Once()
|
|
|
|
// mock getting CPU base frequency metric, for package ID 0.
|
|
mFetcher.On("GetCPUBaseFrequency", 0).Return(uint64(0), errors.New("mock error")).Once()
|
|
|
|
// mock getting CPU base frequency metric, for package ID 1.
|
|
mFetcher.On("GetCPUBaseFrequency", 1).Return(baseFreq, nil).Once()
|
|
|
|
p := &PowerStat{
|
|
PackageMetrics: []packageMetricType{
|
|
// metrics which rely on msr
|
|
packageCPUBaseFrequency,
|
|
},
|
|
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.NoError(t, p.parseConfig())
|
|
|
|
p.addPackageMetrics(acc)
|
|
|
|
require.Len(t, acc.Errors, 1)
|
|
require.ErrorContains(t, acc.FirstError(), fmt.Sprintf("failed to get %q for package ID 0", packageCPUBaseFrequency))
|
|
require.Len(t, acc.GetTelegrafMetrics(), 1)
|
|
acc.AssertContainsTaggedFields(
|
|
t,
|
|
// measurement
|
|
"powerstat_package",
|
|
// fields
|
|
map[string]interface{}{
|
|
"cpu_base_frequency_mhz": baseFreq,
|
|
},
|
|
// tags
|
|
map[string]string{
|
|
"package_id": "1",
|
|
},
|
|
)
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("WithUncoreFreqMetric", func(t *testing.T) {
|
|
dieID := 0
|
|
|
|
initMin := 500.0
|
|
initMax := 2500.0
|
|
|
|
acc := &testutil.Accumulator{}
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting available package IDs.
|
|
mFetcher.On("GetPackageIDs").Return([]int{0, 1}).Once()
|
|
|
|
// mock getting die IDs for package ID.
|
|
mFetcher.On("GetPackageDieIDs", mock.AnythingOfType("int")).Return([]int{dieID}, nil).Twice()
|
|
|
|
// mock getting initial minimum uncore frequency limit.
|
|
mFetcher.On("GetInitialUncoreFrequencyMin", mock.AnythingOfType("int"), dieID).Return(initMin, nil).Twice()
|
|
|
|
// mock getting initial maximum uncore frequency limit.
|
|
mFetcher.On("GetInitialUncoreFrequencyMax", mock.AnythingOfType("int"), dieID).Return(initMax, nil).Twice()
|
|
|
|
// mock getting custom minimum uncore frequency limit.
|
|
mFetcher.On("GetCustomizedUncoreFrequencyMin", mock.AnythingOfType("int"), dieID).Return(600.0, nil).Twice()
|
|
|
|
// mock getting custom maximum uncore frequency limit.
|
|
mFetcher.On("GetCustomizedUncoreFrequencyMax", mock.AnythingOfType("int"), dieID).Return(0.0, errors.New("mock error")).Twice()
|
|
|
|
p := &PowerStat{
|
|
PackageMetrics: []packageMetricType{
|
|
packageUncoreFrequency,
|
|
},
|
|
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.NoError(t, p.parseConfig())
|
|
|
|
p.addPackageMetrics(acc)
|
|
|
|
require.Len(t, acc.Errors, 2)
|
|
require.ErrorContains(t, acc.Errors[0], fmt.Sprintf("failed to get current uncore frequency values for package ID 0 and die ID %v", dieID))
|
|
require.ErrorContains(t, acc.Errors[1], fmt.Sprintf("failed to get current uncore frequency values for package ID 1 and die ID %v", dieID))
|
|
require.Len(t, acc.GetTelegrafMetrics(), 2)
|
|
acc.AssertContainsTaggedFields(
|
|
t,
|
|
// measurement
|
|
"powerstat_package",
|
|
// fields
|
|
map[string]interface{}{
|
|
"uncore_frequency_limit_mhz_min": initMin,
|
|
"uncore_frequency_limit_mhz_max": initMax,
|
|
},
|
|
// tags
|
|
map[string]string{
|
|
"package_id": "0",
|
|
"type": "initial",
|
|
"die": strconv.Itoa(dieID),
|
|
},
|
|
)
|
|
acc.AssertContainsTaggedFields(
|
|
t,
|
|
// measurement
|
|
"powerstat_package",
|
|
// fields
|
|
map[string]interface{}{
|
|
"uncore_frequency_limit_mhz_min": initMin,
|
|
"uncore_frequency_limit_mhz_max": initMax,
|
|
},
|
|
// tags
|
|
map[string]string{
|
|
"package_id": "1",
|
|
"type": "initial",
|
|
"die": strconv.Itoa(dieID),
|
|
},
|
|
)
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
}
|
|
|
|
func TestAddPerPackageRaplMetrics(t *testing.T) {
|
|
t.Run("WithoutRaplMetrics", func(t *testing.T) {
|
|
packageID := 0
|
|
|
|
acc := &testutil.Accumulator{}
|
|
|
|
p := &PowerStat{
|
|
PackageMetrics: []packageMetricType{
|
|
// metrics which do not rely on rapl
|
|
packageCPUBaseFrequency,
|
|
packageUncoreFrequency,
|
|
packageTurboLimit,
|
|
},
|
|
}
|
|
|
|
p.addPerPackageRaplMetrics(acc, packageID)
|
|
|
|
require.Empty(t, acc.Errors)
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
})
|
|
|
|
t.Run("WithModuleNotInitializedError", func(t *testing.T) {
|
|
packageID := 0
|
|
|
|
acc := &testutil.Accumulator{}
|
|
|
|
raplNotInitErr := &ptel.ModuleNotInitializedError{Name: "rapl"}
|
|
mError := fmt.Errorf("mock error: %w", raplNotInitErr)
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting current dram power consumption metric.
|
|
mFetcher.On("GetCurrentDramPowerConsumptionWatts", packageID).Return(0.0, mError).Twice()
|
|
|
|
// mock getting package thermal design power metric.
|
|
mFetcher.On("GetPackageThermalDesignPowerWatts", packageID).Return(0.0, mError).Twice()
|
|
|
|
p := &PowerStat{
|
|
PackageMetrics: []packageMetricType{
|
|
// metrics which rely on rapl
|
|
packageCurrentDramPowerConsumption,
|
|
packageThermalDesignPower,
|
|
|
|
// metrics which do not rely on rapl
|
|
packageCPUBaseFrequency,
|
|
packageUncoreFrequency,
|
|
packageTurboLimit,
|
|
},
|
|
|
|
fetcher: mFetcher,
|
|
|
|
logOnce: map[string]struct{}{},
|
|
}
|
|
|
|
require.NoError(t, p.parseConfig())
|
|
|
|
// First call adds the error to the accumulator and logOnce map.
|
|
p.addPerPackageRaplMetrics(acc, packageID)
|
|
|
|
// Second call detects previous error in logOnce map and skips adding it to the accumulator.
|
|
p.addPerPackageRaplMetrics(acc, packageID)
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
require.Len(t, acc.Errors, 2)
|
|
require.ErrorContains(t, acc.Errors[0], fmt.Sprintf("failed to get %q: %v", packageCurrentDramPowerConsumption, raplNotInitErr))
|
|
require.ErrorContains(t, acc.Errors[1], fmt.Sprintf("failed to get %q: %v", packageThermalDesignPower, raplNotInitErr))
|
|
|
|
require.Len(t, p.logOnce, 2)
|
|
require.Contains(t, p.logOnce, "rapl_current_dram_power_consumption")
|
|
require.Contains(t, p.logOnce, "rapl_thermal_design_power")
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("WithErrors", func(t *testing.T) {
|
|
packageID := 0
|
|
currPower := 30.0
|
|
tdp := 80.0
|
|
|
|
acc := &testutil.Accumulator{}
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting current package power consumption metric.
|
|
mFetcher.On("GetCurrentPackagePowerConsumptionWatts", packageID).Return(currPower, nil).Once()
|
|
|
|
// mock getting current dram power consumption metric.
|
|
mFetcher.On("GetCurrentDramPowerConsumptionWatts", packageID).Return(0.0, errors.New("mock error")).Once()
|
|
|
|
// mock getting package thermal design power metric.
|
|
mFetcher.On("GetPackageThermalDesignPowerWatts", packageID).Return(tdp, nil).Once()
|
|
|
|
p := &PowerStat{
|
|
PackageMetrics: []packageMetricType{
|
|
// metrics which rely on rapl
|
|
packageCurrentPowerConsumption,
|
|
packageCurrentDramPowerConsumption,
|
|
packageThermalDesignPower,
|
|
|
|
// metrics which do not rely on rapl
|
|
packageCPUBaseFrequency,
|
|
packageUncoreFrequency,
|
|
packageTurboLimit,
|
|
},
|
|
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.NoError(t, p.parseConfig())
|
|
|
|
p.addPerPackageRaplMetrics(acc, packageID)
|
|
|
|
require.Len(t, acc.Errors, 1)
|
|
require.ErrorContains(t, acc.FirstError(), fmt.Sprintf("failed to get %q for package ID %v", packageCurrentDramPowerConsumption, packageID))
|
|
require.Len(t, acc.GetTelegrafMetrics(), 2)
|
|
acc.AssertContainsTaggedFields(
|
|
t,
|
|
// measurement
|
|
"powerstat_package",
|
|
// fields
|
|
map[string]interface{}{
|
|
"current_power_consumption_watts": currPower,
|
|
},
|
|
// tags
|
|
map[string]string{
|
|
"package_id": strconv.Itoa(packageID),
|
|
},
|
|
)
|
|
acc.AssertContainsTaggedFields(
|
|
t,
|
|
// measurement
|
|
"powerstat_package",
|
|
// fields
|
|
map[string]interface{}{
|
|
"thermal_design_power_watts": tdp,
|
|
},
|
|
// tags
|
|
map[string]string{
|
|
"package_id": strconv.Itoa(packageID),
|
|
},
|
|
)
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("WithoutErrors", func(t *testing.T) {
|
|
packageID := 0
|
|
currPower := 10.0
|
|
|
|
acc := &testutil.Accumulator{}
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting current dram power consumption metric.
|
|
mFetcher.On("GetCurrentDramPowerConsumptionWatts", packageID).Return(currPower, nil).Once()
|
|
|
|
p := &PowerStat{
|
|
PackageMetrics: []packageMetricType{
|
|
// metrics which rely on rapl
|
|
packageCurrentDramPowerConsumption,
|
|
|
|
// metrics which do not rely on rapl
|
|
packageCPUBaseFrequency,
|
|
packageUncoreFrequency,
|
|
packageTurboLimit,
|
|
},
|
|
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.NoError(t, p.parseConfig())
|
|
|
|
p.addPerPackageRaplMetrics(acc, packageID)
|
|
|
|
require.Empty(t, acc.Errors)
|
|
require.Len(t, acc.GetTelegrafMetrics(), 1)
|
|
acc.AssertContainsTaggedFields(
|
|
t,
|
|
// measurement
|
|
"powerstat_package",
|
|
// fields
|
|
map[string]interface{}{
|
|
"current_dram_power_consumption_watts": currPower,
|
|
},
|
|
// tags
|
|
map[string]string{
|
|
"package_id": strconv.Itoa(packageID),
|
|
},
|
|
)
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
}
|
|
|
|
func TestAddPerPackageMsrMetrics(t *testing.T) {
|
|
t.Run("WithoutMsrMetrics", func(t *testing.T) {
|
|
packageID := 0
|
|
|
|
acc := &testutil.Accumulator{}
|
|
|
|
p := &PowerStat{
|
|
PackageMetrics: []packageMetricType{
|
|
// metrics which do not rely on msr
|
|
packageCurrentPowerConsumption,
|
|
packageCurrentDramPowerConsumption,
|
|
packageThermalDesignPower,
|
|
},
|
|
}
|
|
|
|
p.addPerPackageMsrMetrics(acc, packageID)
|
|
|
|
require.Empty(t, acc.Errors)
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
})
|
|
|
|
t.Run("WithModuleNotInitializedError", func(t *testing.T) {
|
|
packageID := 0
|
|
|
|
acc := &testutil.Accumulator{}
|
|
|
|
msrNotInitErr := &ptel.ModuleNotInitializedError{Name: "msr"}
|
|
mError := fmt.Errorf("mock error: %w", msrNotInitErr)
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting CPU base frequency metric.
|
|
mFetcher.On("GetCPUBaseFrequency", packageID).Return(uint64(400), mError).Twice()
|
|
|
|
// mock getting max turbo frequency list.
|
|
mFetcher.On("GetMaxTurboFreqList", packageID).Return(nil, mError).Twice()
|
|
|
|
p := &PowerStat{
|
|
PackageMetrics: []packageMetricType{
|
|
// metrics which rely on msr
|
|
packageCPUBaseFrequency,
|
|
packageTurboLimit,
|
|
|
|
// metrics which do not rely on msr
|
|
packageCurrentPowerConsumption,
|
|
packageCurrentDramPowerConsumption,
|
|
packageThermalDesignPower,
|
|
},
|
|
|
|
fetcher: mFetcher,
|
|
|
|
logOnce: map[string]struct{}{},
|
|
}
|
|
|
|
require.NoError(t, p.parseConfig())
|
|
|
|
// First call adds the error to the accumulator and logOnce map.
|
|
p.addPerPackageMsrMetrics(acc, packageID)
|
|
|
|
// Second call detects previous error in logOnce map and skips adding it to the accumulator.
|
|
p.addPerPackageMsrMetrics(acc, packageID)
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
require.Len(t, acc.Errors, 2)
|
|
require.ErrorContains(t, acc.Errors[0], fmt.Sprintf("failed to get %q: %v", packageCPUBaseFrequency, msrNotInitErr))
|
|
require.ErrorContains(t, acc.Errors[1], fmt.Sprintf("failed to get %q: %v", packageTurboLimit, msrNotInitErr))
|
|
|
|
require.Len(t, p.logOnce, 2)
|
|
require.Contains(t, p.logOnce, "msr_cpu_base_frequency")
|
|
require.Contains(t, p.logOnce, "msr_max_turbo_frequency")
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("WithErrors", func(t *testing.T) {
|
|
packageID := 0
|
|
baseFreq := uint64(400)
|
|
|
|
acc := &testutil.Accumulator{}
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting CPU base frequency metric.
|
|
mFetcher.On("GetCPUBaseFrequency", packageID).Return(baseFreq, nil).Once()
|
|
|
|
// mock getting max turbo frequency list.
|
|
mFetcher.On("GetMaxTurboFreqList", packageID).Return(nil, errors.New("mock error")).Once()
|
|
|
|
p := &PowerStat{
|
|
PackageMetrics: []packageMetricType{
|
|
// metrics which rely on msr
|
|
packageCPUBaseFrequency,
|
|
packageTurboLimit,
|
|
|
|
// metrics which do not rely on msr
|
|
packageCurrentPowerConsumption,
|
|
packageCurrentDramPowerConsumption,
|
|
packageThermalDesignPower,
|
|
},
|
|
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.NoError(t, p.parseConfig())
|
|
|
|
p.addPerPackageMsrMetrics(acc, packageID)
|
|
|
|
require.Len(t, acc.Errors, 1)
|
|
require.ErrorContains(t, acc.FirstError(), fmt.Sprintf("failed to get %q for package ID %v", packageTurboLimit, packageID))
|
|
require.Len(t, acc.GetTelegrafMetrics(), 1)
|
|
acc.AssertContainsTaggedFields(
|
|
t,
|
|
// measurement
|
|
"powerstat_package",
|
|
// fields
|
|
map[string]interface{}{
|
|
"cpu_base_frequency_mhz": baseFreq,
|
|
},
|
|
// tags
|
|
map[string]string{
|
|
"package_id": strconv.Itoa(packageID),
|
|
},
|
|
)
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("WithoutErrors", func(t *testing.T) {
|
|
packageID := 0
|
|
baseFreq := uint64(400)
|
|
maxTurboFreqList := []ptel.MaxTurboFreq{
|
|
{
|
|
Value: 1000,
|
|
ActiveCores: 10,
|
|
},
|
|
}
|
|
|
|
acc := &testutil.Accumulator{}
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting CPU base frequency metric.
|
|
mFetcher.On("GetCPUBaseFrequency", packageID).Return(baseFreq, nil).Once()
|
|
|
|
// mock getting max turbo frequency list.
|
|
mFetcher.On("GetMaxTurboFreqList", packageID).Return(maxTurboFreqList, nil).Once()
|
|
|
|
p := &PowerStat{
|
|
PackageMetrics: []packageMetricType{
|
|
// metrics which rely on msr
|
|
packageCPUBaseFrequency,
|
|
packageTurboLimit,
|
|
|
|
// metrics which do not rely on msr
|
|
packageCurrentPowerConsumption,
|
|
packageCurrentDramPowerConsumption,
|
|
packageThermalDesignPower,
|
|
},
|
|
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.NoError(t, p.parseConfig())
|
|
|
|
p.addPerPackageMsrMetrics(acc, packageID)
|
|
|
|
require.Empty(t, acc.Errors)
|
|
require.Len(t, acc.GetTelegrafMetrics(), 2)
|
|
acc.AssertContainsTaggedFields(
|
|
t,
|
|
// measurement
|
|
"powerstat_package",
|
|
// fields
|
|
map[string]interface{}{
|
|
"cpu_base_frequency_mhz": baseFreq,
|
|
},
|
|
// tags
|
|
map[string]string{
|
|
"package_id": strconv.Itoa(packageID),
|
|
},
|
|
)
|
|
acc.AssertContainsTaggedFields(
|
|
t,
|
|
// measurement
|
|
"powerstat_package",
|
|
// fields
|
|
map[string]interface{}{
|
|
"cpu_base_frequency_mhz": baseFreq,
|
|
},
|
|
// tags
|
|
map[string]string{
|
|
"package_id": strconv.Itoa(packageID),
|
|
},
|
|
)
|
|
acc.AssertContainsTaggedFields(
|
|
t,
|
|
// measurement
|
|
"powerstat_package",
|
|
// fields
|
|
map[string]interface{}{
|
|
"max_turbo_frequency_mhz": maxTurboFreqList[0].Value,
|
|
},
|
|
// tags
|
|
map[string]string{
|
|
"package_id": strconv.Itoa(packageID),
|
|
"active_cores": strconv.Itoa(int(maxTurboFreqList[0].ActiveCores)),
|
|
},
|
|
)
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
}
|
|
|
|
func TestAddCPUFrequency(t *testing.T) {
|
|
t.Run("FailedToGetMetric", func(t *testing.T) {
|
|
acc := &testutil.Accumulator{}
|
|
|
|
cpuID := 0
|
|
coreID := 1
|
|
packageID := 0
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting CPU frequency metric.
|
|
mFetcher.On("GetCPUFrequency", cpuID).Return(0.0, errors.New("mock error")).Once()
|
|
|
|
p := &PowerStat{
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
|
|
p.addCPUFrequency(acc, cpuID, coreID, packageID)
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
require.Len(t, acc.Errors, 1)
|
|
require.ErrorContains(t, acc.FirstError(), fmt.Sprintf("failed to get %q for CPU ID %v", cpuFrequency, cpuID))
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("Rounded", func(t *testing.T) {
|
|
cpuID := 0
|
|
coreID := 1
|
|
packageID := 0
|
|
cpuFreq := 800.001
|
|
cpuFreqExp := 800.0
|
|
|
|
acc := &testutil.Accumulator{}
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting CPU frequency metric.
|
|
mFetcher.On("GetCPUFrequency", cpuID).Return(cpuFreq, nil).Once()
|
|
|
|
p := &PowerStat{
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
|
|
p.addCPUFrequency(acc, cpuID, coreID, packageID)
|
|
|
|
require.Len(t, acc.GetTelegrafMetrics(), 1)
|
|
require.True(t, acc.HasFloatField("powerstat_core", "cpu_frequency_mhz"))
|
|
acc.AssertContainsTaggedFields(
|
|
t,
|
|
// measurement
|
|
"powerstat_core",
|
|
// fields
|
|
map[string]interface{}{
|
|
"cpu_frequency_mhz": cpuFreqExp,
|
|
},
|
|
// tags
|
|
map[string]string{
|
|
"cpu_id": strconv.Itoa(cpuID),
|
|
"core_id": strconv.Itoa(coreID),
|
|
"package_id": strconv.Itoa(packageID),
|
|
},
|
|
)
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
}
|
|
|
|
func TestAddCPUTemperature(t *testing.T) {
|
|
t.Run("FailedToGetMetric", func(t *testing.T) {
|
|
acc := &testutil.Accumulator{}
|
|
|
|
cpuID := 0
|
|
coreID := 1
|
|
packageID := 0
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting CPU temperature metric.
|
|
mFetcher.On("GetCPUTemperature", cpuID).Return(uint64(0), errors.New("mock error")).Once()
|
|
|
|
p := &PowerStat{
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
|
|
p.addCPUTemperature(acc, cpuID, coreID, packageID)
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
require.Len(t, acc.Errors, 1)
|
|
require.ErrorContains(t, acc.FirstError(), fmt.Sprintf("failed to get %q for CPU ID %v", cpuTemperature, cpuID))
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("Ok", func(t *testing.T) {
|
|
acc := &testutil.Accumulator{}
|
|
|
|
cpuID := 0
|
|
coreID := 1
|
|
packageID := 0
|
|
cpuTemp := uint64(25)
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting cpu temperature metric.
|
|
mFetcher.On("GetCPUTemperature", cpuID).Return(cpuTemp, nil).Once()
|
|
|
|
p := &PowerStat{
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
|
|
p.addCPUTemperature(acc, cpuID, coreID, packageID)
|
|
|
|
require.Len(t, acc.GetTelegrafMetrics(), 1)
|
|
require.True(t, acc.HasUIntField("powerstat_core", "cpu_temperature_celsius"))
|
|
acc.AssertContainsTaggedFields(
|
|
t,
|
|
// measurement
|
|
"powerstat_core",
|
|
// fields
|
|
map[string]interface{}{
|
|
"cpu_temperature_celsius": cpuTemp,
|
|
},
|
|
// tags
|
|
map[string]string{
|
|
"cpu_id": strconv.Itoa(cpuID),
|
|
"core_id": strconv.Itoa(coreID),
|
|
"package_id": strconv.Itoa(packageID),
|
|
},
|
|
)
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
}
|
|
|
|
func TestAddCPUC0StateResidency(t *testing.T) {
|
|
t.Run("FailedToGetMetric", func(t *testing.T) {
|
|
acc := &testutil.Accumulator{}
|
|
|
|
cpuID := 0
|
|
coreID := 1
|
|
packageID := 0
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting CPU C0 state residency metric.
|
|
mFetcher.On("GetCPUC0StateResidency", cpuID).Return(0.0, errors.New("mock error")).Once()
|
|
|
|
p := &PowerStat{
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
|
|
p.addCPUC0StateResidency(acc, cpuID, coreID, packageID)
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
require.Len(t, acc.Errors, 1)
|
|
require.ErrorContains(t, acc.FirstError(), fmt.Sprintf("failed to get %q for CPU ID %v", cpuC0StateResidency, cpuID))
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("Rounded", func(t *testing.T) {
|
|
cpuID := 0
|
|
coreID := 1
|
|
packageID := 0
|
|
c0State := 10.1199
|
|
c0StateExp := 10.12
|
|
|
|
acc := &testutil.Accumulator{}
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting CPU C0 state residency metric.
|
|
mFetcher.On("GetCPUC0StateResidency", cpuID).Return(c0State, nil).Once()
|
|
|
|
p := &PowerStat{
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
|
|
p.addCPUC0StateResidency(acc, cpuID, coreID, packageID)
|
|
|
|
require.Len(t, acc.GetTelegrafMetrics(), 1)
|
|
require.True(t, acc.HasFloatField("powerstat_core", "cpu_c0_state_residency_percent"))
|
|
acc.AssertContainsTaggedFields(
|
|
t,
|
|
// measurement
|
|
"powerstat_core",
|
|
// fields
|
|
map[string]interface{}{
|
|
"cpu_c0_state_residency_percent": c0StateExp,
|
|
},
|
|
// tags
|
|
map[string]string{
|
|
"cpu_id": strconv.Itoa(cpuID),
|
|
"core_id": strconv.Itoa(coreID),
|
|
"package_id": strconv.Itoa(packageID),
|
|
},
|
|
)
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
}
|
|
|
|
func TestAddCPUC1StateResidency(t *testing.T) {
|
|
t.Run("FailedToGetMetric", func(t *testing.T) {
|
|
acc := &testutil.Accumulator{}
|
|
|
|
cpuID := 0
|
|
coreID := 1
|
|
packageID := 0
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting CPU C1 state residency metric.
|
|
mFetcher.On("GetCPUC1StateResidency", cpuID).Return(0.0, errors.New("mock error")).Once()
|
|
|
|
p := &PowerStat{
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
|
|
p.addCPUC1StateResidency(acc, cpuID, coreID, packageID)
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
require.Len(t, acc.Errors, 1)
|
|
require.ErrorContains(t, acc.FirstError(), fmt.Sprintf("failed to get %q for CPU ID %v", cpuC1StateResidency, cpuID))
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("Rounded", func(t *testing.T) {
|
|
acc := &testutil.Accumulator{}
|
|
|
|
cpuID := 0
|
|
coreID := 1
|
|
packageID := 0
|
|
c1State := 10.1144
|
|
c1StateExp := 10.11
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting CPU C1 state residency metric.
|
|
mFetcher.On("GetCPUC1StateResidency", cpuID).Return(c1State, nil).Once()
|
|
|
|
p := &PowerStat{
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
|
|
p.addCPUC1StateResidency(acc, cpuID, coreID, packageID)
|
|
|
|
require.Len(t, acc.GetTelegrafMetrics(), 1)
|
|
require.True(t, acc.HasFloatField("powerstat_core", "cpu_c1_state_residency_percent"))
|
|
acc.AssertContainsTaggedFields(
|
|
t,
|
|
// measurement
|
|
"powerstat_core",
|
|
// fields
|
|
map[string]interface{}{
|
|
"cpu_c1_state_residency_percent": c1StateExp,
|
|
},
|
|
// tags
|
|
map[string]string{
|
|
"cpu_id": strconv.Itoa(cpuID),
|
|
"core_id": strconv.Itoa(coreID),
|
|
"package_id": strconv.Itoa(packageID),
|
|
},
|
|
)
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
}
|
|
|
|
func TestAddCPUC3StateResidency(t *testing.T) {
|
|
t.Run("FailedToGetMetric", func(t *testing.T) {
|
|
acc := &testutil.Accumulator{}
|
|
|
|
cpuID := 0
|
|
coreID := 1
|
|
packageID := 0
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting CPU C3 state residency metric.
|
|
mFetcher.On("GetCPUC3StateResidency", cpuID).Return(0.0, errors.New("mock error")).Once()
|
|
|
|
p := &PowerStat{
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
|
|
p.addCPUC3StateResidency(acc, cpuID, coreID, packageID)
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
require.Len(t, acc.Errors, 1)
|
|
require.ErrorContains(t, acc.FirstError(), fmt.Sprintf("failed to get %q for CPU ID %v", cpuC3StateResidency, cpuID))
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("Rounded", func(t *testing.T) {
|
|
acc := &testutil.Accumulator{}
|
|
|
|
cpuID := 0
|
|
coreID := 1
|
|
packageID := 0
|
|
c3State := 20.1178
|
|
c3StateExp := 20.12
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting CPU C3 state residency metric.
|
|
mFetcher.On("GetCPUC3StateResidency", cpuID).Return(c3State, nil).Once()
|
|
|
|
p := &PowerStat{
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
|
|
p.addCPUC3StateResidency(acc, cpuID, coreID, packageID)
|
|
|
|
require.Len(t, acc.GetTelegrafMetrics(), 1)
|
|
require.True(t, acc.HasFloatField("powerstat_core", "cpu_c3_state_residency_percent"))
|
|
acc.AssertContainsTaggedFields(
|
|
t,
|
|
// measurement
|
|
"powerstat_core",
|
|
// fields
|
|
map[string]interface{}{
|
|
"cpu_c3_state_residency_percent": c3StateExp,
|
|
},
|
|
// tags
|
|
map[string]string{
|
|
"cpu_id": strconv.Itoa(cpuID),
|
|
"core_id": strconv.Itoa(coreID),
|
|
"package_id": strconv.Itoa(packageID),
|
|
},
|
|
)
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
}
|
|
|
|
func TestAddCPUC6StateResidency(t *testing.T) {
|
|
t.Run("FailedToGetMetric", func(t *testing.T) {
|
|
acc := &testutil.Accumulator{}
|
|
|
|
cpuID := 0
|
|
coreID := 1
|
|
packageID := 0
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting CPU C6 state residency metric.
|
|
mFetcher.On("GetCPUC6StateResidency", cpuID).Return(0.0, errors.New("mock error")).Once()
|
|
|
|
p := &PowerStat{
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
|
|
p.addCPUC6StateResidency(acc, cpuID, coreID, packageID)
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
require.Len(t, acc.Errors, 1)
|
|
require.ErrorContains(t, acc.FirstError(), fmt.Sprintf("failed to get %q for CPU ID %v", cpuC6StateResidency, cpuID))
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("Rounded", func(t *testing.T) {
|
|
acc := &testutil.Accumulator{}
|
|
|
|
cpuID := 0
|
|
coreID := 1
|
|
packageID := 0
|
|
c6State := 9.115
|
|
c6StateExp := 9.12
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting CPU C6 state residency metric.
|
|
mFetcher.On("GetCPUC6StateResidency", cpuID).Return(c6State, nil).Once()
|
|
|
|
p := &PowerStat{
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
|
|
p.addCPUC6StateResidency(acc, cpuID, coreID, packageID)
|
|
|
|
require.Len(t, acc.GetTelegrafMetrics(), 1)
|
|
require.True(t, acc.HasFloatField("powerstat_core", "cpu_c6_state_residency_percent"))
|
|
acc.AssertContainsTaggedFields(
|
|
t,
|
|
// measurement
|
|
"powerstat_core",
|
|
// fields
|
|
map[string]interface{}{
|
|
"cpu_c6_state_residency_percent": c6StateExp,
|
|
},
|
|
// tags
|
|
map[string]string{
|
|
"cpu_id": strconv.Itoa(cpuID),
|
|
"core_id": strconv.Itoa(coreID),
|
|
"package_id": strconv.Itoa(packageID),
|
|
},
|
|
)
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
}
|
|
|
|
func TestAddCPUC7StateResidency(t *testing.T) {
|
|
t.Run("FailedToGetMetric", func(t *testing.T) {
|
|
acc := &testutil.Accumulator{}
|
|
|
|
cpuID := 0
|
|
coreID := 1
|
|
packageID := 0
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting CPU C7 state residency metric.
|
|
mFetcher.On("GetCPUC7StateResidency", cpuID).Return(0.0, errors.New("mock error")).Once()
|
|
|
|
p := &PowerStat{
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
|
|
p.addCPUC7StateResidency(acc, cpuID, coreID, packageID)
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
require.Len(t, acc.Errors, 1)
|
|
require.ErrorContains(t, acc.FirstError(), fmt.Sprintf("failed to get %q for CPU ID %v", cpuC7StateResidency, cpuID))
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("Rounded", func(t *testing.T) {
|
|
acc := &testutil.Accumulator{}
|
|
|
|
cpuID := 0
|
|
coreID := 1
|
|
packageID := 0
|
|
c7State := 9.1149
|
|
c7StateExp := 9.11
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting CPU C7 state residency metric.
|
|
mFetcher.On("GetCPUC7StateResidency", cpuID).Return(c7State, nil).Once()
|
|
|
|
p := &PowerStat{
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
|
|
p.addCPUC7StateResidency(acc, cpuID, coreID, packageID)
|
|
|
|
require.Len(t, acc.GetTelegrafMetrics(), 1)
|
|
require.True(t, acc.HasFloatField("powerstat_core", "cpu_c7_state_residency_percent"))
|
|
acc.AssertContainsTaggedFields(
|
|
t,
|
|
// measurement
|
|
"powerstat_core",
|
|
// fields
|
|
map[string]interface{}{
|
|
"cpu_c7_state_residency_percent": c7StateExp,
|
|
},
|
|
// tags
|
|
map[string]string{
|
|
"cpu_id": strconv.Itoa(cpuID),
|
|
"core_id": strconv.Itoa(coreID),
|
|
"package_id": strconv.Itoa(packageID),
|
|
},
|
|
)
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
}
|
|
|
|
func TestAddCPUBusyFrequency(t *testing.T) {
|
|
t.Run("FailedToGetMetric", func(t *testing.T) {
|
|
acc := &testutil.Accumulator{}
|
|
|
|
cpuID := 0
|
|
coreID := 1
|
|
packageID := 0
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting CPU busy frequency metric.
|
|
mFetcher.On("GetCPUBusyFrequencyMhz", cpuID).Return(0.0, errors.New("mock error")).Once()
|
|
|
|
p := &PowerStat{
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
|
|
p.addCPUBusyFrequency(acc, cpuID, coreID, packageID)
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
require.Len(t, acc.Errors, 1)
|
|
require.ErrorContains(t, acc.FirstError(), fmt.Sprintf("failed to get %q for CPU ID %v", cpuBusyFrequency, cpuID))
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("Rounded", func(t *testing.T) {
|
|
acc := &testutil.Accumulator{}
|
|
|
|
cpuID := 0
|
|
coreID := 1
|
|
packageID := 0
|
|
cpuBusyFreq := 800.119
|
|
cpuBusyFreqExp := 800.12
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting CPU busy frequency metric.
|
|
mFetcher.On("GetCPUBusyFrequencyMhz", cpuID).Return(cpuBusyFreq, nil).Once()
|
|
|
|
p := &PowerStat{
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
|
|
p.addCPUBusyFrequency(acc, cpuID, coreID, packageID)
|
|
|
|
require.Len(t, acc.GetTelegrafMetrics(), 1)
|
|
require.True(t, acc.HasFloatField("powerstat_core", "cpu_busy_frequency_mhz"))
|
|
acc.AssertContainsTaggedFields(
|
|
t,
|
|
// measurement
|
|
"powerstat_core",
|
|
// fields
|
|
map[string]interface{}{
|
|
"cpu_busy_frequency_mhz": cpuBusyFreqExp,
|
|
},
|
|
// tags
|
|
map[string]string{
|
|
"cpu_id": strconv.Itoa(cpuID),
|
|
"core_id": strconv.Itoa(coreID),
|
|
"package_id": strconv.Itoa(packageID),
|
|
},
|
|
)
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
}
|
|
|
|
func TestAddCPUBusyCycles(t *testing.T) {
|
|
t.Run("FailedToGetMetric", func(t *testing.T) {
|
|
acc := &testutil.Accumulator{}
|
|
|
|
cpuID := 0
|
|
coreID := 1
|
|
packageID := 0
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting CPU busy cycles metric.
|
|
mFetcher.On("GetCPUC0StateResidency", cpuID).Return(0.0, errors.New("mock error")).Once()
|
|
|
|
p := &PowerStat{
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
|
|
p.addCPUBusyCycles(acc, cpuID, coreID, packageID)
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
require.Len(t, acc.Errors, 1)
|
|
require.ErrorContains(t, acc.FirstError(), fmt.Sprintf("failed to get %q for CPU ID %v", cpuBusyCycles, cpuID))
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("Rounded", func(t *testing.T) {
|
|
acc := &testutil.Accumulator{}
|
|
|
|
cpuID := 0
|
|
coreID := 1
|
|
packageID := 0
|
|
cpuBusyCycles := 10.1149
|
|
cpuBusyCyclesExp := 10.11
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting CPU C0 state residency metric.
|
|
mFetcher.On("GetCPUC0StateResidency", cpuID).Return(cpuBusyCycles, nil).Once()
|
|
|
|
p := &PowerStat{
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
|
|
p.addCPUBusyCycles(acc, cpuID, coreID, packageID)
|
|
|
|
require.Len(t, acc.GetTelegrafMetrics(), 1)
|
|
require.True(t, acc.HasFloatField("powerstat_core", "cpu_busy_cycles_percent"))
|
|
acc.AssertContainsTaggedFields(
|
|
t,
|
|
// measurement
|
|
"powerstat_core",
|
|
// fields
|
|
map[string]interface{}{
|
|
"cpu_busy_cycles_percent": cpuBusyCyclesExp,
|
|
},
|
|
// tags
|
|
map[string]string{
|
|
"cpu_id": strconv.Itoa(cpuID),
|
|
"core_id": strconv.Itoa(coreID),
|
|
"package_id": strconv.Itoa(packageID),
|
|
},
|
|
)
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
}
|
|
|
|
func TestAddCPUC0SubstateC01Percent(t *testing.T) {
|
|
t.Run("FailedToGetMetric", func(t *testing.T) {
|
|
acc := &testutil.Accumulator{}
|
|
|
|
cpuID := 0
|
|
coreID := 1
|
|
packageID := 0
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting CPU C01 metric.
|
|
mFetcher.On("GetCPUC0SubstateC01Percent", cpuID).Return(0.0, errors.New("mock error")).Once()
|
|
|
|
p := &PowerStat{
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
|
|
p.addCPUC0SubstateC01Percent(acc, cpuID, coreID, packageID)
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
require.Len(t, acc.Errors, 1)
|
|
require.ErrorContains(t, acc.FirstError(), fmt.Sprintf("failed to get %q for CPU ID %v", cpuC0SubstateC01Percent, cpuID))
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("Rounded", func(t *testing.T) {
|
|
acc := &testutil.Accumulator{}
|
|
|
|
cpuID := 0
|
|
coreID := 1
|
|
packageID := 0
|
|
c01Percent := 5.9229
|
|
c01PercentExp := 5.92
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting CPU C01 metric.
|
|
mFetcher.On("GetCPUC0SubstateC01Percent", cpuID).Return(c01Percent, nil).Once()
|
|
|
|
p := &PowerStat{
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
|
|
p.addCPUC0SubstateC01Percent(acc, cpuID, coreID, packageID)
|
|
|
|
require.Len(t, acc.GetTelegrafMetrics(), 1)
|
|
require.True(t, acc.HasFloatField("powerstat_core", "cpu_c0_substate_c01_percent"))
|
|
acc.AssertContainsTaggedFields(
|
|
t,
|
|
// measurement
|
|
"powerstat_core",
|
|
// fields
|
|
map[string]interface{}{
|
|
"cpu_c0_substate_c01_percent": c01PercentExp,
|
|
},
|
|
// tags
|
|
map[string]string{
|
|
"cpu_id": strconv.Itoa(cpuID),
|
|
"core_id": strconv.Itoa(coreID),
|
|
"package_id": strconv.Itoa(packageID),
|
|
},
|
|
)
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
}
|
|
|
|
func TestAddCPUC0SubstateC02Percent(t *testing.T) {
|
|
t.Run("FailedToGetMetric", func(t *testing.T) {
|
|
acc := &testutil.Accumulator{}
|
|
|
|
cpuID := 0
|
|
coreID := 1
|
|
packageID := 0
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting CPU C02 metric.
|
|
mFetcher.On("GetCPUC0SubstateC02Percent", cpuID).Return(0.0, errors.New("mock error")).Once()
|
|
|
|
p := &PowerStat{
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
|
|
p.addCPUC0SubstateC02Percent(acc, cpuID, coreID, packageID)
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
require.Len(t, acc.Errors, 1)
|
|
require.ErrorContains(t, acc.FirstError(), fmt.Sprintf("failed to get %q for CPU ID %v", cpuC0SubstateC02Percent, cpuID))
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("Rounded", func(t *testing.T) {
|
|
acc := &testutil.Accumulator{}
|
|
|
|
cpuID := 0
|
|
coreID := 1
|
|
packageID := 0
|
|
c02Percent := 0.001
|
|
c02PercentExp := 0.0
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting CPU C02 metric.
|
|
mFetcher.On("GetCPUC0SubstateC02Percent", cpuID).Return(c02Percent, nil).Once()
|
|
|
|
p := &PowerStat{
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
|
|
p.addCPUC0SubstateC02Percent(acc, cpuID, coreID, packageID)
|
|
|
|
require.Len(t, acc.GetTelegrafMetrics(), 1)
|
|
require.True(t, acc.HasFloatField("powerstat_core", "cpu_c0_substate_c02_percent"))
|
|
acc.AssertContainsTaggedFields(
|
|
t,
|
|
// measurement
|
|
"powerstat_core",
|
|
// fields
|
|
map[string]interface{}{
|
|
"cpu_c0_substate_c02_percent": c02PercentExp,
|
|
},
|
|
// tags
|
|
map[string]string{
|
|
"cpu_id": strconv.Itoa(cpuID),
|
|
"core_id": strconv.Itoa(coreID),
|
|
"package_id": strconv.Itoa(packageID),
|
|
},
|
|
)
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
}
|
|
|
|
func TestAddCPUC0SubstateC0WaitPercent(t *testing.T) {
|
|
t.Run("FailedToGetMetric", func(t *testing.T) {
|
|
acc := &testutil.Accumulator{}
|
|
|
|
cpuID := 0
|
|
coreID := 1
|
|
packageID := 0
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting CPU C0Wait metric.
|
|
mFetcher.On("GetCPUC0SubstateC0WaitPercent", cpuID).Return(0.0, errors.New("mock error")).Once()
|
|
|
|
p := &PowerStat{
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
|
|
p.addCPUC0SubstateC0WaitPercent(acc, cpuID, coreID, packageID)
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics(), 0)
|
|
require.Len(t, acc.Errors, 1)
|
|
require.ErrorContains(t, acc.FirstError(), fmt.Sprintf("failed to get %q for CPU ID %v", cpuC0SubstateC0WaitPercent, cpuID))
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("Rounded", func(t *testing.T) {
|
|
acc := &testutil.Accumulator{}
|
|
|
|
cpuID := 0
|
|
coreID := 1
|
|
packageID := 0
|
|
c0WaitPercent := 0.995
|
|
c0WaitPercentExp := 1.0
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting CPU C0Wait metric.
|
|
mFetcher.On("GetCPUC0SubstateC0WaitPercent", cpuID).Return(c0WaitPercent, nil).Once()
|
|
|
|
p := &PowerStat{
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
|
|
p.addCPUC0SubstateC0WaitPercent(acc, cpuID, coreID, packageID)
|
|
|
|
require.Len(t, acc.GetTelegrafMetrics(), 1)
|
|
require.True(t, acc.HasFloatField("powerstat_core", "cpu_c0_substate_c0_wait_percent"))
|
|
acc.AssertContainsTaggedFields(
|
|
t,
|
|
// measurement
|
|
"powerstat_core",
|
|
// fields
|
|
map[string]interface{}{
|
|
"cpu_c0_substate_c0_wait_percent": c0WaitPercentExp,
|
|
},
|
|
// tags
|
|
map[string]string{
|
|
"cpu_id": strconv.Itoa(cpuID),
|
|
"core_id": strconv.Itoa(coreID),
|
|
"package_id": strconv.Itoa(packageID),
|
|
},
|
|
)
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
}
|
|
|
|
func TestAddCurrentPackagePowerConsumption(t *testing.T) {
|
|
t.Run("FailedToGetMetric", func(t *testing.T) {
|
|
acc := &testutil.Accumulator{}
|
|
|
|
packageID := 0
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting current package power consumption metric.
|
|
mFetcher.On("GetCurrentPackagePowerConsumptionWatts", packageID).Return(0.0, errors.New("mock error")).Once()
|
|
|
|
p := &PowerStat{
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
|
|
p.addCurrentPackagePower(acc, packageID)
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
require.Len(t, acc.Errors, 1)
|
|
require.ErrorContains(t, acc.FirstError(), fmt.Sprintf("failed to get %q for package ID %v", packageCurrentPowerConsumption, packageID))
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("Rounded", func(t *testing.T) {
|
|
acc := &testutil.Accumulator{}
|
|
|
|
packageID := 0
|
|
currPower := float64(30.1999)
|
|
currPowerRounded := float64(30.2)
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting current package power consumption metric.
|
|
mFetcher.On("GetCurrentPackagePowerConsumptionWatts", packageID).Return(currPower, nil).Once()
|
|
|
|
p := &PowerStat{
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
|
|
p.addCurrentPackagePower(acc, packageID)
|
|
|
|
require.Len(t, acc.GetTelegrafMetrics(), 1)
|
|
require.True(t, acc.HasFloatField("powerstat_package", "current_power_consumption_watts"))
|
|
acc.AssertContainsTaggedFields(
|
|
t,
|
|
// measurement
|
|
"powerstat_package",
|
|
// fields
|
|
map[string]interface{}{
|
|
"current_power_consumption_watts": currPowerRounded,
|
|
},
|
|
// tags
|
|
map[string]string{
|
|
"package_id": strconv.Itoa(packageID),
|
|
},
|
|
)
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
}
|
|
|
|
func TestAddCurrentDramPowerConsumption(t *testing.T) {
|
|
t.Run("FailedToGetMetric", func(t *testing.T) {
|
|
acc := &testutil.Accumulator{}
|
|
|
|
packageID := 0
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting current dram power consumption metric.
|
|
mFetcher.On("GetCurrentDramPowerConsumptionWatts", packageID).Return(0.0, errors.New("mock error")).Once()
|
|
|
|
p := &PowerStat{
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
|
|
p.addCurrentDramPower(acc, packageID)
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
require.Len(t, acc.Errors, 1)
|
|
require.ErrorContains(t, acc.FirstError(), fmt.Sprintf("failed to get %q for package ID %v", packageCurrentDramPowerConsumption, packageID))
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("Rounded", func(t *testing.T) {
|
|
acc := &testutil.Accumulator{}
|
|
|
|
packageID := 0
|
|
currPower := float64(30.8235)
|
|
currPowerRounded := float64(30.82)
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting current dram power consumption metric.
|
|
mFetcher.On("GetCurrentDramPowerConsumptionWatts", packageID).Return(currPower, nil).Once()
|
|
|
|
p := &PowerStat{
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
|
|
p.addCurrentDramPower(acc, packageID)
|
|
|
|
require.Len(t, acc.GetTelegrafMetrics(), 1)
|
|
require.True(t, acc.HasFloatField("powerstat_package", "current_dram_power_consumption_watts"))
|
|
acc.AssertContainsTaggedFields(
|
|
t,
|
|
// measurement
|
|
"powerstat_package",
|
|
// fields
|
|
map[string]interface{}{
|
|
"current_dram_power_consumption_watts": currPowerRounded,
|
|
},
|
|
// tags
|
|
map[string]string{
|
|
"package_id": strconv.Itoa(packageID),
|
|
},
|
|
)
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
}
|
|
|
|
func TestAddThermalDesignPower(t *testing.T) {
|
|
t.Run("FailedToGetMetric", func(t *testing.T) {
|
|
acc := &testutil.Accumulator{}
|
|
|
|
packageID := 0
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting package thermal design power metric.
|
|
mFetcher.On("GetPackageThermalDesignPowerWatts", packageID).Return(0.0, errors.New("mock error")).Once()
|
|
|
|
p := &PowerStat{
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
|
|
p.addThermalDesignPower(acc, packageID)
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
require.Len(t, acc.Errors, 1)
|
|
require.ErrorContains(t, acc.FirstError(), fmt.Sprintf("failed to get %q for package ID %v", packageThermalDesignPower, packageID))
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("Rounded", func(t *testing.T) {
|
|
acc := &testutil.Accumulator{}
|
|
|
|
packageID := 0
|
|
tdp := float64(80.1999)
|
|
tdpRounded := float64(80.2)
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting package thermal design power metric.
|
|
mFetcher.On("GetPackageThermalDesignPowerWatts", packageID).Return(tdp, nil).Once()
|
|
|
|
p := &PowerStat{
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
|
|
p.addThermalDesignPower(acc, packageID)
|
|
|
|
require.Len(t, acc.GetTelegrafMetrics(), 1)
|
|
require.True(t, acc.HasFloatField("powerstat_package", "thermal_design_power_watts"))
|
|
acc.AssertContainsTaggedFields(
|
|
t,
|
|
// measurement
|
|
"powerstat_package",
|
|
// fields
|
|
map[string]interface{}{
|
|
"thermal_design_power_watts": tdpRounded,
|
|
},
|
|
// tags
|
|
map[string]string{
|
|
"package_id": strconv.Itoa(packageID),
|
|
},
|
|
)
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
}
|
|
|
|
func TestAddCPUBaseFrequency(t *testing.T) {
|
|
t.Run("FailedToGetMetric", func(t *testing.T) {
|
|
acc := &testutil.Accumulator{}
|
|
|
|
packageID := 0
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting CPU base frequency metric.
|
|
mFetcher.On("GetCPUBaseFrequency", packageID).Return(uint64(0), errors.New("mock error")).Once()
|
|
|
|
p := &PowerStat{
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
|
|
p.addCPUBaseFrequency(acc, packageID)
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
require.Len(t, acc.Errors, 1)
|
|
require.ErrorContains(t, acc.FirstError(), fmt.Sprintf("failed to get %q for package ID %v", packageCPUBaseFrequency, packageID))
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("Ok", func(t *testing.T) {
|
|
acc := &testutil.Accumulator{}
|
|
|
|
packageID := 0
|
|
baseFreq := uint64(700)
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting CPU base frequency metric.
|
|
mFetcher.On("GetCPUBaseFrequency", packageID).Return(baseFreq, nil).Once()
|
|
|
|
p := &PowerStat{
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
|
|
p.addCPUBaseFrequency(acc, packageID)
|
|
|
|
require.Len(t, acc.GetTelegrafMetrics(), 1)
|
|
require.True(t, acc.HasUIntField("powerstat_package", "cpu_base_frequency_mhz"))
|
|
acc.AssertContainsTaggedFields(
|
|
t,
|
|
// measurement
|
|
"powerstat_package",
|
|
// fields
|
|
map[string]interface{}{
|
|
"cpu_base_frequency_mhz": baseFreq,
|
|
},
|
|
// tags
|
|
map[string]string{
|
|
"package_id": strconv.Itoa(packageID),
|
|
},
|
|
)
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
}
|
|
|
|
func TestAddUncoreFrequency(t *testing.T) {
|
|
packageID, dieID := 1, 0
|
|
|
|
t.Run("FailedToGetDieIDs", func(t *testing.T) {
|
|
acc := &testutil.Accumulator{}
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting die IDs for package ID.
|
|
mFetcher.On("GetPackageDieIDs", packageID).Return(nil, errors.New("mock error")).Once()
|
|
|
|
p := &PowerStat{
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
|
|
p.addUncoreFrequency(acc, packageID)
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
require.Len(t, acc.Errors, 1)
|
|
require.ErrorContains(
|
|
t,
|
|
acc.FirstError(),
|
|
fmt.Sprintf("failed to get die IDs for package ID %v", packageID),
|
|
)
|
|
})
|
|
|
|
t.Run("FailedToGetInitialLimits", func(t *testing.T) {
|
|
currMin := 500.0
|
|
currMax := 2500.0
|
|
curr := 1000.0
|
|
|
|
acc := &testutil.Accumulator{}
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting die IDs for package ID.
|
|
mFetcher.On("GetPackageDieIDs", packageID).Return([]int{dieID}, nil).Once()
|
|
|
|
// mock getting initial minimum uncore frequency limit.
|
|
mFetcher.On("GetInitialUncoreFrequencyMin", packageID, dieID).Return(800.0, nil).Once()
|
|
|
|
// mock getting initial maximum uncore frequency limit.
|
|
mFetcher.On("GetInitialUncoreFrequencyMax", packageID, dieID).Return(0.0, errors.New("mock error")).Once()
|
|
|
|
// mock getting custom minimum uncore frequency limit.
|
|
mFetcher.On("GetCustomizedUncoreFrequencyMin", packageID, dieID).Return(currMin, nil).Once()
|
|
|
|
// mock getting custom maximum uncore frequency limit.
|
|
mFetcher.On("GetCustomizedUncoreFrequencyMax", packageID, dieID).Return(currMax, nil).Once()
|
|
|
|
// mock getting current uncore frequency value.
|
|
mFetcher.On("GetCurrentUncoreFrequency", packageID, dieID).Return(curr, nil).Once()
|
|
|
|
p := &PowerStat{
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
|
|
p.addUncoreFrequency(acc, packageID)
|
|
|
|
require.Len(t, acc.Errors, 1)
|
|
require.ErrorContains(
|
|
t,
|
|
acc.FirstError(),
|
|
fmt.Sprintf("failed to get initial uncore frequency limits for package ID %v and die ID %v", packageID, dieID),
|
|
)
|
|
require.Len(t, acc.GetTelegrafMetrics(), 1)
|
|
acc.AssertContainsTaggedFields(
|
|
t,
|
|
// measurement
|
|
"powerstat_package",
|
|
// fields
|
|
map[string]interface{}{
|
|
"uncore_frequency_limit_mhz_min": currMin,
|
|
"uncore_frequency_limit_mhz_max": currMax,
|
|
"uncore_frequency_mhz_cur": uint64(curr),
|
|
},
|
|
// tags
|
|
map[string]string{
|
|
"package_id": strconv.Itoa(packageID),
|
|
"type": "current",
|
|
"die": strconv.Itoa(dieID),
|
|
},
|
|
)
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("FailedToGetCurrentValues", func(t *testing.T) {
|
|
initMin := 300.0
|
|
initMax := 1200.0
|
|
|
|
acc := &testutil.Accumulator{}
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting die IDs for package ID.
|
|
mFetcher.On("GetPackageDieIDs", packageID).Return([]int{dieID}, nil).Once()
|
|
|
|
// mock getting initial minimum uncore frequency limit.
|
|
mFetcher.On("GetInitialUncoreFrequencyMin", packageID, dieID).Return(initMin, nil).Once()
|
|
|
|
// mock getting initial maximum uncore frequency limit.
|
|
mFetcher.On("GetInitialUncoreFrequencyMax", packageID, dieID).Return(initMax, nil).Once()
|
|
|
|
// mock getting custom minimum uncore frequency limit.
|
|
mFetcher.On("GetCustomizedUncoreFrequencyMin", packageID, dieID).Return(500.0, nil).Once()
|
|
|
|
// mock getting custom maximum uncore frequency limit.
|
|
mFetcher.On("GetCustomizedUncoreFrequencyMax", packageID, dieID).Return(1300.0, nil).Once()
|
|
|
|
// mock getting current uncore frequency value.
|
|
mFetcher.On("GetCurrentUncoreFrequency", packageID, dieID).Return(0.0, errors.New("mock error")).Once()
|
|
|
|
p := &PowerStat{
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
|
|
p.addUncoreFrequency(acc, packageID)
|
|
|
|
require.Len(t, acc.Errors, 1)
|
|
require.ErrorContains(
|
|
t,
|
|
acc.FirstError(),
|
|
fmt.Sprintf("failed to get current uncore frequency values for package ID %v and die ID %v", packageID, dieID),
|
|
)
|
|
require.Len(t, acc.GetTelegrafMetrics(), 1)
|
|
acc.AssertContainsTaggedFields(
|
|
t,
|
|
// measurement
|
|
"powerstat_package",
|
|
// fields
|
|
map[string]interface{}{
|
|
"uncore_frequency_limit_mhz_min": initMin,
|
|
"uncore_frequency_limit_mhz_max": initMax,
|
|
},
|
|
// tags
|
|
map[string]string{
|
|
"package_id": strconv.Itoa(packageID),
|
|
"type": "initial",
|
|
"die": strconv.Itoa(dieID),
|
|
},
|
|
)
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("Ok", func(t *testing.T) {
|
|
initMin := 300.0
|
|
initMax := 1200.0
|
|
currMin := 500.0
|
|
currMax := 2500.0
|
|
curr := 1000.0
|
|
|
|
acc := &testutil.Accumulator{}
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting die IDs for package ID.
|
|
mFetcher.On("GetPackageDieIDs", packageID).Return([]int{dieID}, nil).Once()
|
|
|
|
// mock getting initial minimum uncore frequency limit.
|
|
mFetcher.On("GetInitialUncoreFrequencyMin", packageID, dieID).Return(initMin, nil).Once()
|
|
|
|
// mock getting initial maximum uncore frequency limit.
|
|
mFetcher.On("GetInitialUncoreFrequencyMax", packageID, dieID).Return(initMax, nil).Once()
|
|
|
|
// mock getting custom minimum uncore frequency limit.
|
|
mFetcher.On("GetCustomizedUncoreFrequencyMin", packageID, dieID).Return(currMin, nil).Once()
|
|
|
|
// mock getting custom maximum uncore frequency limit.
|
|
mFetcher.On("GetCustomizedUncoreFrequencyMax", packageID, dieID).Return(currMax, nil).Once()
|
|
|
|
// mock getting current uncore frequency value.
|
|
mFetcher.On("GetCurrentUncoreFrequency", packageID, dieID).Return(curr, nil).Once()
|
|
|
|
p := &PowerStat{
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
|
|
p.addUncoreFrequency(acc, packageID)
|
|
|
|
require.Empty(t, acc.Errors)
|
|
require.Len(t, acc.GetTelegrafMetrics(), 2)
|
|
acc.AssertContainsTaggedFields(
|
|
t,
|
|
// measurement
|
|
"powerstat_package",
|
|
// fields
|
|
map[string]interface{}{
|
|
"uncore_frequency_limit_mhz_min": initMin,
|
|
"uncore_frequency_limit_mhz_max": initMax,
|
|
},
|
|
// tags
|
|
map[string]string{
|
|
"package_id": strconv.Itoa(packageID),
|
|
"type": "initial",
|
|
"die": strconv.Itoa(dieID),
|
|
},
|
|
)
|
|
acc.AssertContainsTaggedFields(
|
|
t,
|
|
// measurement
|
|
"powerstat_package",
|
|
// fields
|
|
map[string]interface{}{
|
|
"uncore_frequency_limit_mhz_min": currMin,
|
|
"uncore_frequency_limit_mhz_max": currMax,
|
|
"uncore_frequency_mhz_cur": uint64(curr),
|
|
},
|
|
// tags
|
|
map[string]string{
|
|
"package_id": strconv.Itoa(packageID),
|
|
"type": "current",
|
|
"die": strconv.Itoa(dieID),
|
|
},
|
|
)
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
}
|
|
|
|
func TestAddUncoreFrequencyInitialLimits(t *testing.T) {
|
|
packageID, dieID := 0, 0
|
|
|
|
t.Run("WithModuleNotInitializedError", func(t *testing.T) {
|
|
acc := &testutil.Accumulator{}
|
|
|
|
uncoreFreqErr := &ptel.ModuleNotInitializedError{Name: "uncore_frequency"}
|
|
mError := fmt.Errorf("mock error: %w", uncoreFreqErr)
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting initial minimum uncore frequency limit.
|
|
mFetcher.On("GetInitialUncoreFrequencyMin", packageID, dieID).Return(0.0, mError).Twice()
|
|
|
|
p := &PowerStat{
|
|
fetcher: mFetcher,
|
|
|
|
logOnce: map[string]struct{}{},
|
|
}
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
|
|
// First call adds the error to the accumulator and logOnce map.
|
|
p.addUncoreFrequencyInitialLimits(acc, packageID, dieID)
|
|
|
|
// Second call detects previous error in logOnce map and skips adding it to the accumulator.
|
|
p.addUncoreFrequencyInitialLimits(acc, packageID, dieID)
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
require.Len(t, acc.Errors, 1)
|
|
require.ErrorContains(t, acc.FirstError(), fmt.Sprintf("failed to get %q initial limits", packageUncoreFrequency))
|
|
require.ErrorContains(t, acc.FirstError(), uncoreFreqErr.Error())
|
|
|
|
require.Len(t, p.logOnce, 1)
|
|
require.Contains(t, p.logOnce, fmt.Sprintf("%s_%s_initial", "uncore_frequency", packageUncoreFrequency))
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("WithError", func(t *testing.T) {
|
|
acc := &testutil.Accumulator{}
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting initial minimum uncore frequency limit.
|
|
mFetcher.On("GetInitialUncoreFrequencyMin", packageID, dieID).Return(0.0, errors.New("mock error")).Once()
|
|
|
|
p := &PowerStat{
|
|
fetcher: mFetcher,
|
|
|
|
logOnce: map[string]struct{}{},
|
|
}
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
|
|
p.addUncoreFrequencyInitialLimits(acc, packageID, dieID)
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
require.Len(t, acc.Errors, 1)
|
|
require.ErrorContains(
|
|
t,
|
|
acc.FirstError(),
|
|
fmt.Sprintf("failed to get initial uncore frequency limits for package ID %v and die ID %v", packageID, dieID),
|
|
)
|
|
require.Empty(t, p.logOnce)
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("Ok", func(t *testing.T) {
|
|
initMin := 300.0
|
|
initMax := 1200.0
|
|
|
|
acc := &testutil.Accumulator{}
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting initial minimum uncore frequency limit.
|
|
mFetcher.On("GetInitialUncoreFrequencyMin", packageID, dieID).Return(initMin, nil).Once()
|
|
|
|
// mock getting initial maximum uncore frequency limit.
|
|
mFetcher.On("GetInitialUncoreFrequencyMax", packageID, dieID).Return(initMax, nil).Once()
|
|
|
|
p := &PowerStat{
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
|
|
p.addUncoreFrequencyInitialLimits(acc, packageID, dieID)
|
|
|
|
require.Empty(t, acc.Errors)
|
|
require.Len(t, acc.GetTelegrafMetrics(), 1)
|
|
acc.AssertContainsTaggedFields(
|
|
t,
|
|
// measurement
|
|
"powerstat_package",
|
|
// fields
|
|
map[string]interface{}{
|
|
"uncore_frequency_limit_mhz_min": initMin,
|
|
"uncore_frequency_limit_mhz_max": initMax,
|
|
},
|
|
// tags
|
|
map[string]string{
|
|
"package_id": strconv.Itoa(packageID),
|
|
"type": "initial",
|
|
"die": strconv.Itoa(dieID),
|
|
},
|
|
)
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
}
|
|
|
|
func TestAddUncoreFrequencyCurrentValues(t *testing.T) {
|
|
packageID, dieID := 0, 0
|
|
|
|
t.Run("WithModuleNotInitializedError", func(t *testing.T) {
|
|
acc := &testutil.Accumulator{}
|
|
|
|
uncoreFreqErr := &ptel.ModuleNotInitializedError{Name: "uncore_frequency"}
|
|
mError := fmt.Errorf("mock error: %w", uncoreFreqErr)
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting custom minimum uncore frequency limit.
|
|
mFetcher.On("GetCustomizedUncoreFrequencyMin", packageID, dieID).Return(0.0, mError).Twice()
|
|
|
|
p := &PowerStat{
|
|
fetcher: mFetcher,
|
|
|
|
logOnce: map[string]struct{}{},
|
|
}
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
|
|
// First call adds the error to the accumulator and logOnce map.
|
|
p.addUncoreFrequencyCurrentValues(acc, packageID, dieID)
|
|
|
|
// Second call detects previous error in logOnce map and skips adding it to the accumulator.
|
|
p.addUncoreFrequencyCurrentValues(acc, packageID, dieID)
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
require.Len(t, acc.Errors, 1)
|
|
require.ErrorContains(t, acc.FirstError(), fmt.Sprintf("failed to get %q current value and limits", packageUncoreFrequency))
|
|
require.ErrorContains(t, acc.FirstError(), uncoreFreqErr.Error())
|
|
|
|
require.Len(t, p.logOnce, 1)
|
|
require.Contains(t, p.logOnce, fmt.Sprintf("%s_%s_current", "uncore_frequency", packageUncoreFrequency))
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("WithError", func(t *testing.T) {
|
|
acc := &testutil.Accumulator{}
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting custom minimum uncore frequency limit.
|
|
mFetcher.On("GetCustomizedUncoreFrequencyMin", packageID, dieID).Return(0.0, errors.New("mock error")).Once()
|
|
|
|
p := &PowerStat{
|
|
fetcher: mFetcher,
|
|
|
|
logOnce: map[string]struct{}{},
|
|
}
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
|
|
p.addUncoreFrequencyCurrentValues(acc, packageID, dieID)
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
require.Len(t, acc.Errors, 1)
|
|
require.ErrorContains(
|
|
t,
|
|
acc.FirstError(),
|
|
fmt.Sprintf("failed to get current uncore frequency values for package ID %v and die ID %v", packageID, dieID),
|
|
)
|
|
require.Empty(t, p.logOnce)
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("Ok", func(t *testing.T) {
|
|
currMin := 500.0
|
|
currMax := 2500.0
|
|
curr := 1000.0
|
|
|
|
acc := &testutil.Accumulator{}
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting custom minimum uncore frequency limit.
|
|
mFetcher.On("GetCustomizedUncoreFrequencyMin", packageID, dieID).Return(currMin, nil).Once()
|
|
|
|
// mock getting custom maximum uncore frequency limit.
|
|
mFetcher.On("GetCustomizedUncoreFrequencyMax", packageID, dieID).Return(currMax, nil).Once()
|
|
|
|
// mock getting current uncore frequency value.
|
|
mFetcher.On("GetCurrentUncoreFrequency", packageID, dieID).Return(curr, nil).Once()
|
|
|
|
p := &PowerStat{
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
|
|
p.addUncoreFrequencyCurrentValues(acc, packageID, dieID)
|
|
|
|
require.Empty(t, acc.Errors)
|
|
require.Len(t, acc.GetTelegrafMetrics(), 1)
|
|
acc.AssertContainsTaggedFields(
|
|
t,
|
|
// measurement
|
|
"powerstat_package",
|
|
// fields
|
|
map[string]interface{}{
|
|
"uncore_frequency_limit_mhz_min": currMin,
|
|
"uncore_frequency_limit_mhz_max": currMax,
|
|
"uncore_frequency_mhz_cur": uint64(curr),
|
|
},
|
|
// tags
|
|
map[string]string{
|
|
"package_id": strconv.Itoa(packageID),
|
|
"type": "current",
|
|
"die": strconv.Itoa(dieID),
|
|
},
|
|
)
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
}
|
|
|
|
func TestGetUncoreFreqInitialLimits(t *testing.T) {
|
|
packageID, dieID := 0, 0
|
|
|
|
t.Run("FailsToGetInitialMinLimit", func(t *testing.T) {
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting initial minimum uncore frequency limit.
|
|
mFetcher.On("GetInitialUncoreFrequencyMin", packageID, dieID).Return(0.0, errors.New("mock error")).Once()
|
|
|
|
initMin, initMax, err := getUncoreFreqInitialLimits(mFetcher, packageID, dieID)
|
|
|
|
require.ErrorContains(t, err, "failed to get initial minimum uncore frequency limit")
|
|
require.Zero(t, initMin)
|
|
require.Zero(t, initMax)
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("FailsToGetInitialMaxLimit", func(t *testing.T) {
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting initial minimum uncore frequency limit.
|
|
mFetcher.On("GetInitialUncoreFrequencyMin", packageID, dieID).Return(800.0, nil).Once()
|
|
|
|
// mock getting initial maximum uncore frequency limit.
|
|
mFetcher.On("GetInitialUncoreFrequencyMax", packageID, dieID).Return(0.0, errors.New("mock error")).Once()
|
|
|
|
initMin, initMax, err := getUncoreFreqInitialLimits(mFetcher, packageID, dieID)
|
|
|
|
require.ErrorContains(t, err, "failed to get initial maximum uncore frequency limit")
|
|
require.Zero(t, initMin)
|
|
require.Zero(t, initMax)
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("Ok", func(t *testing.T) {
|
|
initMinExp := 300.0
|
|
initMaxExp := 1500.0
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting initial minimum uncore frequency limit.
|
|
mFetcher.On("GetInitialUncoreFrequencyMin", packageID, dieID).Return(initMinExp, nil).Once()
|
|
|
|
// mock getting initial maximum uncore frequency limit.
|
|
mFetcher.On("GetInitialUncoreFrequencyMax", packageID, dieID).Return(initMaxExp, nil).Once()
|
|
|
|
initMin, initMax, err := getUncoreFreqInitialLimits(mFetcher, packageID, dieID)
|
|
|
|
require.NoError(t, err)
|
|
require.InDelta(t, initMinExp, initMin, testutil.DefaultDelta)
|
|
require.InDelta(t, initMaxExp, initMax, testutil.DefaultDelta)
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
}
|
|
|
|
func TestGetUncoreFreqCurrentValues(t *testing.T) {
|
|
packageID, dieID := 0, 0
|
|
|
|
t.Run("FailsToGetCurrentMinLimit", func(t *testing.T) {
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting current minimum uncore frequency limit.
|
|
mFetcher.On("GetCustomizedUncoreFrequencyMin", packageID, dieID).Return(0.0, errors.New("mock error")).Once()
|
|
|
|
values, err := getUncoreFreqCurrentValues(mFetcher, packageID, dieID)
|
|
|
|
require.ErrorContains(t, err, "failed to get current minimum uncore frequency limit")
|
|
require.Equal(t, uncoreFreqValues{}, values)
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("FailsToGetCurrentMaxLimit", func(t *testing.T) {
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting current minimum uncore frequency limit.
|
|
mFetcher.On("GetCustomizedUncoreFrequencyMin", packageID, dieID).Return(1000.0, nil).Once()
|
|
|
|
// mock getting current maximum uncore frequency limit.
|
|
mFetcher.On("GetCustomizedUncoreFrequencyMax", packageID, dieID).Return(0.0, errors.New("mock error")).Once()
|
|
|
|
values, err := getUncoreFreqCurrentValues(mFetcher, packageID, dieID)
|
|
|
|
require.ErrorContains(t, err, "failed to get current maximum uncore frequency limit")
|
|
require.Equal(t, uncoreFreqValues{}, values)
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("FailsToGetCurrentValue", func(t *testing.T) {
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting custom minimum uncore frequency limit.
|
|
mFetcher.On("GetCustomizedUncoreFrequencyMin", packageID, dieID).Return(1000.0, nil).Once()
|
|
|
|
// mock getting custom maximum uncore frequency limit.
|
|
mFetcher.On("GetCustomizedUncoreFrequencyMax", packageID, dieID).Return(2000.0, nil).Once()
|
|
|
|
// mock getting current uncore frequency value.
|
|
mFetcher.On("GetCurrentUncoreFrequency", packageID, dieID).Return(0.0, errors.New("mock error")).Once()
|
|
|
|
values, err := getUncoreFreqCurrentValues(mFetcher, packageID, dieID)
|
|
|
|
require.ErrorContains(t, err, "failed to get current uncore frequency")
|
|
require.Equal(t, uncoreFreqValues{}, values)
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("Ok", func(t *testing.T) {
|
|
minUncore := 500.0
|
|
maxUncore := 1500.0
|
|
current := 750.0
|
|
|
|
uncoreFreqValExp := uncoreFreqValues{
|
|
currMin: minUncore,
|
|
currMax: maxUncore,
|
|
curr: current,
|
|
}
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting custom minimum uncore frequency limit.
|
|
mFetcher.On("GetCustomizedUncoreFrequencyMin", packageID, dieID).Return(minUncore, nil).Once()
|
|
|
|
// mock getting custom maximum uncore frequency limit.
|
|
mFetcher.On("GetCustomizedUncoreFrequencyMax", packageID, dieID).Return(maxUncore, nil).Once()
|
|
|
|
// mock getting current uncore frequency value.
|
|
mFetcher.On("GetCurrentUncoreFrequency", packageID, dieID).Return(current, nil).Once()
|
|
|
|
values, err := getUncoreFreqCurrentValues(mFetcher, packageID, dieID)
|
|
|
|
require.NoError(t, err)
|
|
require.Equal(t, uncoreFreqValExp, values)
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
}
|
|
|
|
func TestAddMaxTurboFreqLimits(t *testing.T) {
|
|
t.Run("FailedToGetMetricModuleNotInitializedError", func(t *testing.T) {
|
|
packageID := 1
|
|
|
|
acc := &testutil.Accumulator{}
|
|
|
|
mErr := &ptel.ModuleNotInitializedError{Name: "msr"}
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting max turbo frequency list.
|
|
mFetcher.On("GetMaxTurboFreqList", packageID).Return(nil, mErr).Twice()
|
|
|
|
p := &PowerStat{
|
|
fetcher: mFetcher,
|
|
|
|
logOnce: map[string]struct{}{},
|
|
}
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
|
|
// First call adds the error to the accumulator and key to logOnce map.
|
|
p.addMaxTurboFreqLimits(acc, packageID)
|
|
|
|
// Second call detects previous error in logOnce map and skips adding it to the accumulator.
|
|
p.addMaxTurboFreqLimits(acc, packageID)
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
require.Len(t, acc.Errors, 1)
|
|
require.ErrorContains(t, acc.FirstError(), fmt.Sprintf("failed to get %q: %v", packageTurboLimit, mErr))
|
|
|
|
require.Len(t, p.logOnce, 1)
|
|
require.Contains(t, p.logOnce, "msr_max_turbo_frequency")
|
|
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("FailedToGetMetric", func(t *testing.T) {
|
|
packageID := 1
|
|
|
|
acc := &testutil.Accumulator{}
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting max turbo frequency list.
|
|
mFetcher.On("GetMaxTurboFreqList", packageID).Return(nil, errors.New("mock error")).Once()
|
|
|
|
p := &PowerStat{
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
|
|
p.addMaxTurboFreqLimits(acc, packageID)
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
require.Len(t, acc.Errors, 1)
|
|
require.ErrorContains(t, acc.FirstError(), fmt.Sprintf("failed to get %q for package ID %v", packageTurboLimit, packageID))
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("CPUIsHybrid", func(t *testing.T) {
|
|
packageID := 1
|
|
|
|
maxTurboFreqList := []ptel.MaxTurboFreq{
|
|
{
|
|
Value: 1000,
|
|
ActiveCores: 10,
|
|
Secondary: true,
|
|
},
|
|
{
|
|
Value: 2000,
|
|
ActiveCores: 20,
|
|
},
|
|
}
|
|
|
|
acc := &testutil.Accumulator{}
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting max turbo frequency list.
|
|
mFetcher.On("GetMaxTurboFreqList", packageID).Return(maxTurboFreqList, nil).Once()
|
|
|
|
p := &PowerStat{
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
|
|
p.addMaxTurboFreqLimits(acc, packageID)
|
|
|
|
require.Empty(t, acc.Errors)
|
|
require.Len(t, acc.GetTelegrafMetrics(), 2)
|
|
acc.AssertContainsTaggedFields(
|
|
t,
|
|
// measurement
|
|
"powerstat_package",
|
|
// fields
|
|
map[string]interface{}{
|
|
"max_turbo_frequency_mhz": maxTurboFreqList[0].Value,
|
|
},
|
|
// tags
|
|
map[string]string{
|
|
"package_id": strconv.Itoa(packageID),
|
|
"active_cores": strconv.Itoa(int(maxTurboFreqList[0].ActiveCores)),
|
|
"hybrid": "secondary",
|
|
},
|
|
)
|
|
acc.AssertContainsTaggedFields(
|
|
t,
|
|
// measurement
|
|
"powerstat_package",
|
|
// fields
|
|
map[string]interface{}{
|
|
"max_turbo_frequency_mhz": maxTurboFreqList[1].Value,
|
|
},
|
|
// tags
|
|
map[string]string{
|
|
"package_id": strconv.Itoa(packageID),
|
|
"active_cores": strconv.Itoa(int(maxTurboFreqList[1].ActiveCores)),
|
|
"hybrid": "primary",
|
|
},
|
|
)
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("CPUIsNotHybrid", func(t *testing.T) {
|
|
packageID := 1
|
|
|
|
maxTurboFreqList := []ptel.MaxTurboFreq{
|
|
{
|
|
Value: 1000,
|
|
ActiveCores: 10,
|
|
},
|
|
{
|
|
Value: 2000,
|
|
ActiveCores: 20,
|
|
},
|
|
}
|
|
|
|
acc := &testutil.Accumulator{}
|
|
|
|
mFetcher := &fetcherMock{}
|
|
|
|
// mock getting max turbo frequency list.
|
|
mFetcher.On("GetMaxTurboFreqList", packageID).Return(maxTurboFreqList, nil).Once()
|
|
|
|
p := &PowerStat{
|
|
fetcher: mFetcher,
|
|
}
|
|
|
|
require.Empty(t, acc.GetTelegrafMetrics())
|
|
|
|
p.addMaxTurboFreqLimits(acc, packageID)
|
|
|
|
require.Empty(t, acc.Errors)
|
|
require.Len(t, acc.GetTelegrafMetrics(), 2)
|
|
acc.AssertContainsTaggedFields(
|
|
t,
|
|
// measurement
|
|
"powerstat_package",
|
|
// fields
|
|
map[string]interface{}{
|
|
"max_turbo_frequency_mhz": maxTurboFreqList[0].Value,
|
|
},
|
|
// tags
|
|
map[string]string{
|
|
"package_id": strconv.Itoa(packageID),
|
|
"active_cores": strconv.Itoa(int(maxTurboFreqList[0].ActiveCores)),
|
|
},
|
|
)
|
|
acc.AssertContainsTaggedFields(
|
|
t,
|
|
// measurement
|
|
"powerstat_package",
|
|
// fields
|
|
map[string]interface{}{
|
|
"max_turbo_frequency_mhz": maxTurboFreqList[1].Value,
|
|
},
|
|
// tags
|
|
map[string]string{
|
|
"package_id": strconv.Itoa(packageID),
|
|
"active_cores": strconv.Itoa(int(maxTurboFreqList[1].ActiveCores)),
|
|
},
|
|
)
|
|
mFetcher.AssertExpectations(t)
|
|
})
|
|
}
|