feat(intel_powerstat): Add CPU base frequency metric and add support for new platforms (#12452)

This commit is contained in:
Paweł Żak 2023-01-18 14:10:00 +01:00 committed by GitHub
parent 7725896ff4
commit 65b23f112e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 464 additions and 121 deletions

View File

@ -33,7 +33,8 @@ See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
## supported options list
## Supported options:
## "current_power_consumption", "current_dram_power_consumption",
## "thermal_design_power", "max_turbo_frequency", "uncore_frequency"
## "thermal_design_power", "max_turbo_frequency", "uncore_frequency",
## "cpu_base_frequency"
# package_metrics = ["current_power_consumption", "current_dram_power_consumption", "thermal_design_power"]
## The user can choose which per-CPU metrics are monitored by the plugin in
@ -125,7 +126,7 @@ integrated in kernel). Modules might have to be manually enabled by using
```sh
# kernel 5.x.x:
sudo modprobe rapl
subo modprobe msr
sudo modprobe msr
sudo modprobe intel_rapl_common
sudo modprobe intel_rapl_msr
@ -151,6 +152,7 @@ and to retrieve data for calculation per-package specific metric:
- `max_turbo_frequency_mhz`
- `uncore_frequency_mhz_cur`
- `cpu_base_frequency_mhz`
To expose other Intel PowerStat metrics root access may or may not be required
(depending on OS type or configuration).
@ -180,11 +182,12 @@ are required by the plugin:
_powerstat\_core.cpu\_c1\_state\_residency_
- "_dts_" shall be present to collect _powerstat\_core.cpu\_temperature_
- Processor _Model number_ must be one of the following values for plugin to
read _powerstat\_core.cpu\_c1\_state\_residency_ and
_powerstat\_core.cpu\_c6\_state\_residency_ metrics:
read _powerstat\_core.cpu\_c1\_state\_residency_ /
_powerstat\_core.cpu\_c6\_state\_residency_ and
_powerstat\_package.cpu\_base\_frequency_ metrics:
| Model number | Processor name |
|-----|-------------|
|--------------|---------------------------------|
| 0x37 | Intel Atom® Bay Trail |
| 0x4D | Intel Atom® Avaton |
| 0x5C | Intel Atom® Apollo Lake |
@ -208,13 +211,13 @@ are required by the plugin:
| 0x4E | Intel Atom® Silvermont-MID |
| 0x5E | Intel Skylake |
| 0x55 | Intel Skylake-X |
| 0x8E | Intel Kabylake-L |
| 0x9E | Intel Kabylake |
| 0x6A | Intel Icelake-X |
| 0x6C | Intel Icelake-D |
| 0x7D | Intel Icelake |
| 0x7E | Intel Icelake-L |
| 0x9D | Intel Icelake-NNPI |
| 0x8E | Intel KabyLake-L |
| 0x9E | Intel KabyLake |
| 0x6A | Intel IceLake-X |
| 0x6C | Intel IceLake-D |
| 0x7D | Intel IceLake |
| 0x7E | Intel IceLake-L |
| 0x9D | Intel IceLake-NNPI |
| 0x3C | Intel Haswell |
| 0x3F | Intel Haswell-X |
| 0x45 | Intel Haswell-L |
@ -223,14 +226,24 @@ are required by the plugin:
| 0x47 | Intel Broadwell-G |
| 0x4F | Intel Broadwell-X |
| 0x56 | Intel Broadwell-D |
| 0x66 | Intel Cannonlake-L |
| 0x66 | Intel CannonLake-L |
| 0x57 | Intel Xeon® PHI Knights Landing |
| 0x85 | Intel Xeon® PHI Knights Mill |
| 0xA5 | Intel CometLake |
| 0xA6 | Intel CometLake-L |
| 0x8A | Intel Lakefield |
| 0x8F | Intel Sapphire Rapids X |
| 0x8C | Intel TigerLake-L |
| 0x8D | Intel TigerLake |
| 0xA7 | Intel RocketLake |
| 0x97 | Intel AlderLake |
| 0x9A | Intel AlderLake-L |
| 0xBE | Intel AlderLake-N |
| 0xB7 | Intel RaptorLake |
| 0xBA | Intel RaptorLake-P |
| 0xBF | Intel RaptorLake-S |
| 0xAC | Intel MeteorLake |
| 0xAA | Intel MeteorLake-L |
## Metrics
@ -290,6 +303,7 @@ value.
| `uncore_frequency_limit_mhz_min`| Minimum uncore frequency limit for die in processor package | MHz
| `uncore_frequency_limit_mhz_max`| Maximum uncore frequency limit for die in processor package | MHz
| `uncore_frequency_mhz_cur`| Current uncore frequency for die in processor package. Available only with tag `current`. Since this value is not yet available from `intel-uncore-frequency` module it needs to be accessed via MSR. In case of lack of loaded msr, only `uncore_frequency_limit_mhz_min` and `uncore_frequency_limit_mhz_max` metrics will be collected | MHz
| `cpu_base_frequency_mhz`| CPU Base Frequency (maximum non-turbo frequency) for the processor package | MHz
### Known issues
@ -310,9 +324,10 @@ sudo chmod -R a+rx /sys/devices/virtual/powercap/intel-rapl/
## Example Output
```shell
```text
powerstat_package,host=ubuntu,package_id=0 thermal_design_power_watts=160 1606494744000000000
powerstat_package,host=ubuntu,package_id=0 current_power_consumption_watts=35 1606494744000000000
powerstat_package,host=ubuntu,package_id=0 cpu_base_frequency_mhz=2400i 1669118424000000000
powerstat_package,host=ubuntu,package_id=0 current_dram_power_consumption_watts=13.94 1606494744000000000
powerstat_package,host=ubuntu,package_id=0,active_cores=0 max_turbo_frequency_mhz=3000i 1606494744000000000
powerstat_package,host=ubuntu,package_id=0,active_cores=1 max_turbo_frequency_mhz=2800i 1606494744000000000

View File

@ -33,6 +33,7 @@ const (
packageThermalDesignPower = "thermal_design_power"
packageTurboLimit = "max_turbo_frequency"
packageUncoreFrequency = "uncore_frequency"
packageCPUBaseFrequency = "cpu_base_frequency"
percentageMultiplier = 100
)
@ -53,11 +54,15 @@ type PowerStat struct {
cpuC1StateResidency bool
cpuC6StateResidency bool
cpuBusyCycles bool
packageTurboLimit bool
packageCurrentPowerConsumption bool
packageCurrentDramPowerConsumption bool
packageThermalDesignPower bool
packageUncoreFrequency bool
packageCPUBaseFrequency bool
cpuBusClockValue float64
cpuInfo map[string]*cpuInfo
skipFirstIteration bool
logOnce map[string]error
@ -75,23 +80,48 @@ func (p *PowerStat) Init() error {
if err != nil {
return err
}
// Initialize MSR service only when there is at least one metric enabled
if p.cpuFrequency || p.cpuBusyFrequency || p.cpuTemperature || p.cpuC0StateResidency || p.cpuC1StateResidency ||
p.cpuC6StateResidency || p.cpuBusyCycles || p.packageTurboLimit || p.packageUncoreFrequency {
p.msr = newMsrServiceWithFs(p.Log, p.fs)
}
if p.packageCurrentPowerConsumption || p.packageCurrentDramPowerConsumption || p.packageThermalDesignPower || p.packageTurboLimit ||
p.packageUncoreFrequency {
p.rapl = newRaplServiceWithFs(p.Log, p.fs)
}
p.initMSR()
p.initRaplService()
if !p.areCoreMetricsEnabled() && !p.areGlobalMetricsEnabled() {
return fmt.Errorf("all configuration options are empty or invalid. Did not find anything to gather")
}
p.fillCPUBusClock()
return nil
}
func (p *PowerStat) initMSR() {
// Initialize MSR service only when there is at least one metric enabled
if p.cpuFrequency || p.cpuBusyFrequency || p.cpuTemperature || p.cpuC0StateResidency || p.cpuC1StateResidency ||
p.cpuC6StateResidency || p.cpuBusyCycles || p.packageTurboLimit || p.packageUncoreFrequency || p.packageCPUBaseFrequency {
p.msr = newMsrServiceWithFs(p.Log, p.fs)
}
}
func (p *PowerStat) initRaplService() {
if p.packageCurrentPowerConsumption || p.packageCurrentDramPowerConsumption || p.packageThermalDesignPower || p.packageTurboLimit ||
p.packageUncoreFrequency || p.packageCPUBaseFrequency {
p.rapl = newRaplServiceWithFs(p.Log, p.fs)
}
}
// fill CPUBusClockValue if required
func (p *PowerStat) fillCPUBusClock() {
if p.packageCPUBaseFrequency {
// cpuBusClock is the same for every core/socket.
busClockInfo := p.getBusClock("0")
if busClockInfo == 0 {
p.Log.Warn("Disabling package metric: cpu_base_frequency_mhz. Can't detect bus clock value")
p.packageCPUBaseFrequency = false
return
}
p.cpuBusClockValue = busClockInfo
}
}
// Gather takes in an accumulator and adds the metrics that the Input gathers
func (p *PowerStat) Gather(acc telegraf.Accumulator) error {
if p.areGlobalMetricsEnabled() {
@ -133,6 +163,10 @@ func (p *PowerStat) addGlobalMetrics(acc telegraf.Accumulator) {
}
}
if p.packageCPUBaseFrequency {
p.addCPUBaseFreq(socketID, acc)
}
err := p.rapl.retrieveAndCalculateData(socketID)
if err != nil {
// In case of an error skip calculating metrics for this socket
@ -189,22 +223,17 @@ func (p *PowerStat) addUncoreFreq(socketID string, die string, acc telegraf.Accu
func (p *PowerStat) readUncoreFreq(typeFreq string, socketID string, die string, acc telegraf.Accumulator) {
fields := map[string]interface{}{}
cpuID := ""
if typeFreq == "current" {
if p.areCoreMetricsEnabled() && p.msr.isMsrLoaded() {
p.logOnce[socketID+"msr"] = nil
for _, v := range p.cpuInfo {
if v.physicalID == socketID {
cpuID = v.cpuID
}
}
if cpuID == "" {
p.Log.Debugf("error while reading socket ID")
cpuID, err := p.GetCPUIDFromSocketID(socketID)
if err != nil {
p.Log.Debugf("error while reading socket ID: %v", err)
return
}
actualUncoreFreq, err := p.msr.readSingleMsr(cpuID, "MSR_UNCORE_PERF_STATUS")
actualUncoreFreq, err := p.msr.readSingleMsr(cpuID, msrUncorePerfStatusString)
if err != nil {
p.Log.Debugf("error while reading MSR_UNCORE_PERF_STATUS: %v", err)
p.Log.Debugf("error while reading %s: %v", msrUncorePerfStatusString, err)
return
}
actualUncoreFreq = (actualUncoreFreq & 0x3F) * 100
@ -406,15 +435,15 @@ func (p *PowerStat) addTurboRatioLimit(socketID string, acc telegraf.Accumulator
}
}
if cpuID == "" || model == "" {
p.Log.Debugf("error while reading socket ID")
p.Log.Debug("error while reading socket ID")
return
}
// dump_hsw_turbo_ratio_limit
if model == strconv.FormatInt(0x3F, 10) { // INTEL_FAM6_HASWELL_X
coreCounts := uint64(0x1211) // counting the number of active cores 17 and 18
msrTurboRatioLimit2, err := p.msr.readSingleMsr(cpuID, "MSR_TURBO_RATIO_LIMIT2")
msrTurboRatioLimit2, err := p.msr.readSingleMsr(cpuID, msrTurboRatioLimit2String)
if err != nil {
p.Log.Debugf("error while reading MSR_TURBO_RATIO_LIMIT2: %v", err)
p.Log.Debugf("error while reading %s: %v", msrTurboRatioLimit2String, err)
return
}
@ -425,9 +454,9 @@ func (p *PowerStat) addTurboRatioLimit(socketID string, acc telegraf.Accumulator
if (model == strconv.FormatInt(0x3E, 10)) || // INTEL_FAM6_IVYBRIDGE_X
(model == strconv.FormatInt(0x3F, 10)) { // INTEL_FAM6_HASWELL_X
coreCounts := uint64(0x100F0E0D0C0B0A09) // counting the number of active cores 9 to 16
msrTurboRatioLimit1, err := p.msr.readSingleMsr(cpuID, "MSR_TURBO_RATIO_LIMIT1")
msrTurboRatioLimit1, err := p.msr.readSingleMsr(cpuID, msrTurboRatioLimit1String)
if err != nil {
p.Log.Debugf("error while reading MSR_TURBO_RATIO_LIMIT1: %v", err)
p.Log.Debugf("error while reading %s: %v", msrTurboRatioLimit1String, err)
return
}
calculateTurboRatioGroup(coreCounts, msrTurboRatioLimit1, turboRatioLimitGroups)
@ -446,17 +475,17 @@ func (p *PowerStat) addTurboRatioLimit(socketID string, acc telegraf.Accumulator
(model == strconv.FormatInt(0x6C, 10) || model == strconv.FormatInt(0x8F, 10) || model == strconv.FormatInt(0x6A, 10)) || // INTEL_FAM6_ICELAKE_X
(model == strconv.FormatInt(0x5F, 10)) || // INTEL_FAM6_ATOM_GOLDMONT_D
(model == strconv.FormatInt(0x86, 10)) { // INTEL_FAM6_ATOM_TREMONT_D
coreCounts, err = p.msr.readSingleMsr(cpuID, "MSR_TURBO_RATIO_LIMIT1")
coreCounts, err = p.msr.readSingleMsr(cpuID, msrTurboRatioLimit1String)
if err != nil {
p.Log.Debugf("error while reading MSR_TURBO_RATIO_LIMIT1: %v", err)
p.Log.Debugf("error while reading %s: %v", msrTurboRatioLimit1String, err)
return
}
}
msrTurboRatioLimit, err := p.msr.readSingleMsr(cpuID, "MSR_TURBO_RATIO_LIMIT")
msrTurboRatioLimit, err := p.msr.readSingleMsr(cpuID, msrTurboRatioLimitString)
if err != nil {
p.Log.Debugf("error while reading MSR_TURBO_RATIO_LIMIT: %v", err)
p.Log.Debugf("error while reading %s: %v", msrTurboRatioLimitString, err)
return
}
calculateTurboRatioGroup(coreCounts, msrTurboRatioLimit, turboRatioLimitGroups)
@ -466,10 +495,10 @@ func (p *PowerStat) addTurboRatioLimit(socketID string, acc telegraf.Accumulator
model == strconv.FormatInt(0x4A, 10) || // INTEL_FAM6_ATOM_SILVERMONT_MID:
model == strconv.FormatInt(0x5A, 10) { // INTEL_FAM6_ATOM_AIRMONT_MID
coreCounts := uint64(0x04030201) // counting the number of active cores 1 to 4
msrTurboRatioLimit, err := p.msr.readSingleMsr(cpuID, "MSR_ATOM_CORE_TURBO_RATIOS")
msrTurboRatioLimit, err := p.msr.readSingleMsr(cpuID, msrAtomCoreTurboRatiosString)
if err != nil {
p.Log.Debugf("error while reading MSR_ATOM_CORE_TURBO_RATIOS: %v", err)
p.Log.Debugf("error while reading %s: %v", msrAtomCoreTurboRatiosString, err)
return
}
value := uint64(0)
@ -484,9 +513,9 @@ func (p *PowerStat) addTurboRatioLimit(socketID string, acc telegraf.Accumulator
}
// dump_knl_turbo_ratio_limits
if model == strconv.FormatInt(0x57, 10) { // INTEL_FAM6_XEON_PHI_KNL
msrTurboRatioLimit, err := p.msr.readSingleMsr(cpuID, "MSR_TURBO_RATIO_LIMIT")
msrTurboRatioLimit, err := p.msr.readSingleMsr(cpuID, msrTurboRatioLimitString)
if err != nil {
p.Log.Debugf("error while reading MSR_TURBO_RATIO_LIMIT: %v", err)
p.Log.Debugf("error while reading %s: %v", msrTurboRatioLimitString, err)
return
}
@ -654,6 +683,79 @@ func (p *PowerStat) addCPUC0StateResidencyMetric(cpuID string, acc telegraf.Accu
}
}
func (p *PowerStat) addCPUBaseFreq(socketID string, acc telegraf.Accumulator) {
cpuID, err := p.GetCPUIDFromSocketID(socketID)
if err != nil {
p.Log.Debugf("error while getting CPU ID from Socket ID: %v", err)
return
}
msrPlatformInfoMsr, err := p.msr.readSingleMsr(cpuID, msrPlatformInfoString)
if err != nil {
p.Log.Debugf("error while reading %s: %v", msrPlatformInfoString, err)
return
}
// the value of the freq ratio is saved in bits 15 to 8.
// to get the freq -> ratio * busClock
cpuBaseFreq := float64((msrPlatformInfoMsr>>8)&0xFF) * p.cpuBusClockValue
if cpuBaseFreq == 0 {
p.Log.Debugf("error while adding CPU base frequency, cpuBaseFreq is zero for the socket: %s", socketID)
return
}
tags := map[string]string{
"package_id": socketID,
}
fields := map[string]interface{}{
"cpu_base_frequency_mhz": uint64(cpuBaseFreq),
}
acc.AddGauge("powerstat_package", fields, tags)
}
func (p *PowerStat) getBusClock(cpuID string) float64 {
cpuInfo, ok := p.cpuInfo[cpuID]
if !ok {
p.Log.Debugf("cannot find cpuInfo for cpu: %s", cpuID)
return 0
}
model := cpuInfo.model
busClock100 := []int64{0x2A, 0x2D, 0x3A, 0x3E, 0x3C, 0x3F, 0x45, 0x46, 0x3D, 0x47, 0x4F, 0x56, 0x4E, 0x5E, 0x55, 0x8E, 0x9E, 0xA5, 0xA6, 0x66, 0x6A, 0x6C,
0x7D, 0x7E, 0x9D, 0x8A, 0xA7, 0x8C, 0x8D, 0x8F, 0x97, 0x9A, 0xBE, 0xB7, 0xBA, 0xBF, 0xAC, 0xAA, 0x5C, 0x5F, 0x7A, 0x86, 0x96, 0x9C, 0x57, 0x85}
busClock133 := []int64{0x1E, 0x1F, 0x1A, 0x2E, 0x25, 0x2C, 0x2F, 0x4C}
busClockCalculate := []int64{0x37, 0x4D}
if contains(convertIntegerArrayToStringArray(busClock100), model) {
return 100.0
} else if contains(convertIntegerArrayToStringArray(busClock133), model) {
return 133.0
} else if contains(convertIntegerArrayToStringArray(busClockCalculate), model) {
return p.getSilvermontBusClock(cpuID)
}
p.Log.Debugf("couldn't find the freq for the model: %d", model)
return 0.0
}
func (p *PowerStat) getSilvermontBusClock(cpuID string) float64 {
silvermontFreqTable := []float64{83.3, 100.0, 133.3, 116.7, 80.0}
msr, err := p.msr.readSingleMsr(cpuID, msrFSBFreqString)
if err != nil {
p.Log.Debugf("error while reading %s: %v", msrFSBFreqString, err)
return 0.0
}
i := int(msr & 0xf)
if i >= len(silvermontFreqTable) {
p.Log.Debugf("unknown msr value: %d, using default bus clock value: %d", i, silvermontFreqTable[3])
//same behaviour as in turbostat
i = 3
}
return silvermontFreqTable[i]
}
func (p *PowerStat) parsePackageMetricsConfig() {
if p.PackageMetrics == nil {
// if Package Metric config is empty, use the default settings.
@ -679,6 +781,9 @@ func (p *PowerStat) parsePackageMetricsConfig() {
if contains(p.PackageMetrics, packageUncoreFrequency) {
p.packageUncoreFrequency = true
}
if contains(p.PackageMetrics, packageCPUBaseFrequency) {
p.packageCPUBaseFrequency = true
}
}
func (p *PowerStat) parseCPUMetricsConfig() {
@ -719,7 +824,7 @@ func (p *PowerStat) verifyProcessor() error {
allowedProcessorModelsForC1C6 := []int64{0x37, 0x4D, 0x5C, 0x5F, 0x7A, 0x4C, 0x86, 0x96, 0x9C,
0x1A, 0x1E, 0x1F, 0x2E, 0x25, 0x2C, 0x2F, 0x2A, 0x2D, 0x3A, 0x3E, 0x4E, 0x5E, 0x55, 0x8E,
0x9E, 0x6A, 0x6C, 0x7D, 0x7E, 0x9D, 0x3C, 0x3F, 0x45, 0x46, 0x3D, 0x47, 0x4F, 0x56,
0x66, 0x57, 0x85, 0xA5, 0xA6, 0x8F, 0x8C, 0x8D}
0x66, 0x57, 0x85, 0xA5, 0xA6, 0x8A, 0x8F, 0x8C, 0x8D, 0xA7, 0x97, 0x9A, 0xBE, 0xB7, 0xBA, 0xBF, 0xAC, 0xAA}
stats, err := p.fs.getCPUInfoStats()
if err != nil {
return err
@ -743,6 +848,7 @@ func (p *PowerStat) verifyProcessor() error {
}
if !strings.Contains(firstCPU.flags, "msr") {
p.packageCPUBaseFrequency = false
p.cpuTemperature = false
p.cpuC6StateResidency = false
p.cpuC0StateResidency = false
@ -765,9 +871,9 @@ func (p *PowerStat) verifyProcessor() error {
return nil
}
func contains(slice []string, str string) bool {
for _, v := range slice {
if v == str {
func contains[T comparable](s []T, e T) bool {
for _, v := range s {
if v == e {
return true
}
}
@ -782,21 +888,18 @@ func (p *PowerStat) areGlobalMetricsEnabled() bool {
return p.rapl != nil
}
func (p *PowerStat) GetCPUIDFromSocketID(socketID string) (string, error) {
for _, v := range p.cpuInfo {
if v.physicalID == socketID {
return v.cpuID, nil
}
}
return "", fmt.Errorf("can't find cpuID for socketID: %s", socketID)
}
// newPowerStat creates and returns PowerStat struct
func newPowerStat(fs fileService) *PowerStat {
p := &PowerStat{
cpuFrequency: false,
cpuC0StateResidency: false,
cpuC1StateResidency: false,
cpuC6StateResidency: false,
cpuBusyCycles: false,
cpuTemperature: false,
cpuBusyFrequency: false,
packageTurboLimit: false,
packageUncoreFrequency: false,
packageCurrentPowerConsumption: false,
packageCurrentDramPowerConsumption: false,
packageThermalDesignPower: false,
skipFirstIteration: true,
fs: fs,
logOnce: make(map[string]error),

View File

@ -7,10 +7,12 @@ import (
"strconv"
"sync"
"testing"
"time"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/testutil"
)
@ -241,7 +243,7 @@ func TestReadUncoreFreq(t *testing.T) {
mockServices.msr.On("isMsrLoaded").Return(true)
mockServices.msr.On("readSingleMsr", "0", "MSR_UNCORE_PERF_STATUS").Return(uint64(10), nil)
mockServices.msr.On("readSingleMsr", "0", msrUncorePerfStatusString).Return(uint64(10), nil)
mockServices.msr.On("retrieveUncoreFrequency", "0", "initial", "min", "0").
Return(float64(500), nil)
@ -641,3 +643,209 @@ func getPowerWithMockedServices() (*PowerStat, *MockServices) {
return p, &mockServices
}
func TestGetBusClock(t *testing.T) {
tests := []struct {
name string
modelCPU uint64
socketID string
msrFSBFreqValue uint64
readSingleMsrErrFSB error
cpuBusClockValue float64
}{
{
name: "Error_withUnknownCPUmodel",
socketID: "0",
modelCPU: 0xFF,
cpuBusClockValue: 0,
},
{
name: "OK_withFBS100",
socketID: "0",
modelCPU: 106,
msrFSBFreqValue: 1,
cpuBusClockValue: 100.0,
},
{
name: "OK_withFBS133",
socketID: "0",
modelCPU: 0x1F,
cpuBusClockValue: 133,
},
{
name: "Error_withFBSCalculated",
socketID: "0",
modelCPU: 0x37,
msrFSBFreqValue: 0,
readSingleMsrErrFSB: errors.New("something is wrong"),
},
{
name: "OK_withFBSCalculated83.3",
socketID: "0",
modelCPU: 0x37,
msrFSBFreqValue: 0,
cpuBusClockValue: 83.3,
},
{
name: "OK_withFBSCalculated100",
socketID: "0",
modelCPU: 0x37,
msrFSBFreqValue: 1,
cpuBusClockValue: 100,
},
{
name: "OK_withFBSCalculated133.3",
socketID: "0",
modelCPU: 0x37,
msrFSBFreqValue: 2,
cpuBusClockValue: 133.3,
},
{
name: "OK_withFBSCalculated116.7",
socketID: "0",
modelCPU: 0x37,
msrFSBFreqValue: 3,
cpuBusClockValue: 116.7,
},
{
name: "OK_withFBSCalculated80",
socketID: "0",
modelCPU: 0x37,
msrFSBFreqValue: 4,
cpuBusClockValue: 80,
},
{
name: "OK_withFBSCalculatedUnknownFSBFreq",
socketID: "0",
modelCPU: 0x37,
msrFSBFreqValue: 5,
cpuBusClockValue: 116.7,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
p, mockServices := getPowerWithMockedServices()
busClockCalculate := []uint64{0x37, 0x4D}
p.cpuInfo = map[string]*cpuInfo{
tt.socketID: {cpuID: tt.socketID, physicalID: tt.socketID, model: strconv.FormatUint(tt.modelCPU, 10)},
}
if contains(busClockCalculate, tt.modelCPU) {
mockServices.msr.On("readSingleMsr", mock.Anything, msrFSBFreqString).Return(tt.msrFSBFreqValue, tt.readSingleMsrErrFSB)
}
defer mockServices.msr.AssertExpectations(t)
value := p.getBusClock(tt.socketID)
require.Equal(t, tt.cpuBusClockValue, value)
})
}
}
func TestFillCPUBusClock(t *testing.T) {
tests := []struct {
name string
modelCPU uint64
busClockValue float64
packageCPUBaseFrequencySet bool
}{
{
name: "NotSet_0",
modelCPU: 0xFF,
busClockValue: 0,
},
{
name: "Set_100",
modelCPU: 0x2A,
busClockValue: 100,
packageCPUBaseFrequencySet: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
p, _ := getPowerWithMockedServices()
p.packageCPUBaseFrequency = true
p.cpuInfo = map[string]*cpuInfo{
"0": {cpuID: "0", physicalID: "0", model: strconv.FormatUint(tt.modelCPU, 10)},
}
p.fillCPUBusClock()
require.Equal(t, tt.busClockValue, p.cpuBusClockValue)
require.Equal(t, tt.packageCPUBaseFrequencySet, p.packageCPUBaseFrequency)
})
}
}
func TestAddCPUBaseFreq(t *testing.T) {
tests := []struct {
name string
socketID string
readSingleMsrErrRatio error
msrPlatformInfoValue uint64
setupPowerstat func(t *testing.T)
clockBusValue float64
nonTurboRatio float64
metricExpected bool
}{
{
name: "Error_reading_msr",
socketID: "0",
clockBusValue: 100,
readSingleMsrErrRatio: errors.New("can't read msr"),
metricExpected: false,
},
{
name: "NoMetric_Ratio_is_0",
socketID: "0",
msrPlatformInfoValue: 0x8008082FF2810000,
clockBusValue: 100,
nonTurboRatio: 0,
metricExpected: false,
},
{
name: "OK_Ratio_is_24",
socketID: "0",
msrPlatformInfoValue: 0x8008082FF2811800,
clockBusValue: 100,
nonTurboRatio: 24,
metricExpected: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var acc testutil.Accumulator
p, mockServices := getPowerWithMockedServices()
p.cpuInfo = map[string]*cpuInfo{
tt.socketID: {cpuID: tt.socketID, physicalID: tt.socketID},
}
p.cpuBusClockValue = tt.clockBusValue
mockServices.msr.On("readSingleMsr", mock.Anything, msrPlatformInfoString).Return(tt.msrPlatformInfoValue, tt.readSingleMsrErrRatio)
defer mockServices.msr.AssertExpectations(t)
p.addCPUBaseFreq(tt.socketID, &acc)
actual := acc.GetTelegrafMetrics()
if !tt.metricExpected {
require.Len(t, actual, 0)
return
}
require.Len(t, actual, 1)
expected := []telegraf.Metric{
testutil.MustMetric(
"powerstat_package",
map[string]string{
"package_id": tt.socketID,
},
map[string]interface{}{
"cpu_base_frequency_mhz": uint64(tt.nonTurboRatio * tt.clockBusValue),
},
time.Unix(0, 0),
telegraf.Gauge,
),
}
testutil.RequireMetricsEqual(t, expected, actual, testutil.IgnoreTime())
})
}
}

View File

@ -33,6 +33,18 @@ const (
turboRatioLimit2Location = 0x1AF
atomCoreTurboRatiosLocation = 0x66C
uncorePerfStatusLocation = 0x621
platformInfo = 0xCE
fsbFreq = 0xCD
)
const (
msrTurboRatioLimitString = "MSR_TURBO_RATIO_LIMIT"
msrTurboRatioLimit1String = "MSR_TURBO_RATIO_LIMIT1"
msrTurboRatioLimit2String = "MSR_TURBO_RATIO_LIMIT2"
msrAtomCoreTurboRatiosString = "MSR_ATOM_CORE_TURBO_RATIOS"
msrUncorePerfStatusString = "MSR_UNCORE_PERF_STATUS"
msrPlatformInfoString = "MSR_PLATFORM_INFO"
msrFSBFreqString = "MSR_FSB_FREQ"
)
// msrService is responsible for interactions with MSR.
@ -157,16 +169,20 @@ func (m *msrServiceImpl) readSingleMsr(core string, msr string) (uint64, error)
var msrAddress int64
switch msr {
case "MSR_TURBO_RATIO_LIMIT":
case msrTurboRatioLimitString:
msrAddress = turboRatioLimitLocation
case "MSR_TURBO_RATIO_LIMIT1":
case msrTurboRatioLimit1String:
msrAddress = turboRatioLimit1Location
case "MSR_TURBO_RATIO_LIMIT2":
case msrTurboRatioLimit2String:
msrAddress = turboRatioLimit2Location
case "MSR_ATOM_CORE_TURBO_RATIOS":
case msrAtomCoreTurboRatiosString:
msrAddress = atomCoreTurboRatiosLocation
case "MSR_UNCORE_PERF_STATUS":
case msrUncorePerfStatusString:
msrAddress = uncorePerfStatusLocation
case msrPlatformInfoString:
msrAddress = platformInfo
case msrFSBFreqString:
msrAddress = fsbFreq
default:
return 0, fmt.Errorf("incorect name of MSR %s", msr)
}

View File

@ -10,7 +10,8 @@
## supported options list
## Supported options:
## "current_power_consumption", "current_dram_power_consumption",
## "thermal_design_power", "max_turbo_frequency", "uncore_frequency"
## "thermal_design_power", "max_turbo_frequency", "uncore_frequency",
## "cpu_base_frequency"
# package_metrics = ["current_power_consumption", "current_dram_power_consumption", "thermal_design_power"]
## The user can choose which per-CPU metrics are monitored by the plugin in