1
0
Fork 0
telegraf/plugins/inputs/intel_powerstat/metrics.go

321 lines
8.2 KiB
Go
Raw Normal View History

//go:build linux && amd64
package intel_powerstat
import (
"errors"
"fmt"
"math"
"strconv"
ptel "github.com/intel/powertelemetry"
"github.com/influxdata/telegraf"
)
// cpuMetricType is an enum type to identify core metrics.
type cpuMetricType int
// cpuMetricType enum defines supported core metrics.
const (
// metric relying on cpuFreq
cpuFrequency cpuMetricType = iota
// metric relying on msr
cpuTemperature
// metrics relying on msr with storage
cpuC0StateResidency
cpuC1StateResidency
cpuC3StateResidency
cpuC6StateResidency
cpuC7StateResidency
cpuBusyCycles // alias of cpuC0StateResidency
cpuBusyFrequency
// metrics relying on perf
cpuC0SubstateC01Percent
cpuC0SubstateC02Percent
cpuC0SubstateC0WaitPercent
)
// Helper method to return a string representation of a core metric.
func (m cpuMetricType) String() string {
switch m {
case cpuFrequency:
return "cpu_frequency"
case cpuTemperature:
return "cpu_temperature"
case cpuBusyFrequency:
return "cpu_busy_frequency"
case cpuC0StateResidency:
return "cpu_c0_state_residency"
case cpuC1StateResidency:
return "cpu_c1_state_residency"
case cpuC3StateResidency:
return "cpu_c3_state_residency"
case cpuC6StateResidency:
return "cpu_c6_state_residency"
case cpuC7StateResidency:
return "cpu_c7_state_residency"
case cpuBusyCycles:
return "cpu_busy_cycles"
case cpuC0SubstateC01Percent:
return "cpu_c0_substate_c01"
case cpuC0SubstateC02Percent:
return "cpu_c0_substate_c02"
case cpuC0SubstateC0WaitPercent:
return "cpu_c0_substate_c0_wait"
}
return ""
}
// UnmarshalText parses the cpu metric from the TOML config file
func (m *cpuMetricType) UnmarshalText(data []byte) (err error) {
parsedMetric, err := cpuMetricTypeFromString(string(data))
if err != nil {
return err
}
*m = parsedMetric
return nil
}
func cpuMetricTypeFromString(metric string) (cpuMetricType, error) {
switch metric {
case "cpu_frequency":
return cpuFrequency, nil
case "cpu_temperature":
return cpuTemperature, nil
case "cpu_busy_frequency":
return cpuBusyFrequency, nil
case "cpu_c0_state_residency":
return cpuC0StateResidency, nil
case "cpu_c1_state_residency":
return cpuC1StateResidency, nil
case "cpu_c3_state_residency":
return cpuC3StateResidency, nil
case "cpu_c6_state_residency":
return cpuC6StateResidency, nil
case "cpu_c7_state_residency":
return cpuC7StateResidency, nil
case "cpu_busy_cycles":
return cpuBusyCycles, nil
case "cpu_c0_substate_c01":
return cpuC0SubstateC01Percent, nil
case "cpu_c0_substate_c02":
return cpuC0SubstateC02Percent, nil
case "cpu_c0_substate_c0_wait":
return cpuC0SubstateC0WaitPercent, nil
}
return -1, fmt.Errorf("invalid cpu metric specified: %q", metric)
}
// packageMetricType is an enum type to identify package metrics.
type packageMetricType int
// packageMetricType enum defines supported package metrics.
const (
// metrics relying on rapl
packageCurrentPowerConsumption packageMetricType = iota
packageCurrentDramPowerConsumption
packageThermalDesignPower
// metrics relying on msr
packageCPUBaseFrequency
// hybrid metric relying on uncoreFreq as a primary mechanism and on msr as fallback mechanism.
packageUncoreFrequency
// metrics relying on msr
packageTurboLimit
)
// Helper method to return a string representation of a package metric.
func (m packageMetricType) String() string {
switch m {
case packageCurrentPowerConsumption:
return "current_power_consumption"
case packageCurrentDramPowerConsumption:
return "current_dram_power_consumption"
case packageThermalDesignPower:
return "thermal_design_power"
case packageCPUBaseFrequency:
return "cpu_base_frequency"
case packageUncoreFrequency:
return "uncore_frequency"
case packageTurboLimit:
return "max_turbo_frequency"
}
return ""
}
// UnmarshalText parses the package metric from the TOML config file
func (m *packageMetricType) UnmarshalText(data []byte) (err error) {
parsedMetric, err := packageMetricTypeFromString(string(data))
if err != nil {
return err
}
*m = parsedMetric
return nil
}
func packageMetricTypeFromString(metric string) (packageMetricType, error) {
switch metric {
case "current_power_consumption":
return packageCurrentPowerConsumption, nil
case "current_dram_power_consumption":
return packageCurrentDramPowerConsumption, nil
case "thermal_design_power":
return packageThermalDesignPower, nil
case "cpu_base_frequency":
return packageCPUBaseFrequency, nil
case "uncore_frequency":
return packageUncoreFrequency, nil
case "max_turbo_frequency":
return packageTurboLimit, nil
}
return -1, fmt.Errorf("invalid package metric specified: %q", metric)
}
// numeric is a type constraint definition.
type numeric interface {
float64 | uint64
}
// metricInfoProvider provides measurement name, fields, and tags needed by the accumulator to add a metric.
type metricInfoProvider interface {
// measurement returns a string with the name of measurement.
measurement() string
// fields returns a map of string keys with metric name and metric values.
fields() (map[string]interface{}, error)
// tags returns a map of string key and string value to add additional metric-specific information.
tags() map[string]string
// name returns the name of a metric.
name() string
}
// addMetric takes a metricInfoProvider interface and adds metric information to an accumulator.
func addMetric(acc telegraf.Accumulator, m metricInfoProvider, logOnceMap map[string]struct{}) {
fields, err := m.fields()
if err == nil {
acc.AddGauge(
m.measurement(),
fields,
m.tags(),
)
return
}
// Always add to the accumulator errors not related to module not initialized.
var moduleErr *ptel.ModuleNotInitializedError
if !errors.As(err, &moduleErr) {
acc.AddError(err)
return
}
// Add only once module not initialized error related to module and metric name.
logErrorOnce(
acc,
logOnceMap,
fmt.Sprintf("%s_%s", moduleErr.Name, m.name()),
fmt.Errorf("failed to get %q: %w", m.name(), moduleErr),
)
}
// metricCommon has metric information common to different types.
type metricCommon struct {
metric interface{}
units string
}
func (m *metricCommon) name() string {
switch m.metric.(type) {
case cpuMetricType:
return m.metric.(cpuMetricType).String()
case packageMetricType:
return m.metric.(packageMetricType).String()
default:
return ""
}
}
func (m *metricCommon) measurement() string {
switch m.metric.(type) {
case cpuMetricType:
return "powerstat_core"
case packageMetricType:
return "powerstat_package"
default:
return ""
}
}
// cpuMetric is a generic type that has the information to identify a CPU-related metric,
// as well as function to retrieve its value at any time. Implements metricAdder interface.
type cpuMetric[T numeric] struct {
metricCommon
cpuID int
coreID int
packageID int
fetchFn func(cpuID int) (T, error)
}
func (m *cpuMetric[T]) fields() (map[string]interface{}, error) {
val, err := m.fetchFn(m.cpuID)
if err != nil {
return nil, fmt.Errorf("failed to get %q for CPU ID %v: %w", m.metric, m.cpuID, err)
}
return map[string]interface{}{
fmt.Sprintf("%s_%s", m.metric, m.units): round(val),
}, nil
}
func (m *cpuMetric[T]) tags() map[string]string {
return map[string]string{
"core_id": strconv.Itoa(m.coreID),
"cpu_id": strconv.Itoa(m.cpuID),
"package_id": strconv.Itoa(m.packageID),
}
}
// packageMetric is a generic type that has the information to identify a package-related metric,
// as well as the function to retrieve its value at any time. Implements metricAdder interface.
type packageMetric[T numeric] struct {
metricCommon
packageID int
fetchFn func(packageID int) (T, error)
}
func (m *packageMetric[T]) fields() (map[string]interface{}, error) {
val, err := m.fetchFn(m.packageID)
if err != nil {
return nil, fmt.Errorf("failed to get %q for package ID %v: %w", m.metric, m.packageID, err)
}
return map[string]interface{}{
fmt.Sprintf("%s_%s", m.metric, m.units): round(val),
}, nil
}
func (m *packageMetric[T]) tags() map[string]string {
return map[string]string{
"package_id": strconv.Itoa(m.packageID),
}
}
// round returns the result of rounding the argument, only if it's a 64 bit floating-point type.
func round[T numeric](val T) T {
if v, ok := any(val).(float64); ok {
val = T(math.Round(v*100) / 100)
}
return val
}