feat(inputs.intel_powerstat): Extract business logic to external library (#14363)
This commit is contained in:
parent
57fbc73814
commit
5d598321bb
|
|
@ -201,6 +201,7 @@ following works:
|
|||
- github.com/influxdata/toml [MIT License](https://github.com/influxdata/toml/blob/master/LICENSE)
|
||||
- github.com/influxdata/wlog [MIT License](https://github.com/influxdata/wlog/blob/master/LICENSE)
|
||||
- github.com/intel/iaevents [Apache License 2.0](https://github.com/intel/iaevents/blob/main/LICENSE)
|
||||
- github.com/intel/powertelemetry [Apache License 2.0](https://github.com/intel/powertelemetry/blob/main/LICENSE)
|
||||
- github.com/jackc/chunkreader [MIT License](https://github.com/jackc/chunkreader/blob/master/LICENSE)
|
||||
- github.com/jackc/pgconn [MIT License](https://github.com/jackc/pgconn/blob/master/LICENSE)
|
||||
- github.com/jackc/pgio [MIT License](https://github.com/jackc/pgio/blob/master/LICENSE)
|
||||
|
|
@ -220,6 +221,7 @@ following works:
|
|||
- github.com/jeremywohl/flatten [MIT License](https://github.com/jeremywohl/flatten/blob/master/LICENSE)
|
||||
- github.com/jhump/protoreflect [Apache License 2.0](https://github.com/jhump/protoreflect/blob/master/LICENSE)
|
||||
- github.com/jmespath/go-jmespath [Apache License 2.0](https://github.com/jmespath/go-jmespath/blob/master/LICENSE)
|
||||
- github.com/jmhodges/clock [MIT Licence](https://github.com/jmhodges/clock/blob/main/LICENSE)
|
||||
- github.com/josharian/intern [MIT License](https://github.com/josharian/intern/blob/master/LICENSE.md)
|
||||
- github.com/josharian/native [MIT License](https://github.com/josharian/native/blob/main/license)
|
||||
- github.com/jpillora/backoff [MIT License](https://github.com/jpillora/backoff/blob/master/LICENSE)
|
||||
|
|
|
|||
6
go.mod
6
go.mod
|
|
@ -112,6 +112,7 @@ require (
|
|||
github.com/influxdata/toml v0.0.0-20190415235208-270119a8ce65
|
||||
github.com/influxdata/wlog v0.0.0-20160411224016-7c63b0a71ef8
|
||||
github.com/intel/iaevents v1.1.0
|
||||
github.com/intel/powertelemetry v1.0.0
|
||||
github.com/jackc/pgconn v1.14.1
|
||||
github.com/jackc/pgio v1.0.0
|
||||
github.com/jackc/pgtype v1.14.0
|
||||
|
|
@ -363,6 +364,7 @@ require (
|
|||
github.com/jcmturner/gofork v1.7.6 // indirect
|
||||
github.com/jcmturner/gokrb5/v8 v8.4.4 // indirect
|
||||
github.com/jcmturner/rpc/v2 v2.0.3 // indirect
|
||||
github.com/jmhodges/clock v1.2.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/josharian/native v1.1.0 // indirect
|
||||
github.com/jpillora/backoff v1.0.0 // indirect
|
||||
|
|
@ -374,7 +376,7 @@ require (
|
|||
github.com/kr/fs v0.1.0 // indirect
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/leodido/ragel-machinery v0.0.0-20181214104525-299bdde78165 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20230110061619-bbe2e5e100de // indirect
|
||||
github.com/magiconair/properties v1.8.7 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
|
|
@ -418,7 +420,7 @@ require (
|
|||
github.com/pkg/sftp v1.13.5 // indirect
|
||||
github.com/pkg/xattr v0.4.9 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20220216144756-c35f1ee13d7c // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect
|
||||
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/robertkrimen/otto v0.0.0-20191219234010-c382bd3c16ff // indirect
|
||||
|
|
|
|||
12
go.sum
12
go.sum
|
|
@ -1535,6 +1535,8 @@ github.com/influxdata/wlog v0.0.0-20160411224016-7c63b0a71ef8 h1:W2IgzRCb0L9VzMu
|
|||
github.com/influxdata/wlog v0.0.0-20160411224016-7c63b0a71ef8/go.mod h1:/2NMgWB1DHM1ti/gqhOlg+LJeBVk6FqR5aVGYY0hlwI=
|
||||
github.com/intel/iaevents v1.1.0 h1:FzxMBfXk/apG2EUXUCfaq3gUQ+q+TgZ1HNMjjUILUGE=
|
||||
github.com/intel/iaevents v1.1.0/go.mod h1:CyUUzXw0lHRCsmyyF7Pwco9Y7NiTNQUUlcJ7RJAazKs=
|
||||
github.com/intel/powertelemetry v1.0.0 h1:9MP7OjNSqPPok1GCMRcVvToAcIJ4HvuNgt9rq7shnfk=
|
||||
github.com/intel/powertelemetry v1.0.0/go.mod h1:0/EKcFml0Imic4Mva8QzsZhT/L0nc3Y+MbT9IU0y1FA=
|
||||
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
|
||||
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
||||
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
|
||||
|
|
@ -1610,6 +1612,8 @@ github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9Y
|
|||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||
github.com/jmhodges/clock v1.2.0 h1:eq4kys+NI0PLngzaHEe7AmPT90XMGIEySD1JfV1PDIs=
|
||||
github.com/jmhodges/clock v1.2.0/go.mod h1:qKjhA7x7u/lQpPB1XAqX1b1lCI/w3/fNuYpI/ZjLynI=
|
||||
github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
|
||||
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
||||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||
|
|
@ -1721,8 +1725,8 @@ github.com/loov/hrtime v1.0.3/go.mod h1:yDY3Pwv2izeY4sq7YcPX/dtLwzg5NU1AxWuWxKwd
|
|||
github.com/loov/hrtime/hrplot v1.0.2/go.mod h1:9t65xYn4d42ntjv40Wt5lbU72/VC5S0zGDgjC8kD5BU=
|
||||
github.com/loov/plot v0.0.0-20200413101321-e09a6f01d2f5/go.mod h1:gSrhfSMoiPGG0CZ9E66kXjaHxFw0fzJhooyicOnz5z4=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
||||
github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c h1:VtwQ41oftZwlMnOEbMWQtSEUgU64U4s+GHk7hZK+jtY=
|
||||
github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c/go.mod h1:JKx41uQRwqlTZabZc+kILPrO/3jlKnQ2Z8b7YiVw5cE=
|
||||
github.com/lufia/plan9stats v0.0.0-20230110061619-bbe2e5e100de h1:V53FWzU6KAZVi1tPp5UIsMoUWJ2/PNwYIDXnu7QuBCE=
|
||||
github.com/lufia/plan9stats v0.0.0-20230110061619-bbe2e5e100de/go.mod h1:JKx41uQRwqlTZabZc+kILPrO/3jlKnQ2Z8b7YiVw5cE=
|
||||
github.com/lxc/lxd v0.0.0-20220920163450-e9b4b514106a h1:VCh69Giyzh/1qPZHC62ysQvFGI93vEQkFNo7iApvlzM=
|
||||
github.com/lxc/lxd v0.0.0-20220920163450-e9b4b514106a/go.mod h1:Y+Ny8KSylQRtfyOxVN0Cha/jAfd2l1AlHiDulGP+GQk=
|
||||
github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA=
|
||||
|
|
@ -2003,8 +2007,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH
|
|||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/power-devops/perfstat v0.0.0-20220216144756-c35f1ee13d7c h1:NRoLoZvkBTKvR5gQLgA3e0hqjkY9u1wm+iOL45VN/qI=
|
||||
github.com/power-devops/perfstat v0.0.0-20220216144756-c35f1ee13d7c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b h1:0LFwY6Q3gMACTjAbMZBjXAqTOzOwFaj2Ld6cjeQ7Rig=
|
||||
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/prometheus-community/pro-bing v0.3.0 h1:SFT6gHqXwbItEDJhTkzPWVqU6CLEtqEfNAPp47RUON4=
|
||||
github.com/prometheus-community/pro-bing v0.3.0/go.mod h1:p9dLb9zdmv+eLxWfCT6jESWuDrS+YzpPkQBgysQF8a0=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
|
|
|
|||
|
|
@ -27,15 +27,15 @@ See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
|
|||
# Intel PowerStat plugin enables monitoring of platform metrics (power, TDP)
|
||||
# and per-CPU metrics like temperature, power and utilization. Please see the
|
||||
# plugin readme for details on software and hardware compatability.
|
||||
# This plugin ONLY supports Linux
|
||||
# This plugin ONLY supports Linux.
|
||||
[[inputs.intel_powerstat]]
|
||||
## The user can choose which package metrics are monitored by the plugin with
|
||||
## the package_metrics setting:
|
||||
## - The default, will collect "current_power_consumption",
|
||||
## "current_dram_power_consumption" and "thermal_design_power"
|
||||
## - Leaving this setting empty means no package metrics will be collected
|
||||
## "current_dram_power_consumption" and "thermal_design_power".
|
||||
## - Leaving this setting empty means no package metrics will be collected.
|
||||
## - Finally, a user can specify individual metrics to capture from the
|
||||
## supported options list
|
||||
## supported options list.
|
||||
## Supported options:
|
||||
## "current_power_consumption", "current_dram_power_consumption",
|
||||
## "thermal_design_power", "max_turbo_frequency", "uncore_frequency",
|
||||
|
|
@ -48,13 +48,49 @@ See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
|
|||
## by the plugin.
|
||||
## Supported options:
|
||||
## "cpu_frequency", "cpu_c0_state_residency", "cpu_c1_state_residency",
|
||||
## "cpu_c6_state_residency", "cpu_busy_cycles", "cpu_temperature",
|
||||
## "cpu_busy_frequency"
|
||||
## ATTENTION: cpu_busy_cycles is DEPRECATED - use cpu_c0_state_residency
|
||||
## "cpu_c3_state_residency", "cpu_c6_state_residency", "cpu_c7_state_residency",
|
||||
## "cpu_temperature", "cpu_busy_frequency", "cpu_c0_substate_c01",
|
||||
## "cpu_c0_substate_c02", "cpu_c0_substate_c0_wait"
|
||||
# cpu_metrics = []
|
||||
|
||||
## Optionally the user can choose for which CPUs metrics configured in cpu_metrics array should be gathered.
|
||||
## Can't be combined with excluded_cpus.
|
||||
## Empty or missing array means CPU metrics are gathered for all CPUs.
|
||||
## e.g. ["0-3", "4,5,6"] or ["1-3,4"]
|
||||
# included_cpus = []
|
||||
|
||||
## Optionally the user can choose which CPUs should be excluded from gathering metrics configured in cpu_metrics array.
|
||||
## Can't be combined with included_cpus.
|
||||
## Empty or missing array means CPU metrics are gathered for all CPUs.
|
||||
## e.g. ["0-3", "4,5,6"] or ["1-3,4"]
|
||||
# excluded_cpus = []
|
||||
|
||||
## Filesystem location of JSON file that contains PMU event definitions.
|
||||
## Mandatory only for perf-related metrics (cpu_c0_substate_c01, cpu_c0_substate_c02, cpu_c0_substate_c0_wait).
|
||||
# event_definitions = ""
|
||||
|
||||
## The user can set the timeout duration for MSR reading.
|
||||
## Enabling this timeout can be useful in situations where, on heavily loaded systems,
|
||||
## the code waits too long for a kernel response to MSR read requests.
|
||||
## 0 disables the timeout (default).
|
||||
# msr_read_timeout = "0ms"
|
||||
```
|
||||
|
||||
## Example: Configuration with no per-CPU telemetry
|
||||
### Configuration notes
|
||||
|
||||
1. The configuration of `included_cpus` or `excluded_cpus` may affect the ability to collect `package_metrics`.
|
||||
Some of them (`max_turbo_frequency`, `cpu_base_frequency`, and `uncore_frequency`) need to read data
|
||||
from exactly one processor for each package. If `included_cpus` or `excluded_cpus` exclude all processors
|
||||
from the package, reading the mentioned metrics for that package will not be possible.
|
||||
2. `event_definitions` JSON file for specific architecture can be found at [perfmon](https://github.com/intel/perfmon).
|
||||
A script to download the event definition that is appropriate for current environment (`event_download.py`) is
|
||||
available at [pmu-tools](https://github.com/andikleen/pmu-tools).
|
||||
For perf-related metrics supported by this plugin, an event definition JSON file
|
||||
with events for the `core` is required.
|
||||
|
||||
For example: `sapphirerapids_core.json` or `GenuineIntel-6-8F-core.json`.
|
||||
|
||||
### Example: Configuration with no per-CPU telemetry
|
||||
|
||||
This configuration allows getting default processor package specific metrics,
|
||||
no per-CPU metrics are collected:
|
||||
|
|
@ -64,7 +100,7 @@ no per-CPU metrics are collected:
|
|||
cpu_metrics = []
|
||||
```
|
||||
|
||||
## Example: Configuration with no per-CPU telemetry - equivalent case
|
||||
### Example: Configuration with no per-CPU telemetry - equivalent case
|
||||
|
||||
This configuration allows getting default processor package specific metrics,
|
||||
no per-CPU metrics are collected:
|
||||
|
|
@ -73,268 +109,276 @@ no per-CPU metrics are collected:
|
|||
[[inputs.intel_powerstat]]
|
||||
```
|
||||
|
||||
## Example: Configuration for CPU Temperature and CPU Frequency
|
||||
### Example: Configuration for CPU Temperature and CPU Frequency
|
||||
|
||||
This configuration allows getting default processor package specific metrics,
|
||||
plus subset of per-CPU metrics (CPU Temperature and CPU Frequency):
|
||||
plus subset of per-CPU metrics (CPU Temperature and CPU Frequency) which will be
|
||||
gathered only for `cpu_id = 0`:
|
||||
|
||||
```toml
|
||||
[[inputs.intel_powerstat]]
|
||||
cpu_metrics = ["cpu_frequency", "cpu_temperature"]
|
||||
included_cpus = ["0"]
|
||||
```
|
||||
|
||||
## Example: Configuration for CPU Temperature and CPU Frequency without default package metrics
|
||||
### Example: Configuration for CPU Temperature and CPU Frequency without default package metrics
|
||||
|
||||
This configuration allows getting only a subset of per-CPU metrics (CPU
|
||||
Temperature and CPU Frequency):
|
||||
This configuration allows getting only a subset of per-CPU metrics
|
||||
(CPU Temperature and CPU Frequency) which will be gathered for
|
||||
all `cpus` except `cpu_id = ["1-3"]`:
|
||||
|
||||
```toml
|
||||
[[inputs.intel_powerstat]]
|
||||
package_metrics = []
|
||||
cpu_metrics = ["cpu_frequency", "cpu_temperature"]
|
||||
excluded_cpus = ["1-3"]
|
||||
```
|
||||
|
||||
## Example: Configuration with all available metrics
|
||||
### Example: Configuration with all available metrics
|
||||
|
||||
This configuration allows getting all processor package specific metrics and
|
||||
all per-CPU metrics:
|
||||
|
||||
```toml
|
||||
[[inputs.intel_powerstat]]
|
||||
package_metrics = ["current_power_consumption", "current_dram_power_consumption", "thermal_design_power", "max_turbo_frequency", "uncore_frequency"]
|
||||
cpu_metrics = ["cpu_frequency", "cpu_busy_frequency", "cpu_temperature", "cpu_c0_state_residency", "cpu_c1_state_residency", "cpu_c6_state_residency"]
|
||||
package_metrics = ["current_power_consumption", "current_dram_power_consumption", "thermal_design_power", "max_turbo_frequency", "uncore_frequency", "cpu_base_frequency"]
|
||||
cpu_metrics = ["cpu_frequency", "cpu_c0_state_residency", "cpu_c1_state_residency", "cpu_c3_state_residency", "cpu_c6_state_residency", "cpu_c7_state_residency", "cpu_temperature", "cpu_busy_frequency", "cpu_c0_substate_c01", "cpu_c0_substate_c02", "cpu_c0_substate_c0_wait"]
|
||||
event_definitions = "/home/telegraf/.cache/pmu-events/GenuineIntel-6-8F-core.json"
|
||||
```
|
||||
|
||||
## SW Dependencies
|
||||
|
||||
Plugin is based on Linux Kernel modules that expose specific metrics over
|
||||
### Kernel modules
|
||||
|
||||
Plugin is mostly based on Linux Kernel modules that expose specific metrics over
|
||||
`sysfs` or `devfs` interfaces. The following dependencies are expected by
|
||||
plugin:
|
||||
|
||||
- _intel-rapl_ module which exposes Intel Runtime Power Limiting metrics over
|
||||
- `intel-rapl` kernel module which exposes Intel Runtime Power Limiting metrics over
|
||||
`sysfs` (`/sys/devices/virtual/powercap/intel-rapl`),
|
||||
- _msr_ kernel module that provides access to processor model specific
|
||||
- `msr` kernel module that provides access to processor model specific
|
||||
registers over `devfs` (`/dev/cpu/cpu%d/msr`),
|
||||
- _cpufreq_ kernel module - which exposes per-CPU Frequency over `sysfs`
|
||||
(`/sys/devices/system/cpu/cpu%d/cpufreq/scaling_cur_freq`).
|
||||
- _intel-uncore-frequency_ module exposes Intel uncore frequency metrics
|
||||
over `sysfs` (`/sys/devices/system/cpu/intel_uncore_frequency`),
|
||||
- `cpufreq` kernel module - which exposes per-CPU Frequency over `sysfs`
|
||||
(`/sys/devices/system/cpu/cpu%d/cpufreq/scaling_cur_freq`),
|
||||
- `intel-uncore-frequency` kernel module exposes Intel uncore frequency metrics
|
||||
over `sysfs` (`/sys/devices/system/cpu/intel_uncore_frequency`).
|
||||
|
||||
Minimum kernel version required is 3.13 to satisfy most of requirements,
|
||||
for `uncore_frequency` metrics `intel-uncore-frequency` module is required
|
||||
(available since kernel 5.6).
|
||||
|
||||
Please make sure that kernel modules are loaded and running (cpufreq is
|
||||
integrated in kernel). Modules might have to be manually enabled by using
|
||||
`modprobe`. Depending on the kernel version, run commands:
|
||||
Make sure that required kernel modules are loaded and running.
|
||||
Modules might have to be manually enabled by using `modprobe`.
|
||||
Depending on the kernel version, run commands:
|
||||
|
||||
```sh
|
||||
# kernel 5.x.x:
|
||||
# rapl modules:
|
||||
## kernel < 4.0
|
||||
sudo modprobe intel_rapl
|
||||
## kernel >= 4.0
|
||||
sudo modprobe rapl
|
||||
sudo modprobe msr
|
||||
sudo modprobe intel_rapl_common
|
||||
sudo modprobe intel_rapl_msr
|
||||
|
||||
# also for kernel >= 5.6.0
|
||||
sudo modprobe intel-uncore-frequency
|
||||
|
||||
# kernel 4.x.x:
|
||||
# msr module:
|
||||
sudo modprobe msr
|
||||
sudo modprobe intel_rapl
|
||||
|
||||
# cpufreq module:
|
||||
### integrated in kernel
|
||||
|
||||
# intel-uncore-frequency module:
|
||||
## only for kernel >= 5.6.0
|
||||
sudo modprobe intel-uncore-frequency
|
||||
```
|
||||
|
||||
**Telegraf with Intel PowerStat plugin enabled may require root access to read
|
||||
model specific registers (MSRs)** to retrieve data for calculation of most
|
||||
critical per-CPU specific metrics:
|
||||
### Kernel's perf interface
|
||||
|
||||
- `cpu_busy_frequency_mhz`
|
||||
- `cpu_temperature_celsius`
|
||||
- `cpu_c0_state_residency_percent`
|
||||
- `cpu_c1_state_residency_percent`
|
||||
- `cpu_c6_state_residency_percent`
|
||||
For perf-related metrics, when Telegraf is not running as root,
|
||||
the following capability should be added to the Telegraf executable:
|
||||
|
||||
and to retrieve data for calculation per-package specific metric:
|
||||
```sh
|
||||
sudo setcap cap_sys_admin+ep <path_to_telegraf_binary>
|
||||
```
|
||||
|
||||
- `max_turbo_frequency_mhz`
|
||||
- `uncore_frequency_mhz_cur`
|
||||
- `cpu_base_frequency_mhz`
|
||||
Alternatively, `/proc/sys/kernel/perf_event_paranoid` has to be set to
|
||||
value less than 1.
|
||||
|
||||
To expose other Intel PowerStat metrics root access may or may not be required
|
||||
Depending on environment and configuration (number of monitored CPUs
|
||||
and number of enabled metrics), it might be required to increase
|
||||
the limit on the number of open file descriptors allowed.
|
||||
This can be done for example by using `ulimit -n` command.
|
||||
|
||||
### Dependencies of metrics on system configuration
|
||||
|
||||
Details of these dependencies are discussed above:
|
||||
|
||||
| Configuration option | Type | Dependency |
|
||||
|-------------------------------------------------------------------------------------|-------------------|------------------------------------------------|
|
||||
| `current_power_consumption` | `package_metrics` | `rapl` kernel module(s) |
|
||||
| `current_dram_power_consumption` | `package_metrics` | `rapl` kernel module(s) |
|
||||
| `thermal_design_power` | `package_metrics` | `rapl` kernel module(s) |
|
||||
| `max_turbo_frequency` | `package_metrics` | `msr` kernel module |
|
||||
| `uncore_frequency` | `package_metrics` | `intel-uncore-frequency`/`msr` kernel modules* |
|
||||
| `cpu_base_frequency` | `package_metrics` | `msr` kernel module |
|
||||
| `cpu_frequency` | `cpu_metrics` | `cpufreq` kernel module |
|
||||
| `cpu_c0_state_residency` | `cpu_metrics` | `msr` kernel module |
|
||||
| `cpu_c1_state_residency` | `cpu_metrics` | `msr` kernel module |
|
||||
| `cpu_c3_state_residency` | `cpu_metrics` | `msr` kernel module |
|
||||
| `cpu_c6_state_residency` | `cpu_metrics` | `msr` kernel module |
|
||||
| `cpu_c7_state_residency` | `cpu_metrics` | `msr` kernel module |
|
||||
| `cpu_busy_cycles` (**DEPRECATED** - superseded by `cpu_c0_state_residency_percent`) | `cpu_metrics` | `msr` kernel module |
|
||||
| `cpu_temperature` | `cpu_metrics` | `msr` kernel module |
|
||||
| `cpu_busy_frequency` | `cpu_metrics` | `msr` kernel module |
|
||||
| `cpu_c0_substate_c01` | `cpu_metrics` | kernel's `perf` interface |
|
||||
| `cpu_c0_substate_c02` | `cpu_metrics` | kernel's `perf` interface |
|
||||
| `cpu_c0_substate_c0_wait` | `cpu_metrics` | kernel's `perf` interface |
|
||||
|
||||
*for all metrics enabled by the configuration option `uncore_frequency`,
|
||||
starting from kernel version 5.18, only the `intel-uncore-frequency` module
|
||||
is required. For older kernel versions, the metric `uncore_frequency_mhz_cur`
|
||||
requires the `msr` module to be enabled.
|
||||
|
||||
### Root privileges
|
||||
|
||||
**Telegraf with Intel PowerStat plugin enabled may require
|
||||
root privileges to read all the metrics**
|
||||
(depending on OS type or configuration).
|
||||
|
||||
Alternatively, the following capabilities can be added to
|
||||
the Telegraf executable:
|
||||
|
||||
```sh
|
||||
#without perf-related metrics:
|
||||
sudo setcap cap_sys_rawio,cap_dac_read_search+ep <path_to_telegraf_binary>
|
||||
|
||||
#with perf-related metrics:
|
||||
sudo setcap cap_sys_rawio,cap_dac_read_search,cap_sys_admin+ep <path_to_telegraf_binary>
|
||||
```
|
||||
|
||||
## HW Dependencies
|
||||
|
||||
Specific metrics require certain processor features to be present, otherwise
|
||||
Intel PowerStat plugin won't be able to read them. When using Linux Kernel based
|
||||
OS, user can detect supported processor features reading `/proc/cpuinfo` file.
|
||||
Intel PowerStat plugin won't be able to read them. The user can detect supported
|
||||
processor features by reading `/proc/cpuinfo` file.
|
||||
Plugin assumes crucial properties are the same for all CPU cores in the system.
|
||||
The following processor properties are examined in more detail in this section:
|
||||
processor _cpu family_, _model_ and _flags_. The following processor properties
|
||||
are required by the plugin:
|
||||
|
||||
- Processor _cpu family_ must be Intel (0x6) - since data used by the plugin
|
||||
assumes Intel specific model specific registers for all features
|
||||
The following `processor` properties are examined in more detail
|
||||
in this section:
|
||||
|
||||
- `vendor_id`
|
||||
- `cpu family`
|
||||
- `model`
|
||||
- `flags`
|
||||
|
||||
The following processor properties are required by the plugin:
|
||||
|
||||
- Processor `vendor_id` must be `GenuineIntel` and `cpu family` must be `6` -
|
||||
since data used by the plugin are Intel-specific.
|
||||
- The following processor flags shall be present:
|
||||
- "_msr_" shall be present for plugin to read platform data from processor
|
||||
- `msr` shall be present for plugin to read platform data from processor
|
||||
model specific registers and collect the following metrics:
|
||||
_powerstat\_core.cpu\_temperature_, _powerstat\_core.cpu\_busy\_frequency_,
|
||||
_powerstat\_core.cpu\_c0\_state\_residency_,
|
||||
_powerstat\_core.cpu\_c1\_state\_residency_,
|
||||
_powerstat\_core.cpu\_c6\_state\_residency_
|
||||
- "_aperfmperf_" shall be present to collect the following metrics:
|
||||
_powerstat\_core.cpu\_busy\_frequency_,
|
||||
_powerstat\_core.cpu\_c0\_state\_residency_,
|
||||
_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_ /
|
||||
_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 |
|
||||
| 0x5F | Intel Atom® Denverton |
|
||||
| 0x7A | Intel Atom® Goldmont |
|
||||
| 0x4C | Intel Atom® Airmont |
|
||||
| 0x86 | Intel Atom® Jacobsville |
|
||||
| 0x96 | Intel Atom® Elkhart Lake |
|
||||
| 0x9C | Intel Atom® Jasper Lake |
|
||||
| 0x1A | Intel Nehalem-EP |
|
||||
| 0x1E | Intel Nehalem |
|
||||
| 0x1F | Intel Nehalem-G |
|
||||
| 0x2E | Intel Nehalem-EX |
|
||||
| 0x25 | Intel Westmere |
|
||||
| 0x2C | Intel Westmere-EP |
|
||||
| 0x2F | Intel Westmere-EX |
|
||||
| 0x2A | Intel Sandybridge |
|
||||
| 0x2D | Intel Sandybridge-X |
|
||||
| 0x3A | Intel Ivybridge |
|
||||
| 0x3E | Intel Ivybridge-X |
|
||||
| 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 |
|
||||
| 0x3C | Intel Haswell |
|
||||
| 0x3F | Intel Haswell-X |
|
||||
| 0x45 | Intel Haswell-L |
|
||||
| 0x46 | Intel Haswell-G |
|
||||
| 0x3D | Intel Broadwell |
|
||||
| 0x47 | Intel Broadwell-G |
|
||||
| 0x4F | Intel Broadwell-X |
|
||||
| 0x56 | Intel Broadwell-D |
|
||||
| 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 |
|
||||
|
||||
### uncore frequency
|
||||
|
||||
Note that only certain processors support the uncore frequency module as well:
|
||||
|
||||
| Model number | Processor name |
|
||||
|--------------|---------------------------------|
|
||||
| 0x55 | Intel Skylake-X |
|
||||
| 0x6A | Intel IceLake-X |
|
||||
| 0x6C | Intel IceLake-D |
|
||||
| 0x47 | Intel Broadwell-G |
|
||||
| 0x4F | Intel Broadwell-X |
|
||||
| 0x56 | Intel Broadwell-D |
|
||||
| 0x8F | Intel Sapphire Rapids X |
|
||||
| 0xCF | Intel Emerald Rapids X |
|
||||
- `cpu_c0_state_residency`
|
||||
- `cpu_c1_state_residency`
|
||||
- `cpu_c3_state_residency`
|
||||
- `cpu_c6_state_residency`
|
||||
- `cpu_c7_state_residency`
|
||||
- `cpu_busy_cycles` (**DEPRECATED** - superseded by `cpu_c0_state_residency_percent`)
|
||||
- `cpu_busy_frequency`
|
||||
- `cpu_temperature`
|
||||
- `cpu_base_frequency`
|
||||
- `max_turbo_frequency`
|
||||
- `uncore_frequency` (for kernel < 5.18)
|
||||
- `aperfmperf` shall be present to collect the following metrics:
|
||||
- `cpu_c0_state_residency`
|
||||
- `cpu_c1_state_residency`
|
||||
- `cpu_busy_cycles` (**DEPRECATED** - superseded by `cpu_c0_state_residency_percent`)
|
||||
- `cpu_busy_frequency`
|
||||
- `dts` shall be present to collect:
|
||||
- `cpu_temperature`
|
||||
- Please consult the table of [supported CPU models](#supported-cpu-models) to see which metrics are supported by your `model`. The following metrics exist:
|
||||
- `cpu_c1_state_residency`
|
||||
- `cpu_c3_state_residency`
|
||||
- `cpu_c6_state_residency`
|
||||
- `cpu_c7_state_residency`
|
||||
- `cpu_temperature`
|
||||
- `cpu_base_frequency`
|
||||
- `uncore_frequency`
|
||||
|
||||
## Metrics
|
||||
|
||||
All metrics collected by Intel PowerStat plugin are collected in fixed
|
||||
intervals. Metrics that reports processor C-state residency or power are
|
||||
calculated over elapsed intervals. When starting to measure metrics, plugin
|
||||
skips first iteration of metrics if they are based on deltas with previous
|
||||
value.
|
||||
calculated over elapsed intervals.
|
||||
|
||||
**The following measurements are supported by Intel PowerStat plugin:**
|
||||
|
||||
- powerstat_core
|
||||
- `powerstat_core`
|
||||
- The following tags are returned by plugin with
|
||||
`powerstat_core` measurements:
|
||||
|
||||
- The following Tags are returned by plugin with powerstat_core measurements:
|
||||
| Tag | Description |
|
||||
|--------------|--------------------------------|
|
||||
| `package_id` | ID of platform package/socket. |
|
||||
| `core_id` | ID of physical processor core. |
|
||||
| `cpu_id` | ID of logical processor core. |
|
||||
|
||||
| Tag | Description |
|
||||
|--------------|-------------------------------|
|
||||
| `package_id` | ID of platform package/socket |
|
||||
| `core_id` | ID of physical processor core |
|
||||
| `cpu_id` | ID of logical processor core |
|
||||
Measurement `powerstat_core` metrics are collected per-CPU (`cpu_id` is the key)
|
||||
while `core_id` and `package_id` tags are additional topology information.
|
||||
|
||||
Measurement powerstat_core metrics are collected per-CPU (cpu_id is the key)
|
||||
while core_id and package_id tags are additional topology information.
|
||||
- Available metrics for `powerstat_core` measurement:
|
||||
|
||||
- Available metrics for powerstat_core measurement
|
||||
| Metric name (field) | Description | Units |
|
||||
|-----------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------|
|
||||
| `cpu_frequency_mhz` | Current operational frequency of CPU Core. | MHz |
|
||||
| `cpu_busy_frequency_mhz` | CPU Core Busy Frequency measured as frequency adjusted to CPU Core busy cycles. | MHz |
|
||||
| `cpu_temperature_celsius` | Current temperature of CPU Core. | Celsius degrees |
|
||||
| `cpu_c0_state_residency_percent` | Percentage of time that CPU Core spent in C0 Core residency state. | % |
|
||||
| `cpu_c1_state_residency_percent` | Percentage of time that CPU Core spent in C1 Core residency state. | % |
|
||||
| `cpu_c3_state_residency_percent` | Percentage of time that CPU Core spent in C3 Core residency state. | % |
|
||||
| `cpu_c6_state_residency_percent` | Percentage of time that CPU Core spent in C6 Core residency state. | % |
|
||||
| `cpu_c7_state_residency_percent` | Percentage of time that CPU Core spent in C7 Core residency state. | % |
|
||||
| `cpu_c0_substate_c01_percent` | Percentage of time that CPU Core spent in C0.1 substate out of the total time in the C0 state. | % |
|
||||
| `cpu_c0_substate_c02_percent` | Percentage of time that CPU Core spent in C0.2 substate out of the total time in the C0 state. | % |
|
||||
| `cpu_c0_substate_c0_wait_percent` | Percentage of time that CPU Core spent in C0_Wait substate out of the total time in the C0 state. | % |
|
||||
| `cpu_busy_cycles_percent` | (**DEPRECATED** - superseded by cpu_c0_state_residency_percent) CPU Core Busy cycles as a ratio of Cycles spent in C0 state residency to all cycles executed by CPU Core. | % |
|
||||
|
||||
| Metric name (field) | Description | Units |
|
||||
|---------------------|-------------|-------|
|
||||
| `cpu_frequency_mhz` | Current operational frequency of CPU Core | MHz |
|
||||
| `cpu_busy_frequency_mhz` | CPU Core Busy Frequency measured as frequency adjusted to CPU Core busy cycles | MHz |
|
||||
| `cpu_temperature_celsius` | Current temperature of CPU Core | Celsius degrees |
|
||||
| `cpu_c0_state_residency_percent` | Percentage of time that CPU Core spent in C0 Core residency state | % |
|
||||
| `cpu_c1_state_residency_percent` | Percentage of time that CPU Core spent in C1 Core residency state | % |
|
||||
| `cpu_c6_state_residency_percent` | Percentage of time that CPU Core spent in C6 Core residency state | % |
|
||||
| `cpu_busy_cycles_percent` | (**DEPRECATED** - superseded by cpu_c0_state_residency_percent) CPU Core Busy cycles as a ratio of Cycles spent in C0 state residency to all cycles executed by CPU Core | % |
|
||||
- `powerstat_package`
|
||||
- The following tags are returned by plugin with `powerstat_package` measurements:
|
||||
|
||||
- powerstat_package
|
||||
- The following Tags are returned by plugin with powerstat_package measurements:
|
||||
| Tag | Description |
|
||||
|----------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `package_id` | ID of platform package/socket. |
|
||||
| `active_cores` | Specific tag for `max_turbo_frequency_mhz` metric. The maximum number of activated cores for reachable turbo frequency. |
|
||||
| `hybrid` | Specific tag for `max_turbo_frequency_mhz` metric. Available only for hybrid processors. Will be set to `primary` for primary cores of a hybrid architecture, and to `secondary` for secondary cores of a hybrid architecture. |
|
||||
| `die` | Specific tag for all `uncore_frequency` metrics. Id of die. |
|
||||
| `type` | Specific tag for all `uncore_frequency` metrics. Type of uncore frequency (`current` or `initial`). |
|
||||
|
||||
| Tag | Description |
|
||||
|-----|-------------|
|
||||
| `package_id` | ID of platform package/socket |
|
||||
| `active_cores`| Specific tag for `max_turbo_frequency_mhz` metric. The maximum number of activated cores for reachable turbo frequency
|
||||
| `die`| Specific tag for all `uncore_frequency` metrics. Id of die
|
||||
| `type`| Specific tag for all `uncore_frequency` metrics. Type of uncore frequency (current or initial)
|
||||
Measurement `powerstat_package` metrics are collected per processor package
|
||||
`package_id` tag indicates which package metric refers to.
|
||||
|
||||
Measurement powerstat_package metrics are collected per processor package
|
||||
_package_id_ tag indicates which package metric refers to.
|
||||
- Available metrics for powerstat_package measurement
|
||||
- Available metrics for `powerstat_package` measurement:
|
||||
|
||||
| Metric name (field) | Description | Units |
|
||||
|-----|-------------|-----|
|
||||
| `thermal_design_power_watts` | Maximum Thermal Design Power (TDP) available for processor package | Watts |
|
||||
| `current_power_consumption_watts` | Current power consumption of processor package | Watts |
|
||||
| `current_dram_power_consumption_watts` | Current power consumption of processor package DRAM subsystem | Watts |
|
||||
| `max_turbo_frequency_mhz`| Maximum reachable turbo frequency for number of cores active | MHz
|
||||
| `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
|
||||
| Metric name (field) | Description | Units |
|
||||
|----------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------|
|
||||
| `thermal_design_power_watts` | Maximum Thermal Design Power (TDP) available for processor package. | Watts |
|
||||
| `current_power_consumption_watts` | Current power consumption of processor package. | Watts |
|
||||
| `current_dram_power_consumption_watts` | Current power consumption of processor package DRAM subsystem. | Watts |
|
||||
| `max_turbo_frequency_mhz` | Maximum reachable turbo frequency for number of cores active. | MHz |
|
||||
| `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`. This value is available from `intel-uncore-frequency` module for kernel >= 5.18. For older kernel versions 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
|
||||
|
||||
From linux kernel version v5.4.77 with [this kernel change][19f6d91b] resources
|
||||
like `/sys/class/powercap/intel-rapl*/*/energy_uj` are readable only by root for
|
||||
security reasons, so this plugin needs root privileges to work properly.
|
||||
Starting from Linux kernel version v5.4.77, due to
|
||||
[this kernel change][19f6d91b], resources such as
|
||||
`/sys/devices/virtual/powercap/intel-rapl//*/energy_uj`
|
||||
can only be accessed by the root user for security reasons.
|
||||
Therefore, this plugin requires root privileges to gather
|
||||
`rapl` metrics correctly.
|
||||
|
||||
If such strict security restrictions are not relevant, reading permissions to
|
||||
files in `/sys/devices/virtual/powercap/intel-rapl/` directory can be manually
|
||||
changed for example with `chmod` command with custom parameters. For example to
|
||||
give all users permission to all files in `intel-rapl` directory:
|
||||
If such strict security restrictions are not relevant, reading permissions for
|
||||
files in the `/sys/devices/virtual/powercap/intel-rapl/` directory can be
|
||||
manually altered, for example, using the chmod command with custom parameters.
|
||||
For instance, read and execute permissions for all files in the
|
||||
intel-rapl directory can be granted to all users using:
|
||||
|
||||
```bash
|
||||
sudo chmod -R a+rx /sys/devices/virtual/powercap/intel-rapl/
|
||||
|
|
@ -355,8 +399,82 @@ powerstat_package,die=0,host=ubuntu,package_id=0,type=initial uncore_frequency_l
|
|||
powerstat_package,die=0,host=ubuntu,package_id=0,type=current uncore_frequency_mhz_cur=800i,uncore_frequency_limit_mhz_min=800,uncore_frequency_limit_mhz_max=2400 1606494744000000000
|
||||
powerstat_core,core_id=0,cpu_id=0,host=ubuntu,package_id=0 cpu_frequency_mhz=1200.29 1606494744000000000
|
||||
powerstat_core,core_id=0,cpu_id=0,host=ubuntu,package_id=0 cpu_temperature_celsius=34i 1606494744000000000
|
||||
powerstat_core,core_id=0,cpu_id=0,host=ubuntu,package_id=0 cpu_c6_state_residency_percent=92.52 1606494744000000000
|
||||
powerstat_core,core_id=0,cpu_id=0,host=ubuntu,package_id=0 cpu_c1_state_residency_percent=6.68 1606494744000000000
|
||||
powerstat_core,core_id=0,cpu_id=0,host=ubuntu,package_id=0 cpu_c0_state_residency_percent=0.8 1606494744000000000
|
||||
powerstat_core,core_id=0,cpu_id=0,host=ubuntu,package_id=0 cpu_c1_state_residency_percent=6.68 1606494744000000000
|
||||
powerstat_core,core_id=0,cpu_id=0,host=ubuntu,package_id=0 cpu_c3_state_residency_percent=0 1606494744000000000
|
||||
powerstat_core,core_id=0,cpu_id=0,host=ubuntu,package_id=0 cpu_c6_state_residency_percent=92.52 1606494744000000000
|
||||
powerstat_core,core_id=0,cpu_id=0,host=ubuntu,package_id=0 cpu_c7_state_residency_percent=0 1606494744000000000
|
||||
powerstat_core,core_id=0,cpu_id=0,host=ubuntu,package_id=0 cpu_busy_frequency_mhz=1213.24 1606494744000000000
|
||||
powerstat_core,core_id=0,cpu_id=0,host=ubuntu,package_id=0 cpu_c0_substate_c01_percent=0 1606494744000000000
|
||||
powerstat_core,core_id=0,cpu_id=0,host=ubuntu,package_id=0 cpu_c0_substate_c02_percent=5.68 1606494744000000000
|
||||
powerstat_core,core_id=0,cpu_id=0,host=ubuntu,package_id=0 cpu_c0_substate_c0_wait_percent=43.74 1606494744000000000
|
||||
```
|
||||
|
||||
## Supported CPU models
|
||||
|
||||
| Model number | Processor name | `cpu_c1_state_residency`<br/>`cpu_c6_state_residency`<br/>`cpu_temperature`<br/>`cpu_base_frequency` | `cpu_c3_state_residency` | `cpu_c7_state_residency` | `uncore_frequency` |
|
||||
|--------------|---------------------------------|:----------------------------------------------------------------------------------------------------:|:------------------------:|:------------------------:|:------------------:|
|
||||
| 0x1E | Intel Nehalem | ✓ | ✓ | | |
|
||||
| 0x1F | Intel Nehalem-G | ✓ | ✓ | | |
|
||||
| 0x1A | Intel Nehalem-EP | ✓ | ✓ | | |
|
||||
| 0x2E | Intel Nehalem-EX | ✓ | ✓ | | |
|
||||
| 0x25 | Intel Westmere | ✓ | ✓ | | |
|
||||
| 0x2C | Intel Westmere-EP | ✓ | ✓ | | |
|
||||
| 0x2F | Intel Westmere-EX | ✓ | ✓ | | |
|
||||
| 0x2A | Intel Sandybridge | ✓ | ✓ | ✓ | |
|
||||
| 0x2D | Intel Sandybridge-X | ✓ | ✓ | ✓ | |
|
||||
| 0x3A | Intel Ivybridge | ✓ | ✓ | ✓ | |
|
||||
| 0x3E | Intel Ivybridge-X | ✓ | ✓ | ✓ | |
|
||||
| 0x3C | Intel Haswell | ✓ | ✓ | ✓ | |
|
||||
| 0x3F | Intel Haswell-X | ✓ | ✓ | ✓ | |
|
||||
| 0x45 | Intel Haswell-L | ✓ | ✓ | ✓ | |
|
||||
| 0x46 | Intel Haswell-G | ✓ | ✓ | ✓ | |
|
||||
| 0x3D | Intel Broadwell | ✓ | ✓ | ✓ | |
|
||||
| 0x47 | Intel Broadwell-G | ✓ | ✓ | ✓ | ✓ |
|
||||
| 0x4F | Intel Broadwell-X | ✓ | ✓ | | ✓ |
|
||||
| 0x56 | Intel Broadwell-D | ✓ | ✓ | | ✓ |
|
||||
| 0x4E | Intel Skylake-L | ✓ | ✓ | ✓ | |
|
||||
| 0x5E | Intel Skylake | ✓ | ✓ | ✓ | |
|
||||
| 0x55 | Intel Skylake-X | ✓ | | | ✓ |
|
||||
| 0x8E | Intel KabyLake-L | ✓ | ✓ | ✓ | |
|
||||
| 0x9E | Intel KabyLake | ✓ | ✓ | ✓ | |
|
||||
| 0xA5 | Intel CometLake | ✓ | ✓ | ✓ | |
|
||||
| 0xA6 | Intel CometLake-L | ✓ | ✓ | ✓ | |
|
||||
| 0x66 | Intel CannonLake-L | ✓ | | ✓ | |
|
||||
| 0x6A | Intel IceLake-X | ✓ | | | ✓ |
|
||||
| 0x6C | Intel IceLake-D | ✓ | | | ✓ |
|
||||
| 0x7D | Intel IceLake | ✓ | | | |
|
||||
| 0x7E | Intel IceLake-L | ✓ | | ✓ | |
|
||||
| 0x9D | Intel IceLake-NNPI | ✓ | | ✓ | |
|
||||
| 0xA7 | Intel RocketLake | ✓ | | ✓ | |
|
||||
| 0x8C | Intel TigerLake-L | ✓ | | ✓ | |
|
||||
| 0x8D | Intel TigerLake | ✓ | | ✓ | |
|
||||
| 0x8F | Intel Sapphire Rapids X | ✓ | | | ✓ |
|
||||
| 0xCF | Intel Emerald Rapids X | ✓ | | | ✓ |
|
||||
| 0xAD | Intel Granite Rapids X | ✓ | | | |
|
||||
| 0x8A | Intel Lakefield | ✓ | | ✓ | |
|
||||
| 0x97 | Intel AlderLake | ✓ | | ✓ | ✓ |
|
||||
| 0x9A | Intel AlderLake-L | ✓ | | ✓ | ✓ |
|
||||
| 0xB7 | Intel RaptorLake | ✓ | | ✓ | ✓ |
|
||||
| 0xBA | Intel RaptorLake-P | ✓ | | ✓ | ✓ |
|
||||
| 0xBF | Intel RaptorLake-S | ✓ | | ✓ | ✓ |
|
||||
| 0xAC | Intel MeteorLake | ✓ | | ✓ | ✓ |
|
||||
| 0xAA | Intel MeteorLake-L | ✓ | | ✓ | ✓ |
|
||||
| 0xC6 | Intel ArrowLake | ✓ | | ✓ | |
|
||||
| 0xBD | Intel LunarLake | ✓ | | ✓ | |
|
||||
| 0x37 | Intel Atom® Bay Trail | ✓ | | | |
|
||||
| 0x4D | Intel Atom® Avaton | ✓ | | | |
|
||||
| 0x4A | Intel Atom® Merrifield | ✓ | | | |
|
||||
| 0x5A | Intel Atom® Moorefield | ✓ | | | |
|
||||
| 0x4C | Intel Atom® Airmont | ✓ | ✓ | | |
|
||||
| 0x5C | Intel Atom® Apollo Lake | ✓ | ✓ | ✓ | |
|
||||
| 0x5F | Intel Atom® Denverton | ✓ | | | |
|
||||
| 0x7A | Intel Atom® Goldmont | ✓ | ✓ | ✓ | |
|
||||
| 0x86 | Intel Atom® Jacobsville | ✓ | | | |
|
||||
| 0x96 | Intel Atom® Elkhart Lake | ✓ | | ✓ | |
|
||||
| 0x9C | Intel Atom® Jasper Lake | ✓ | | ✓ | |
|
||||
| 0xBE | Intel AlderLake-N | ✓ | | ✓ | |
|
||||
| 0xAF | Intel Sierra Forest | ✓ | | | |
|
||||
| 0xB6 | Intel Grand Ridge | ✓ | | | |
|
||||
| 0x57 | Intel Xeon® PHI Knights Landing | ✓ | | | |
|
||||
| 0x85 | Intel Xeon® PHI Knights Mill | ✓ | | | |
|
||||
|
|
|
|||
|
|
@ -1,39 +0,0 @@
|
|||
//go:build linux
|
||||
|
||||
package intel_powerstat
|
||||
|
||||
type msrData struct {
|
||||
mperf uint64
|
||||
aperf uint64
|
||||
timeStampCounter uint64
|
||||
c3 uint64
|
||||
c6 uint64
|
||||
c7 uint64
|
||||
throttleTemp int64
|
||||
temp int64
|
||||
mperfDelta uint64
|
||||
aperfDelta uint64
|
||||
timeStampCounterDelta uint64
|
||||
c3Delta uint64
|
||||
c6Delta uint64
|
||||
c7Delta uint64
|
||||
readDate int64
|
||||
}
|
||||
|
||||
type raplData struct {
|
||||
dramCurrentEnergy float64
|
||||
socketCurrentEnergy float64
|
||||
socketEnergy float64
|
||||
dramEnergy float64
|
||||
readDate int64
|
||||
}
|
||||
|
||||
type cpuInfo struct {
|
||||
physicalID string
|
||||
coreID string
|
||||
cpuID string
|
||||
vendorID string
|
||||
cpuFamily string
|
||||
model string
|
||||
flags string
|
||||
}
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
//go:build linux && amd64
|
||||
|
||||
package intel_powerstat
|
||||
|
||||
import (
|
||||
ptel "github.com/intel/powertelemetry"
|
||||
)
|
||||
|
||||
// topologyFetcher fetches topology information of the host.
|
||||
type topologyFetcher interface {
|
||||
// GetMsrCPUIDs returns a slice with available CPU IDs of the host for which msr will access to.
|
||||
GetMsrCPUIDs() []int
|
||||
|
||||
// GetPerfCPUIDs returns a slice with available CPU IDs of the host for which perf will access to.
|
||||
GetPerfCPUIDs() []int
|
||||
|
||||
// GetPackageIDs returns a slice with available package IDs of the host.
|
||||
GetPackageIDs() []int
|
||||
|
||||
// GetCPUPackageID returns the package ID of the host corresponding to the given CPU ID.
|
||||
GetCPUPackageID(cpuID int) (int, error)
|
||||
|
||||
// GetCPUCoreID returns the core ID of the host corresponding to the given CPU ID.
|
||||
GetCPUCoreID(cpuID int) (int, error)
|
||||
|
||||
// GetPackageDieIDs returns the die IDs of the host corresponding to the given package ID.
|
||||
GetPackageDieIDs(packageID int) ([]int, error)
|
||||
}
|
||||
|
||||
// cpuFreqFetcher fetches supported CPU-related metrics relying on core frequency.
|
||||
type cpuFreqFetcher interface {
|
||||
// GetCPUFrequency returns the current frequency value of a given CPU ID, in MHz.
|
||||
GetCPUFrequency(cpuID int) (float64, error)
|
||||
}
|
||||
|
||||
// cpuMsrFetcher fetches supported CPU-related metrics relying on msr registers.
|
||||
type cpuMsrFetcher interface {
|
||||
// GetCPUTemperature returns the temperature value of a given CPU ID, in degrees Celsius.
|
||||
GetCPUTemperature(cpuID int) (uint64, error)
|
||||
|
||||
// UpdatePerCPUMetrics reads multiple MSR offsets needed to get metric values that are time sensitive.
|
||||
// Below are the list of methods that need the update to be performed beforehand.
|
||||
UpdatePerCPUMetrics(cpuID int) error
|
||||
|
||||
// GetCPUC0StateResidency returns the C0 state residency value of a given CPU ID, as a percentage.
|
||||
GetCPUC0StateResidency(cpuID int) (float64, error)
|
||||
|
||||
// GetCPUC1StateResidency returns the C1 state residency value of a given CPU ID, as a percentage.
|
||||
GetCPUC1StateResidency(cpuID int) (float64, error)
|
||||
|
||||
// GetCPUC3StateResidency returns the C3 state residency value of a given CPU ID, as a percentage.
|
||||
GetCPUC3StateResidency(cpuID int) (float64, error)
|
||||
|
||||
// GetCPUC6StateResidency returns the C6 state residency value of a given CPU ID, as a percentage.
|
||||
GetCPUC6StateResidency(cpuID int) (float64, error)
|
||||
|
||||
// GetCPUC7StateResidency returns the C7 state residency value of a given CPU ID, as a percentage.
|
||||
GetCPUC7StateResidency(cpuID int) (float64, error)
|
||||
|
||||
// GetCPUBusyFrequencyMhz returns the busy frequency value of a given CPU ID, in MHz.
|
||||
GetCPUBusyFrequencyMhz(cpuID int) (float64, error)
|
||||
}
|
||||
|
||||
// cpuPerfFetcher fetches supported CPU-related metrics relying on perf events.
|
||||
type cpuPerfFetcher interface {
|
||||
// ReadPerfEvents reads values of perf events needed to get C0X state residency metrics.
|
||||
// Below getter methods that need this operation to be performed previously.
|
||||
ReadPerfEvents() error
|
||||
|
||||
// DeactivatePerfEvents deactivates perf events. It closes file descriptors used to get perf event values.
|
||||
DeactivatePerfEvents() error
|
||||
|
||||
// GetCPUC0SubstateC01Percent takes a CPU ID and returns a value indicating the percentage of time
|
||||
// the processor spent in its C0.1 substate out of the total time in the C0 state.
|
||||
// C0.1 is characterized by a light-weight slower wakeup time but more power-saving optimized state.
|
||||
GetCPUC0SubstateC01Percent(cpuID int) (float64, error)
|
||||
|
||||
// GetCPUC0SubstateC02Percent takes a CPU ID and returns a value indicating the percentage of time
|
||||
// the processor spent in its C0.2 substate out of the total time in the C0 state.
|
||||
// C0.2 is characterized by a light-weight faster wakeup time but less power saving optimized state.
|
||||
GetCPUC0SubstateC02Percent(cpuID int) (float64, error)
|
||||
|
||||
// GetCPUC0SubstateC0WaitPercent takes a CPU ID and returns a value indicating the percentage of time
|
||||
// the processor spent in its C0_Wait substate out of the total time in the C0 state.
|
||||
// CPU is in C0_Wait substate when the thread is in the C0.1 or C0.2 or running a PAUSE in C0 ACPI state.
|
||||
GetCPUC0SubstateC0WaitPercent(cpuID int) (float64, error)
|
||||
}
|
||||
|
||||
// packageRaplFetcher fetches supported package related metrics relying on rapl.
|
||||
type packageRaplFetcher interface {
|
||||
// GetCurrentPackagePowerConsumptionWatts returns the current package power consumption value of a given package ID, in watts.
|
||||
GetCurrentPackagePowerConsumptionWatts(packageID int) (float64, error)
|
||||
|
||||
// GetCurrentDramPowerConsumptionWatts returns the current dram power consumption value of a given package ID, in watts.
|
||||
GetCurrentDramPowerConsumptionWatts(packageID int) (float64, error)
|
||||
|
||||
// GetPackageThermalDesignPowerWatts returns the thermal power design value of a given package ID, in watts.
|
||||
GetPackageThermalDesignPowerWatts(packageID int) (float64, error)
|
||||
}
|
||||
|
||||
// packageUncoreFreqFetcher fetches supported package related metrics relying on uncore frequency.
|
||||
type packageUncoreFreqFetcher interface {
|
||||
// GetInitialUncoreFrequencyMin returns the minimum initial uncore frequency value of a given package ID, in MHz.
|
||||
GetInitialUncoreFrequencyMin(packageID, dieID int) (float64, error)
|
||||
|
||||
// GetInitialUncoreFrequencyMax returns the maximum initial uncore frequency value of a given package ID, in MHz.
|
||||
GetInitialUncoreFrequencyMax(packageID, dieID int) (float64, error)
|
||||
|
||||
// GetCustomizedUncoreFrequencyMin returns the minimum custom uncore frequency value of a given package ID, in MHz.
|
||||
GetCustomizedUncoreFrequencyMin(packageID, dieID int) (float64, error)
|
||||
|
||||
// GetCustomizedUncoreFrequencyMax returns the maximum custom uncore frequency value of a given package ID, in MHz.
|
||||
GetCustomizedUncoreFrequencyMax(packageID, dieID int) (float64, error)
|
||||
|
||||
// GetCurrentUncoreFrequency returns the current uncore frequency value of a given package ID, in MHz.
|
||||
GetCurrentUncoreFrequency(packageID, dieID int) (float64, error)
|
||||
}
|
||||
|
||||
// packageMsrFetcher fetches supported package related metrics relying on msr registers.
|
||||
type packageMsrFetcher interface {
|
||||
// GetCPUBaseFrequency returns the CPU base frequency value of a given package ID, in MHz.
|
||||
GetCPUBaseFrequency(packageID int) (uint64, error)
|
||||
|
||||
// GetMaxTurboFreqList returns a list of max turbo frequencies and related active cores of a given package ID.
|
||||
GetMaxTurboFreqList(packageID int) ([]ptel.MaxTurboFreq, error)
|
||||
}
|
||||
|
||||
// metricFetcher fetches metrics supported by this plugin.
|
||||
type metricFetcher interface {
|
||||
topologyFetcher
|
||||
|
||||
cpuFreqFetcher
|
||||
cpuMsrFetcher
|
||||
cpuPerfFetcher
|
||||
|
||||
packageRaplFetcher
|
||||
packageUncoreFreqFetcher
|
||||
packageMsrFetcher
|
||||
}
|
||||
|
|
@ -1,172 +0,0 @@
|
|||
//go:build linux
|
||||
|
||||
package intel_powerstat
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// fileService is responsible for handling operations on files.
|
||||
type fileService interface {
|
||||
getCPUInfoStats() (map[string]*cpuInfo, error)
|
||||
getStringsMatchingPatternOnPath(path string) ([]string, error)
|
||||
readFile(path string) ([]byte, error)
|
||||
readFileToFloat64(reader io.Reader) (float64, int64, error)
|
||||
readFileAtOffsetToUint64(reader io.ReaderAt, offset int64) (uint64, error)
|
||||
}
|
||||
|
||||
type fileServiceImpl struct {
|
||||
}
|
||||
|
||||
// getCPUInfoStats retrieves basic information about CPU from /proc/cpuinfo.
|
||||
func (fs *fileServiceImpl) getCPUInfoStats() (map[string]*cpuInfo, error) {
|
||||
path := "/proc/cpuinfo"
|
||||
cpuInfoFile, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error while reading %q: %w", path, err)
|
||||
}
|
||||
defer cpuInfoFile.Close()
|
||||
|
||||
scanner := bufio.NewScanner(cpuInfoFile)
|
||||
|
||||
processorRegexp := regexp.MustCompile(`^processor\t+:\s([0-9]+)\n*$`)
|
||||
physicalIDRegexp := regexp.MustCompile(`^physical id\t+:\s([0-9]+)\n*$`)
|
||||
coreIDRegexp := regexp.MustCompile(`^core id\t+:\s([0-9]+)\n*$`)
|
||||
vendorIDRegexp := regexp.MustCompile(`^vendor_id\t+:\s([a-zA-Z]+)\n*$`)
|
||||
cpuFamilyRegexp := regexp.MustCompile(`^cpu\sfamily\t+:\s([0-9]+)\n*$`)
|
||||
modelRegexp := regexp.MustCompile(`^model\t+:\s([0-9]+)\n*$`)
|
||||
flagsRegexp := regexp.MustCompile(`^flags\t+:\s(.+)\n*$`)
|
||||
|
||||
stats := make(map[string]*cpuInfo)
|
||||
currentInfo := &cpuInfo{}
|
||||
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
|
||||
processorRes := processorRegexp.FindStringSubmatch(line)
|
||||
if len(processorRes) > 1 {
|
||||
currentInfo = &cpuInfo{
|
||||
cpuID: processorRes[1],
|
||||
}
|
||||
}
|
||||
|
||||
vendorIDRes := vendorIDRegexp.FindStringSubmatch(line)
|
||||
if len(vendorIDRes) > 1 {
|
||||
currentInfo.vendorID = vendorIDRes[1]
|
||||
}
|
||||
|
||||
physicalIDRes := physicalIDRegexp.FindStringSubmatch(line)
|
||||
if len(physicalIDRes) > 1 {
|
||||
currentInfo.physicalID = physicalIDRes[1]
|
||||
}
|
||||
|
||||
coreIDRes := coreIDRegexp.FindStringSubmatch(line)
|
||||
if len(coreIDRes) > 1 {
|
||||
currentInfo.coreID = coreIDRes[1]
|
||||
}
|
||||
|
||||
cpuFamilyRes := cpuFamilyRegexp.FindStringSubmatch(line)
|
||||
if len(cpuFamilyRes) > 1 {
|
||||
currentInfo.cpuFamily = cpuFamilyRes[1]
|
||||
}
|
||||
|
||||
modelRes := modelRegexp.FindStringSubmatch(line)
|
||||
if len(modelRes) > 1 {
|
||||
currentInfo.model = modelRes[1]
|
||||
}
|
||||
|
||||
flagsRes := flagsRegexp.FindStringSubmatch(line)
|
||||
if len(flagsRes) > 1 {
|
||||
currentInfo.flags = flagsRes[1]
|
||||
|
||||
// Flags is the last value we have to acquire, so currentInfo is added to map.
|
||||
stats[currentInfo.cpuID] = currentInfo
|
||||
}
|
||||
}
|
||||
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
// getStringsMatchingPatternOnPath looks for filenames and directory names on path matching given regexp.
|
||||
// It ignores file system errors such as I/O errors reading directories. The only possible returned error
|
||||
// is ErrBadPattern, when pattern is malformed.
|
||||
func (fs *fileServiceImpl) getStringsMatchingPatternOnPath(path string) ([]string, error) {
|
||||
return filepath.Glob(path)
|
||||
}
|
||||
|
||||
// readFile reads file on path and return string content.
|
||||
func (fs *fileServiceImpl) readFile(path string) ([]byte, error) {
|
||||
out, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return make([]byte, 0), err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// readFileToFloat64 reads file on path and tries to parse content to float64.
|
||||
func (fs *fileServiceImpl) readFileToFloat64(reader io.Reader) (float64, int64, error) {
|
||||
read, err := io.ReadAll(reader)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
readDate := time.Now().UnixNano()
|
||||
|
||||
// Remove new line character
|
||||
trimmedString := strings.TrimRight(string(read), "\n")
|
||||
// Parse result to float64
|
||||
parsedValue, err := strconv.ParseFloat(trimmedString, 64)
|
||||
if err != nil {
|
||||
return 0, 0, fmt.Errorf("error parsing string to float for %s", trimmedString)
|
||||
}
|
||||
|
||||
return parsedValue, readDate, nil
|
||||
}
|
||||
|
||||
// readFileAtOffsetToUint64 reads 8 bytes from passed file at given offset.
|
||||
func (fs *fileServiceImpl) readFileAtOffsetToUint64(reader io.ReaderAt, offset int64) (uint64, error) {
|
||||
buffer := make([]byte, 8)
|
||||
|
||||
if offset == 0 {
|
||||
return 0, fmt.Errorf("file offset %d should not be 0", offset)
|
||||
}
|
||||
|
||||
_, err := reader.ReadAt(buffer, offset)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("error on reading file at offset %d: %w", offset, err)
|
||||
}
|
||||
|
||||
return binary.LittleEndian.Uint64(buffer), nil
|
||||
}
|
||||
|
||||
func newFileService() *fileServiceImpl {
|
||||
return &fileServiceImpl{}
|
||||
}
|
||||
|
||||
func checkFile(path string) error {
|
||||
if path == "" {
|
||||
return fmt.Errorf("empty path given")
|
||||
}
|
||||
|
||||
lInfo, err := os.Lstat(path)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return fmt.Errorf("file %q doesn't exist", path)
|
||||
}
|
||||
return fmt.Errorf("cannot obtain file info of %q: %w", path, err)
|
||||
}
|
||||
mode := lInfo.Mode()
|
||||
if mode&os.ModeSymlink != 0 {
|
||||
return fmt.Errorf("file %q is a symlink", path)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -1,149 +0,0 @@
|
|||
//go:build linux
|
||||
|
||||
// Code generated by mockery v2.12.3. DO NOT EDIT.
|
||||
|
||||
package intel_powerstat
|
||||
|
||||
import (
|
||||
io "io"
|
||||
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// mockFileService is an autogenerated mock type for the mockFileService type
|
||||
type mockFileService struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// getCPUInfoStats provides a mock function with given fields:
|
||||
func (_m *mockFileService) getCPUInfoStats() (map[string]*cpuInfo, error) {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 map[string]*cpuInfo
|
||||
if rf, ok := ret.Get(0).(func() map[string]*cpuInfo); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(map[string]*cpuInfo)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func() error); ok {
|
||||
r1 = rf()
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// getStringsMatchingPatternOnPath provides a mock function with given fields: path
|
||||
func (_m *mockFileService) getStringsMatchingPatternOnPath(path string) ([]string, error) {
|
||||
ret := _m.Called(path)
|
||||
|
||||
var r0 []string
|
||||
if rf, ok := ret.Get(0).(func(string) []string); ok {
|
||||
r0 = rf(path)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]string)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(string) error); ok {
|
||||
r1 = rf(path)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// readFile provides a mock function with given fields: path
|
||||
func (_m *mockFileService) readFile(path string) ([]byte, error) {
|
||||
ret := _m.Called(path)
|
||||
|
||||
var r0 []byte
|
||||
if rf, ok := ret.Get(0).(func(string) []byte); ok {
|
||||
r0 = rf(path)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]byte)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(string) error); ok {
|
||||
r1 = rf(path)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// readFileAtOffsetToUint64 provides a mock function with given fields: reader, offset
|
||||
func (_m *mockFileService) readFileAtOffsetToUint64(reader io.ReaderAt, offset int64) (uint64, error) {
|
||||
ret := _m.Called(reader, offset)
|
||||
|
||||
var r0 uint64
|
||||
if rf, ok := ret.Get(0).(func(io.ReaderAt, int64) uint64); ok {
|
||||
r0 = rf(reader, offset)
|
||||
} else {
|
||||
r0 = ret.Get(0).(uint64)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(io.ReaderAt, int64) error); ok {
|
||||
r1 = rf(reader, offset)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// readFileToFloat64 provides a mock function with given fields: reader
|
||||
func (_m *mockFileService) readFileToFloat64(reader io.Reader) (float64, int64, error) {
|
||||
ret := _m.Called(reader)
|
||||
|
||||
var r0 float64
|
||||
if rf, ok := ret.Get(0).(func(io.Reader) float64); ok {
|
||||
r0 = rf(reader)
|
||||
} else {
|
||||
r0 = ret.Get(0).(float64)
|
||||
}
|
||||
|
||||
var r1 int64
|
||||
if rf, ok := ret.Get(1).(func(io.Reader) int64); ok {
|
||||
r1 = rf(reader)
|
||||
} else {
|
||||
r1 = ret.Get(1).(int64)
|
||||
}
|
||||
|
||||
var r2 error
|
||||
if rf, ok := ret.Get(2).(func(io.Reader) error); ok {
|
||||
r2 = rf(reader)
|
||||
} else {
|
||||
r2 = ret.Error(2)
|
||||
}
|
||||
|
||||
return r0, r1, r2
|
||||
}
|
||||
|
||||
type newmockFileServiceT interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}
|
||||
|
||||
// newmockFileService creates a new instance of mockFileService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
func newmockFileService(t newmockFileServiceT) *mockFileService {
|
||||
mock := &mockFileService{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,5 +1,5 @@
|
|||
//go:generate ../../../tools/readme_config_includer/generator
|
||||
//go:build !linux
|
||||
//go:build !linux || !amd64
|
||||
|
||||
package intel_powerstat
|
||||
|
||||
|
|
@ -21,6 +21,7 @@ func (i *IntelPowerstat) Init() error {
|
|||
i.Log.Warn("current platform is not supported")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*IntelPowerstat) SampleConfig() string { return sampleConfig }
|
||||
func (*IntelPowerstat) Gather(_ telegraf.Accumulator) error { return nil }
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,322 @@
|
|||
//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)
|
||||
}
|
||||
|
||||
//nolint:revive // Confusing-naming caused by a generic type that implements this interface method.
|
||||
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
|
||||
}
|
||||
|
||||
//nolint:revive // Confusing-naming caused by a generic type that implements this interface method.
|
||||
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
|
||||
}
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
//go:build linux && amd64
|
||||
|
||||
package intel_powerstat
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestCoreMetric_String(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
metricName string
|
||||
}{
|
||||
{
|
||||
name: "CPUFrequency",
|
||||
metricName: "cpu_frequency",
|
||||
},
|
||||
{
|
||||
name: "CPUTemperature",
|
||||
metricName: "cpu_temperature",
|
||||
},
|
||||
{
|
||||
name: "CPUC0StateResidency",
|
||||
metricName: "cpu_c0_state_residency",
|
||||
},
|
||||
{
|
||||
name: "CPUC1StateResidency",
|
||||
metricName: "cpu_c1_state_residency",
|
||||
},
|
||||
{
|
||||
name: "CPUC3StateResidency",
|
||||
metricName: "cpu_c3_state_residency",
|
||||
},
|
||||
{
|
||||
name: "CPUC6StateResidency",
|
||||
metricName: "cpu_c6_state_residency",
|
||||
},
|
||||
{
|
||||
name: "CPUC7StateResidency",
|
||||
metricName: "cpu_c7_state_residency",
|
||||
},
|
||||
{
|
||||
name: "CPUBusyCycles",
|
||||
metricName: "cpu_busy_cycles",
|
||||
},
|
||||
{
|
||||
name: "CPUBusyFrequency",
|
||||
metricName: "cpu_busy_frequency",
|
||||
},
|
||||
{
|
||||
name: "CPUC0SubstateC01Percent",
|
||||
metricName: "cpu_c0_substate_c01",
|
||||
},
|
||||
{
|
||||
name: "CPUC0SubstateC02Percent",
|
||||
metricName: "cpu_c0_substate_c02",
|
||||
},
|
||||
{
|
||||
name: "CPUC0SubstateC0WaitPercent",
|
||||
metricName: "cpu_c0_substate_c0_wait",
|
||||
},
|
||||
{
|
||||
name: "Invalid",
|
||||
metricName: "",
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
metric := cpuMetricType(i)
|
||||
require.Equal(t, tc.metricName, metric.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPackageMetric_String(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
metricName string
|
||||
}{
|
||||
{
|
||||
name: "PackageCurrentPowerConsumption",
|
||||
metricName: "current_power_consumption",
|
||||
},
|
||||
{
|
||||
name: "PackageCurrentDramPowerConsumption",
|
||||
metricName: "current_dram_power_consumption",
|
||||
},
|
||||
{
|
||||
name: "PackageThermalDesignPower",
|
||||
metricName: "thermal_design_power",
|
||||
},
|
||||
{
|
||||
name: "PackageCPUBaseFrequency",
|
||||
metricName: "cpu_base_frequency",
|
||||
},
|
||||
{
|
||||
name: "PackageUncoreFrequency",
|
||||
metricName: "uncore_frequency",
|
||||
},
|
||||
{
|
||||
name: "PackageTurboLimit",
|
||||
metricName: "max_turbo_frequency",
|
||||
},
|
||||
{
|
||||
name: "Invalid",
|
||||
metricName: "",
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
metric := packageMetricType(i)
|
||||
require.Equal(t, tc.metricName, metric.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCPUMetricTypeFromString(t *testing.T) {
|
||||
t.Run("Valid", func(t *testing.T) {
|
||||
for m := cpuMetricType(0); m < cpuC0SubstateC0WaitPercent+1; m++ {
|
||||
val, err := cpuMetricTypeFromString(m.String())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, m, val)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Invalid", func(t *testing.T) {
|
||||
val, err := cpuMetricTypeFromString("invalid")
|
||||
require.Error(t, err)
|
||||
require.Equal(t, cpuMetricType(-1), val)
|
||||
})
|
||||
}
|
||||
|
||||
func TestPackageMetricTypeFromString(t *testing.T) {
|
||||
t.Run("Valid", func(t *testing.T) {
|
||||
for m := packageMetricType(0); m < packageTurboLimit+1; m++ {
|
||||
val, err := packageMetricTypeFromString(m.String())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, m, val)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Invalid", func(t *testing.T) {
|
||||
val, err := packageMetricTypeFromString("invalid")
|
||||
require.Error(t, err)
|
||||
require.Equal(t, packageMetricType(-1), val)
|
||||
})
|
||||
}
|
||||
|
|
@ -1,327 +0,0 @@
|
|||
//go:build linux
|
||||
|
||||
package intel_powerstat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
)
|
||||
|
||||
const (
|
||||
systemCPUPath = "/sys/devices/system/cpu/"
|
||||
cpuCurrentFreqPartialPath = "/sys/devices/system/cpu/cpu%s/cpufreq/scaling_cur_freq"
|
||||
msrPartialPath = "/dev/cpu/%s/msr"
|
||||
uncoreFreqPath = "/sys/devices/system/cpu/intel_uncore_frequency/package_%s_die_%s/%s%s_freq_khz"
|
||||
c3StateResidencyLocation = 0x3FC
|
||||
c6StateResidencyLocation = 0x3FD
|
||||
c7StateResidencyLocation = 0x3FE
|
||||
maximumFrequencyClockCountLocation = 0xE7
|
||||
actualFrequencyClockCountLocation = 0xE8
|
||||
throttleTemperatureLocation = 0x1A2
|
||||
temperatureLocation = 0x19C
|
||||
timestampCounterLocation = 0x10
|
||||
turboRatioLimitLocation = 0x1AD
|
||||
turboRatioLimit1Location = 0x1AE
|
||||
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.
|
||||
type msrService interface {
|
||||
getCPUCoresData() map[string]*msrData
|
||||
retrieveCPUFrequencyForCore(core string) (float64, error)
|
||||
retrieveUncoreFrequency(socketID string, typeFreq string, kind string, die string) (float64, error)
|
||||
openAndReadMsr(core string) error
|
||||
readSingleMsr(core string, msr string) (uint64, error)
|
||||
isMsrLoaded() bool
|
||||
}
|
||||
|
||||
type msrServiceImpl struct {
|
||||
cpuCoresData map[string]*msrData
|
||||
msrOffsets []int64
|
||||
fs fileService
|
||||
log telegraf.Logger
|
||||
}
|
||||
|
||||
func (m *msrServiceImpl) getCPUCoresData() map[string]*msrData {
|
||||
return m.cpuCoresData
|
||||
}
|
||||
|
||||
func (m *msrServiceImpl) isMsrLoaded() bool {
|
||||
for cpuID := range m.getCPUCoresData() {
|
||||
err := m.openAndReadMsr(cpuID)
|
||||
if err == nil {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
func (m *msrServiceImpl) retrieveCPUFrequencyForCore(core string) (float64, error) {
|
||||
cpuFreqPath := fmt.Sprintf(cpuCurrentFreqPartialPath, core)
|
||||
err := checkFile(cpuFreqPath)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
cpuFreqFile, err := os.Open(cpuFreqPath)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("error opening scaling_cur_freq file on path %q: %w", cpuFreqPath, err)
|
||||
}
|
||||
defer cpuFreqFile.Close()
|
||||
|
||||
cpuFreq, _, err := m.fs.readFileToFloat64(cpuFreqFile)
|
||||
return convertKiloHertzToMegaHertz(cpuFreq), err
|
||||
}
|
||||
|
||||
func (m *msrServiceImpl) retrieveUncoreFrequency(socketID string, typeFreq string, kind string, die string) (float64, error) {
|
||||
uncoreFreqPath, err := createUncoreFreqPath(socketID, typeFreq, kind, die)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("unable to create uncore freq read path for socketID %q, and frequency type %q: %w", socketID, typeFreq, err)
|
||||
}
|
||||
err = checkFile(uncoreFreqPath)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
uncoreFreqFile, err := os.Open(uncoreFreqPath)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("error opening uncore frequncy file on %q: %w", uncoreFreqPath, err)
|
||||
}
|
||||
defer uncoreFreqFile.Close()
|
||||
|
||||
uncoreFreq, _, err := m.fs.readFileToFloat64(uncoreFreqFile)
|
||||
return convertKiloHertzToMegaHertz(uncoreFreq), err
|
||||
}
|
||||
|
||||
func createUncoreFreqPath(socketID string, typeFreq string, kind string, die string) (string, error) {
|
||||
if socketID >= "0" && socketID <= "9" {
|
||||
socketID = fmt.Sprintf("0%s", socketID)
|
||||
}
|
||||
if die >= "0" && die <= "9" {
|
||||
die = fmt.Sprintf("0%s", die)
|
||||
}
|
||||
var prefix string
|
||||
|
||||
switch typeFreq {
|
||||
case "initial":
|
||||
prefix = "initial_"
|
||||
case "current":
|
||||
prefix = ""
|
||||
default:
|
||||
return "", fmt.Errorf("unknown frequency type %s, only 'initial' and 'current' are supported", typeFreq)
|
||||
}
|
||||
|
||||
if kind != "min" && kind != "max" {
|
||||
return "", fmt.Errorf("unknown frequency type %s, only 'min' and 'max' are supported", kind)
|
||||
}
|
||||
return fmt.Sprintf(uncoreFreqPath, socketID, die, prefix, kind), nil
|
||||
}
|
||||
|
||||
func (m *msrServiceImpl) openAndReadMsr(core string) error {
|
||||
path := fmt.Sprintf(msrPartialPath, core)
|
||||
err := checkFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
msrFile, err := os.Open(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error opening MSR file on path %q: %w", path, err)
|
||||
}
|
||||
defer msrFile.Close()
|
||||
|
||||
err = m.readDataFromMsr(core, msrFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading data from MSR for core %q: %w", core, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *msrServiceImpl) readSingleMsr(core string, msr string) (uint64, error) {
|
||||
path := fmt.Sprintf(msrPartialPath, core)
|
||||
err := checkFile(path)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
msrFile, err := os.Open(path)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("error opening MSR file on path %q: %w", path, err)
|
||||
}
|
||||
defer msrFile.Close()
|
||||
|
||||
var msrAddress int64
|
||||
switch msr {
|
||||
case msrTurboRatioLimitString:
|
||||
msrAddress = turboRatioLimitLocation
|
||||
case msrTurboRatioLimit1String:
|
||||
msrAddress = turboRatioLimit1Location
|
||||
case msrTurboRatioLimit2String:
|
||||
msrAddress = turboRatioLimit2Location
|
||||
case msrAtomCoreTurboRatiosString:
|
||||
msrAddress = atomCoreTurboRatiosLocation
|
||||
case msrUncorePerfStatusString:
|
||||
msrAddress = uncorePerfStatusLocation
|
||||
case msrPlatformInfoString:
|
||||
msrAddress = platformInfo
|
||||
case msrFSBFreqString:
|
||||
msrAddress = fsbFreq
|
||||
default:
|
||||
return 0, fmt.Errorf("incorrect name of MSR %s", msr)
|
||||
}
|
||||
|
||||
value, err := m.fs.readFileAtOffsetToUint64(msrFile, msrAddress)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func (m *msrServiceImpl) readDataFromMsr(core string, reader io.ReaderAt) error {
|
||||
g, ctx := errgroup.WithContext(context.Background())
|
||||
|
||||
// Create and populate a map that contains msr offsets along with their respective channels
|
||||
msrOffsetsWithChannels := make(map[int64]chan uint64)
|
||||
for _, offset := range m.msrOffsets {
|
||||
msrOffsetsWithChannels[offset] = make(chan uint64)
|
||||
}
|
||||
|
||||
// Start a goroutine for each msr offset
|
||||
for offset, channel := range msrOffsetsWithChannels {
|
||||
// Wrap around function to avoid race on loop counter
|
||||
func(off int64, ch chan uint64) {
|
||||
g.Go(func() error {
|
||||
defer close(ch)
|
||||
|
||||
err := m.readValueFromFileAtOffset(ctx, ch, reader, off)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading MSR file: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}(offset, channel)
|
||||
}
|
||||
|
||||
newC3 := <-msrOffsetsWithChannels[c3StateResidencyLocation]
|
||||
newC6 := <-msrOffsetsWithChannels[c6StateResidencyLocation]
|
||||
newC7 := <-msrOffsetsWithChannels[c7StateResidencyLocation]
|
||||
newMperf := <-msrOffsetsWithChannels[maximumFrequencyClockCountLocation]
|
||||
newAperf := <-msrOffsetsWithChannels[actualFrequencyClockCountLocation]
|
||||
newTsc := <-msrOffsetsWithChannels[timestampCounterLocation]
|
||||
newThrottleTemp := <-msrOffsetsWithChannels[throttleTemperatureLocation]
|
||||
newTemp := <-msrOffsetsWithChannels[temperatureLocation]
|
||||
|
||||
if err := g.Wait(); err != nil {
|
||||
return fmt.Errorf("received error during reading MSR values in goroutines: %w", err)
|
||||
}
|
||||
|
||||
m.cpuCoresData[core].c3Delta = newC3 - m.cpuCoresData[core].c3
|
||||
m.cpuCoresData[core].c6Delta = newC6 - m.cpuCoresData[core].c6
|
||||
m.cpuCoresData[core].c7Delta = newC7 - m.cpuCoresData[core].c7
|
||||
m.cpuCoresData[core].mperfDelta = newMperf - m.cpuCoresData[core].mperf
|
||||
m.cpuCoresData[core].aperfDelta = newAperf - m.cpuCoresData[core].aperf
|
||||
m.cpuCoresData[core].timeStampCounterDelta = newTsc - m.cpuCoresData[core].timeStampCounter
|
||||
|
||||
m.cpuCoresData[core].c3 = newC3
|
||||
m.cpuCoresData[core].c6 = newC6
|
||||
m.cpuCoresData[core].c7 = newC7
|
||||
m.cpuCoresData[core].mperf = newMperf
|
||||
m.cpuCoresData[core].aperf = newAperf
|
||||
m.cpuCoresData[core].timeStampCounter = newTsc
|
||||
// MSR (1A2h) IA32_TEMPERATURE_TARGET bits 23:16.
|
||||
m.cpuCoresData[core].throttleTemp = int64((newThrottleTemp >> 16) & 0xFF)
|
||||
// MSR (19Ch) IA32_THERM_STATUS bits 22:16.
|
||||
m.cpuCoresData[core].temp = int64((newTemp >> 16) & 0x7F)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *msrServiceImpl) readValueFromFileAtOffset(ctx context.Context, ch chan uint64, reader io.ReaderAt, offset int64) error {
|
||||
value, err := m.fs.readFileAtOffsetToUint64(reader, offset)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Detect context cancellation and return an error if other goroutine fails
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case ch <- value:
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// setCPUCores initialize cpuCoresData map.
|
||||
func (m *msrServiceImpl) setCPUCores() error {
|
||||
m.cpuCoresData = make(map[string]*msrData)
|
||||
cpuPrefix := "cpu"
|
||||
cpuCore := fmt.Sprintf("%s%s", cpuPrefix, "[0-9]*")
|
||||
cpuCorePattern := fmt.Sprintf("%s/%s", systemCPUPath, cpuCore)
|
||||
cpuPaths, err := m.fs.getStringsMatchingPatternOnPath(cpuCorePattern)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(cpuPaths) == 0 {
|
||||
m.log.Debugf("CPU core data wasn't found using pattern: %s", cpuCorePattern)
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, cpuPath := range cpuPaths {
|
||||
core := strings.TrimPrefix(filepath.Base(cpuPath), cpuPrefix)
|
||||
m.cpuCoresData[core] = &msrData{
|
||||
mperf: 0,
|
||||
aperf: 0,
|
||||
timeStampCounter: 0,
|
||||
c3: 0,
|
||||
c6: 0,
|
||||
c7: 0,
|
||||
throttleTemp: 0,
|
||||
temp: 0,
|
||||
mperfDelta: 0,
|
||||
aperfDelta: 0,
|
||||
timeStampCounterDelta: 0,
|
||||
c3Delta: 0,
|
||||
c6Delta: 0,
|
||||
c7Delta: 0,
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func newMsrServiceWithFs(logger telegraf.Logger, fs fileService) *msrServiceImpl {
|
||||
msrService := &msrServiceImpl{
|
||||
fs: fs,
|
||||
log: logger,
|
||||
}
|
||||
err := msrService.setCPUCores()
|
||||
if err != nil {
|
||||
// This error does not prevent plugin from working thus it is not returned.
|
||||
msrService.log.Error(err)
|
||||
}
|
||||
|
||||
msrService.msrOffsets = []int64{c3StateResidencyLocation, c6StateResidencyLocation, c7StateResidencyLocation,
|
||||
maximumFrequencyClockCountLocation, actualFrequencyClockCountLocation, timestampCounterLocation,
|
||||
throttleTemperatureLocation, temperatureLocation}
|
||||
return msrService
|
||||
}
|
||||
|
|
@ -1,134 +0,0 @@
|
|||
//go:build linux
|
||||
|
||||
// Code generated by mockery v2.12.3. DO NOT EDIT.
|
||||
|
||||
package intel_powerstat
|
||||
|
||||
import mock "github.com/stretchr/testify/mock"
|
||||
|
||||
// mockMsrService is an autogenerated mock type for the mockMsrService type
|
||||
type mockMsrService struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// isMsrLoaded provides a mock function with given fields:
|
||||
func (_m *mockMsrService) isMsrLoaded() bool {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 bool
|
||||
if rf, ok := ret.Get(0).(func() bool); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
r0 = ret.Get(0).(bool)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// getCPUCoresData provides a mock function with given fields:
|
||||
func (_m *mockMsrService) getCPUCoresData() map[string]*msrData {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 map[string]*msrData
|
||||
if rf, ok := ret.Get(0).(func() map[string]*msrData); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(map[string]*msrData)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// openAndReadMsr provides a mock function with given fields: core
|
||||
func (_m *mockMsrService) openAndReadMsr(core string) error {
|
||||
ret := _m.Called(core)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(string) error); ok {
|
||||
r0 = rf(core)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// readSingleMsr provides a mock function with given fields: core, msr
|
||||
func (_m *mockMsrService) readSingleMsr(core string, msr string) (uint64, error) {
|
||||
ret := _m.Called(core, msr)
|
||||
|
||||
var r0 uint64
|
||||
if rf, ok := ret.Get(0).(func(string, string) uint64); ok {
|
||||
r0 = rf(core, msr)
|
||||
} else {
|
||||
r0 = ret.Get(0).(uint64)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(string, string) error); ok {
|
||||
r1 = rf(core, msr)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// retrieveCPUFrequencyForCore provides a mock function with given fields: core
|
||||
func (_m *mockMsrService) retrieveCPUFrequencyForCore(core string) (float64, error) {
|
||||
ret := _m.Called(core)
|
||||
|
||||
var r0 float64
|
||||
if rf, ok := ret.Get(0).(func(string) float64); ok {
|
||||
r0 = rf(core)
|
||||
} else {
|
||||
r0 = ret.Get(0).(float64)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(string) error); ok {
|
||||
r1 = rf(core)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// retrieveUncoreFrequency provides a mock function with given fields: socketID, typeFreq, kind, die
|
||||
func (_m *mockMsrService) retrieveUncoreFrequency(socketID string, typeFreq string, kind string, die string) (float64, error) {
|
||||
ret := _m.Called(socketID, typeFreq, kind, die)
|
||||
|
||||
var r0 float64
|
||||
if rf, ok := ret.Get(0).(func(string, string, string, string) float64); ok {
|
||||
r0 = rf(socketID, typeFreq, kind, die)
|
||||
} else {
|
||||
r0 = ret.Get(0).(float64)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(string, string, string, string) error); ok {
|
||||
r1 = rf(socketID, typeFreq, kind, die)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
type newmockMsrServiceT interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}
|
||||
|
||||
// newmockMsrService creates a new instance of mockMsrService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
func newmockMsrService(t newmockMsrServiceT) *mockMsrService {
|
||||
mock := &mockMsrService{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
||||
|
|
@ -1,188 +0,0 @@
|
|||
//go:build linux
|
||||
|
||||
package intel_powerstat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/influxdata/telegraf/testutil"
|
||||
)
|
||||
|
||||
func TestReadDataFromMsrPositive(t *testing.T) {
|
||||
firstValue := uint64(1000000)
|
||||
secondValue := uint64(5000000)
|
||||
delta := secondValue - firstValue
|
||||
cpuCores := []string{"cpu0", "cpu1"}
|
||||
msr, fsMock := getMsrServiceWithMockedFs()
|
||||
prepareTestData(fsMock, cpuCores, msr, t)
|
||||
cores := trimCPUFromCores(cpuCores)
|
||||
|
||||
methodCallNumberForFirstValue := len(msr.msrOffsets) * len(cores)
|
||||
methodCallNumberForSecondValue := methodCallNumberForFirstValue * 2
|
||||
|
||||
fsMock.On("readFileAtOffsetToUint64", mock.Anything, mock.Anything).
|
||||
Return(firstValue, nil).Times(methodCallNumberForFirstValue)
|
||||
for _, core := range cores {
|
||||
require.NoError(t, msr.readDataFromMsr(core, nil))
|
||||
}
|
||||
fsMock.AssertNumberOfCalls(t, "readFileAtOffsetToUint64", methodCallNumberForFirstValue)
|
||||
verifyCPUCoresData(cores, t, msr, firstValue, false, 0)
|
||||
|
||||
fsMock.On("readFileAtOffsetToUint64", mock.Anything, mock.Anything).
|
||||
Return(secondValue, nil).Times(methodCallNumberForFirstValue)
|
||||
for _, core := range cores {
|
||||
require.NoError(t, msr.readDataFromMsr(core, nil))
|
||||
}
|
||||
fsMock.AssertNumberOfCalls(t, "readFileAtOffsetToUint64", methodCallNumberForSecondValue)
|
||||
verifyCPUCoresData(cores, t, msr, secondValue, true, delta)
|
||||
}
|
||||
|
||||
func trimCPUFromCores(cpuCores []string) []string {
|
||||
cores := make([]string, 0)
|
||||
for _, core := range cpuCores {
|
||||
cores = append(cores, strings.TrimPrefix(core, "cpu"))
|
||||
}
|
||||
return cores
|
||||
}
|
||||
|
||||
func TestReadDataFromMsrNegative(t *testing.T) {
|
||||
firstValue := uint64(1000000)
|
||||
cpuCores := []string{"cpu0", "cpu1"}
|
||||
msr, fsMock := getMsrServiceWithMockedFs()
|
||||
|
||||
prepareTestData(fsMock, cpuCores, msr, t)
|
||||
cores := trimCPUFromCores(cpuCores)
|
||||
|
||||
methodCallNumberPerCore := len(msr.msrOffsets)
|
||||
|
||||
// Normal execution for first core.
|
||||
fsMock.On("readFileAtOffsetToUint64", mock.Anything, mock.Anything).
|
||||
Return(firstValue, nil).Times(methodCallNumberPerCore).
|
||||
// Fail to read file for second core.
|
||||
On("readFileAtOffsetToUint64", mock.Anything, mock.Anything).
|
||||
Return(uint64(0), errors.New("error reading file")).Times(methodCallNumberPerCore)
|
||||
|
||||
require.NoError(t, msr.readDataFromMsr(cores[0], nil))
|
||||
require.Error(t, msr.readDataFromMsr(cores[1], nil))
|
||||
}
|
||||
|
||||
func TestReadValueFromFileAtOffset(t *testing.T) {
|
||||
cores := []string{"cpu0", "cpu1"}
|
||||
msr, fsMock := getMsrServiceWithMockedFs()
|
||||
ctx := context.Background()
|
||||
testChannel := make(chan uint64, 1)
|
||||
defer close(testChannel)
|
||||
zero := uint64(0)
|
||||
|
||||
prepareTestData(fsMock, cores, msr, t)
|
||||
|
||||
fsMock.On("readFileAtOffsetToUint64", mock.Anything, mock.Anything).
|
||||
Return(zero, errors.New("error reading file")).Once()
|
||||
require.Error(t, msr.readValueFromFileAtOffset(ctx, testChannel, nil, 0))
|
||||
|
||||
fsMock.On("readFileAtOffsetToUint64", mock.Anything, mock.Anything).
|
||||
Return(zero, nil).Once()
|
||||
require.NoError(t, msr.readValueFromFileAtOffset(ctx, testChannel, nil, 0))
|
||||
require.Equal(t, zero, <-testChannel)
|
||||
}
|
||||
|
||||
func TestCreateUncoreFreqPath(t *testing.T) {
|
||||
path, err := createUncoreFreqPath("0", "initial", "min", "0")
|
||||
expectedPath := "/sys/devices/system/cpu/intel_uncore_frequency/package_00_die_00/initial_min_freq_khz"
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expectedPath, path)
|
||||
|
||||
path, err = createUncoreFreqPath("0", "initial", "max", "0")
|
||||
expectedPath = "/sys/devices/system/cpu/intel_uncore_frequency/package_00_die_00/initial_max_freq_khz"
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expectedPath, path)
|
||||
|
||||
path, err = createUncoreFreqPath("0", "current", "min", "0")
|
||||
expectedPath = "/sys/devices/system/cpu/intel_uncore_frequency/package_00_die_00/min_freq_khz"
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expectedPath, path)
|
||||
|
||||
path, err = createUncoreFreqPath("0", "current", "max", "0")
|
||||
expectedPath = "/sys/devices/system/cpu/intel_uncore_frequency/package_00_die_00/max_freq_khz"
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expectedPath, path)
|
||||
|
||||
path, err = createUncoreFreqPath("9", "current", "max", "0")
|
||||
expectedPath = "/sys/devices/system/cpu/intel_uncore_frequency/package_09_die_00/max_freq_khz"
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expectedPath, path)
|
||||
|
||||
path, err = createUncoreFreqPath("99", "current", "max", "0")
|
||||
expectedPath = "/sys/devices/system/cpu/intel_uncore_frequency/package_99_die_00/max_freq_khz"
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expectedPath, path)
|
||||
|
||||
path, err = createUncoreFreqPath("0", "current", "max", "9")
|
||||
expectedPath = "/sys/devices/system/cpu/intel_uncore_frequency/package_00_die_09/max_freq_khz"
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expectedPath, path)
|
||||
|
||||
path, err = createUncoreFreqPath("0", "current", "max", "99")
|
||||
expectedPath = "/sys/devices/system/cpu/intel_uncore_frequency/package_00_die_99/max_freq_khz"
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expectedPath, path)
|
||||
|
||||
path, err = createUncoreFreqPath("0", "foo", "max", "0")
|
||||
expectedPath = ""
|
||||
expectedError := errors.New("unknown frequency type foo, only 'initial' and 'current' are supported")
|
||||
require.Equal(t, expectedError, err)
|
||||
require.Equal(t, expectedPath, path)
|
||||
|
||||
path, err = createUncoreFreqPath("0", "current", "bar", "0")
|
||||
expectedPath = ""
|
||||
expectedError = errors.New("unknown frequency type bar, only 'min' and 'max' are supported")
|
||||
require.Equal(t, expectedError, err)
|
||||
require.Equal(t, expectedPath, path)
|
||||
}
|
||||
|
||||
func prepareTestData(fsMock *mockFileService, cores []string, msr *msrServiceImpl, t *testing.T) {
|
||||
// Prepare MSR offsets and CPUCoresData for test.
|
||||
fsMock.On("getStringsMatchingPatternOnPath", mock.Anything).
|
||||
Return(cores, nil).Once()
|
||||
require.NoError(t, msr.setCPUCores())
|
||||
fsMock.AssertCalled(t, "getStringsMatchingPatternOnPath", mock.Anything)
|
||||
}
|
||||
|
||||
func verifyCPUCoresData(cores []string, t *testing.T, msr *msrServiceImpl, expectedValue uint64, verifyDelta bool, delta uint64) {
|
||||
for _, core := range cores {
|
||||
require.Equal(t, expectedValue, msr.cpuCoresData[core].c3)
|
||||
require.Equal(t, expectedValue, msr.cpuCoresData[core].c6)
|
||||
require.Equal(t, expectedValue, msr.cpuCoresData[core].c7)
|
||||
require.Equal(t, expectedValue, msr.cpuCoresData[core].mperf)
|
||||
require.Equal(t, expectedValue, msr.cpuCoresData[core].aperf)
|
||||
require.Equal(t, expectedValue, msr.cpuCoresData[core].timeStampCounter)
|
||||
require.Equal(t, int64((expectedValue>>16)&0xFF), msr.cpuCoresData[core].throttleTemp)
|
||||
require.Equal(t, int64((expectedValue>>16)&0x7F), msr.cpuCoresData[core].temp)
|
||||
|
||||
if verifyDelta {
|
||||
require.Equal(t, delta, msr.cpuCoresData[core].c3Delta)
|
||||
require.Equal(t, delta, msr.cpuCoresData[core].c6Delta)
|
||||
require.Equal(t, delta, msr.cpuCoresData[core].c7Delta)
|
||||
require.Equal(t, delta, msr.cpuCoresData[core].mperfDelta)
|
||||
require.Equal(t, delta, msr.cpuCoresData[core].aperfDelta)
|
||||
require.Equal(t, delta, msr.cpuCoresData[core].timeStampCounterDelta)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getMsrServiceWithMockedFs() (*msrServiceImpl, *mockFileService) {
|
||||
cores := []string{"cpu0", "cpu1", "cpu2", "cpu3"}
|
||||
logger := testutil.Logger{Name: "PowerPluginTest"}
|
||||
fsMock := &mockFileService{}
|
||||
fsMock.On("getStringsMatchingPatternOnPath", mock.Anything).
|
||||
Return(cores, nil).Once()
|
||||
msr := newMsrServiceWithFs(logger, fsMock)
|
||||
|
||||
return msr, fsMock
|
||||
}
|
||||
|
|
@ -0,0 +1,184 @@
|
|||
//go:build linux && amd64
|
||||
|
||||
package intel_powerstat
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
ptel "github.com/intel/powertelemetry"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
)
|
||||
|
||||
// optConfig represents plugin configuration fields needed to generate options.
|
||||
type optConfig struct {
|
||||
cpuMetrics []cpuMetricType
|
||||
packageMetrics []packageMetricType
|
||||
includedCPUs []int
|
||||
excludedCPUs []int
|
||||
perfEventFile string
|
||||
msrReadTimeout time.Duration
|
||||
log telegraf.Logger
|
||||
}
|
||||
|
||||
// optionGenerator takes a struct with the plugin configuration, and generates options
|
||||
// needed to gather metrics.
|
||||
type optionGenerator interface {
|
||||
generate(cfg optConfig) []ptel.Option
|
||||
}
|
||||
|
||||
// optGenerator implements optionGenerator interface.
|
||||
type optGenerator struct{}
|
||||
|
||||
// generate takes plugin configuration options and generates options needed
|
||||
// to gather requested metrics.
|
||||
func (g *optGenerator) generate(cfg optConfig) []ptel.Option {
|
||||
opts := make([]ptel.Option, 0)
|
||||
if len(cfg.includedCPUs) != 0 {
|
||||
opts = append(opts, ptel.WithIncludedCPUs(cfg.includedCPUs))
|
||||
}
|
||||
|
||||
if len(cfg.excludedCPUs) != 0 {
|
||||
opts = append(opts, ptel.WithExcludedCPUs(cfg.excludedCPUs))
|
||||
}
|
||||
|
||||
if needsMsrCPU(cfg.cpuMetrics) || needsMsrPackage(cfg.packageMetrics) {
|
||||
if cfg.msrReadTimeout == 0 {
|
||||
opts = append(opts, ptel.WithMsr())
|
||||
} else {
|
||||
opts = append(opts, ptel.WithMsrTimeout(cfg.msrReadTimeout))
|
||||
}
|
||||
}
|
||||
|
||||
if needsRapl(cfg.packageMetrics) {
|
||||
opts = append(opts, ptel.WithRapl())
|
||||
}
|
||||
|
||||
if needsCoreFreq(cfg.cpuMetrics) {
|
||||
opts = append(opts, ptel.WithCoreFrequency())
|
||||
}
|
||||
|
||||
if needsUncoreFreq(cfg.packageMetrics) {
|
||||
opts = append(opts, ptel.WithUncoreFrequency())
|
||||
}
|
||||
|
||||
if needsPerf(cfg.cpuMetrics) {
|
||||
opts = append(opts, ptel.WithPerf(cfg.perfEventFile))
|
||||
}
|
||||
|
||||
if cfg.log != nil {
|
||||
opts = append(opts, ptel.WithLogger(cfg.log))
|
||||
}
|
||||
|
||||
return opts
|
||||
}
|
||||
|
||||
// needsMsr takes a slice of strings, representing supported metrics, and
|
||||
// returns true if any relies on msr registers.
|
||||
func needsMsrCPU(metrics []cpuMetricType) bool {
|
||||
for _, m := range metrics {
|
||||
switch m {
|
||||
case cpuTemperature:
|
||||
case cpuC0StateResidency:
|
||||
case cpuC1StateResidency:
|
||||
case cpuC3StateResidency:
|
||||
case cpuC6StateResidency:
|
||||
case cpuC7StateResidency:
|
||||
case cpuBusyCycles:
|
||||
case cpuBusyFrequency:
|
||||
default:
|
||||
continue
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// needsMsrPackage takes a slice of strings, representing supported metrics, and
|
||||
// returns true if any relies on msr registers.
|
||||
func needsMsrPackage(metrics []packageMetricType) bool {
|
||||
for _, m := range metrics {
|
||||
switch m {
|
||||
case packageCPUBaseFrequency:
|
||||
case packageTurboLimit:
|
||||
case packageUncoreFrequency:
|
||||
// Fallback mechanism retrieves this metric from MSR registers.
|
||||
default:
|
||||
continue
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// needsTimeRelatedMsr takes a slice of strings, representing supported metrics, and
|
||||
// returns true if any relies on time-related reads of msr registers.
|
||||
func needsTimeRelatedMsr(metrics []cpuMetricType) bool {
|
||||
for _, m := range metrics {
|
||||
switch m {
|
||||
case cpuC0StateResidency:
|
||||
case cpuC1StateResidency:
|
||||
case cpuC3StateResidency:
|
||||
case cpuC6StateResidency:
|
||||
case cpuC7StateResidency:
|
||||
case cpuBusyCycles:
|
||||
case cpuBusyFrequency:
|
||||
default:
|
||||
continue
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// needsRapl takes a slice of strings, representing supported metrics, and
|
||||
// returns true if any relies on intel-rapl control zone.
|
||||
func needsRapl(metrics []packageMetricType) bool {
|
||||
for _, m := range metrics {
|
||||
switch m {
|
||||
case packageCurrentPowerConsumption:
|
||||
case packageCurrentDramPowerConsumption:
|
||||
case packageThermalDesignPower:
|
||||
default:
|
||||
continue
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// needsCoreFreq takes a slice of strings, representing supported metrics, and
|
||||
// returns true if any relies on sysfs "/sys/devices/system/cpu/" with global and
|
||||
// individual CPU attributes.
|
||||
func needsCoreFreq(metrics []cpuMetricType) bool {
|
||||
return slices.Contains(metrics, cpuFrequency)
|
||||
}
|
||||
|
||||
// needsUncoreFreq takes a slice of strings, representing supported metrics, and returns
|
||||
// true if any relies on sysfs interface "/sys/devices/system/cpu/intel_uncore_frequency/"
|
||||
// provided by intel_uncore_frequency kernel module.
|
||||
func needsUncoreFreq(metrics []packageMetricType) bool {
|
||||
return slices.Contains(metrics, packageUncoreFrequency)
|
||||
}
|
||||
|
||||
// needsPerf takes a slice of strings, representing supported metrics, and
|
||||
// returns true if any relies on perf_events interface.
|
||||
func needsPerf(metrics []cpuMetricType) bool {
|
||||
for _, m := range metrics {
|
||||
switch m {
|
||||
case cpuC0SubstateC01Percent:
|
||||
case cpuC0SubstateC02Percent:
|
||||
case cpuC0SubstateC0WaitPercent:
|
||||
default:
|
||||
continue
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
@ -0,0 +1,305 @@
|
|||
//go:build linux && amd64
|
||||
|
||||
package intel_powerstat
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/influxdata/telegraf/testutil"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestGenerate(t *testing.T) {
|
||||
t.Run("NoCPUsSpecified", func(t *testing.T) {
|
||||
g := &optGenerator{}
|
||||
opts := g.generate(optConfig{
|
||||
cpuMetrics: []cpuMetricType{
|
||||
cpuFrequency, // needs coreFreq
|
||||
cpuC0SubstateC01Percent, // needs perf
|
||||
},
|
||||
packageMetrics: []packageMetricType{
|
||||
packageCurrentPowerConsumption, // needs rapl
|
||||
packageUncoreFrequency, // needs uncoreFreq and msr
|
||||
},
|
||||
})
|
||||
|
||||
require.Len(t, opts, 5)
|
||||
})
|
||||
|
||||
t.Run("ExcludedCPUs", func(t *testing.T) {
|
||||
g := &optGenerator{}
|
||||
opts := g.generate(optConfig{
|
||||
excludedCPUs: []int{0, 1, 2, 3},
|
||||
cpuMetrics: []cpuMetricType{
|
||||
// needs msr
|
||||
cpuTemperature,
|
||||
},
|
||||
packageMetrics: []packageMetricType{
|
||||
// needs rapl
|
||||
packageCurrentPowerConsumption,
|
||||
},
|
||||
})
|
||||
|
||||
require.Len(t, opts, 3)
|
||||
})
|
||||
|
||||
t.Run("IncludedCPUs", func(t *testing.T) {
|
||||
g := &optGenerator{}
|
||||
opts := g.generate(optConfig{
|
||||
includedCPUs: []int{0, 1, 2, 3},
|
||||
cpuMetrics: []cpuMetricType{
|
||||
cpuFrequency, // needs coreFreq
|
||||
cpuC0SubstateC0WaitPercent, // needs perf
|
||||
},
|
||||
packageMetrics: []packageMetricType{
|
||||
packageTurboLimit, // needs msr
|
||||
packageCurrentDramPowerConsumption, // needs rapl
|
||||
packageUncoreFrequency, // needs uncoreFreq
|
||||
},
|
||||
})
|
||||
|
||||
require.Len(t, opts, 6)
|
||||
})
|
||||
|
||||
t.Run("WithMsrTimeout", func(t *testing.T) {
|
||||
g := &optGenerator{}
|
||||
opts := g.generate(optConfig{
|
||||
cpuMetrics: []cpuMetricType{
|
||||
cpuTemperature,
|
||||
},
|
||||
msrReadTimeout: time.Second,
|
||||
})
|
||||
|
||||
require.Len(t, opts, 1)
|
||||
|
||||
withMsrTimeoutUsed := false
|
||||
for _, opt := range opts {
|
||||
if strings.Contains(runtime.FuncForPC(reflect.ValueOf(opt).Pointer()).Name(), ".WithMsrTimeout.") {
|
||||
withMsrTimeoutUsed = true
|
||||
continue
|
||||
}
|
||||
}
|
||||
require.True(t, withMsrTimeoutUsed, "WithMsrTimeout wasn't included in the generated options")
|
||||
})
|
||||
|
||||
t.Run("WithMsr", func(t *testing.T) {
|
||||
g := &optGenerator{}
|
||||
opts := g.generate(optConfig{
|
||||
cpuMetrics: []cpuMetricType{
|
||||
cpuC7StateResidency,
|
||||
},
|
||||
msrReadTimeout: 0, //timeout disabled
|
||||
})
|
||||
|
||||
require.Len(t, opts, 1)
|
||||
|
||||
withMsrUsed := false
|
||||
for _, opt := range opts {
|
||||
if strings.Contains(runtime.FuncForPC(reflect.ValueOf(opt).Pointer()).Name(), ".WithMsr.") {
|
||||
withMsrUsed = true
|
||||
continue
|
||||
}
|
||||
}
|
||||
require.True(t, withMsrUsed, "WithMsr wasn't included in the generated options")
|
||||
})
|
||||
|
||||
t.Run("WithLogger", func(t *testing.T) {
|
||||
g := &optGenerator{}
|
||||
opts := g.generate(optConfig{
|
||||
cpuMetrics: []cpuMetricType{
|
||||
cpuC3StateResidency,
|
||||
},
|
||||
log: &testutil.Logger{},
|
||||
})
|
||||
|
||||
require.Len(t, opts, 2)
|
||||
|
||||
withLoggerUsed := false
|
||||
for _, opt := range opts {
|
||||
if strings.Contains(runtime.FuncForPC(reflect.ValueOf(opt).Pointer()).Name(), ".WithLogger.") {
|
||||
withLoggerUsed = true
|
||||
continue
|
||||
}
|
||||
}
|
||||
require.True(t, withLoggerUsed, "WithLogger wasn't included in the generated options")
|
||||
})
|
||||
}
|
||||
|
||||
func TestNeedsMsrPackage(t *testing.T) {
|
||||
packageMetrics := []packageMetricType{
|
||||
packageThermalDesignPower, // needs rapl
|
||||
packageCurrentDramPowerConsumption, // needs rapl
|
||||
packageMetricType(420),
|
||||
}
|
||||
|
||||
t.Run("False", func(t *testing.T) {
|
||||
require.False(t, needsMsrPackage(packageMetrics))
|
||||
})
|
||||
|
||||
t.Run("True", func(t *testing.T) {
|
||||
t.Run("CPUBaseFreq", func(t *testing.T) {
|
||||
packageMetrics[len(packageMetrics)-1] = packageCPUBaseFrequency
|
||||
require.True(t, needsMsrPackage(packageMetrics))
|
||||
})
|
||||
|
||||
t.Run("PackageTurboLimit", func(t *testing.T) {
|
||||
packageMetrics[len(packageMetrics)-1] = packageTurboLimit
|
||||
require.True(t, needsMsrPackage(packageMetrics))
|
||||
})
|
||||
|
||||
t.Run("PackageUncoreFrequency", func(t *testing.T) {
|
||||
packageMetrics[len(packageMetrics)-1] = packageUncoreFrequency
|
||||
require.True(t, needsMsrPackage(packageMetrics))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestNeedsMsrCPU(t *testing.T) {
|
||||
cpuMetrics := []cpuMetricType{
|
||||
cpuFrequency, // needs cpuFreq
|
||||
cpuC0SubstateC01Percent, // needs perf
|
||||
}
|
||||
|
||||
t.Run("False", func(t *testing.T) {
|
||||
require.False(t, needsMsrCPU(cpuMetrics))
|
||||
})
|
||||
|
||||
t.Run("True", func(t *testing.T) {
|
||||
t.Run("CPUTemperature", func(t *testing.T) {
|
||||
cpuMetrics[len(cpuMetrics)-1] = cpuTemperature
|
||||
require.True(t, needsMsrCPU(cpuMetrics))
|
||||
})
|
||||
|
||||
t.Run("CPUC0StateResidency", func(t *testing.T) {
|
||||
cpuMetrics[len(cpuMetrics)-1] = cpuC0StateResidency
|
||||
require.True(t, needsMsrCPU(cpuMetrics))
|
||||
})
|
||||
|
||||
t.Run("CPUC1StateResidency", func(t *testing.T) {
|
||||
cpuMetrics[len(cpuMetrics)-1] = cpuC1StateResidency
|
||||
require.True(t, needsMsrCPU(cpuMetrics))
|
||||
})
|
||||
|
||||
t.Run("CPUC3StateResidency", func(t *testing.T) {
|
||||
cpuMetrics[len(cpuMetrics)-1] = cpuC3StateResidency
|
||||
require.True(t, needsMsrCPU(cpuMetrics))
|
||||
})
|
||||
|
||||
t.Run("CPUC6StateResidency", func(t *testing.T) {
|
||||
cpuMetrics[len(cpuMetrics)-1] = cpuC6StateResidency
|
||||
require.True(t, needsMsrCPU(cpuMetrics))
|
||||
})
|
||||
|
||||
t.Run("CPUC7StateResidency", func(t *testing.T) {
|
||||
cpuMetrics[len(cpuMetrics)-1] = cpuC7StateResidency
|
||||
require.True(t, needsMsrCPU(cpuMetrics))
|
||||
})
|
||||
|
||||
t.Run("CPUBusyCycles", func(t *testing.T) {
|
||||
cpuMetrics[len(cpuMetrics)-1] = cpuBusyCycles
|
||||
require.True(t, needsMsrCPU(cpuMetrics))
|
||||
})
|
||||
|
||||
t.Run("CPUBusyFrequency", func(t *testing.T) {
|
||||
cpuMetrics[len(cpuMetrics)-1] = cpuBusyFrequency
|
||||
require.True(t, needsMsrCPU(cpuMetrics))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestNeedsRapl(t *testing.T) {
|
||||
metrics := []packageMetricType{
|
||||
packageCPUBaseFrequency, // needs msr
|
||||
packageUncoreFrequency, // needs uncoreFreq
|
||||
packageMetricType(420),
|
||||
}
|
||||
|
||||
t.Run("False", func(t *testing.T) {
|
||||
require.False(t, needsRapl(metrics))
|
||||
})
|
||||
|
||||
t.Run("True", func(t *testing.T) {
|
||||
t.Run("PackageCurrentPowerConsumption", func(t *testing.T) {
|
||||
metrics[len(metrics)-1] = packageCurrentPowerConsumption
|
||||
require.True(t, needsRapl(metrics))
|
||||
})
|
||||
|
||||
t.Run("PackageCurrentDramPowerConsumption", func(t *testing.T) {
|
||||
metrics[len(metrics)-1] = packageCurrentDramPowerConsumption
|
||||
require.True(t, needsRapl(metrics))
|
||||
})
|
||||
|
||||
t.Run("PackageThermalDesignPower", func(t *testing.T) {
|
||||
metrics[len(metrics)-1] = packageThermalDesignPower
|
||||
require.True(t, needsRapl(metrics))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestNeedsCoreFreq(t *testing.T) {
|
||||
metrics := []cpuMetricType{
|
||||
cpuTemperature, // needs msr
|
||||
cpuC1StateResidency, // needs msr
|
||||
cpuC0SubstateC01Percent, // needs perf
|
||||
cpuMetricType(420),
|
||||
}
|
||||
|
||||
t.Run("False", func(t *testing.T) {
|
||||
require.False(t, needsCoreFreq(metrics))
|
||||
})
|
||||
|
||||
t.Run("True", func(t *testing.T) {
|
||||
metrics[len(metrics)-1] = cpuFrequency
|
||||
require.True(t, needsCoreFreq(metrics))
|
||||
})
|
||||
}
|
||||
|
||||
func TestNeedsUncoreFreq(t *testing.T) {
|
||||
metrics := []packageMetricType{
|
||||
packageCPUBaseFrequency, // needs msr
|
||||
packageThermalDesignPower, // needs rapl
|
||||
packageMetricType(420),
|
||||
}
|
||||
|
||||
t.Run("False", func(t *testing.T) {
|
||||
require.False(t, needsUncoreFreq(metrics))
|
||||
})
|
||||
|
||||
t.Run("True", func(t *testing.T) {
|
||||
metrics[len(metrics)-1] = packageUncoreFrequency
|
||||
require.True(t, needsUncoreFreq(metrics))
|
||||
})
|
||||
}
|
||||
|
||||
func TestNeedsPerf(t *testing.T) {
|
||||
metrics := []cpuMetricType{
|
||||
cpuFrequency, // needs cpuFreq
|
||||
cpuC1StateResidency, // needs msr
|
||||
cpuMetricType(420),
|
||||
}
|
||||
|
||||
t.Run("False", func(t *testing.T) {
|
||||
require.False(t, needsPerf(metrics))
|
||||
})
|
||||
|
||||
t.Run("True", func(t *testing.T) {
|
||||
t.Run("CPUC0SubstateC01Percent", func(t *testing.T) {
|
||||
metrics[len(metrics)-1] = cpuC0SubstateC01Percent
|
||||
require.True(t, needsPerf(metrics))
|
||||
})
|
||||
|
||||
t.Run("CPUC0SubstateC02Percent", func(t *testing.T) {
|
||||
metrics[len(metrics)-1] = cpuC0SubstateC02Percent
|
||||
require.True(t, needsPerf(metrics))
|
||||
})
|
||||
|
||||
t.Run("CPUC0SubstateC0WaitPercent", func(t *testing.T) {
|
||||
metrics[len(metrics)-1] = cpuC0SubstateC0WaitPercent
|
||||
require.True(t, needsPerf(metrics))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
@ -1,265 +0,0 @@
|
|||
//go:build linux
|
||||
|
||||
package intel_powerstat
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
)
|
||||
|
||||
const (
|
||||
intelRaplPath = "/sys/devices/virtual/powercap/intel-rapl"
|
||||
intelRaplSocketPartialPath = "%s/intel-rapl:%s"
|
||||
energyUjPartialPath = "%s/energy_uj"
|
||||
maxEnergyRangeUjPartialPath = "%s/max_energy_range_uj"
|
||||
maxPowerUwPartialPath = "%s/constraint_0_max_power_uw"
|
||||
intelRaplDramPartialPath = "%s/intel-rapl:%s/%s"
|
||||
intelRaplDramNamePartialPath = "%s/name"
|
||||
)
|
||||
|
||||
// raplService is responsible for interactions with RAPL.
|
||||
type raplService interface {
|
||||
initializeRaplData()
|
||||
getRaplData() map[string]*raplData
|
||||
retrieveAndCalculateData(socketID string) error
|
||||
getConstraintMaxPowerWatts(socketID string) (float64, error)
|
||||
}
|
||||
|
||||
type raplServiceImpl struct {
|
||||
log telegraf.Logger
|
||||
data map[string]*raplData
|
||||
dramFolders map[string]string
|
||||
fs fileService
|
||||
logOnce map[string]error
|
||||
}
|
||||
|
||||
// initializeRaplData looks for RAPL folders and initializes data map with fetched information.
|
||||
func (r *raplServiceImpl) initializeRaplData() {
|
||||
r.prepareData()
|
||||
r.findDramFolders()
|
||||
}
|
||||
|
||||
func (r *raplServiceImpl) getRaplData() map[string]*raplData {
|
||||
return r.data
|
||||
}
|
||||
|
||||
func (r *raplServiceImpl) retrieveAndCalculateData(socketID string) error {
|
||||
socketRaplPath := fmt.Sprintf(intelRaplSocketPartialPath, intelRaplPath, socketID)
|
||||
socketEnergyUjPath := fmt.Sprintf(energyUjPartialPath, socketRaplPath)
|
||||
err := checkFile(socketEnergyUjPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
socketEnergyUjFile, err := os.Open(socketEnergyUjPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error opening socket energy_uj file on path %q: %w", socketEnergyUjPath, err)
|
||||
}
|
||||
defer socketEnergyUjFile.Close()
|
||||
|
||||
dramRaplPath := fmt.Sprintf(intelRaplDramPartialPath, intelRaplPath, socketID, r.dramFolders[socketID])
|
||||
dramEnergyUjPath := fmt.Sprintf(energyUjPartialPath, dramRaplPath)
|
||||
err = checkFile(dramEnergyUjPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dramEnergyUjFile, err := os.Open(dramEnergyUjPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error opening dram energy_uj file on path %q: %w", dramEnergyUjPath, err)
|
||||
}
|
||||
defer dramEnergyUjFile.Close()
|
||||
|
||||
socketMaxEnergyUjPath := fmt.Sprintf(maxEnergyRangeUjPartialPath, socketRaplPath)
|
||||
err = checkFile(socketMaxEnergyUjPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
socketMaxEnergyUjFile, err := os.Open(socketMaxEnergyUjPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error opening socket max_energy_range_uj file on path %q: %w", socketMaxEnergyUjPath, err)
|
||||
}
|
||||
defer socketMaxEnergyUjFile.Close()
|
||||
|
||||
dramMaxEnergyUjPath := fmt.Sprintf(maxEnergyRangeUjPartialPath, dramRaplPath)
|
||||
err = checkFile(dramMaxEnergyUjPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dramMaxEnergyUjFile, err := os.Open(dramMaxEnergyUjPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error opening dram max_energy_range_uj file on path %q: %w", dramMaxEnergyUjPath, err)
|
||||
}
|
||||
defer dramMaxEnergyUjFile.Close()
|
||||
|
||||
return r.calculateData(socketID, socketEnergyUjFile, dramEnergyUjFile, socketMaxEnergyUjFile, dramMaxEnergyUjFile)
|
||||
}
|
||||
|
||||
func (r *raplServiceImpl) getConstraintMaxPowerWatts(socketID string) (float64, error) {
|
||||
socketRaplPath := fmt.Sprintf(intelRaplSocketPartialPath, intelRaplPath, socketID)
|
||||
socketMaxPowerPath := fmt.Sprintf(maxPowerUwPartialPath, socketRaplPath)
|
||||
err := checkFile(socketMaxPowerPath)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
socketMaxPowerFile, err := os.Open(socketMaxPowerPath)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("error opening constraint_0_max_power_uw file on path %q: %w", socketMaxPowerPath, err)
|
||||
}
|
||||
defer socketMaxPowerFile.Close()
|
||||
|
||||
socketMaxPower, _, err := r.fs.readFileToFloat64(socketMaxPowerFile)
|
||||
return convertMicroWattToWatt(socketMaxPower), err
|
||||
}
|
||||
|
||||
func (r *raplServiceImpl) prepareData() {
|
||||
intelRaplPrefix := "intel-rapl:"
|
||||
intelRapl := fmt.Sprintf("%s%s", intelRaplPrefix, "[0-9]*")
|
||||
raplPattern := fmt.Sprintf("%s/%s", intelRaplPath, intelRapl)
|
||||
|
||||
raplPaths, err := r.fs.getStringsMatchingPatternOnPath(raplPattern)
|
||||
if err != nil {
|
||||
r.log.Errorf("error while preparing RAPL data: %v", err)
|
||||
r.data = make(map[string]*raplData)
|
||||
return
|
||||
}
|
||||
if len(raplPaths) == 0 {
|
||||
r.log.Debugf("RAPL data wasn't found using pattern: %s", raplPattern)
|
||||
r.data = make(map[string]*raplData)
|
||||
return
|
||||
}
|
||||
|
||||
// If RAPL exists initialize data map (if it wasn't initialized before).
|
||||
if len(r.data) == 0 {
|
||||
for _, raplPath := range raplPaths {
|
||||
socketID := strings.TrimPrefix(filepath.Base(raplPath), intelRaplPrefix)
|
||||
r.data[socketID] = &raplData{
|
||||
socketCurrentEnergy: 0,
|
||||
dramCurrentEnergy: 0,
|
||||
socketEnergy: 0,
|
||||
dramEnergy: 0,
|
||||
readDate: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *raplServiceImpl) findDramFolders() {
|
||||
intelRaplPrefix := "intel-rapl:"
|
||||
intelRaplDram := fmt.Sprintf("%s%s", intelRaplPrefix, "[0-9]*[0-9]*")
|
||||
// Clean existing map
|
||||
r.dramFolders = make(map[string]string)
|
||||
|
||||
for socketID := range r.data {
|
||||
path := fmt.Sprintf(intelRaplSocketPartialPath, intelRaplPath, socketID)
|
||||
raplFoldersPattern := fmt.Sprintf("%s/%s", path, intelRaplDram)
|
||||
pathsToRaplFolders, err := r.fs.getStringsMatchingPatternOnPath(raplFoldersPattern)
|
||||
if err != nil {
|
||||
r.log.Errorf("error during lookup for rapl dram: %v", err)
|
||||
continue
|
||||
}
|
||||
if len(pathsToRaplFolders) == 0 {
|
||||
r.log.Debugf("RAPL folders weren't found using pattern: %s", raplFoldersPattern)
|
||||
continue
|
||||
}
|
||||
|
||||
raplFolders := make([]string, 0)
|
||||
for _, folderPath := range pathsToRaplFolders {
|
||||
raplFolders = append(raplFolders, filepath.Base(folderPath))
|
||||
}
|
||||
|
||||
r.findDramFolder(raplFolders, socketID)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *raplServiceImpl) findDramFolder(raplFolders []string, socketID string) {
|
||||
if r.logOnce == nil {
|
||||
r.logOnce = make(map[string]error)
|
||||
}
|
||||
|
||||
for _, raplFolder := range raplFolders {
|
||||
potentialDramPath := fmt.Sprintf(intelRaplDramPartialPath, intelRaplPath, socketID, raplFolder)
|
||||
nameFilePath := fmt.Sprintf(intelRaplDramNamePartialPath, potentialDramPath)
|
||||
read, err := r.fs.readFile(nameFilePath)
|
||||
if err != nil {
|
||||
if val := r.logOnce[nameFilePath]; val == nil || val.Error() != err.Error() {
|
||||
r.log.Errorf("error reading file on path %q: %v", nameFilePath, err)
|
||||
r.logOnce[nameFilePath] = err
|
||||
}
|
||||
continue
|
||||
}
|
||||
r.logOnce[nameFilePath] = nil
|
||||
// Remove new line character
|
||||
trimmedString := strings.TrimRight(string(read), "\n")
|
||||
if trimmedString == "dram" {
|
||||
// There should be only one DRAM folder per socket
|
||||
r.dramFolders[socketID] = raplFolder
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *raplServiceImpl) calculateData(socketID string, socketEnergyUjFile io.Reader, dramEnergyUjFile io.Reader,
|
||||
socketMaxEnergyUjFile io.Reader, dramMaxEnergyUjFile io.Reader,
|
||||
) error {
|
||||
newSocketEnergy, _, err := r.readEnergyInJoules(socketEnergyUjFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
newDramEnergy, readDate, err := r.readEnergyInJoules(dramEnergyUjFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
interval := convertNanoSecondsToSeconds(readDate - r.data[socketID].readDate)
|
||||
r.data[socketID].readDate = readDate
|
||||
if interval == 0 {
|
||||
return fmt.Errorf("interval between last two Telegraf cycles is 0")
|
||||
}
|
||||
|
||||
if newSocketEnergy >= r.data[socketID].socketEnergy {
|
||||
r.data[socketID].socketCurrentEnergy = (newSocketEnergy - r.data[socketID].socketEnergy) / interval
|
||||
} else {
|
||||
socketMaxEnergy, _, err := r.readEnergyInJoules(socketMaxEnergyUjFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// When socket energy_uj counter reaches maximum value defined in max_energy_range_uj file it
|
||||
// starts counting from 0.
|
||||
r.data[socketID].socketCurrentEnergy = (socketMaxEnergy - r.data[socketID].socketEnergy + newSocketEnergy) / interval
|
||||
}
|
||||
|
||||
if newDramEnergy >= r.data[socketID].dramEnergy {
|
||||
r.data[socketID].dramCurrentEnergy = (newDramEnergy - r.data[socketID].dramEnergy) / interval
|
||||
} else {
|
||||
dramMaxEnergy, _, err := r.readEnergyInJoules(dramMaxEnergyUjFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// When dram energy_uj counter reaches maximum value defined in max_energy_range_uj file it
|
||||
// starts counting from 0.
|
||||
r.data[socketID].dramCurrentEnergy = (dramMaxEnergy - r.data[socketID].dramEnergy + newDramEnergy) / interval
|
||||
}
|
||||
r.data[socketID].socketEnergy = newSocketEnergy
|
||||
r.data[socketID].dramEnergy = newDramEnergy
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *raplServiceImpl) readEnergyInJoules(reader io.Reader) (float64, int64, error) {
|
||||
currentEnergy, readDate, err := r.fs.readFileToFloat64(reader)
|
||||
return convertMicroJoulesToJoules(currentEnergy), readDate, err
|
||||
}
|
||||
|
||||
func newRaplServiceWithFs(logger telegraf.Logger, fs fileService) *raplServiceImpl {
|
||||
return &raplServiceImpl{
|
||||
log: logger,
|
||||
data: make(map[string]*raplData),
|
||||
dramFolders: make(map[string]string),
|
||||
fs: fs,
|
||||
}
|
||||
}
|
||||
|
|
@ -1,83 +0,0 @@
|
|||
//go:build linux
|
||||
|
||||
// Code generated by mockery v2.12.3. DO NOT EDIT.
|
||||
|
||||
package intel_powerstat
|
||||
|
||||
import mock "github.com/stretchr/testify/mock"
|
||||
|
||||
// mockRaplService is an autogenerated mock type for the mockRaplService type
|
||||
type mockRaplService struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// getConstraintMaxPowerWatts provides a mock function with given fields: socketID
|
||||
func (_m *mockRaplService) getConstraintMaxPowerWatts(socketID string) (float64, error) {
|
||||
ret := _m.Called(socketID)
|
||||
|
||||
var r0 float64
|
||||
if rf, ok := ret.Get(0).(func(string) float64); ok {
|
||||
r0 = rf(socketID)
|
||||
} else {
|
||||
r0 = ret.Get(0).(float64)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(string) error); ok {
|
||||
r1 = rf(socketID)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// getRaplData provides a mock function with given fields:
|
||||
func (_m *mockRaplService) getRaplData() map[string]*raplData {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 map[string]*raplData
|
||||
if rf, ok := ret.Get(0).(func() map[string]*raplData); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(map[string]*raplData)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// initializeRaplData provides a mock function with given fields:
|
||||
func (_m *mockRaplService) initializeRaplData() {
|
||||
_m.Called()
|
||||
}
|
||||
|
||||
// retrieveAndCalculateData provides a mock function with given fields: socketID
|
||||
func (_m *mockRaplService) retrieveAndCalculateData(socketID string) error {
|
||||
ret := _m.Called(socketID)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(string) error); ok {
|
||||
r0 = rf(socketID)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
type newmockRaplServiceT interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}
|
||||
|
||||
// newmockRaplService creates a new instance of mockRaplService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
func newmockRaplService(t newmockRaplServiceT) *mockRaplService {
|
||||
mock := &mockRaplService{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
||||
|
|
@ -1,115 +0,0 @@
|
|||
//go:build linux
|
||||
|
||||
package intel_powerstat
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/influxdata/telegraf/testutil"
|
||||
)
|
||||
|
||||
func TestPrepareData(t *testing.T) {
|
||||
sockets := []string{"intel-rapl:0", "intel-rapl:1"}
|
||||
rapl, fsMock := getRaplWithMockedFs()
|
||||
fsMock.On("getStringsMatchingPatternOnPath", mock.Anything).Return(sockets, nil).Twice()
|
||||
rapl.prepareData()
|
||||
fsMock.AssertCalled(t, "getStringsMatchingPatternOnPath", mock.Anything)
|
||||
require.Equal(t, len(sockets), len(rapl.getRaplData()))
|
||||
|
||||
// Verify no data is wiped in the next calls
|
||||
socketEnergy := 74563813417.0
|
||||
socketID := "0"
|
||||
rapl.data[socketID].socketEnergy = socketEnergy
|
||||
|
||||
rapl.prepareData()
|
||||
fsMock.AssertCalled(t, "getStringsMatchingPatternOnPath", mock.Anything)
|
||||
require.Equal(t, len(sockets), len(rapl.getRaplData()))
|
||||
require.Equal(t, socketEnergy, rapl.data[socketID].socketEnergy)
|
||||
|
||||
// Verify data is wiped once there is no RAPL folders
|
||||
fsMock.On("getStringsMatchingPatternOnPath", mock.Anything).
|
||||
Return(nil, errors.New("missing RAPL")).Once()
|
||||
rapl.prepareData()
|
||||
fsMock.AssertCalled(t, "getStringsMatchingPatternOnPath", mock.Anything)
|
||||
require.Empty(t, rapl.getRaplData())
|
||||
}
|
||||
|
||||
func TestFindDramFolders(t *testing.T) {
|
||||
sockets := []string{"0", "1"}
|
||||
raplFolders := []string{"intel-rapl:0:1", "intel-rapl:0:2", "intel-rapl:0:3"}
|
||||
rapl, fsMock := getRaplWithMockedFs()
|
||||
|
||||
for _, socketID := range sockets {
|
||||
rapl.data[socketID] = &raplData{}
|
||||
}
|
||||
|
||||
firstPath := fmt.Sprintf(intelRaplDramNamePartialPath,
|
||||
fmt.Sprintf(intelRaplDramPartialPath, intelRaplPath, "0", raplFolders[2]))
|
||||
secondPath := fmt.Sprintf(intelRaplDramNamePartialPath,
|
||||
fmt.Sprintf(intelRaplDramPartialPath, intelRaplPath, "1", raplFolders[1]))
|
||||
|
||||
fsMock.
|
||||
On("getStringsMatchingPatternOnPath", mock.Anything).Return(raplFolders, nil).Twice().
|
||||
On("readFile", firstPath).Return([]byte("dram"), nil).Once().
|
||||
On("readFile", secondPath).Return([]byte("dram"), nil).Once().
|
||||
On("readFile", mock.Anything).Return([]byte("random"), nil)
|
||||
|
||||
rapl.findDramFolders()
|
||||
|
||||
require.Equal(t, len(sockets), len(rapl.dramFolders))
|
||||
require.Equal(t, raplFolders[2], rapl.dramFolders["0"])
|
||||
require.Equal(t, raplFolders[1], rapl.dramFolders["1"])
|
||||
fsMock.AssertNumberOfCalls(t, "readFile", 5)
|
||||
}
|
||||
|
||||
func TestCalculateDataOverflowCases(t *testing.T) {
|
||||
socketID := "1"
|
||||
rapl, fsMock := getRaplWithMockedFs()
|
||||
|
||||
rapl.data[socketID] = &raplData{}
|
||||
rapl.data[socketID].socketEnergy = convertMicroJoulesToJoules(23424123.1)
|
||||
rapl.data[socketID].dramEnergy = convertMicroJoulesToJoules(345611233.2)
|
||||
rapl.data[socketID].readDate = 54123
|
||||
|
||||
interval := int64(54343)
|
||||
convertedInterval := convertNanoSecondsToSeconds(interval - rapl.data[socketID].readDate)
|
||||
|
||||
newEnergy := 3343443.4
|
||||
maxEnergy := 234324546456.6
|
||||
convertedNewEnergy := convertMicroJoulesToJoules(newEnergy)
|
||||
convertedMaxNewEnergy := convertMicroJoulesToJoules(maxEnergy)
|
||||
|
||||
maxDramEnergy := 981230834098.3
|
||||
newDramEnergy := 4533311.1
|
||||
convertedMaxDramEnergy := convertMicroJoulesToJoules(maxDramEnergy)
|
||||
convertedDramEnergy := convertMicroJoulesToJoules(newDramEnergy)
|
||||
|
||||
expectedCurrentEnergy := (convertedMaxNewEnergy - rapl.data[socketID].socketEnergy + convertedNewEnergy) / convertedInterval
|
||||
expectedDramCurrentEnergy := (convertedMaxDramEnergy - rapl.data[socketID].dramEnergy + convertedDramEnergy) / convertedInterval
|
||||
|
||||
fsMock.
|
||||
On("readFileToFloat64", mock.Anything).Return(newEnergy, int64(12321), nil).Once().
|
||||
On("readFileToFloat64", mock.Anything).Return(newDramEnergy, interval, nil).Once().
|
||||
On("readFileToFloat64", mock.Anything).Return(maxEnergy, int64(64534), nil).Once().
|
||||
On("readFileToFloat64", mock.Anything).Return(maxDramEnergy, int64(98342), nil).Once()
|
||||
|
||||
require.NoError(t, rapl.calculateData(socketID, strings.NewReader(mock.Anything), strings.NewReader(mock.Anything),
|
||||
strings.NewReader(mock.Anything), strings.NewReader(mock.Anything)))
|
||||
|
||||
require.Equal(t, expectedCurrentEnergy, rapl.data[socketID].socketCurrentEnergy)
|
||||
require.Equal(t, expectedDramCurrentEnergy, rapl.data[socketID].dramCurrentEnergy)
|
||||
}
|
||||
|
||||
func getRaplWithMockedFs() (*raplServiceImpl, *mockFileService) {
|
||||
logger := testutil.Logger{Name: "PowerPluginTest"}
|
||||
fsMock := &mockFileService{}
|
||||
rapl := newRaplServiceWithFs(logger, fsMock)
|
||||
|
||||
return rapl, fsMock
|
||||
}
|
||||
|
|
@ -1,15 +1,15 @@
|
|||
# Intel PowerStat plugin enables monitoring of platform metrics (power, TDP)
|
||||
# and per-CPU metrics like temperature, power and utilization. Please see the
|
||||
# plugin readme for details on software and hardware compatability.
|
||||
# This plugin ONLY supports Linux
|
||||
# This plugin ONLY supports Linux.
|
||||
[[inputs.intel_powerstat]]
|
||||
## The user can choose which package metrics are monitored by the plugin with
|
||||
## the package_metrics setting:
|
||||
## - The default, will collect "current_power_consumption",
|
||||
## "current_dram_power_consumption" and "thermal_design_power"
|
||||
## - Leaving this setting empty means no package metrics will be collected
|
||||
## "current_dram_power_consumption" and "thermal_design_power".
|
||||
## - Leaving this setting empty means no package metrics will be collected.
|
||||
## - Finally, a user can specify individual metrics to capture from the
|
||||
## supported options list
|
||||
## supported options list.
|
||||
## Supported options:
|
||||
## "current_power_consumption", "current_dram_power_consumption",
|
||||
## "thermal_design_power", "max_turbo_frequency", "uncore_frequency",
|
||||
|
|
@ -22,7 +22,29 @@
|
|||
## by the plugin.
|
||||
## Supported options:
|
||||
## "cpu_frequency", "cpu_c0_state_residency", "cpu_c1_state_residency",
|
||||
## "cpu_c6_state_residency", "cpu_busy_cycles", "cpu_temperature",
|
||||
## "cpu_busy_frequency"
|
||||
## ATTENTION: cpu_busy_cycles is DEPRECATED - use cpu_c0_state_residency
|
||||
## "cpu_c3_state_residency", "cpu_c6_state_residency", "cpu_c7_state_residency",
|
||||
## "cpu_temperature", "cpu_busy_frequency", "cpu_c0_substate_c01",
|
||||
## "cpu_c0_substate_c02", "cpu_c0_substate_c0_wait"
|
||||
# cpu_metrics = []
|
||||
|
||||
## Optionally the user can choose for which CPUs metrics configured in cpu_metrics array should be gathered.
|
||||
## Can't be combined with excluded_cpus.
|
||||
## Empty or missing array means CPU metrics are gathered for all CPUs.
|
||||
## e.g. ["0-3", "4,5,6"] or ["1-3,4"]
|
||||
# included_cpus = []
|
||||
|
||||
## Optionally the user can choose which CPUs should be excluded from gathering metrics configured in cpu_metrics array.
|
||||
## Can't be combined with included_cpus.
|
||||
## Empty or missing array means CPU metrics are gathered for all CPUs.
|
||||
## e.g. ["0-3", "4,5,6"] or ["1-3,4"]
|
||||
# excluded_cpus = []
|
||||
|
||||
## Filesystem location of JSON file that contains PMU event definitions.
|
||||
## Mandatory only for perf-related metrics (cpu_c0_substate_c01, cpu_c0_substate_c02, cpu_c0_substate_c0_wait).
|
||||
# event_definitions = ""
|
||||
|
||||
## The user can set the timeout duration for MSR reading.
|
||||
## Enabling this timeout can be useful in situations where, on heavily loaded systems,
|
||||
## the code waits too long for a kernel response to MSR read requests.
|
||||
## 0 disables the timeout (default).
|
||||
# msr_read_timeout = "0ms"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
processor : 0
|
||||
vendor_id : GenuineIntel
|
||||
cpu family : 6
|
||||
model : 143
|
||||
model name : Intel(R) Xeon(R) Platinum 8480+
|
||||
stepping : 8
|
||||
microcode : 0xab0000c0
|
||||
cpu MHz : 2000.000
|
||||
cache size : 107520 KB
|
||||
physical id : 0
|
||||
siblings : 112
|
||||
core id : 0
|
||||
cpu cores : 56
|
||||
apicid : 0
|
||||
initial apicid : 0
|
||||
fpu : yes
|
||||
fpu_exception : yes
|
||||
cpuid level : 32
|
||||
wp : yes
|
||||
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid tsc_known_freq pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb cat_l3 cat_l2 cdp_l3 invpcid_single intel_ppin cdp_l2 ssbd mba ibrs ibpb stibp ibrs_enhanced tpr_shadow vnmi flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid cqm rdt_a avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb intel_pt avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local split_lock_detect avx_vnni avx512_bf16 wbnoinvd dtherm ida arat pln pts hwp hwp_act_window hwp_epp hwp_pkg_req avx512vbmi umip pku ospke waitpkg avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg tme avx512_vpopcntdq la57 rdpid bus_lock_detect cldemote movdiri movdir64b enqcmd fsrm md_clear serialize tsxldtrk pconfig arch_lbr ibt amx_bf16 avx512_fp16 amx_tile amx_int8 flush_l1d arch_capabilities
|
||||
vmx flags : vnmi preemption_timer posted_intr invvpid ept_x_only ept_ad ept_1gb flexpriority apicv tsc_offset vtpr mtf vapic ept vpid unrestricted_guest vapic_reg vid ple shadow_vmcs pml ept_mode_based_exec tsc_scaling usr_wait_pause
|
||||
bugs : spectre_v1 spectre_v2 spec_store_bypass swapgs eibrs_pbrsb
|
||||
bogomips : 4000.00
|
||||
clflush size : 64
|
||||
cache_alignment : 64
|
||||
address sizes : 52 bits physical, 57 bits virtual
|
||||
power management:
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
processor : 0
|
||||
vendor_id : GenuineIntel
|
||||
cpu family : 6
|
||||
model name : Intel(R) Xeon(R) Platinum 8480+
|
||||
stepping : 8
|
||||
microcode : 0xab0000c0
|
||||
cpu MHz : 2000.000
|
||||
cache size : 107520 KB
|
||||
physical id : 0
|
||||
siblings : 112
|
||||
core id : 0
|
||||
cpu cores : 56
|
||||
apicid : 0
|
||||
initial apicid : 0
|
||||
fpu : yes
|
||||
fpu_exception : yes
|
||||
cpuid level : 32
|
||||
wp : yes
|
||||
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf tsc_known_freq pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb cat_l3 cat_l2 cdp_l3 invpcid_single intel_ppin cdp_l2 ssbd mba ibrs ibpb stibp ibrs_enhanced tpr_shadow vnmi flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid cqm rdt_a avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb intel_pt avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local split_lock_detect avx_vnni avx512_bf16 wbnoinvd dtherm ida arat pln pts hwp hwp_act_window hwp_epp hwp_pkg_req avx512vbmi umip pku ospke waitpkg avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg tme avx512_vpopcntdq la57 rdpid bus_lock_detect cldemote movdiri movdir64b enqcmd fsrm md_clear serialize tsxldtrk pconfig arch_lbr ibt amx_bf16 avx512_fp16 amx_tile amx_int8 flush_l1d arch_capabilities
|
||||
vmx flags : vnmi preemption_timer posted_intr invvpid ept_x_only ept_ad ept_1gb flexpriority apicv tsc_offset vtpr mtf vapic ept vpid unrestricted_guest vapic_reg vid ple shadow_vmcs pml ept_mode_based_exec tsc_scaling usr_wait_pause
|
||||
bugs : spectre_v1 spectre_v2 spec_store_bypass swapgs eibrs_pbrsb
|
||||
bogomips : 4000.00
|
||||
clflush size : 64
|
||||
cache_alignment : 64
|
||||
address sizes : 52 bits physical, 57 bits virtual
|
||||
power management:
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
processor : 0
|
||||
vendor_id : GenuineIntel
|
||||
cpu family : 6
|
||||
model : 143
|
||||
model name : Intel(R) Xeon(R) Platinum 8480+
|
||||
stepping : 8
|
||||
microcode : 0xab0000c0
|
||||
cpu MHz : 2000.000
|
||||
cache size : 107520 KB
|
||||
physical id : 0
|
||||
siblings : 112
|
||||
core id : 0
|
||||
cpu cores : 56
|
||||
apicid : 0
|
||||
initial apicid : 0
|
||||
fpu : yes
|
||||
fpu_exception : yes
|
||||
cpuid level : 32
|
||||
wp : yes
|
||||
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf tsc_known_freq pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb cat_l3 cat_l2 cdp_l3 invpcid_single intel_ppin cdp_l2 ssbd mba ibrs ibpb stibp ibrs_enhanced tpr_shadow vnmi flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid cqm rdt_a avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb intel_pt avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local split_lock_detect avx_vnni avx512_bf16 wbnoinvd dtherm ida arat pln pts hwp hwp_act_window hwp_epp hwp_pkg_req avx512vbmi umip pku ospke waitpkg avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg tme avx512_vpopcntdq la57 rdpid bus_lock_detect cldemote movdiri movdir64b enqcmd fsrm md_clear serialize tsxldtrk pconfig arch_lbr ibt amx_bf16 avx512_fp16 amx_tile amx_int8 flush_l1d arch_capabilities
|
||||
vmx flags : vnmi preemption_timer posted_intr invvpid ept_x_only ept_ad ept_1gb flexpriority apicv tsc_offset vtpr mtf vapic ept vpid unrestricted_guest vapic_reg vid ple shadow_vmcs pml ept_mode_based_exec tsc_scaling usr_wait_pause
|
||||
bugs : spectre_v1 spectre_v2 spec_store_bypass swapgs eibrs_pbrsb
|
||||
bogomips : 4000.00
|
||||
clflush size : 64
|
||||
cache_alignment : 64
|
||||
address sizes : 52 bits physical, 57 bits virtual
|
||||
power management:
|
||||
|
||||
processor : 1
|
||||
vendor_id : GenuineIntel
|
||||
cpu family : 6
|
||||
model : 143
|
||||
model name : Intel(R) Xeon(R) Platinum 8480+
|
||||
stepping : 8
|
||||
microcode : 0xab0000c0
|
||||
cpu MHz : 2000.000
|
||||
cache size : 107520 KB
|
||||
physical id : 0
|
||||
siblings : 112
|
||||
core id : 1
|
||||
cpu cores : 56
|
||||
apicid : 2
|
||||
initial apicid : 2
|
||||
fpu : yes
|
||||
fpu_exception : yes
|
||||
cpuid level : 32
|
||||
wp : yes
|
||||
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf tsc_known_freq pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb cat_l3 cat_l2 cdp_l3 invpcid_single intel_ppin cdp_l2 ssbd mba ibrs ibpb stibp ibrs_enhanced tpr_shadow vnmi flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid cqm rdt_a avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb intel_pt avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local split_lock_detect avx_vnni avx512_bf16 wbnoinvd dtherm ida arat pln pts hwp hwp_act_window hwp_epp hwp_pkg_req avx512vbmi umip pku ospke waitpkg avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg tme avx512_vpopcntdq la57 rdpid bus_lock_detect cldemote movdiri movdir64b enqcmd fsrm md_clear serialize tsxldtrk pconfig arch_lbr ibt amx_bf16 avx512_fp16 amx_tile amx_int8 flush_l1d arch_capabilities
|
||||
vmx flags : vnmi preemption_timer posted_intr invvpid ept_x_only ept_ad ept_1gb flexpriority apicv tsc_offset vtpr mtf vapic ept vpid unrestricted_guest vapic_reg vid ple shadow_vmcs pml ept_mode_based_exec tsc_scaling usr_wait_pause
|
||||
bugs : spectre_v1 spectre_v2 spec_store_bypass swapgs eibrs_pbrsb
|
||||
bogomips : 4000.00
|
||||
clflush size : 64
|
||||
cache_alignment : 64
|
||||
address sizes : 52 bits physical, 57 bits virtual
|
||||
power management:
|
||||
|
||||
processor : 2
|
||||
vendor_id : GenuineIntel
|
||||
cpu family : 6
|
||||
model : 143
|
||||
model name : Intel(R) Xeon(R) Platinum 8480+
|
||||
stepping : 8
|
||||
microcode : 0xab0000c0
|
||||
cpu MHz : 2000.000
|
||||
cache size : 107520 KB
|
||||
physical id : 0
|
||||
siblings : 112
|
||||
core id : 2
|
||||
cpu cores : 56
|
||||
apicid : 4
|
||||
initial apicid : 4
|
||||
fpu : yes
|
||||
fpu_exception : yes
|
||||
cpuid level : 32
|
||||
wp : yes
|
||||
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf tsc_known_freq pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb cat_l3 cat_l2 cdp_l3 invpcid_single intel_ppin cdp_l2 ssbd mba ibrs ibpb stibp ibrs_enhanced tpr_shadow vnmi flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid cqm rdt_a avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb intel_pt avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local split_lock_detect avx_vnni avx512_bf16 wbnoinvd dtherm ida arat pln pts hwp hwp_act_window hwp_epp hwp_pkg_req avx512vbmi umip pku ospke waitpkg avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg tme avx512_vpopcntdq la57 rdpid bus_lock_detect cldemote movdiri movdir64b enqcmd fsrm md_clear serialize tsxldtrk pconfig arch_lbr ibt amx_bf16 avx512_fp16 amx_tile amx_int8 flush_l1d arch_capabilities
|
||||
vmx flags : vnmi preemption_timer posted_intr invvpid ept_x_only ept_ad ept_1gb flexpriority apicv tsc_offset vtpr mtf vapic ept vpid unrestricted_guest vapic_reg vid ple shadow_vmcs pml ept_mode_based_exec tsc_scaling usr_wait_pause
|
||||
bugs : spectre_v1 spectre_v2 spec_store_bypass swapgs eibrs_pbrsb
|
||||
bogomips : 4000.00
|
||||
clflush size : 64
|
||||
cache_alignment : 64
|
||||
address sizes : 52 bits physical, 57 bits virtual
|
||||
power management:
|
||||
|
||||
processor : 3
|
||||
vendor_id : GenuineIntel
|
||||
cpu family : 6
|
||||
model : 143
|
||||
model name : Intel(R) Xeon(R) Platinum 8480+
|
||||
stepping : 8
|
||||
microcode : 0xab0000c0
|
||||
cpu MHz : 2000.000
|
||||
cache size : 107520 KB
|
||||
physical id : 0
|
||||
siblings : 112
|
||||
core id : 3
|
||||
cpu cores : 56
|
||||
apicid : 6
|
||||
initial apicid : 6
|
||||
fpu : yes
|
||||
fpu_exception : yes
|
||||
cpuid level : 32
|
||||
wp : yes
|
||||
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf tsc_known_freq pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb cat_l3 cat_l2 cdp_l3 invpcid_single intel_ppin cdp_l2 ssbd mba ibrs ibpb stibp ibrs_enhanced tpr_shadow vnmi flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid cqm rdt_a avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb intel_pt avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local split_lock_detect avx_vnni avx512_bf16 wbnoinvd dtherm ida arat pln pts hwp hwp_act_window hwp_epp hwp_pkg_req avx512vbmi umip pku ospke waitpkg avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg tme avx512_vpopcntdq la57 rdpid bus_lock_detect cldemote movdiri movdir64b enqcmd fsrm md_clear serialize tsxldtrk pconfig arch_lbr ibt amx_bf16 avx512_fp16 amx_tile amx_int8 flush_l1d arch_capabilities
|
||||
vmx flags : vnmi preemption_timer posted_intr invvpid ept_x_only ept_ad ept_1gb flexpriority apicv tsc_offset vtpr mtf vapic ept vpid unrestricted_guest vapic_reg vid ple shadow_vmcs pml ept_mode_based_exec tsc_scaling usr_wait_pause
|
||||
bugs : spectre_v1 spectre_v2 spec_store_bypass swapgs eibrs_pbrsb
|
||||
bogomips : 4000.00
|
||||
clflush size : 64
|
||||
cache_alignment : 64
|
||||
address sizes : 52 bits physical, 57 bits virtual
|
||||
power management:
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
processor : 0
|
||||
vendor_id : GenuineIntel
|
||||
cpu family : 6
|
||||
model : 143
|
||||
model name : Intel(R) Xeon(R) Platinum 8480+
|
||||
stepping : 8
|
||||
microcode : 0xab0000c0
|
||||
cpu MHz : 2000.000
|
||||
cache size : 107520 KB
|
||||
physical id : 0
|
||||
siblings : 112
|
||||
core id : 0
|
||||
cpu cores : 56
|
||||
apicid : 0
|
||||
initial apicid : 0
|
||||
fpu : yes
|
||||
fpu_exception : yes
|
||||
cpuid level : 32
|
||||
wp : yes
|
||||
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf tsc_known_freq pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb cat_l3 cat_l2 cdp_l3 invpcid_single intel_ppin cdp_l2 ssbd mba ibrs ibpb stibp ibrs_enhanced tpr_shadow vnmi flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid cqm rdt_a avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb intel_pt avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local split_lock_detect avx_vnni avx512_bf16 wbnoinvd dtherm ida arat pln pts hwp hwp_act_window hwp_epp hwp_pkg_req avx512vbmi umip pku ospke waitpkg avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg tme avx512_vpopcntdq la57 rdpid bus_lock_detect cldemote movdiri movdir64b enqcmd fsrm md_clear serialize tsxldtrk pconfig arch_lbr ibt amx_bf16 avx512_fp16 amx_tile amx_int8 flush_l1d arch_capabilities
|
||||
vmx flags : vnmi preemption_timer posted_intr invvpid ept_x_only ept_ad ept_1gb flexpriority apicv tsc_offset vtpr mtf vapic ept vpid unrestricted_guest vapic_reg vid ple shadow_vmcs pml ept_mode_based_exec tsc_scaling usr_wait_pause
|
||||
bugs : spectre_v1 spectre_v2 spec_store_bypass swapgs eibrs_pbrsb
|
||||
bogomips : 4000.00
|
||||
clflush size : 64
|
||||
cache_alignment : 64
|
||||
address sizes : 52 bits physical, 57 bits virtual
|
||||
power management:
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
processor : 0
|
||||
vendor_id : GenuineIntel
|
||||
cpu family : 6
|
||||
model : 14
|
||||
model name : Intel(R) Xeon(R) Platinum 8480+
|
||||
stepping : 8
|
||||
microcode : 0xab0000c0
|
||||
cpu MHz : 2000.000
|
||||
cache size : 107520 KB
|
||||
physical id : 0
|
||||
siblings : 112
|
||||
core id : 0
|
||||
cpu cores : 56
|
||||
apicid : 0
|
||||
initial apicid : 0
|
||||
fpu : yes
|
||||
fpu_exception : yes
|
||||
cpuid level : 32
|
||||
wp : yes
|
||||
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf tsc_known_freq pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb cat_l3 cat_l2 cdp_l3 invpcid_single intel_ppin cdp_l2 ssbd mba ibrs ibpb stibp ibrs_enhanced tpr_shadow vnmi flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid cqm rdt_a avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb intel_pt avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local split_lock_detect avx_vnni avx512_bf16 wbnoinvd dtherm ida arat pln pts hwp hwp_act_window hwp_epp hwp_pkg_req avx512vbmi umip pku ospke waitpkg avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg tme avx512_vpopcntdq la57 rdpid bus_lock_detect cldemote movdiri movdir64b enqcmd fsrm md_clear serialize tsxldtrk pconfig arch_lbr ibt amx_bf16 avx512_fp16 amx_tile amx_int8 flush_l1d arch_capabilities
|
||||
vmx flags : vnmi preemption_timer posted_intr invvpid ept_x_only ept_ad ept_1gb flexpriority apicv tsc_offset vtpr mtf vapic ept vpid unrestricted_guest vapic_reg vid ple shadow_vmcs pml ept_mode_based_exec tsc_scaling usr_wait_pause
|
||||
bugs : spectre_v1 spectre_v2 spec_store_bypass swapgs eibrs_pbrsb
|
||||
bogomips : 4000.00
|
||||
clflush size : 64
|
||||
cache_alignment : 64
|
||||
address sizes : 52 bits physical, 57 bits virtual
|
||||
power management:
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
processor : 0
|
||||
vendor_id : GenuineIntel
|
||||
cpu family : 6
|
||||
model : 143
|
||||
model name : Intel(R) Xeon(R) Platinum 8480+
|
||||
stepping : 8
|
||||
microcode : 0xab0000c0
|
||||
cpu MHz : 2000.000
|
||||
cache size : 107520 KB
|
||||
physical id : 0
|
||||
siblings : 112
|
||||
core id : 0
|
||||
cpu cores : 56
|
||||
apicid : 0
|
||||
initial apicid : 0
|
||||
fpu : yes
|
||||
fpu_exception : yes
|
||||
cpuid level : 32
|
||||
wp : yes
|
||||
flags : fpu vme de pse tsc pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf tsc_known_freq pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb cat_l3 cat_l2 cdp_l3 invpcid_single intel_ppin cdp_l2 ssbd mba ibrs ibpb stibp ibrs_enhanced tpr_shadow vnmi flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid cqm rdt_a avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb intel_pt avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local split_lock_detect avx_vnni avx512_bf16 wbnoinvd dtherm ida arat pln pts hwp hwp_act_window hwp_epp hwp_pkg_req avx512vbmi umip pku ospke waitpkg avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg tme avx512_vpopcntdq la57 rdpid bus_lock_detect cldemote movdiri movdir64b enqcmd fsrm md_clear serialize tsxldtrk pconfig arch_lbr ibt amx_bf16 avx512_fp16 amx_tile amx_int8 flush_l1d arch_capabilities
|
||||
vmx flags : vnmi preemption_timer posted_intr invvpid ept_x_only ept_ad ept_1gb flexpriority apicv tsc_offset vtpr mtf vapic ept vpid unrestricted_guest vapic_reg vid ple shadow_vmcs pml ept_mode_based_exec tsc_scaling usr_wait_pause
|
||||
bugs : spectre_v1 spectre_v2 spec_store_bypass swapgs eibrs_pbrsb
|
||||
bogomips : 4000.00
|
||||
clflush size : 64
|
||||
cache_alignment : 64
|
||||
address sizes : 52 bits physical, 57 bits virtual
|
||||
power management:
|
||||
|
|
@ -0,0 +1,299 @@
|
|||
{
|
||||
"Header": {
|
||||
"Copyright": "Copyright (c) 2001 - 2023 Intel Corporation. All rights reserved.",
|
||||
"Info": "Performance Monitoring Events for 4th Generation Intel(R) Xeon(R) Processor Scalable Family based on Sapphire Rapids microarchitecture - V1.15",
|
||||
"DatePublished": "06/28/2023",
|
||||
"Version": "1.15",
|
||||
"Legend": ""
|
||||
},
|
||||
"Events": [
|
||||
{
|
||||
"EventCode": "0x00",
|
||||
"UMask": "0x02",
|
||||
"EventName": "CPU_CLK_UNHALTED.THREAD",
|
||||
"BriefDescription": "Core cycles when the thread is not in halt state",
|
||||
"PublicDescription": "Counts the number of core cycles while the thread is not in a halt state. The thread enters the halt state when it is running the HLT instruction. This event is a component in many key event ratios. The core frequency may change from time to time due to transitions associated with Enhanced Intel SpeedStep Technology or TM2. For this reason this event may have a changing ratio with regards to time. When the core frequency is constant, this event can approximate elapsed time while the core was not in the halt state. It is counted on a dedicated fixed counter, leaving the eight programmable counters available for other events.",
|
||||
"Counter": "Fixed counter 1",
|
||||
"PEBScounters": "33",
|
||||
"SampleAfterValue": "2000003",
|
||||
"MSRIndex": "0x00",
|
||||
"MSRValue": "0x00",
|
||||
"CollectPEBSRecord": "2",
|
||||
"TakenAlone": "0",
|
||||
"CounterMask": "0",
|
||||
"Invert": "0",
|
||||
"EdgeDetect": "0",
|
||||
"PEBS": "0",
|
||||
"Data_LA": "0",
|
||||
"L1_Hit_Indication": "0",
|
||||
"Errata": "null",
|
||||
"Offcore": "0",
|
||||
"Deprecated": "0",
|
||||
"Speculative": "1"
|
||||
},
|
||||
{
|
||||
"EventCode": "0x00",
|
||||
"UMask": "0x03",
|
||||
"EventName": "CPU_CLK_UNHALTED.REF_TSC",
|
||||
"BriefDescription": "Reference cycles when the core is not in halt state.",
|
||||
"PublicDescription": "Counts the number of reference cycles when the core is not in a halt state. The core enters the halt state when it is running the HLT instruction or the MWAIT instruction. This event is not affected by core frequency changes (for example, P states, TM2 transitions) but has the same incrementing frequency as the time stamp counter. This event can approximate elapsed time while the core was not in a halt state. It is counted on a dedicated fixed counter, leaving the eight programmable counters available for other events. Note: On all current platforms this event stops counting during 'throttling (TM)' states duty off periods the processor is 'halted'. The counter update is done at a lower clock rate then the core clock the overflow status bit for this counter may appear 'sticky'. After the counter has overflowed and software clears the overflow status bit and resets the counter to less than MAX. The reset value to the counter is not clocked immediately so the overflow status bit will flip 'high (1)' and generate another PMI (if enabled) after which the reset value gets clocked into the counter. Therefore, software will get the interrupt, read the overflow status bit '1 for bit 34 while the counter value is less than MAX. Software should ignore this case.",
|
||||
"Counter": "Fixed counter 2",
|
||||
"PEBScounters": "34",
|
||||
"SampleAfterValue": "2000003",
|
||||
"MSRIndex": "0x00",
|
||||
"MSRValue": "0x00",
|
||||
"CollectPEBSRecord": "2",
|
||||
"TakenAlone": "0",
|
||||
"CounterMask": "0",
|
||||
"Invert": "0",
|
||||
"EdgeDetect": "0",
|
||||
"PEBS": "0",
|
||||
"Data_LA": "0",
|
||||
"L1_Hit_Indication": "0",
|
||||
"Errata": "null",
|
||||
"Offcore": "0",
|
||||
"Deprecated": "0",
|
||||
"Speculative": "1"
|
||||
},
|
||||
{
|
||||
"EventCode": "0x3c",
|
||||
"UMask": "0x00",
|
||||
"EventName": "CPU_CLK_UNHALTED.THREAD_P",
|
||||
"BriefDescription": "Thread cycles when thread is not in halt state",
|
||||
"PublicDescription": "This is an architectural event that counts the number of thread cycles while the thread is not in a halt state. The thread enters the halt state when it is running the HLT instruction. The core frequency may change from time to time due to power or thermal throttling. For this reason, this event may have a changing ratio with regards to wall clock time.",
|
||||
"Counter": "0,1,2,3,4,5,6,7",
|
||||
"PEBScounters": "0,1,2,3,4,5,6,7",
|
||||
"SampleAfterValue": "2000003",
|
||||
"MSRIndex": "0x00",
|
||||
"MSRValue": "0x00",
|
||||
"CollectPEBSRecord": "2",
|
||||
"TakenAlone": "0",
|
||||
"CounterMask": "0",
|
||||
"Invert": "0",
|
||||
"EdgeDetect": "0",
|
||||
"PEBS": "0",
|
||||
"Data_LA": "0",
|
||||
"L1_Hit_Indication": "0",
|
||||
"Errata": "null",
|
||||
"Offcore": "0",
|
||||
"Deprecated": "0",
|
||||
"Speculative": "1"
|
||||
},
|
||||
{
|
||||
"EventCode": "0x3c",
|
||||
"UMask": "0x01",
|
||||
"EventName": "CPU_CLK_UNHALTED.REF_TSC_P",
|
||||
"BriefDescription": "Reference cycles when the core is not in halt state.",
|
||||
"PublicDescription": "Counts the number of reference cycles when the core is not in a halt state. The core enters the halt state when it is running the HLT instruction or the MWAIT instruction. This event is not affected by core frequency changes (for example, P states, TM2 transitions) but has the same incrementing frequency as the time stamp counter. This event can approximate elapsed time while the core was not in a halt state. It is counted on a dedicated fixed counter, leaving the four (eight when Hyperthreading is disabled) programmable counters available for other events. Note: On all current platforms this event stops counting during 'throttling (TM)' states duty off periods the processor is 'halted'. The counter update is done at a lower clock rate then the core clock the overflow status bit for this counter may appear 'sticky'. After the counter has overflowed and software clears the overflow status bit and resets the counter to less than MAX. The reset value to the counter is not clocked immediately so the overflow status bit will flip 'high (1)' and generate another PMI (if enabled) after which the reset value gets clocked into the counter. Therefore, software will get the interrupt, read the overflow status bit '1 for bit 34 while the counter value is less than MAX. Software should ignore this case.",
|
||||
"Counter": "0,1,2,3,4,5,6,7",
|
||||
"PEBScounters": "0,1,2,3,4,5,6,7",
|
||||
"SampleAfterValue": "2000003",
|
||||
"MSRIndex": "0x00",
|
||||
"MSRValue": "0x00",
|
||||
"CollectPEBSRecord": "2",
|
||||
"TakenAlone": "0",
|
||||
"CounterMask": "0",
|
||||
"Invert": "0",
|
||||
"EdgeDetect": "0",
|
||||
"PEBS": "0",
|
||||
"Data_LA": "0",
|
||||
"L1_Hit_Indication": "0",
|
||||
"Errata": "null",
|
||||
"Offcore": "0",
|
||||
"Deprecated": "0",
|
||||
"Speculative": "1"
|
||||
},
|
||||
{
|
||||
"EventCode": "0x3c",
|
||||
"UMask": "0x02",
|
||||
"EventName": "CPU_CLK_UNHALTED.ONE_THREAD_ACTIVE",
|
||||
"BriefDescription": "Core crystal clock cycles when this thread is unhalted and the other thread is halted.",
|
||||
"PublicDescription": "Counts Core crystal clock cycles when current thread is unhalted and the other thread is halted.",
|
||||
"Counter": "0,1,2,3,4,5,6,7",
|
||||
"PEBScounters": "0,1,2,3,4,5,6,7",
|
||||
"SampleAfterValue": "25003",
|
||||
"MSRIndex": "0x00",
|
||||
"MSRValue": "0x00",
|
||||
"CollectPEBSRecord": "2",
|
||||
"TakenAlone": "0",
|
||||
"CounterMask": "0",
|
||||
"Invert": "0",
|
||||
"EdgeDetect": "0",
|
||||
"PEBS": "0",
|
||||
"Data_LA": "0",
|
||||
"L1_Hit_Indication": "0",
|
||||
"Errata": "null",
|
||||
"Offcore": "0",
|
||||
"Deprecated": "0",
|
||||
"Speculative": "1"
|
||||
},
|
||||
{
|
||||
"EventCode": "0x3c",
|
||||
"UMask": "0x08",
|
||||
"EventName": "CPU_CLK_UNHALTED.REF_DISTRIBUTED",
|
||||
"BriefDescription": "Core crystal clock cycles. Cycle counts are evenly distributed between active threads in the Core.",
|
||||
"PublicDescription": "This event distributes Core crystal clock cycle counts between active hyperthreads, i.e., those in C0 sleep-state. A hyperthread becomes inactive when it executes the HLT or MWAIT instructions. If one thread is active in a core, all counts are attributed to this hyperthread. To obtain the full count when the Core is active, sum the counts from each hyperthread.",
|
||||
"Counter": "0,1,2,3,4,5,6,7",
|
||||
"PEBScounters": "0,1,2,3,4,5,6,7",
|
||||
"SampleAfterValue": "2000003",
|
||||
"MSRIndex": "0x00",
|
||||
"MSRValue": "0x00",
|
||||
"CollectPEBSRecord": "2",
|
||||
"TakenAlone": "0",
|
||||
"CounterMask": "0",
|
||||
"Invert": "0",
|
||||
"EdgeDetect": "0",
|
||||
"PEBS": "0",
|
||||
"Data_LA": "0",
|
||||
"L1_Hit_Indication": "0",
|
||||
"Errata": "null",
|
||||
"Offcore": "0",
|
||||
"Deprecated": "0",
|
||||
"Speculative": "1"
|
||||
},
|
||||
{
|
||||
"EventCode": "0xec",
|
||||
"UMask": "0x02",
|
||||
"EventName": "CPU_CLK_UNHALTED.DISTRIBUTED",
|
||||
"BriefDescription": "Cycle counts are evenly distributed between active threads in the Core.",
|
||||
"PublicDescription": "This event distributes cycle counts between active hyperthreads, i.e., those in C0. A hyperthread becomes inactive when it executes the HLT or MWAIT instructions. If all other hyperthreads are inactive (or disabled or do not exist), all counts are attributed to this hyperthread. To obtain the full count when the Core is active, sum the counts from each hyperthread.",
|
||||
"Counter": "0,1,2,3,4,5,6,7",
|
||||
"PEBScounters": "0,1,2,3,4,5,6,7",
|
||||
"SampleAfterValue": "2000003",
|
||||
"MSRIndex": "0x00",
|
||||
"MSRValue": "0x00",
|
||||
"CollectPEBSRecord": "2",
|
||||
"TakenAlone": "0",
|
||||
"CounterMask": "0",
|
||||
"Invert": "0",
|
||||
"EdgeDetect": "0",
|
||||
"PEBS": "0",
|
||||
"Data_LA": "0",
|
||||
"L1_Hit_Indication": "0",
|
||||
"Errata": "null",
|
||||
"Offcore": "0",
|
||||
"Deprecated": "0",
|
||||
"Speculative": "1"
|
||||
},
|
||||
{
|
||||
"EventCode": "0xec",
|
||||
"UMask": "0x10",
|
||||
"EventName": "CPU_CLK_UNHALTED.C01",
|
||||
"BriefDescription": "Core clocks when the thread is in the C0.1 light-weight slower wakeup time but more power saving optimized state.",
|
||||
"PublicDescription": "Counts core clocks when the thread is in the C0.1 light-weight slower wakeup time but more power saving optimized state. This state can be entered via the TPAUSE or UMWAIT instructions.",
|
||||
"Counter": "0,1,2,3,4,5,6,7",
|
||||
"PEBScounters": "0,1,2,3,4,5,6,7",
|
||||
"SampleAfterValue": "2000003",
|
||||
"MSRIndex": "0x00",
|
||||
"MSRValue": "0x00",
|
||||
"CollectPEBSRecord": "2",
|
||||
"TakenAlone": "0",
|
||||
"CounterMask": "0",
|
||||
"Invert": "0",
|
||||
"EdgeDetect": "0",
|
||||
"PEBS": "0",
|
||||
"Data_LA": "0",
|
||||
"L1_Hit_Indication": "0",
|
||||
"Errata": "null",
|
||||
"Offcore": "0",
|
||||
"Deprecated": "0",
|
||||
"Speculative": "1"
|
||||
},
|
||||
{
|
||||
"EventCode": "0xec",
|
||||
"UMask": "0x20",
|
||||
"EventName": "CPU_CLK_UNHALTED.C02",
|
||||
"BriefDescription": "Core clocks when the thread is in the C0.2 light-weight faster wakeup time but less power saving optimized state.",
|
||||
"PublicDescription": "Counts core clocks when the thread is in the C0.2 light-weight faster wakeup time but less power saving optimized state. This state can be entered via the TPAUSE or UMWAIT instructions.",
|
||||
"Counter": "0,1,2,3,4,5,6,7",
|
||||
"PEBScounters": "0,1,2,3,4,5,6,7",
|
||||
"SampleAfterValue": "2000003",
|
||||
"MSRIndex": "0x00",
|
||||
"MSRValue": "0x00",
|
||||
"CollectPEBSRecord": "2",
|
||||
"TakenAlone": "0",
|
||||
"CounterMask": "0",
|
||||
"Invert": "0",
|
||||
"EdgeDetect": "0",
|
||||
"PEBS": "0",
|
||||
"Data_LA": "0",
|
||||
"L1_Hit_Indication": "0",
|
||||
"Errata": "null",
|
||||
"Offcore": "0",
|
||||
"Deprecated": "0",
|
||||
"Speculative": "1"
|
||||
},
|
||||
{
|
||||
"EventCode": "0xec",
|
||||
"UMask": "0x40",
|
||||
"EventName": "CPU_CLK_UNHALTED.PAUSE",
|
||||
"BriefDescription": "CPU_CLK_UNHALTED.PAUSE",
|
||||
"PublicDescription": "CPU_CLK_UNHALTED.PAUSE",
|
||||
"Counter": "0,1,2,3,4,5,6,7",
|
||||
"PEBScounters": "0,1,2,3,4,5,6,7",
|
||||
"SampleAfterValue": "2000003",
|
||||
"MSRIndex": "0x00",
|
||||
"MSRValue": "0x00",
|
||||
"CollectPEBSRecord": "2",
|
||||
"TakenAlone": "0",
|
||||
"CounterMask": "0",
|
||||
"Invert": "0",
|
||||
"EdgeDetect": "0",
|
||||
"PEBS": "0",
|
||||
"Data_LA": "0",
|
||||
"L1_Hit_Indication": "0",
|
||||
"Errata": "null",
|
||||
"Offcore": "0",
|
||||
"Deprecated": "0",
|
||||
"Speculative": "1"
|
||||
},
|
||||
{
|
||||
"EventCode": "0xec",
|
||||
"UMask": "0x40",
|
||||
"EventName": "CPU_CLK_UNHALTED.PAUSE_INST",
|
||||
"BriefDescription": "CPU_CLK_UNHALTED.PAUSE_INST",
|
||||
"PublicDescription": "CPU_CLK_UNHALTED.PAUSE_INST",
|
||||
"Counter": "0,1,2,3,4,5,6,7",
|
||||
"PEBScounters": "0,1,2,3,4,5,6,7",
|
||||
"SampleAfterValue": "2000003",
|
||||
"MSRIndex": "0x00",
|
||||
"MSRValue": "0x00",
|
||||
"CollectPEBSRecord": "2",
|
||||
"TakenAlone": "0",
|
||||
"CounterMask": "1",
|
||||
"Invert": "0",
|
||||
"EdgeDetect": "1",
|
||||
"PEBS": "0",
|
||||
"Data_LA": "0",
|
||||
"L1_Hit_Indication": "0",
|
||||
"Errata": "null",
|
||||
"Offcore": "0",
|
||||
"Deprecated": "0",
|
||||
"Speculative": "1"
|
||||
},
|
||||
{
|
||||
"EventCode": "0xec",
|
||||
"UMask": "0x70",
|
||||
"EventName": "CPU_CLK_UNHALTED.C0_WAIT",
|
||||
"BriefDescription": "Core clocks when the thread is in the C0.1 or C0.2 or running a PAUSE in C0 ACPI state.",
|
||||
"PublicDescription": "Counts core clocks when the thread is in the C0.1 or C0.2 power saving optimized states (TPAUSE or UMWAIT instructions) or running the PAUSE instruction.",
|
||||
"Counter": "0,1,2,3,4,5,6,7",
|
||||
"PEBScounters": "0,1,2,3,4,5,6,7",
|
||||
"SampleAfterValue": "2000003",
|
||||
"MSRIndex": "0x00",
|
||||
"MSRValue": "0x00",
|
||||
"CollectPEBSRecord": "2",
|
||||
"TakenAlone": "0",
|
||||
"CounterMask": "0",
|
||||
"Invert": "0",
|
||||
"EdgeDetect": "0",
|
||||
"PEBS": "0",
|
||||
"Data_LA": "0",
|
||||
"L1_Hit_Indication": "0",
|
||||
"Errata": "null",
|
||||
"Offcore": "0",
|
||||
"Deprecated": "0",
|
||||
"Speculative": "1"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
//go:build linux
|
||||
|
||||
package intel_powerstat
|
||||
|
||||
import (
|
||||
"math"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const (
|
||||
microJouleToJoule = 1.0 / 1000000
|
||||
microWattToWatt = 1.0 / 1000000
|
||||
kiloHertzToMegaHertz = 1.0 / 1000
|
||||
nanoSecondsToSeconds = 1.0 / 1000000000
|
||||
cyclesToHertz = 1.0 / 1000000
|
||||
)
|
||||
|
||||
func convertMicroJoulesToJoules(mJ float64) float64 {
|
||||
return mJ * microJouleToJoule
|
||||
}
|
||||
|
||||
func convertMicroWattToWatt(mW float64) float64 {
|
||||
return mW * microWattToWatt
|
||||
}
|
||||
|
||||
func convertKiloHertzToMegaHertz(kiloHertz float64) float64 {
|
||||
return kiloHertz * kiloHertzToMegaHertz
|
||||
}
|
||||
|
||||
func convertNanoSecondsToSeconds(ns int64) float64 {
|
||||
return float64(ns) * nanoSecondsToSeconds
|
||||
}
|
||||
|
||||
func convertProcessorCyclesToHertz(pc uint64) float64 {
|
||||
return float64(pc) * cyclesToHertz
|
||||
}
|
||||
|
||||
func roundFloatToNearestTwoDecimalPlaces(n float64) float64 {
|
||||
return math.Round(n*100) / 100
|
||||
}
|
||||
|
||||
func convertIntegerArrayToStringArray(array []int64) []string {
|
||||
stringArray := make([]string, 0)
|
||||
for _, value := range array {
|
||||
stringArray = append(stringArray, strconv.FormatInt(value, 10))
|
||||
}
|
||||
|
||||
return stringArray
|
||||
}
|
||||
Loading…
Reference in New Issue