summaryrefslogtreecommitdiff
path: root/tools/power/x86/turbostat/turbostat.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/power/x86/turbostat/turbostat.c')
-rw-r--r--tools/power/x86/turbostat/turbostat.c4385
1 files changed, 3505 insertions, 880 deletions
diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c
index 98256468e248..0170d3cc6819 100644
--- a/tools/power/x86/turbostat/turbostat.c
+++ b/tools/power/x86/turbostat/turbostat.c
@@ -3,13 +3,41 @@
* turbostat -- show CPU frequency and C-state residency
* on modern Intel and AMD processors.
*
- * Copyright (c) 2024 Intel Corporation.
+ * Copyright (c) 2025 Intel Corporation.
* Len Brown <len.brown@intel.com>
*/
#define _GNU_SOURCE
#include MSRHEADER
+
+// copied from arch/x86/include/asm/cpu_device_id.h
+#define VFM_MODEL_BIT 0
+#define VFM_FAMILY_BIT 8
+#define VFM_VENDOR_BIT 16
+#define VFM_RSVD_BIT 24
+
+#define VFM_MODEL_MASK GENMASK(VFM_FAMILY_BIT - 1, VFM_MODEL_BIT)
+#define VFM_FAMILY_MASK GENMASK(VFM_VENDOR_BIT - 1, VFM_FAMILY_BIT)
+#define VFM_VENDOR_MASK GENMASK(VFM_RSVD_BIT - 1, VFM_VENDOR_BIT)
+
+#define VFM_MODEL(vfm) (((vfm) & VFM_MODEL_MASK) >> VFM_MODEL_BIT)
+#define VFM_FAMILY(vfm) (((vfm) & VFM_FAMILY_MASK) >> VFM_FAMILY_BIT)
+#define VFM_VENDOR(vfm) (((vfm) & VFM_VENDOR_MASK) >> VFM_VENDOR_BIT)
+
+#define VFM_MAKE(_vendor, _family, _model) ( \
+ ((_model) << VFM_MODEL_BIT) | \
+ ((_family) << VFM_FAMILY_BIT) | \
+ ((_vendor) << VFM_VENDOR_BIT) \
+)
+// end copied section
+
+#define CPUID_LEAF_MODEL_ID 0x1A
+#define CPUID_LEAF_MODEL_ID_CORE_TYPE_SHIFT 24
+
+#define X86_VENDOR_INTEL 0
+
#include INTEL_FAMILY_HEADER
+#include BUILD_BUG_HEADER
#include <stdarg.h>
#include <stdio.h>
#include <err.h>
@@ -19,6 +47,7 @@
#include <sys/stat.h>
#include <sys/select.h>
#include <sys/resource.h>
+#include <sys/mman.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/time.h>
@@ -54,19 +83,55 @@
*/
#define NAME_BYTES 20
#define PATH_BYTES 128
+#define PERF_NAME_BYTES 128
#define MAX_NOFILE 0x8000
+#define COUNTER_KIND_PERF_PREFIX "perf/"
+#define COUNTER_KIND_PERF_PREFIX_LEN strlen(COUNTER_KIND_PERF_PREFIX)
+#define PERF_DEV_NAME_BYTES 32
+#define PERF_EVT_NAME_BYTES 32
+
+#define INTEL_ECORE_TYPE 0x20
+#define INTEL_PCORE_TYPE 0x40
+
+#define ROUND_UP_TO_PAGE_SIZE(n) (((n) + 0x1000UL-1UL) & ~(0x1000UL-1UL))
+
enum counter_scope { SCOPE_CPU, SCOPE_CORE, SCOPE_PACKAGE };
-enum counter_type { COUNTER_ITEMS, COUNTER_CYCLES, COUNTER_SECONDS, COUNTER_USEC };
-enum counter_format { FORMAT_RAW, FORMAT_DELTA, FORMAT_PERCENT };
-enum amperf_source { AMPERF_SOURCE_PERF, AMPERF_SOURCE_MSR };
-enum rapl_source { RAPL_SOURCE_NONE, RAPL_SOURCE_PERF, RAPL_SOURCE_MSR };
+enum counter_type { COUNTER_ITEMS, COUNTER_CYCLES, COUNTER_SECONDS, COUNTER_USEC, COUNTER_K2M };
+enum counter_format { FORMAT_RAW, FORMAT_DELTA, FORMAT_PERCENT, FORMAT_AVERAGE };
+enum counter_source { COUNTER_SOURCE_NONE, COUNTER_SOURCE_PERF, COUNTER_SOURCE_MSR };
+
+struct perf_counter_info {
+ struct perf_counter_info *next;
+
+ /* How to open the counter / What counter it is. */
+ char device[PERF_DEV_NAME_BYTES];
+ char event[PERF_EVT_NAME_BYTES];
+
+ /* How to show/format the counter. */
+ char name[PERF_NAME_BYTES];
+ unsigned int width;
+ enum counter_scope scope;
+ enum counter_type type;
+ enum counter_format format;
+ double scale;
+
+ /* For reading the counter. */
+ int *fd_perf_per_domain;
+ size_t num_domains;
+};
+
+struct sysfs_path {
+ char path[PATH_BYTES];
+ int id;
+ struct sysfs_path *next;
+};
struct msr_counter {
unsigned int msr_num;
char name[NAME_BYTES];
- char path[PATH_BYTES];
+ struct sysfs_path *sp;
unsigned int width;
enum counter_type type;
enum counter_format format;
@@ -78,64 +143,70 @@ struct msr_counter {
};
struct msr_counter bic[] = {
- { 0x0, "usec", "", 0, 0, 0, NULL, 0 },
- { 0x0, "Time_Of_Day_Seconds", "", 0, 0, 0, NULL, 0 },
- { 0x0, "Package", "", 0, 0, 0, NULL, 0 },
- { 0x0, "Node", "", 0, 0, 0, NULL, 0 },
- { 0x0, "Avg_MHz", "", 0, 0, 0, NULL, 0 },
- { 0x0, "Busy%", "", 0, 0, 0, NULL, 0 },
- { 0x0, "Bzy_MHz", "", 0, 0, 0, NULL, 0 },
- { 0x0, "TSC_MHz", "", 0, 0, 0, NULL, 0 },
- { 0x0, "IRQ", "", 0, 0, 0, NULL, 0 },
- { 0x0, "SMI", "", 32, 0, FORMAT_DELTA, NULL, 0 },
- { 0x0, "sysfs", "", 0, 0, 0, NULL, 0 },
- { 0x0, "CPU%c1", "", 0, 0, 0, NULL, 0 },
- { 0x0, "CPU%c3", "", 0, 0, 0, NULL, 0 },
- { 0x0, "CPU%c6", "", 0, 0, 0, NULL, 0 },
- { 0x0, "CPU%c7", "", 0, 0, 0, NULL, 0 },
- { 0x0, "ThreadC", "", 0, 0, 0, NULL, 0 },
- { 0x0, "CoreTmp", "", 0, 0, 0, NULL, 0 },
- { 0x0, "CoreCnt", "", 0, 0, 0, NULL, 0 },
- { 0x0, "PkgTmp", "", 0, 0, 0, NULL, 0 },
- { 0x0, "GFX%rc6", "", 0, 0, 0, NULL, 0 },
- { 0x0, "GFXMHz", "", 0, 0, 0, NULL, 0 },
- { 0x0, "Pkg%pc2", "", 0, 0, 0, NULL, 0 },
- { 0x0, "Pkg%pc3", "", 0, 0, 0, NULL, 0 },
- { 0x0, "Pkg%pc6", "", 0, 0, 0, NULL, 0 },
- { 0x0, "Pkg%pc7", "", 0, 0, 0, NULL, 0 },
- { 0x0, "Pkg%pc8", "", 0, 0, 0, NULL, 0 },
- { 0x0, "Pkg%pc9", "", 0, 0, 0, NULL, 0 },
- { 0x0, "Pk%pc10", "", 0, 0, 0, NULL, 0 },
- { 0x0, "CPU%LPI", "", 0, 0, 0, NULL, 0 },
- { 0x0, "SYS%LPI", "", 0, 0, 0, NULL, 0 },
- { 0x0, "PkgWatt", "", 0, 0, 0, NULL, 0 },
- { 0x0, "CorWatt", "", 0, 0, 0, NULL, 0 },
- { 0x0, "GFXWatt", "", 0, 0, 0, NULL, 0 },
- { 0x0, "PkgCnt", "", 0, 0, 0, NULL, 0 },
- { 0x0, "RAMWatt", "", 0, 0, 0, NULL, 0 },
- { 0x0, "PKG_%", "", 0, 0, 0, NULL, 0 },
- { 0x0, "RAM_%", "", 0, 0, 0, NULL, 0 },
- { 0x0, "Pkg_J", "", 0, 0, 0, NULL, 0 },
- { 0x0, "Cor_J", "", 0, 0, 0, NULL, 0 },
- { 0x0, "GFX_J", "", 0, 0, 0, NULL, 0 },
- { 0x0, "RAM_J", "", 0, 0, 0, NULL, 0 },
- { 0x0, "Mod%c6", "", 0, 0, 0, NULL, 0 },
- { 0x0, "Totl%C0", "", 0, 0, 0, NULL, 0 },
- { 0x0, "Any%C0", "", 0, 0, 0, NULL, 0 },
- { 0x0, "GFX%C0", "", 0, 0, 0, NULL, 0 },
- { 0x0, "CPUGFX%", "", 0, 0, 0, NULL, 0 },
- { 0x0, "Core", "", 0, 0, 0, NULL, 0 },
- { 0x0, "CPU", "", 0, 0, 0, NULL, 0 },
- { 0x0, "APIC", "", 0, 0, 0, NULL, 0 },
- { 0x0, "X2APIC", "", 0, 0, 0, NULL, 0 },
- { 0x0, "Die", "", 0, 0, 0, NULL, 0 },
- { 0x0, "GFXAMHz", "", 0, 0, 0, NULL, 0 },
- { 0x0, "IPC", "", 0, 0, 0, NULL, 0 },
- { 0x0, "CoreThr", "", 0, 0, 0, NULL, 0 },
- { 0x0, "UncMHz", "", 0, 0, 0, NULL, 0 },
- { 0x0, "SAM%mc6", "", 0, 0, 0, NULL, 0 },
- { 0x0, "SAMMHz", "", 0, 0, 0, NULL, 0 },
- { 0x0, "SAMAMHz", "", 0, 0, 0, NULL, 0 },
+ { 0x0, "usec", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "Time_Of_Day_Seconds", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "Package", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "Node", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "Avg_MHz", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "Busy%", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "Bzy_MHz", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "TSC_MHz", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "IRQ", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "SMI", NULL, 32, 0, FORMAT_DELTA, NULL, 0 },
+ { 0x0, "cpuidle", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "CPU%c1", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "CPU%c3", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "CPU%c6", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "CPU%c7", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "ThreadC", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "CoreTmp", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "CoreCnt", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "PkgTmp", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "GFX%rc6", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "GFXMHz", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "Pkg%pc2", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "Pkg%pc3", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "Pkg%pc6", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "Pkg%pc7", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "Pkg%pc8", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "Pkg%pc9", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "Pk%pc10", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "CPU%LPI", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "SYS%LPI", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "PkgWatt", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "CorWatt", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "GFXWatt", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "PkgCnt", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "RAMWatt", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "PKG_%", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "RAM_%", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "Pkg_J", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "Cor_J", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "GFX_J", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "RAM_J", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "Mod%c6", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "Totl%C0", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "Any%C0", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "GFX%C0", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "CPUGFX%", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "Core", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "CPU", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "APIC", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "X2APIC", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "Die", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "GFXAMHz", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "IPC", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "CoreThr", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "UncMHz", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "SAM%mc6", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "SAMMHz", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "SAMAMHz", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "Die%c6", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "SysWatt", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "Sys_J", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "NMI", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "CPU%c1e", NULL, 0, 0, 0, NULL, 0 },
+ { 0x0, "pct_idle", NULL, 0, 0, 0, NULL, 0 },
};
#define MAX_BIC (sizeof(bic) / sizeof(struct msr_counter))
@@ -149,7 +220,7 @@ struct msr_counter bic[] = {
#define BIC_TSC_MHz (1ULL << 7)
#define BIC_IRQ (1ULL << 8)
#define BIC_SMI (1ULL << 9)
-#define BIC_sysfs (1ULL << 10)
+#define BIC_cpuidle (1ULL << 10)
#define BIC_CPU_c1 (1ULL << 11)
#define BIC_CPU_c3 (1ULL << 12)
#define BIC_CPU_c6 (1ULL << 13)
@@ -197,17 +268,25 @@ struct msr_counter bic[] = {
#define BIC_SAM_mc6 (1ULL << 55)
#define BIC_SAMMHz (1ULL << 56)
#define BIC_SAMACTMHz (1ULL << 57)
-
-#define BIC_TOPOLOGY (BIC_Package | BIC_Node | BIC_CoreCnt | BIC_PkgCnt | BIC_Core | BIC_CPU | BIC_Die )
-#define BIC_THERMAL_PWR ( BIC_CoreTmp | BIC_PkgTmp | BIC_PkgWatt | BIC_CorWatt | BIC_GFXWatt | BIC_RAMWatt | BIC_PKG__ | BIC_RAM__)
-#define BIC_FREQUENCY (BIC_Avg_MHz | BIC_Busy | BIC_Bzy_MHz | BIC_TSC_MHz | BIC_GFXMHz | BIC_GFXACTMHz | BIC_SAMMHz | BIC_SAMACTMHz | BIC_UNCORE_MHZ)
-#define BIC_IDLE (BIC_sysfs | BIC_CPU_c1 | BIC_CPU_c3 | BIC_CPU_c6 | BIC_CPU_c7 | BIC_GFX_rc6 | BIC_Pkgpc2 | BIC_Pkgpc3 | BIC_Pkgpc6 | BIC_Pkgpc7 | BIC_Pkgpc8 | BIC_Pkgpc9 | BIC_Pkgpc10 | BIC_CPU_LPI | BIC_SYS_LPI | BIC_Mod_c6 | BIC_Totl_c0 | BIC_Any_c0 | BIC_GFX_c0 | BIC_CPUGFX | BIC_SAM_mc6)
-#define BIC_OTHER ( BIC_IRQ | BIC_SMI | BIC_ThreadC | BIC_CoreTmp | BIC_IPC)
-
-#define BIC_DISABLED_BY_DEFAULT (BIC_USEC | BIC_TOD | BIC_APIC | BIC_X2APIC)
+#define BIC_Diec6 (1ULL << 58)
+#define BIC_SysWatt (1ULL << 59)
+#define BIC_Sys_J (1ULL << 60)
+#define BIC_NMI (1ULL << 61)
+#define BIC_CPU_c1e (1ULL << 62)
+#define BIC_pct_idle (1ULL << 63)
+
+#define BIC_GROUP_TOPOLOGY (BIC_Package | BIC_Node | BIC_CoreCnt | BIC_PkgCnt | BIC_Core | BIC_CPU | BIC_Die)
+#define BIC_GROUP_THERMAL_PWR (BIC_CoreTmp | BIC_PkgTmp | BIC_PkgWatt | BIC_CorWatt | BIC_GFXWatt | BIC_RAMWatt | BIC_PKG__ | BIC_RAM__ | BIC_SysWatt)
+#define BIC_GROUP_FREQUENCY (BIC_Avg_MHz | BIC_Busy | BIC_Bzy_MHz | BIC_TSC_MHz | BIC_GFXMHz | BIC_GFXACTMHz | BIC_SAMMHz | BIC_SAMACTMHz | BIC_UNCORE_MHZ)
+#define BIC_GROUP_HW_IDLE (BIC_Busy | BIC_CPU_c1 | BIC_CPU_c3 | BIC_CPU_c6 | BIC_CPU_c7 | BIC_GFX_rc6 | BIC_Pkgpc2 | BIC_Pkgpc3 | BIC_Pkgpc6 | BIC_Pkgpc7 | BIC_Pkgpc8 | BIC_Pkgpc9 | BIC_Pkgpc10 | BIC_CPU_LPI | BIC_SYS_LPI | BIC_Mod_c6 | BIC_Totl_c0 | BIC_Any_c0 | BIC_GFX_c0 | BIC_CPUGFX | BIC_SAM_mc6 | BIC_Diec6)
+#define BIC_GROUP_SW_IDLE (BIC_Busy | BIC_cpuidle | BIC_pct_idle )
+#define BIC_GROUP_IDLE (BIC_GROUP_HW_IDLE | BIC_pct_idle)
+#define BIC_OTHER (BIC_IRQ | BIC_NMI | BIC_SMI | BIC_ThreadC | BIC_CoreTmp | BIC_IPC)
+
+#define BIC_DISABLED_BY_DEFAULT (BIC_USEC | BIC_TOD | BIC_APIC | BIC_X2APIC | BIC_cpuidle)
unsigned long long bic_enabled = (0xFFFFFFFFFFFFFFFFULL & ~BIC_DISABLED_BY_DEFAULT);
-unsigned long long bic_present = BIC_USEC | BIC_TOD | BIC_sysfs | BIC_APIC | BIC_X2APIC;
+unsigned long long bic_present = BIC_USEC | BIC_TOD | BIC_cpuidle | BIC_pct_idle | BIC_APIC | BIC_X2APIC;
#define DO_BIC(COUNTER_NAME) (bic_enabled & bic_present & COUNTER_NAME)
#define DO_BIC_READ(COUNTER_NAME) (bic_present & COUNTER_NAME)
@@ -216,13 +295,34 @@ unsigned long long bic_present = BIC_USEC | BIC_TOD | BIC_sysfs | BIC_APIC | BIC
#define BIC_NOT_PRESENT(COUNTER_BIT) (bic_present &= ~COUNTER_BIT)
#define BIC_IS_ENABLED(COUNTER_BIT) (bic_enabled & COUNTER_BIT)
+/*
+ * MSR_PKG_CST_CONFIG_CONTROL decoding for pkg_cstate_limit:
+ * If you change the values, note they are used both in comparisons
+ * (>= PCL__7) and to index pkg_cstate_limit_strings[].
+ */
+#define PCLUKN 0 /* Unknown */
+#define PCLRSV 1 /* Reserved */
+#define PCL__0 2 /* PC0 */
+#define PCL__1 3 /* PC1 */
+#define PCL__2 4 /* PC2 */
+#define PCL__3 5 /* PC3 */
+#define PCL__4 6 /* PC4 */
+#define PCL__6 7 /* PC6 */
+#define PCL_6N 8 /* PC6 No Retention */
+#define PCL_6R 9 /* PC6 Retention */
+#define PCL__7 10 /* PC7 */
+#define PCL_7S 11 /* PC7 Shrink */
+#define PCL__8 12 /* PC8 */
+#define PCL__9 13 /* PC9 */
+#define PCL_10 14 /* PC10 */
+#define PCLUNL 15 /* Unlimited */
+
struct amperf_group_fd;
char *proc_stat = "/proc/stat";
FILE *outf;
int *fd_percpu;
int *fd_instr_count_percpu;
-struct amperf_group_fd *fd_amperf_percpu; /* File descriptors for perf group with APERF and MPERF counters. */
struct timeval interval_tv = { 5, 0 };
struct timespec interval_ts = { 5, 0 };
@@ -236,7 +336,9 @@ unsigned int rapl_joules;
unsigned int summary_only;
unsigned int list_header_only;
unsigned int dump_only;
+unsigned int force_load;
unsigned int has_aperf;
+unsigned int has_aperf_access;
unsigned int has_epb;
unsigned int has_turbo;
unsigned int is_hybrid;
@@ -262,7 +364,7 @@ unsigned long long cpuidle_cur_sys_lpi_us;
unsigned int tj_max;
unsigned int tj_max_override;
double rapl_power_units, rapl_time_units;
-double rapl_dram_energy_units, rapl_energy_units;
+double rapl_dram_energy_units, rapl_energy_units, rapl_psys_energy_units;
double rapl_joule_counter_range;
unsigned int crystal_hz;
unsigned long long tsc_hz;
@@ -274,10 +376,12 @@ unsigned int has_hwp_activity_window; /* IA32_HWP_REQUEST[bits 41:32] */
unsigned int has_hwp_epp; /* IA32_HWP_REQUEST[bits 31:24] */
unsigned int has_hwp_pkg; /* IA32_HWP_REQUEST_PKG */
unsigned int first_counter_read = 1;
+
+static struct timeval procsysfs_tv_begin;
+
int ignore_stdin;
bool no_msr;
bool no_perf;
-enum amperf_source amperf_source;
enum gfx_sysfs_idx {
GFX_rc6,
@@ -290,7 +394,6 @@ enum gfx_sysfs_idx {
};
struct gfx_sysfs_info {
- const char *path;
FILE *fp;
unsigned int val;
unsigned long long val_ull;
@@ -299,6 +402,9 @@ struct gfx_sysfs_info {
static struct gfx_sysfs_info gfx_info[GFX_MAX];
int get_msr(int cpu, off_t offset, unsigned long long *msr);
+int add_counter(unsigned int msr_num, char *path, char *name,
+ unsigned int width, enum counter_scope scope,
+ enum counter_type type, enum counter_format format, int flags, int package_num);
/* Model specific support Start */
@@ -327,6 +433,7 @@ struct platform_features {
bool has_per_core_rapl; /* Indicates cores energy collection is per-core, not per-package. AMD specific for now */
bool has_rapl_divisor; /* Divisor for Energy unit raw value from MSR_RAPL_POWER_UNIT */
bool has_fixed_rapl_unit; /* Fixed Energy Unit used for DRAM RAPL Domain */
+ bool has_fixed_rapl_psys_unit; /* Fixed Energy Unit used for PSYS RAPL Domain */
int rapl_quirk_tdp; /* Hardcoded TDP value when cannot be retrieved from hardware */
int tcc_offset_bits; /* TCC Offset bits in MSR_IA32_TEMPERATURE_TARGET */
bool enable_tsc_tweak; /* Use CPU Base freq instead of TSC freq for aperf/mperf counter */
@@ -334,7 +441,7 @@ struct platform_features {
};
struct platform_data {
- unsigned int model;
+ unsigned int vfm;
const struct platform_features *features;
};
@@ -419,12 +526,15 @@ enum rapl_msrs {
RAPL_AMD_PWR_UNIT = BIT(14), /* 0xc0010299 MSR_AMD_RAPL_POWER_UNIT */
RAPL_AMD_CORE_ENERGY_STAT = BIT(15), /* 0xc001029a MSR_AMD_CORE_ENERGY_STATUS */
RAPL_AMD_PKG_ENERGY_STAT = BIT(16), /* 0xc001029b MSR_AMD_PKG_ENERGY_STATUS */
+ RAPL_PLATFORM_ENERGY_LIMIT = BIT(17), /* 0x64c MSR_PLATFORM_ENERGY_LIMIT */
+ RAPL_PLATFORM_ENERGY_STATUS = BIT(18), /* 0x64d MSR_PLATFORM_ENERGY_STATUS */
};
#define RAPL_PKG (RAPL_PKG_ENERGY_STATUS | RAPL_PKG_POWER_LIMIT)
#define RAPL_DRAM (RAPL_DRAM_ENERGY_STATUS | RAPL_DRAM_POWER_LIMIT)
#define RAPL_CORE (RAPL_CORE_ENERGY_STATUS | RAPL_CORE_POWER_LIMIT)
#define RAPL_GFX (RAPL_GFX_POWER_LIMIT | RAPL_GFX_ENERGY_STATUS)
+#define RAPL_PSYS (RAPL_PLATFORM_ENERGY_STATUS | RAPL_PLATFORM_ENERGY_LIMIT)
#define RAPL_PKG_ALL (RAPL_PKG | RAPL_PKG_PERF_STATUS | RAPL_PKG_POWER_INFO)
#define RAPL_DRAM_ALL (RAPL_DRAM | RAPL_DRAM_PERF_STATUS | RAPL_DRAM_POWER_INFO)
@@ -625,7 +735,7 @@ static const struct platform_features skl_features = {
.has_ext_cst_msrs = 1,
.trl_msrs = TRL_BASE,
.tcc_offset_bits = 6,
- .rapl_msrs = RAPL_PKG_ALL | RAPL_CORE_ALL | RAPL_DRAM | RAPL_DRAM_PERF_STATUS | RAPL_GFX,
+ .rapl_msrs = RAPL_PKG_ALL | RAPL_CORE_ALL | RAPL_DRAM | RAPL_DRAM_PERF_STATUS | RAPL_GFX | RAPL_PSYS,
.enable_tsc_tweak = 1,
};
@@ -642,25 +752,44 @@ static const struct platform_features cnl_features = {
.has_ext_cst_msrs = 1,
.trl_msrs = TRL_BASE,
.tcc_offset_bits = 6,
- .rapl_msrs = RAPL_PKG_ALL | RAPL_CORE_ALL | RAPL_DRAM | RAPL_DRAM_PERF_STATUS | RAPL_GFX,
+ .rapl_msrs = RAPL_PKG_ALL | RAPL_CORE_ALL | RAPL_DRAM | RAPL_DRAM_PERF_STATUS | RAPL_GFX | RAPL_PSYS,
.enable_tsc_tweak = 1,
};
+/* Copied from cnl_features, with PC7/PC9 removed */
static const struct platform_features adl_features = {
- .has_msr_misc_feature_control = 1,
- .has_msr_misc_pwr_mgmt = 1,
- .has_nhm_msrs = 1,
- .has_config_tdp = 1,
- .bclk_freq = BCLK_100MHZ,
- .supported_cstates = CC1 | CC6 | CC7 | PC2 | PC3 | PC6 | PC8 | PC10,
- .cst_limit = CST_LIMIT_HSW,
- .has_irtl_msrs = 1,
- .has_msr_core_c1_res = 1,
- .has_ext_cst_msrs = 1,
- .trl_msrs = TRL_BASE,
- .tcc_offset_bits = 6,
- .rapl_msrs = RAPL_PKG_ALL | RAPL_CORE_ALL | RAPL_DRAM | RAPL_DRAM_PERF_STATUS | RAPL_GFX,
- .enable_tsc_tweak = 1,
+ .has_msr_misc_feature_control = cnl_features.has_msr_misc_feature_control,
+ .has_msr_misc_pwr_mgmt = cnl_features.has_msr_misc_pwr_mgmt,
+ .has_nhm_msrs = cnl_features.has_nhm_msrs,
+ .has_config_tdp = cnl_features.has_config_tdp,
+ .bclk_freq = cnl_features.bclk_freq,
+ .supported_cstates = CC1 | CC6 | CC7 | PC2 | PC3 | PC6 | PC8 | PC10,
+ .cst_limit = cnl_features.cst_limit,
+ .has_irtl_msrs = cnl_features.has_irtl_msrs,
+ .has_msr_core_c1_res = cnl_features.has_msr_core_c1_res,
+ .has_ext_cst_msrs = cnl_features.has_ext_cst_msrs,
+ .trl_msrs = cnl_features.trl_msrs,
+ .tcc_offset_bits = cnl_features.tcc_offset_bits,
+ .rapl_msrs = cnl_features.rapl_msrs,
+ .enable_tsc_tweak = cnl_features.enable_tsc_tweak,
+};
+
+/* Copied from adl_features, with PC3/PC8 removed */
+static const struct platform_features lnl_features = {
+ .has_msr_misc_feature_control = adl_features.has_msr_misc_feature_control,
+ .has_msr_misc_pwr_mgmt = adl_features.has_msr_misc_pwr_mgmt,
+ .has_nhm_msrs = adl_features.has_nhm_msrs,
+ .has_config_tdp = adl_features.has_config_tdp,
+ .bclk_freq = adl_features.bclk_freq,
+ .supported_cstates = CC1 | CC6 | CC7 | PC2 | PC6 | PC10,
+ .cst_limit = adl_features.cst_limit,
+ .has_irtl_msrs = adl_features.has_irtl_msrs,
+ .has_msr_core_c1_res = adl_features.has_msr_core_c1_res,
+ .has_ext_cst_msrs = adl_features.has_ext_cst_msrs,
+ .trl_msrs = adl_features.trl_msrs,
+ .tcc_offset_bits = adl_features.tcc_offset_bits,
+ .rapl_msrs = adl_features.rapl_msrs,
+ .enable_tsc_tweak = adl_features.enable_tsc_tweak,
};
static const struct platform_features skx_features = {
@@ -690,7 +819,7 @@ static const struct platform_features icx_features = {
.has_irtl_msrs = 1,
.has_cst_prewake_bit = 1,
.trl_msrs = TRL_BASE | TRL_CORECOUNT,
- .rapl_msrs = RAPL_PKG_ALL | RAPL_DRAM_ALL,
+ .rapl_msrs = RAPL_PKG_ALL | RAPL_DRAM_ALL | RAPL_PSYS,
.has_fixed_rapl_unit = 1,
};
@@ -705,8 +834,9 @@ static const struct platform_features spr_features = {
.has_msr_core_c1_res = 1,
.has_irtl_msrs = 1,
.has_cst_prewake_bit = 1,
+ .has_fixed_rapl_psys_unit = 1,
.trl_msrs = TRL_BASE | TRL_CORECOUNT,
- .rapl_msrs = RAPL_PKG_ALL | RAPL_DRAM_ALL,
+ .rapl_msrs = RAPL_PKG_ALL | RAPL_DRAM_ALL | RAPL_PSYS,
};
static const struct platform_features srf_features = {
@@ -722,7 +852,7 @@ static const struct platform_features srf_features = {
.has_irtl_msrs = 1,
.has_cst_prewake_bit = 1,
.trl_msrs = TRL_BASE | TRL_CORECOUNT,
- .rapl_msrs = RAPL_PKG_ALL | RAPL_DRAM_ALL,
+ .rapl_msrs = RAPL_PKG_ALL | RAPL_DRAM_ALL | RAPL_PSYS,
};
static const struct platform_features grr_features = {
@@ -738,7 +868,7 @@ static const struct platform_features grr_features = {
.has_irtl_msrs = 1,
.has_cst_prewake_bit = 1,
.trl_msrs = TRL_BASE | TRL_CORECOUNT,
- .rapl_msrs = RAPL_PKG_ALL | RAPL_DRAM_ALL,
+ .rapl_msrs = RAPL_PKG_ALL | RAPL_DRAM_ALL | RAPL_PSYS,
};
static const struct platform_features slv_features = {
@@ -860,73 +990,78 @@ static const struct platform_features amd_features_with_rapl = {
};
static const struct platform_data turbostat_pdata[] = {
- { INTEL_FAM6_NEHALEM, &nhm_features },
- { INTEL_FAM6_NEHALEM_G, &nhm_features },
- { INTEL_FAM6_NEHALEM_EP, &nhm_features },
- { INTEL_FAM6_NEHALEM_EX, &nhx_features },
- { INTEL_FAM6_WESTMERE, &nhm_features },
- { INTEL_FAM6_WESTMERE_EP, &nhm_features },
- { INTEL_FAM6_WESTMERE_EX, &nhx_features },
- { INTEL_FAM6_SANDYBRIDGE, &snb_features },
- { INTEL_FAM6_SANDYBRIDGE_X, &snx_features },
- { INTEL_FAM6_IVYBRIDGE, &ivb_features },
- { INTEL_FAM6_IVYBRIDGE_X, &ivx_features },
- { INTEL_FAM6_HASWELL, &hsw_features },
- { INTEL_FAM6_HASWELL_X, &hsx_features },
- { INTEL_FAM6_HASWELL_L, &hswl_features },
- { INTEL_FAM6_HASWELL_G, &hswg_features },
- { INTEL_FAM6_BROADWELL, &bdw_features },
- { INTEL_FAM6_BROADWELL_G, &bdwg_features },
- { INTEL_FAM6_BROADWELL_X, &bdx_features },
- { INTEL_FAM6_BROADWELL_D, &bdx_features },
- { INTEL_FAM6_SKYLAKE_L, &skl_features },
- { INTEL_FAM6_SKYLAKE, &skl_features },
- { INTEL_FAM6_SKYLAKE_X, &skx_features },
- { INTEL_FAM6_KABYLAKE_L, &skl_features },
- { INTEL_FAM6_KABYLAKE, &skl_features },
- { INTEL_FAM6_COMETLAKE, &skl_features },
- { INTEL_FAM6_COMETLAKE_L, &skl_features },
- { INTEL_FAM6_CANNONLAKE_L, &cnl_features },
- { INTEL_FAM6_ICELAKE_X, &icx_features },
- { INTEL_FAM6_ICELAKE_D, &icx_features },
- { INTEL_FAM6_ICELAKE_L, &cnl_features },
- { INTEL_FAM6_ICELAKE_NNPI, &cnl_features },
- { INTEL_FAM6_ROCKETLAKE, &cnl_features },
- { INTEL_FAM6_TIGERLAKE_L, &cnl_features },
- { INTEL_FAM6_TIGERLAKE, &cnl_features },
- { INTEL_FAM6_SAPPHIRERAPIDS_X, &spr_features },
- { INTEL_FAM6_EMERALDRAPIDS_X, &spr_features },
- { INTEL_FAM6_GRANITERAPIDS_X, &spr_features },
- { INTEL_FAM6_LAKEFIELD, &cnl_features },
- { INTEL_FAM6_ALDERLAKE, &adl_features },
- { INTEL_FAM6_ALDERLAKE_L, &adl_features },
- { INTEL_FAM6_RAPTORLAKE, &adl_features },
- { INTEL_FAM6_RAPTORLAKE_P, &adl_features },
- { INTEL_FAM6_RAPTORLAKE_S, &adl_features },
- { INTEL_FAM6_METEORLAKE, &cnl_features },
- { INTEL_FAM6_METEORLAKE_L, &cnl_features },
- { INTEL_FAM6_ARROWLAKE, &cnl_features },
- { INTEL_FAM6_LUNARLAKE_M, &cnl_features },
- { INTEL_FAM6_ATOM_SILVERMONT, &slv_features },
- { INTEL_FAM6_ATOM_SILVERMONT_D, &slvd_features },
- { INTEL_FAM6_ATOM_AIRMONT, &amt_features },
- { INTEL_FAM6_ATOM_GOLDMONT, &gmt_features },
- { INTEL_FAM6_ATOM_GOLDMONT_D, &gmtd_features },
- { INTEL_FAM6_ATOM_GOLDMONT_PLUS, &gmtp_features },
- { INTEL_FAM6_ATOM_TREMONT_D, &tmtd_features },
- { INTEL_FAM6_ATOM_TREMONT, &tmt_features },
- { INTEL_FAM6_ATOM_TREMONT_L, &tmt_features },
- { INTEL_FAM6_ATOM_GRACEMONT, &adl_features },
- { INTEL_FAM6_ATOM_CRESTMONT_X, &srf_features },
- { INTEL_FAM6_ATOM_CRESTMONT, &grr_features },
- { INTEL_FAM6_XEON_PHI_KNL, &knl_features },
- { INTEL_FAM6_XEON_PHI_KNM, &knl_features },
+ { INTEL_NEHALEM, &nhm_features },
+ { INTEL_NEHALEM_G, &nhm_features },
+ { INTEL_NEHALEM_EP, &nhm_features },
+ { INTEL_NEHALEM_EX, &nhx_features },
+ { INTEL_WESTMERE, &nhm_features },
+ { INTEL_WESTMERE_EP, &nhm_features },
+ { INTEL_WESTMERE_EX, &nhx_features },
+ { INTEL_SANDYBRIDGE, &snb_features },
+ { INTEL_SANDYBRIDGE_X, &snx_features },
+ { INTEL_IVYBRIDGE, &ivb_features },
+ { INTEL_IVYBRIDGE_X, &ivx_features },
+ { INTEL_HASWELL, &hsw_features },
+ { INTEL_HASWELL_X, &hsx_features },
+ { INTEL_HASWELL_L, &hswl_features },
+ { INTEL_HASWELL_G, &hswg_features },
+ { INTEL_BROADWELL, &bdw_features },
+ { INTEL_BROADWELL_G, &bdwg_features },
+ { INTEL_BROADWELL_X, &bdx_features },
+ { INTEL_BROADWELL_D, &bdx_features },
+ { INTEL_SKYLAKE_L, &skl_features },
+ { INTEL_SKYLAKE, &skl_features },
+ { INTEL_SKYLAKE_X, &skx_features },
+ { INTEL_KABYLAKE_L, &skl_features },
+ { INTEL_KABYLAKE, &skl_features },
+ { INTEL_COMETLAKE, &skl_features },
+ { INTEL_COMETLAKE_L, &skl_features },
+ { INTEL_CANNONLAKE_L, &cnl_features },
+ { INTEL_ICELAKE_X, &icx_features },
+ { INTEL_ICELAKE_D, &icx_features },
+ { INTEL_ICELAKE_L, &cnl_features },
+ { INTEL_ICELAKE_NNPI, &cnl_features },
+ { INTEL_ROCKETLAKE, &cnl_features },
+ { INTEL_TIGERLAKE_L, &cnl_features },
+ { INTEL_TIGERLAKE, &cnl_features },
+ { INTEL_SAPPHIRERAPIDS_X, &spr_features },
+ { INTEL_EMERALDRAPIDS_X, &spr_features },
+ { INTEL_GRANITERAPIDS_X, &spr_features },
+ { INTEL_GRANITERAPIDS_D, &spr_features },
+ { INTEL_LAKEFIELD, &cnl_features },
+ { INTEL_ALDERLAKE, &adl_features },
+ { INTEL_ALDERLAKE_L, &adl_features },
+ { INTEL_RAPTORLAKE, &adl_features },
+ { INTEL_RAPTORLAKE_P, &adl_features },
+ { INTEL_RAPTORLAKE_S, &adl_features },
+ { INTEL_METEORLAKE, &adl_features },
+ { INTEL_METEORLAKE_L, &adl_features },
+ { INTEL_ARROWLAKE_H, &adl_features },
+ { INTEL_ARROWLAKE_U, &adl_features },
+ { INTEL_ARROWLAKE, &adl_features },
+ { INTEL_LUNARLAKE_M, &lnl_features },
+ { INTEL_PANTHERLAKE_L, &lnl_features },
+ { INTEL_ATOM_SILVERMONT, &slv_features },
+ { INTEL_ATOM_SILVERMONT_D, &slvd_features },
+ { INTEL_ATOM_AIRMONT, &amt_features },
+ { INTEL_ATOM_GOLDMONT, &gmt_features },
+ { INTEL_ATOM_GOLDMONT_D, &gmtd_features },
+ { INTEL_ATOM_GOLDMONT_PLUS, &gmtp_features },
+ { INTEL_ATOM_TREMONT_D, &tmtd_features },
+ { INTEL_ATOM_TREMONT, &tmt_features },
+ { INTEL_ATOM_TREMONT_L, &tmt_features },
+ { INTEL_ATOM_GRACEMONT, &adl_features },
+ { INTEL_ATOM_CRESTMONT_X, &srf_features },
+ { INTEL_ATOM_CRESTMONT, &grr_features },
+ { INTEL_ATOM_DARKMONT_X, &srf_features },
+ { INTEL_XEON_PHI_KNL, &knl_features },
+ { INTEL_XEON_PHI_KNM, &knl_features },
/*
* Missing support for
- * INTEL_FAM6_ICELAKE
- * INTEL_FAM6_ATOM_SILVERMONT_MID
- * INTEL_FAM6_ATOM_AIRMONT_MID
- * INTEL_FAM6_ATOM_AIRMONT_NP
+ * INTEL_ICELAKE
+ * INTEL_ATOM_SILVERMONT_MID
+ * INTEL_ATOM_SILVERMONT_MID2
+ * INTEL_ATOM_AIRMONT_NP
*/
{ 0, NULL },
};
@@ -937,9 +1072,10 @@ void probe_platform_features(unsigned int family, unsigned int model)
{
int i;
- platform = &default_features;
if (authentic_amd || hygon_genuine) {
+ /* fallback to default features on unsupported models */
+ force_load++;
if (max_extended_level >= 0x80000007) {
unsigned int eax, ebx, ecx, edx;
@@ -948,18 +1084,31 @@ void probe_platform_features(unsigned int family, unsigned int model)
if ((edx & (1 << 14)) && family >= 0x17)
platform = &amd_features_with_rapl;
}
- return;
+ goto end;
}
- if (!genuine_intel || family != 6)
- return;
+ if (!genuine_intel)
+ goto end;
for (i = 0; turbostat_pdata[i].features; i++) {
- if (turbostat_pdata[i].model == model) {
+ if (VFM_FAMILY(turbostat_pdata[i].vfm) == family && VFM_MODEL(turbostat_pdata[i].vfm) == model) {
platform = turbostat_pdata[i].features;
return;
}
}
+
+end:
+ if (force_load && !platform) {
+ fprintf(outf, "Forced to run on unsupported platform!\n");
+ platform = &default_features;
+ }
+
+ if (platform)
+ return;
+
+ fprintf(stderr, "Unsupported platform detected.\n"
+ "\tSee RUN THE LATEST VERSION on turbostat(8)\n");
+ exit(1);
}
/* Model specific support End */
@@ -976,13 +1125,19 @@ void probe_platform_features(unsigned int family, unsigned int model)
int backwards_count;
char *progname;
-#define CPU_SUBSET_MAXCPUS 1024 /* need to use before probe... */
-cpu_set_t *cpu_present_set, *cpu_effective_set, *cpu_allowed_set, *cpu_affinity_set, *cpu_subset;
-size_t cpu_present_setsize, cpu_effective_setsize, cpu_allowed_setsize, cpu_affinity_setsize, cpu_subset_size;
-#define MAX_ADDED_COUNTERS 8
+#define CPU_SUBSET_MAXCPUS 8192 /* need to use before probe... */
+cpu_set_t *cpu_present_set, *cpu_possible_set, *cpu_effective_set, *cpu_allowed_set, *cpu_affinity_set, *cpu_subset;
+size_t cpu_present_setsize, cpu_possible_setsize, cpu_effective_setsize, cpu_allowed_setsize, cpu_affinity_setsize, cpu_subset_size;
#define MAX_ADDED_THREAD_COUNTERS 24
+#define MAX_ADDED_CORE_COUNTERS 8
+#define MAX_ADDED_PACKAGE_COUNTERS 16
+#define PMT_MAX_ADDED_THREAD_COUNTERS 24
+#define PMT_MAX_ADDED_CORE_COUNTERS 8
+#define PMT_MAX_ADDED_PACKAGE_COUNTERS 16
#define BITMASK_SIZE 32
+#define ZERO_ARRAY(arr) (memset(arr, 0, sizeof(arr)) + __must_be_array(arr))
+
/* Indexes used to map data read from perf and MSRs into global variables */
enum rapl_rci_index {
RAPL_RCI_INDEX_ENERGY_PKG = 0,
@@ -992,6 +1147,7 @@ enum rapl_rci_index {
RAPL_RCI_INDEX_PKG_PERF_STATUS = 4,
RAPL_RCI_INDEX_DRAM_PERF_STATUS = 5,
RAPL_RCI_INDEX_CORE_ENERGY = 6,
+ RAPL_RCI_INDEX_ENERGY_PLATFORM = 7,
NUM_RAPL_COUNTERS,
};
@@ -1003,26 +1159,22 @@ enum rapl_unit {
struct rapl_counter_info_t {
unsigned long long data[NUM_RAPL_COUNTERS];
- enum rapl_source source[NUM_RAPL_COUNTERS];
+ enum counter_source source[NUM_RAPL_COUNTERS];
unsigned long long flags[NUM_RAPL_COUNTERS];
double scale[NUM_RAPL_COUNTERS];
enum rapl_unit unit[NUM_RAPL_COUNTERS];
-
- union {
- /* Active when source == RAPL_SOURCE_MSR */
- struct {
- unsigned long long msr[NUM_RAPL_COUNTERS];
- unsigned long long msr_mask[NUM_RAPL_COUNTERS];
- int msr_shift[NUM_RAPL_COUNTERS];
- };
- };
+ unsigned long long msr[NUM_RAPL_COUNTERS];
+ unsigned long long msr_mask[NUM_RAPL_COUNTERS];
+ int msr_shift[NUM_RAPL_COUNTERS];
int fd_perf;
};
/* struct rapl_counter_info_t for each RAPL domain */
struct rapl_counter_info_t *rapl_counter_info_perdomain;
+unsigned int rapl_counter_info_perdomain_size;
+#define RAPL_COUNTER_FLAG_PLATFORM_COUNTER (1u << 0)
#define RAPL_COUNTER_FLAG_USE_MSR_SUM (1u << 1)
struct rapl_counter_arch_info {
@@ -1144,6 +1296,19 @@ static const struct rapl_counter_arch_info rapl_counter_arch_infos[] = {
.compat_scale = 1.0,
.flags = 0,
},
+ {
+ .feature_mask = RAPL_PSYS,
+ .perf_subsys = "power",
+ .perf_name = "energy-psys",
+ .msr = MSR_PLATFORM_ENERGY_STATUS,
+ .msr_mask = 0x00000000FFFFFFFF,
+ .msr_shift = 0,
+ .platform_rapl_msr_scale = &rapl_psys_energy_units,
+ .rci_index = RAPL_RCI_INDEX_ENERGY_PLATFORM,
+ .bic = BIC_SysWatt | BIC_Sys_J,
+ .compat_scale = 1.0,
+ .flags = RAPL_COUNTER_FLAG_PLATFORM_COUNTER | RAPL_COUNTER_FLAG_USE_MSR_SUM,
+ },
};
struct rapl_counter {
@@ -1152,6 +1317,421 @@ struct rapl_counter {
double scale;
};
+/* Indexes used to map data read from perf and MSRs into global variables */
+enum ccstate_rci_index {
+ CCSTATE_RCI_INDEX_C1_RESIDENCY = 0,
+ CCSTATE_RCI_INDEX_C3_RESIDENCY = 1,
+ CCSTATE_RCI_INDEX_C6_RESIDENCY = 2,
+ CCSTATE_RCI_INDEX_C7_RESIDENCY = 3,
+ PCSTATE_RCI_INDEX_C2_RESIDENCY = 4,
+ PCSTATE_RCI_INDEX_C3_RESIDENCY = 5,
+ PCSTATE_RCI_INDEX_C6_RESIDENCY = 6,
+ PCSTATE_RCI_INDEX_C7_RESIDENCY = 7,
+ PCSTATE_RCI_INDEX_C8_RESIDENCY = 8,
+ PCSTATE_RCI_INDEX_C9_RESIDENCY = 9,
+ PCSTATE_RCI_INDEX_C10_RESIDENCY = 10,
+ NUM_CSTATE_COUNTERS,
+};
+
+struct cstate_counter_info_t {
+ unsigned long long data[NUM_CSTATE_COUNTERS];
+ enum counter_source source[NUM_CSTATE_COUNTERS];
+ unsigned long long msr[NUM_CSTATE_COUNTERS];
+ int fd_perf_core;
+ int fd_perf_pkg;
+};
+
+struct cstate_counter_info_t *ccstate_counter_info;
+unsigned int ccstate_counter_info_size;
+
+#define CSTATE_COUNTER_FLAG_COLLECT_PER_CORE (1u << 0)
+#define CSTATE_COUNTER_FLAG_COLLECT_PER_THREAD ((1u << 1) | CSTATE_COUNTER_FLAG_COLLECT_PER_CORE)
+#define CSTATE_COUNTER_FLAG_SOFT_C1_DEPENDENCY (1u << 2)
+
+struct cstate_counter_arch_info {
+ int feature_mask; /* Mask for testing if the counter is supported on host */
+ const char *perf_subsys;
+ const char *perf_name;
+ unsigned long long msr;
+ unsigned int rci_index; /* Maps data from perf counters to global variables */
+ unsigned long long bic;
+ unsigned long long flags;
+ int pkg_cstate_limit;
+};
+
+static struct cstate_counter_arch_info ccstate_counter_arch_infos[] = {
+ {
+ .feature_mask = CC1,
+ .perf_subsys = "cstate_core",
+ .perf_name = "c1-residency",
+ .msr = MSR_CORE_C1_RES,
+ .rci_index = CCSTATE_RCI_INDEX_C1_RESIDENCY,
+ .bic = BIC_CPU_c1,
+ .flags = CSTATE_COUNTER_FLAG_COLLECT_PER_THREAD,
+ .pkg_cstate_limit = 0,
+ },
+ {
+ .feature_mask = CC3,
+ .perf_subsys = "cstate_core",
+ .perf_name = "c3-residency",
+ .msr = MSR_CORE_C3_RESIDENCY,
+ .rci_index = CCSTATE_RCI_INDEX_C3_RESIDENCY,
+ .bic = BIC_CPU_c3,
+ .flags = CSTATE_COUNTER_FLAG_COLLECT_PER_CORE | CSTATE_COUNTER_FLAG_SOFT_C1_DEPENDENCY,
+ .pkg_cstate_limit = 0,
+ },
+ {
+ .feature_mask = CC6,
+ .perf_subsys = "cstate_core",
+ .perf_name = "c6-residency",
+ .msr = MSR_CORE_C6_RESIDENCY,
+ .rci_index = CCSTATE_RCI_INDEX_C6_RESIDENCY,
+ .bic = BIC_CPU_c6,
+ .flags = CSTATE_COUNTER_FLAG_COLLECT_PER_CORE | CSTATE_COUNTER_FLAG_SOFT_C1_DEPENDENCY,
+ .pkg_cstate_limit = 0,
+ },
+ {
+ .feature_mask = CC7,
+ .perf_subsys = "cstate_core",
+ .perf_name = "c7-residency",
+ .msr = MSR_CORE_C7_RESIDENCY,
+ .rci_index = CCSTATE_RCI_INDEX_C7_RESIDENCY,
+ .bic = BIC_CPU_c7,
+ .flags = CSTATE_COUNTER_FLAG_COLLECT_PER_CORE | CSTATE_COUNTER_FLAG_SOFT_C1_DEPENDENCY,
+ .pkg_cstate_limit = 0,
+ },
+ {
+ .feature_mask = PC2,
+ .perf_subsys = "cstate_pkg",
+ .perf_name = "c2-residency",
+ .msr = MSR_PKG_C2_RESIDENCY,
+ .rci_index = PCSTATE_RCI_INDEX_C2_RESIDENCY,
+ .bic = BIC_Pkgpc2,
+ .flags = 0,
+ .pkg_cstate_limit = PCL__2,
+ },
+ {
+ .feature_mask = PC3,
+ .perf_subsys = "cstate_pkg",
+ .perf_name = "c3-residency",
+ .msr = MSR_PKG_C3_RESIDENCY,
+ .rci_index = PCSTATE_RCI_INDEX_C3_RESIDENCY,
+ .bic = BIC_Pkgpc3,
+ .flags = 0,
+ .pkg_cstate_limit = PCL__3,
+ },
+ {
+ .feature_mask = PC6,
+ .perf_subsys = "cstate_pkg",
+ .perf_name = "c6-residency",
+ .msr = MSR_PKG_C6_RESIDENCY,
+ .rci_index = PCSTATE_RCI_INDEX_C6_RESIDENCY,
+ .bic = BIC_Pkgpc6,
+ .flags = 0,
+ .pkg_cstate_limit = PCL__6,
+ },
+ {
+ .feature_mask = PC7,
+ .perf_subsys = "cstate_pkg",
+ .perf_name = "c7-residency",
+ .msr = MSR_PKG_C7_RESIDENCY,
+ .rci_index = PCSTATE_RCI_INDEX_C7_RESIDENCY,
+ .bic = BIC_Pkgpc7,
+ .flags = 0,
+ .pkg_cstate_limit = PCL__7,
+ },
+ {
+ .feature_mask = PC8,
+ .perf_subsys = "cstate_pkg",
+ .perf_name = "c8-residency",
+ .msr = MSR_PKG_C8_RESIDENCY,
+ .rci_index = PCSTATE_RCI_INDEX_C8_RESIDENCY,
+ .bic = BIC_Pkgpc8,
+ .flags = 0,
+ .pkg_cstate_limit = PCL__8,
+ },
+ {
+ .feature_mask = PC9,
+ .perf_subsys = "cstate_pkg",
+ .perf_name = "c9-residency",
+ .msr = MSR_PKG_C9_RESIDENCY,
+ .rci_index = PCSTATE_RCI_INDEX_C9_RESIDENCY,
+ .bic = BIC_Pkgpc9,
+ .flags = 0,
+ .pkg_cstate_limit = PCL__9,
+ },
+ {
+ .feature_mask = PC10,
+ .perf_subsys = "cstate_pkg",
+ .perf_name = "c10-residency",
+ .msr = MSR_PKG_C10_RESIDENCY,
+ .rci_index = PCSTATE_RCI_INDEX_C10_RESIDENCY,
+ .bic = BIC_Pkgpc10,
+ .flags = 0,
+ .pkg_cstate_limit = PCL_10,
+ },
+};
+
+/* Indexes used to map data read from perf and MSRs into global variables */
+enum msr_rci_index {
+ MSR_RCI_INDEX_APERF = 0,
+ MSR_RCI_INDEX_MPERF = 1,
+ MSR_RCI_INDEX_SMI = 2,
+ NUM_MSR_COUNTERS,
+};
+
+struct msr_counter_info_t {
+ unsigned long long data[NUM_MSR_COUNTERS];
+ enum counter_source source[NUM_MSR_COUNTERS];
+ unsigned long long msr[NUM_MSR_COUNTERS];
+ unsigned long long msr_mask[NUM_MSR_COUNTERS];
+ int fd_perf;
+};
+
+struct msr_counter_info_t *msr_counter_info;
+unsigned int msr_counter_info_size;
+
+struct msr_counter_arch_info {
+ const char *perf_subsys;
+ const char *perf_name;
+ unsigned long long msr;
+ unsigned long long msr_mask;
+ unsigned int rci_index; /* Maps data from perf counters to global variables */
+ bool needed;
+ bool present;
+};
+
+enum msr_arch_info_index {
+ MSR_ARCH_INFO_APERF_INDEX = 0,
+ MSR_ARCH_INFO_MPERF_INDEX = 1,
+ MSR_ARCH_INFO_SMI_INDEX = 2,
+};
+
+static struct msr_counter_arch_info msr_counter_arch_infos[] = {
+ [MSR_ARCH_INFO_APERF_INDEX] = {
+ .perf_subsys = "msr",
+ .perf_name = "aperf",
+ .msr = MSR_IA32_APERF,
+ .msr_mask = 0xFFFFFFFFFFFFFFFF,
+ .rci_index = MSR_RCI_INDEX_APERF,
+ },
+
+ [MSR_ARCH_INFO_MPERF_INDEX] = {
+ .perf_subsys = "msr",
+ .perf_name = "mperf",
+ .msr = MSR_IA32_MPERF,
+ .msr_mask = 0xFFFFFFFFFFFFFFFF,
+ .rci_index = MSR_RCI_INDEX_MPERF,
+ },
+
+ [MSR_ARCH_INFO_SMI_INDEX] = {
+ .perf_subsys = "msr",
+ .perf_name = "smi",
+ .msr = MSR_SMI_COUNT,
+ .msr_mask = 0xFFFFFFFF,
+ .rci_index = MSR_RCI_INDEX_SMI,
+ },
+};
+
+/* Can be redefined when compiling, useful for testing. */
+#ifndef SYSFS_TELEM_PATH
+#define SYSFS_TELEM_PATH "/sys/class/intel_pmt"
+#endif
+
+#define PMT_COUNTER_MTL_DC6_OFFSET 120
+#define PMT_COUNTER_MTL_DC6_LSB 0
+#define PMT_COUNTER_MTL_DC6_MSB 63
+#define PMT_MTL_DC6_GUID 0x1a067102
+#define PMT_MTL_DC6_SEQ 0
+
+#define PMT_COUNTER_CWF_MC1E_OFFSET_BASE 20936
+#define PMT_COUNTER_CWF_MC1E_OFFSET_INCREMENT 24
+#define PMT_COUNTER_CWF_MC1E_NUM_MODULES_PER_FILE 12
+#define PMT_COUNTER_CWF_CPUS_PER_MODULE 4
+#define PMT_COUNTER_CWF_MC1E_LSB 0
+#define PMT_COUNTER_CWF_MC1E_MSB 63
+#define PMT_CWF_MC1E_GUID 0x14421519
+
+unsigned long long tcore_clock_freq_hz = 800000000;
+
+#define PMT_COUNTER_NAME_SIZE_BYTES 16
+#define PMT_COUNTER_TYPE_NAME_SIZE_BYTES 32
+
+struct pmt_mmio {
+ struct pmt_mmio *next;
+
+ unsigned int guid;
+ unsigned int size;
+
+ /* Base pointer to the mmaped memory. */
+ void *mmio_base;
+
+ /*
+ * Offset to be applied to the mmio_base
+ * to get the beginning of the PMT counters for given GUID.
+ */
+ unsigned long pmt_offset;
+} *pmt_mmios;
+
+enum pmt_datatype {
+ PMT_TYPE_RAW,
+ PMT_TYPE_XTAL_TIME,
+ PMT_TYPE_TCORE_CLOCK,
+};
+
+struct pmt_domain_info {
+ /*
+ * Pointer to the MMIO obtained by applying a counter offset
+ * to the mmio_base of the mmaped region for the given GUID.
+ *
+ * This is where to read the raw value of the counter from.
+ */
+ unsigned long *pcounter;
+};
+
+struct pmt_counter {
+ struct pmt_counter *next;
+
+ /* PMT metadata */
+ char name[PMT_COUNTER_NAME_SIZE_BYTES];
+ enum pmt_datatype type;
+ enum counter_scope scope;
+ unsigned int lsb;
+ unsigned int msb;
+
+ /* BIC-like metadata */
+ enum counter_format format;
+
+ unsigned int num_domains;
+ struct pmt_domain_info *domains;
+};
+
+/*
+ * PMT telemetry directory iterator.
+ * Used to iterate telemetry files in sysfs in correct order.
+ */
+struct pmt_diriter_t {
+ DIR *dir;
+ struct dirent **namelist;
+ unsigned int num_names;
+ unsigned int current_name_idx;
+};
+
+int pmt_telemdir_filter(const struct dirent *e)
+{
+ unsigned int dummy;
+
+ return sscanf(e->d_name, "telem%u", &dummy);
+}
+
+int pmt_telemdir_sort(const struct dirent **a, const struct dirent **b)
+{
+ unsigned int aidx = 0, bidx = 0;
+
+ sscanf((*a)->d_name, "telem%u", &aidx);
+ sscanf((*b)->d_name, "telem%u", &bidx);
+
+ return aidx >= bidx;
+}
+
+const struct dirent *pmt_diriter_next(struct pmt_diriter_t *iter)
+{
+ const struct dirent *ret = NULL;
+
+ if (!iter->dir)
+ return NULL;
+
+ if (iter->current_name_idx >= iter->num_names)
+ return NULL;
+
+ ret = iter->namelist[iter->current_name_idx];
+ ++iter->current_name_idx;
+
+ return ret;
+}
+
+const struct dirent *pmt_diriter_begin(struct pmt_diriter_t *iter, const char *pmt_root_path)
+{
+ int num_names = iter->num_names;
+
+ if (!iter->dir) {
+ iter->dir = opendir(pmt_root_path);
+ if (iter->dir == NULL)
+ return NULL;
+
+ num_names = scandir(pmt_root_path, &iter->namelist, pmt_telemdir_filter, pmt_telemdir_sort);
+ if (num_names == -1)
+ return NULL;
+ }
+
+ iter->current_name_idx = 0;
+ iter->num_names = num_names;
+
+ return pmt_diriter_next(iter);
+}
+
+void pmt_diriter_init(struct pmt_diriter_t *iter)
+{
+ memset(iter, 0, sizeof(*iter));
+}
+
+void pmt_diriter_remove(struct pmt_diriter_t *iter)
+{
+ if (iter->namelist) {
+ for (unsigned int i = 0; i < iter->num_names; i++) {
+ free(iter->namelist[i]);
+ iter->namelist[i] = NULL;
+ }
+ }
+
+ free(iter->namelist);
+ iter->namelist = NULL;
+ iter->num_names = 0;
+ iter->current_name_idx = 0;
+
+ closedir(iter->dir);
+ iter->dir = NULL;
+}
+
+unsigned int pmt_counter_get_width(const struct pmt_counter *p)
+{
+ return (p->msb - p->lsb) + 1;
+}
+
+void pmt_counter_resize_(struct pmt_counter *pcounter, unsigned int new_size)
+{
+ struct pmt_domain_info *new_mem;
+
+ new_mem = (struct pmt_domain_info *)reallocarray(pcounter->domains, new_size, sizeof(*pcounter->domains));
+ if (!new_mem) {
+ fprintf(stderr, "%s: failed to allocate memory for PMT counters\n", __func__);
+ exit(1);
+ }
+
+ /* Zero initialize just allocated memory. */
+ const size_t num_new_domains = new_size - pcounter->num_domains;
+
+ memset(&new_mem[pcounter->num_domains], 0, num_new_domains * sizeof(*pcounter->domains));
+
+ pcounter->num_domains = new_size;
+ pcounter->domains = new_mem;
+}
+
+void pmt_counter_resize(struct pmt_counter *pcounter, unsigned int new_size)
+{
+ /*
+ * Allocate more memory ahead of time.
+ *
+ * Always allocate space for at least 8 elements
+ * and double the size when growing.
+ */
+ if (new_size < 8)
+ new_size = 8;
+ new_size = MAX(new_size, pcounter->num_domains * 2);
+
+ pmt_counter_resize_(pcounter, new_size);
+}
+
struct thread_data {
struct timeval tv_begin;
struct timeval tv_end;
@@ -1162,6 +1742,7 @@ struct thread_data {
unsigned long long c1;
unsigned long long instr_count;
unsigned long long irq_count;
+ unsigned long long nmi_count;
unsigned int smi_count;
unsigned int cpu_id;
unsigned int apic_id;
@@ -1169,6 +1750,8 @@ struct thread_data {
unsigned int flags;
bool is_atom;
unsigned long long counter[MAX_ADDED_THREAD_COUNTERS];
+ unsigned long long perf_counter[MAX_ADDED_THREAD_COUNTERS];
+ unsigned long long pmt_counter[PMT_MAX_ADDED_THREAD_COUNTERS];
} *thread_even, *thread_odd;
struct core_data {
@@ -1181,7 +1764,9 @@ struct core_data {
struct rapl_counter core_energy; /* MSR_CORE_ENERGY_STAT */
unsigned int core_id;
unsigned long long core_throt_cnt;
- unsigned long long counter[MAX_ADDED_COUNTERS];
+ unsigned long long counter[MAX_ADDED_CORE_COUNTERS];
+ unsigned long long perf_counter[MAX_ADDED_CORE_COUNTERS];
+ unsigned long long pmt_counter[PMT_MAX_ADDED_CORE_COUNTERS];
} *core_even, *core_odd;
struct pkg_data {
@@ -1214,7 +1799,10 @@ struct pkg_data {
struct rapl_counter rapl_dram_perf_status; /* MSR_DRAM_PERF_STATUS */
unsigned int pkg_temp_c;
unsigned int uncore_mhz;
- unsigned long long counter[MAX_ADDED_COUNTERS];
+ unsigned long long die_c6;
+ unsigned long long counter[MAX_ADDED_PACKAGE_COUNTERS];
+ unsigned long long perf_counter[MAX_ADDED_PACKAGE_COUNTERS];
+ unsigned long long pmt_counter[PMT_MAX_ADDED_PACKAGE_COUNTERS];
} *package_even, *package_odd;
#define ODD_COUNTERS thread_odd, core_odd, package_odd
@@ -1248,6 +1836,7 @@ enum {
IDX_PP1_ENERGY,
IDX_PKG_PERF,
IDX_DRAM_PERF,
+ IDX_PSYS_ENERGY,
IDX_COUNT,
};
@@ -1292,6 +1881,9 @@ off_t idx_to_offset(int idx)
case IDX_DRAM_PERF:
offset = MSR_DRAM_PERF_STATUS;
break;
+ case IDX_PSYS_ENERGY:
+ offset = MSR_PLATFORM_ENERGY_STATUS;
+ break;
default:
offset = -1;
}
@@ -1322,6 +1914,9 @@ int offset_to_idx(off_t offset)
case MSR_DRAM_PERF_STATUS:
idx = IDX_DRAM_PERF;
break;
+ case MSR_PLATFORM_ENERGY_STATUS:
+ idx = IDX_PSYS_ENERGY;
+ break;
default:
idx = -1;
}
@@ -1343,50 +1938,71 @@ int idx_valid(int idx)
return platform->rapl_msrs & RAPL_PKG_PERF_STATUS;
case IDX_DRAM_PERF:
return platform->rapl_msrs & RAPL_DRAM_PERF_STATUS;
+ case IDX_PSYS_ENERGY:
+ return platform->rapl_msrs & RAPL_PSYS;
default:
return 0;
}
}
struct sys_counters {
+ /* MSR added counters */
unsigned int added_thread_counters;
unsigned int added_core_counters;
unsigned int added_package_counters;
struct msr_counter *tp;
struct msr_counter *cp;
struct msr_counter *pp;
+
+ /* perf added counters */
+ unsigned int added_thread_perf_counters;
+ unsigned int added_core_perf_counters;
+ unsigned int added_package_perf_counters;
+ struct perf_counter_info *perf_tp;
+ struct perf_counter_info *perf_cp;
+ struct perf_counter_info *perf_pp;
+
+ struct pmt_counter *pmt_tp;
+ struct pmt_counter *pmt_cp;
+ struct pmt_counter *pmt_pp;
} sys;
-void free_sys_counters(void)
+static size_t free_msr_counters_(struct msr_counter **pp)
{
- struct msr_counter *p = sys.tp, *pnext = NULL;
+ struct msr_counter *p = NULL;
+ size_t num_freed = 0;
- while (p) {
- pnext = p->next;
- free(p);
- p = pnext;
- }
+ while (*pp) {
+ p = *pp;
- p = sys.cp, pnext = NULL;
- while (p) {
- pnext = p->next;
- free(p);
- p = pnext;
- }
+ if (p->msr_num != 0) {
+ *pp = p->next;
+
+ free(p);
+ ++num_freed;
+
+ continue;
+ }
- p = sys.pp, pnext = NULL;
- while (p) {
- pnext = p->next;
- free(p);
- p = pnext;
+ pp = &p->next;
}
- sys.added_thread_counters = 0;
- sys.added_core_counters = 0;
- sys.added_package_counters = 0;
- sys.tp = NULL;
- sys.cp = NULL;
- sys.pp = NULL;
+ return num_freed;
+}
+
+/*
+ * Free all added counters accessed via msr.
+ */
+static void free_sys_msr_counters(void)
+{
+ /* Thread counters */
+ sys.added_thread_counters -= free_msr_counters_(&sys.tp);
+
+ /* Core counters */
+ sys.added_core_counters -= free_msr_counters_(&sys.cp);
+
+ /* Package counters */
+ sys.added_package_counters -= free_msr_counters_(&sys.pp);
}
struct system_summary {
@@ -1395,6 +2011,10 @@ struct system_summary {
struct pkg_data packages;
} average;
+struct platform_counters {
+ struct rapl_counter energy_psys; /* MSR_PLATFORM_ENERGY_STATUS */
+} platform_counters_odd, platform_counters_even;
+
struct cpu_topology {
int physical_package_id;
int die_id;
@@ -1403,6 +2023,7 @@ struct cpu_topology {
int logical_node_id; /* 0-based count within the package */
int physical_core_id;
int thread_id;
+ int type;
cpu_set_t *put_ids; /* Processing Unit/Thread IDs */
} *cpus;
@@ -1415,6 +2036,9 @@ struct topo_params {
int allowed_cpus;
int allowed_cores;
int max_cpu_num;
+ int max_core_id;
+ int max_package_id;
+ int max_die_id;
int max_node_num;
int nodes_per_pkg;
int cores_per_node;
@@ -1425,6 +2049,7 @@ struct timeval tv_even, tv_odd, tv_delta;
int *irq_column_2_cpu; /* /proc/interrupts column numbers */
int *irqs_per_cpu; /* indexed by cpu_num */
+int *nmi_per_cpu; /* indexed by cpu_num */
void setup_all_buffers(bool startup);
@@ -1452,6 +2077,8 @@ int for_all_cpus(int (func) (struct thread_data *, struct core_data *, struct pk
{
int retval, pkg_no, core_no, thread_no, node_no;
+ retval = 0;
+
for (pkg_no = 0; pkg_no < topo.num_packages; ++pkg_no) {
for (node_no = 0; node_no < topo.nodes_per_pkg; node_no++) {
for (core_no = 0; core_no < topo.cores_per_node; ++core_no) {
@@ -1467,14 +2094,12 @@ int for_all_cpus(int (func) (struct thread_data *, struct core_data *, struct pk
c = GET_CORE(core_base, core_no, node_no, pkg_no);
p = GET_PKG(pkg_base, pkg_no);
- retval = func(t, c, p);
- if (retval)
- return retval;
+ retval |= func(t, c, p);
}
}
}
}
- return 0;
+ return retval;
}
int is_cpu_first_thread_in_core(struct thread_data *t, struct core_data *c, struct pkg_data *p)
@@ -1529,23 +2154,12 @@ int get_msr_fd(int cpu)
static void bic_disable_msr_access(void)
{
- const unsigned long bic_msrs =
- BIC_SMI |
- BIC_CPU_c1 |
- BIC_CPU_c3 |
- BIC_CPU_c6 |
- BIC_CPU_c7 |
- BIC_Mod_c6 |
- BIC_CoreTmp |
- BIC_Totl_c0 |
- BIC_Any_c0 |
- BIC_GFX_c0 |
- BIC_CPUGFX |
- BIC_Pkgpc2 | BIC_Pkgpc3 | BIC_Pkgpc6 | BIC_Pkgpc7 | BIC_Pkgpc8 | BIC_Pkgpc9 | BIC_Pkgpc10 | BIC_PkgTmp;
+ const unsigned long bic_msrs = BIC_Mod_c6 | BIC_CoreTmp |
+ BIC_Totl_c0 | BIC_Any_c0 | BIC_GFX_c0 | BIC_CPUGFX | BIC_PkgTmp;
bic_enabled &= ~bic_msrs;
- free_sys_counters();
+ free_sys_msr_counters();
}
static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid, int cpu, int group_fd, unsigned long flags)
@@ -1601,21 +2215,51 @@ int get_msr(int cpu, off_t offset, unsigned long long *msr)
return 0;
}
-int probe_msr(int cpu, off_t offset)
+int probe_rapl_msr(int cpu, off_t offset, int index)
{
ssize_t retval;
- unsigned long long dummy;
+ unsigned long long value;
assert(!no_msr);
- retval = pread(get_msr_fd(cpu), &dummy, sizeof(dummy), offset);
+ retval = pread(get_msr_fd(cpu), &value, sizeof(value), offset);
- if (retval != sizeof(dummy))
+ /* if the read failed, the probe fails */
+ if (retval != sizeof(value))
return 1;
+ /* If an Energy Status Counter MSR returns 0, the probe fails */
+ switch (index) {
+ case RAPL_RCI_INDEX_ENERGY_PKG:
+ case RAPL_RCI_INDEX_ENERGY_CORES:
+ case RAPL_RCI_INDEX_DRAM:
+ case RAPL_RCI_INDEX_GFX:
+ case RAPL_RCI_INDEX_ENERGY_PLATFORM:
+ if (value == 0)
+ return 1;
+ }
+
+ /* PKG,DRAM_PERF_STATUS MSRs, can return any value */
return 0;
}
+/* Convert CPU ID to domain ID for given added perf counter. */
+unsigned int cpu_to_domain(const struct perf_counter_info *pc, int cpu)
+{
+ switch (pc->scope) {
+ case SCOPE_CPU:
+ return cpu;
+
+ case SCOPE_CORE:
+ return cpus[cpu].physical_core_id;
+
+ case SCOPE_PACKAGE:
+ return cpus[cpu].physical_package_id;
+ }
+
+ __builtin_unreachable();
+}
+
#define MAX_DEFERRED 16
char *deferred_add_names[MAX_DEFERRED];
char *deferred_skip_names[MAX_DEFERRED];
@@ -1637,38 +2281,54 @@ void help(void)
"when COMMAND completes.\n"
"If no COMMAND is specified, turbostat wakes every 5-seconds\n"
"to print statistics, until interrupted.\n"
- " -a, --add add a counter\n"
+ " -a, --add counter\n"
+ " add a counter\n"
" eg. --add msr0x10,u64,cpu,delta,MY_TSC\n"
- " -c, --cpu cpu-set limit output to summary plus cpu-set:\n"
+ " eg. --add perf/cstate_pkg/c2-residency,package,delta,percent,perfPC2\n"
+ " eg. --add pmt,name=XTAL,type=raw,domain=package0,offset=0,lsb=0,msb=63,guid=0x1a067102\n"
+ " -c, --cpu cpu-set\n"
+ " limit output to summary plus cpu-set:\n"
" {core | package | j,k,l..m,n-p }\n"
- " -d, --debug displays usec, Time_Of_Day_Seconds and more debugging\n"
- " -D, --Dump displays the raw counter values\n"
- " -e, --enable [all | column]\n"
+ " -d, --debug\n"
+ " displays usec, Time_Of_Day_Seconds and more debugging\n"
+ " debug messages are printed to stderr\n"
+ " -D, --Dump\n"
+ " displays the raw counter values\n"
+ " -e, --enable [all | column]\n"
" shows all or the specified disabled column\n"
- " -H, --hide [column|column,column,...]\n"
+ " -f, --force\n"
+ " force load turbostat with minimum default features on unsupported platforms.\n"
+ " -H, --hide [column | column,column,...]\n"
" hide the specified column(s)\n"
" -i, --interval sec.subsec\n"
- " Override default 5-second measurement interval\n"
- " -J, --Joules displays energy in Joules instead of Watts\n"
- " -l, --list list column headers only\n"
- " -M, --no-msr Disable all uses of the MSR driver\n"
- " -P, --no-perf Disable all uses of the perf API\n"
+ " override default 5-second measurement interval\n"
+ " -J, --Joules\n"
+ " displays energy in Joules instead of Watts\n"
+ " -l, --list\n"
+ " list column headers only\n"
+ " -M, --no-msr\n"
+ " disable all uses of the MSR driver\n"
+ " -P, --no-perf\n"
+ " disable all uses of the perf API\n"
" -n, --num_iterations num\n"
" number of the measurement iterations\n"
" -N, --header_iterations num\n"
" print header every num iterations\n"
" -o, --out file\n"
" create or truncate \"file\" for all output\n"
- " -q, --quiet skip decoding system configuration header\n"
- " -s, --show [column|column,column,...]\n"
+ " -q, --quiet\n"
+ " skip decoding system configuration header\n"
+ " -s, --show [column | column,column,...]\n"
" show only the specified column(s)\n"
" -S, --Summary\n"
" limits output to 1-line system summary per interval\n"
" -T, --TCC temperature\n"
" sets the Thermal Control Circuit temperature in\n"
" degrees Celsius\n"
- " -h, --help print this help message\n"
- " -v, --version print version information\n" "\n" "For more help, run \"man turbostat\"\n");
+ " -h, --help\n"
+ " print this help message\n"
+ " -v, --version\n"
+ " print version information\n\nFor more help, run \"man turbostat\"\n");
}
/*
@@ -1698,16 +2358,25 @@ unsigned long long bic_lookup(char *name_list, enum show_hide_mode mode)
retval |= ~0;
break;
} else if (!strcmp(name_list, "topology")) {
- retval |= BIC_TOPOLOGY;
+ retval |= BIC_GROUP_TOPOLOGY;
break;
} else if (!strcmp(name_list, "power")) {
- retval |= BIC_THERMAL_PWR;
+ retval |= BIC_GROUP_THERMAL_PWR;
break;
} else if (!strcmp(name_list, "idle")) {
- retval |= BIC_IDLE;
+ retval |= BIC_GROUP_IDLE;
+ break;
+ } else if (!strcmp(name_list, "swidle")) {
+ retval |= BIC_GROUP_SW_IDLE;
+ break;
+ } else if (!strcmp(name_list, "sysfs")) { /* legacy compatibility */
+ retval |= BIC_GROUP_SW_IDLE;
+ break;
+ } else if (!strcmp(name_list, "hwidle")) {
+ retval |= BIC_GROUP_HW_IDLE;
break;
} else if (!strcmp(name_list, "frequency")) {
- retval |= BIC_FREQUENCY;
+ retval |= BIC_GROUP_FREQUENCY;
break;
} else if (!strcmp(name_list, "other")) {
retval |= BIC_OTHER;
@@ -1716,6 +2385,7 @@ unsigned long long bic_lookup(char *name_list, enum show_hide_mode mode)
}
if (i == MAX_BIC) {
+ fprintf(stderr, "deferred %s\n", name_list);
if (mode == SHOW_LIST) {
deferred_add_names[deferred_add_index++] = name_list;
if (deferred_add_index >= MAX_DEFERRED) {
@@ -1748,6 +2418,8 @@ unsigned long long bic_lookup(char *name_list, enum show_hide_mode mode)
void print_header(char *delim)
{
struct msr_counter *mp;
+ struct perf_counter_info *pp;
+ struct pmt_counter *ppmt;
int printed = 0;
if (DO_BIC(BIC_USEC))
@@ -1786,6 +2458,12 @@ void print_header(char *delim)
else
outp += sprintf(outp, "%sIRQ", (printed++ ? delim : ""));
}
+ if (DO_BIC(BIC_NMI)) {
+ if (sums_need_wide_columns)
+ outp += sprintf(outp, "%s NMI", (printed++ ? delim : ""));
+ else
+ outp += sprintf(outp, "%sNMI", (printed++ ? delim : ""));
+ }
if (DO_BIC(BIC_SMI))
outp += sprintf(outp, "%sSMI", (printed++ ? delim : ""));
@@ -1805,6 +2483,41 @@ void print_header(char *delim)
}
}
+ for (pp = sys.perf_tp; pp; pp = pp->next) {
+
+ if (pp->format == FORMAT_RAW) {
+ if (pp->width == 64)
+ outp += sprintf(outp, "%s%18.18s", (printed++ ? delim : ""), pp->name);
+ else
+ outp += sprintf(outp, "%s%10.10s", (printed++ ? delim : ""), pp->name);
+ } else {
+ if ((pp->type == COUNTER_ITEMS) && sums_need_wide_columns)
+ outp += sprintf(outp, "%s%8s", (printed++ ? delim : ""), pp->name);
+ else
+ outp += sprintf(outp, "%s%s", (printed++ ? delim : ""), pp->name);
+ }
+ }
+
+ ppmt = sys.pmt_tp;
+ while (ppmt) {
+ switch (ppmt->type) {
+ case PMT_TYPE_RAW:
+ if (pmt_counter_get_width(ppmt) <= 32)
+ outp += sprintf(outp, "%s%10.10s", (printed++ ? delim : ""), ppmt->name);
+ else
+ outp += sprintf(outp, "%s%18.18s", (printed++ ? delim : ""), ppmt->name);
+
+ break;
+
+ case PMT_TYPE_XTAL_TIME:
+ case PMT_TYPE_TCORE_CLOCK:
+ outp += sprintf(outp, "%s%s", (printed++ ? delim : ""), ppmt->name);
+ break;
+ }
+
+ ppmt = ppmt->next;
+ }
+
if (DO_BIC(BIC_CPU_c1))
outp += sprintf(outp, "%sCPU%%c1", (printed++ ? delim : ""));
if (DO_BIC(BIC_CPU_c3))
@@ -1845,6 +2558,41 @@ void print_header(char *delim)
}
}
+ for (pp = sys.perf_cp; pp; pp = pp->next) {
+
+ if (pp->format == FORMAT_RAW) {
+ if (pp->width == 64)
+ outp += sprintf(outp, "%s%18.18s", (printed++ ? delim : ""), pp->name);
+ else
+ outp += sprintf(outp, "%s%10.10s", (printed++ ? delim : ""), pp->name);
+ } else {
+ if ((pp->type == COUNTER_ITEMS) && sums_need_wide_columns)
+ outp += sprintf(outp, "%s%8s", (printed++ ? delim : ""), pp->name);
+ else
+ outp += sprintf(outp, "%s%s", (printed++ ? delim : ""), pp->name);
+ }
+ }
+
+ ppmt = sys.pmt_cp;
+ while (ppmt) {
+ switch (ppmt->type) {
+ case PMT_TYPE_RAW:
+ if (pmt_counter_get_width(ppmt) <= 32)
+ outp += sprintf(outp, "%s%10.10s", (printed++ ? delim : ""), ppmt->name);
+ else
+ outp += sprintf(outp, "%s%18.18s", (printed++ ? delim : ""), ppmt->name);
+
+ break;
+
+ case PMT_TYPE_XTAL_TIME:
+ case PMT_TYPE_TCORE_CLOCK:
+ outp += sprintf(outp, "%s%s", (printed++ ? delim : ""), ppmt->name);
+ break;
+ }
+
+ ppmt = ppmt->next;
+ }
+
if (DO_BIC(BIC_PkgTmp))
outp += sprintf(outp, "%sPkgTmp", (printed++ ? delim : ""));
@@ -1889,6 +2637,8 @@ void print_header(char *delim)
outp += sprintf(outp, "%sPkg%%pc9", (printed++ ? delim : ""));
if (DO_BIC(BIC_Pkgpc10))
outp += sprintf(outp, "%sPk%%pc10", (printed++ ? delim : ""));
+ if (DO_BIC(BIC_Diec6))
+ outp += sprintf(outp, "%sDie%%c6", (printed++ ? delim : ""));
if (DO_BIC(BIC_CPU_LPI))
outp += sprintf(outp, "%sCPU%%LPI", (printed++ ? delim : ""));
if (DO_BIC(BIC_SYS_LPI))
@@ -1928,16 +2678,58 @@ void print_header(char *delim)
if (mp->format == FORMAT_RAW) {
if (mp->width == 64)
outp += sprintf(outp, "%s%18.18s", delim, mp->name);
- else
+ else if (mp->width == 32)
outp += sprintf(outp, "%s%10.10s", delim, mp->name);
+ else
+ outp += sprintf(outp, "%s%7.7s", delim, mp->name);
} else {
if ((mp->type == COUNTER_ITEMS) && sums_need_wide_columns)
outp += sprintf(outp, "%s%8s", delim, mp->name);
else
- outp += sprintf(outp, "%s%s", delim, mp->name);
+ outp += sprintf(outp, "%s%7.7s", delim, mp->name);
+ }
+ }
+
+ for (pp = sys.perf_pp; pp; pp = pp->next) {
+
+ if (pp->format == FORMAT_RAW) {
+ if (pp->width == 64)
+ outp += sprintf(outp, "%s%18.18s", (printed++ ? delim : ""), pp->name);
+ else
+ outp += sprintf(outp, "%s%10.10s", (printed++ ? delim : ""), pp->name);
+ } else {
+ if ((pp->type == COUNTER_ITEMS) && sums_need_wide_columns)
+ outp += sprintf(outp, "%s%8s", (printed++ ? delim : ""), pp->name);
+ else
+ outp += sprintf(outp, "%s%s", (printed++ ? delim : ""), pp->name);
}
}
+ ppmt = sys.pmt_pp;
+ while (ppmt) {
+ switch (ppmt->type) {
+ case PMT_TYPE_RAW:
+ if (pmt_counter_get_width(ppmt) <= 32)
+ outp += sprintf(outp, "%s%10.10s", (printed++ ? delim : ""), ppmt->name);
+ else
+ outp += sprintf(outp, "%s%18.18s", (printed++ ? delim : ""), ppmt->name);
+
+ break;
+
+ case PMT_TYPE_XTAL_TIME:
+ case PMT_TYPE_TCORE_CLOCK:
+ outp += sprintf(outp, "%s%s", (printed++ ? delim : ""), ppmt->name);
+ break;
+ }
+
+ ppmt = ppmt->next;
+ }
+
+ if (DO_BIC(BIC_SysWatt))
+ outp += sprintf(outp, "%sSysWatt", (printed++ ? delim : ""));
+ if (DO_BIC(BIC_Sys_J))
+ outp += sprintf(outp, "%sSys_J", (printed++ ? delim : ""));
+
outp += sprintf(outp, "\n");
}
@@ -1945,6 +2737,7 @@ int dump_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p
{
int i;
struct msr_counter *mp;
+ struct platform_counters *pplat_cnt = p == package_odd ? &platform_counters_odd : &platform_counters_even;
outp += sprintf(outp, "t %p, c %p, p %p\n", t, c, p);
@@ -1960,13 +2753,15 @@ int dump_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p
if (DO_BIC(BIC_IRQ))
outp += sprintf(outp, "IRQ: %lld\n", t->irq_count);
+ if (DO_BIC(BIC_NMI))
+ outp += sprintf(outp, "IRQ: %lld\n", t->nmi_count);
if (DO_BIC(BIC_SMI))
outp += sprintf(outp, "SMI: %d\n", t->smi_count);
for (i = 0, mp = sys.tp; mp; i++, mp = mp->next) {
outp +=
sprintf(outp, "tADDED [%d] %8s msr0x%x: %08llX %s\n", i, mp->name, mp->msr_num,
- t->counter[i], mp->path);
+ t->counter[i], mp->sp->path);
}
}
@@ -1987,7 +2782,7 @@ int dump_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p
for (i = 0, mp = sys.cp; mp; i++, mp = mp->next) {
outp +=
sprintf(outp, "cADDED [%d] %8s msr0x%x: %08llX %s\n", i, mp->name, mp->msr_num,
- c->counter[i], mp->path);
+ c->counter[i], mp->sp->path);
}
outp += sprintf(outp, "mc6_us: %016llX\n", c->mc6_us);
}
@@ -2016,6 +2811,7 @@ int dump_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p
outp += sprintf(outp, "Joules COR: %0llX\n", p->energy_cores.raw_value);
outp += sprintf(outp, "Joules GFX: %0llX\n", p->energy_gfx.raw_value);
outp += sprintf(outp, "Joules RAM: %0llX\n", p->energy_dram.raw_value);
+ outp += sprintf(outp, "Joules PSYS: %0llX\n", pplat_cnt->energy_psys.raw_value);
outp += sprintf(outp, "Throttle PKG: %0llX\n", p->rapl_pkg_perf_status.raw_value);
outp += sprintf(outp, "Throttle RAM: %0llX\n", p->rapl_dram_perf_status.raw_value);
outp += sprintf(outp, "PTM: %dC\n", p->pkg_temp_c);
@@ -2023,7 +2819,7 @@ int dump_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p
for (i = 0, mp = sys.pp; mp; i++, mp = mp->next) {
outp +=
sprintf(outp, "pADDED [%d] %8s msr0x%x: %08llX %s\n", i, mp->name, mp->msr_num,
- p->counter[i], mp->path);
+ p->counter[i], mp->sp->path);
}
}
@@ -2054,13 +2850,23 @@ double rapl_counter_get_value(const struct rapl_counter *c, enum rapl_unit desir
*/
int format_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p)
{
+ static int count;
+
+ struct platform_counters *pplat_cnt = NULL;
double interval_float, tsc;
char *fmt8;
int i;
struct msr_counter *mp;
+ struct perf_counter_info *pp;
+ struct pmt_counter *ppmt;
char *delim = "\t";
int printed = 0;
+ if (t == &average.threads) {
+ pplat_cnt = count & 1 ? &platform_counters_odd : &platform_counters_even;
+ ++count;
+ }
+
/* if showing only 1st thread in core and this isn't one, bail out */
if (show_core_only && !is_cpu_first_thread_in_core(t, c, p))
return 0;
@@ -2168,6 +2974,14 @@ int format_counters(struct thread_data *t, struct core_data *c, struct pkg_data
outp += sprintf(outp, "%s%lld", (printed++ ? delim : ""), t->irq_count);
}
+ /* NMI */
+ if (DO_BIC(BIC_NMI)) {
+ if (sums_need_wide_columns)
+ outp += sprintf(outp, "%s%8lld", (printed++ ? delim : ""), t->nmi_count);
+ else
+ outp += sprintf(outp, "%s%lld", (printed++ ? delim : ""), t->nmi_count);
+ }
+
/* SMI */
if (DO_BIC(BIC_SMI))
outp += sprintf(outp, "%s%d", (printed++ ? delim : ""), t->smi_count);
@@ -2195,6 +3009,55 @@ int format_counters(struct thread_data *t, struct core_data *c, struct pkg_data
}
}
+ /* Added perf counters */
+ for (i = 0, pp = sys.perf_tp; pp; ++i, pp = pp->next) {
+ if (pp->format == FORMAT_RAW) {
+ if (pp->width == 32)
+ outp +=
+ sprintf(outp, "%s0x%08x", (printed++ ? delim : ""),
+ (unsigned int)t->perf_counter[i]);
+ else
+ outp += sprintf(outp, "%s0x%016llx", (printed++ ? delim : ""), t->perf_counter[i]);
+ } else if (pp->format == FORMAT_DELTA) {
+ if ((pp->type == COUNTER_ITEMS) && sums_need_wide_columns)
+ outp += sprintf(outp, "%s%8lld", (printed++ ? delim : ""), t->perf_counter[i]);
+ else
+ outp += sprintf(outp, "%s%lld", (printed++ ? delim : ""), t->perf_counter[i]);
+ } else if (pp->format == FORMAT_PERCENT) {
+ if (pp->type == COUNTER_USEC)
+ outp +=
+ sprintf(outp, "%s%.2f", (printed++ ? delim : ""),
+ t->perf_counter[i] / interval_float / 10000);
+ else
+ outp +=
+ sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * t->perf_counter[i] / tsc);
+ }
+ }
+
+ for (i = 0, ppmt = sys.pmt_tp; ppmt; i++, ppmt = ppmt->next) {
+ const unsigned long value_raw = t->pmt_counter[i];
+ double value_converted;
+ switch (ppmt->type) {
+ case PMT_TYPE_RAW:
+ if (pmt_counter_get_width(ppmt) <= 32)
+ outp += sprintf(outp, "%s0x%08x", (printed++ ? delim : ""),
+ (unsigned int)t->pmt_counter[i]);
+ else
+ outp += sprintf(outp, "%s0x%016llx", (printed++ ? delim : ""), t->pmt_counter[i]);
+
+ break;
+
+ case PMT_TYPE_XTAL_TIME:
+ value_converted = 100.0 * value_raw / crystal_hz / interval_float;
+ outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), value_converted);
+ break;
+
+ case PMT_TYPE_TCORE_CLOCK:
+ value_converted = 100.0 * value_raw / tcore_clock_freq_hz / interval_float;
+ outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), value_converted);
+ }
+ }
+
/* C1 */
if (DO_BIC(BIC_CPU_c1))
outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * t->c1 / tsc);
@@ -2238,6 +3101,48 @@ int format_counters(struct thread_data *t, struct core_data *c, struct pkg_data
}
}
+ for (i = 0, pp = sys.perf_cp; pp; i++, pp = pp->next) {
+ if (pp->format == FORMAT_RAW) {
+ if (pp->width == 32)
+ outp +=
+ sprintf(outp, "%s0x%08x", (printed++ ? delim : ""),
+ (unsigned int)c->perf_counter[i]);
+ else
+ outp += sprintf(outp, "%s0x%016llx", (printed++ ? delim : ""), c->perf_counter[i]);
+ } else if (pp->format == FORMAT_DELTA) {
+ if ((pp->type == COUNTER_ITEMS) && sums_need_wide_columns)
+ outp += sprintf(outp, "%s%8lld", (printed++ ? delim : ""), c->perf_counter[i]);
+ else
+ outp += sprintf(outp, "%s%lld", (printed++ ? delim : ""), c->perf_counter[i]);
+ } else if (pp->format == FORMAT_PERCENT) {
+ outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * c->perf_counter[i] / tsc);
+ }
+ }
+
+ for (i = 0, ppmt = sys.pmt_cp; ppmt; i++, ppmt = ppmt->next) {
+ const unsigned long value_raw = c->pmt_counter[i];
+ double value_converted;
+ switch (ppmt->type) {
+ case PMT_TYPE_RAW:
+ if (pmt_counter_get_width(ppmt) <= 32)
+ outp += sprintf(outp, "%s0x%08x", (printed++ ? delim : ""),
+ (unsigned int)c->pmt_counter[i]);
+ else
+ outp += sprintf(outp, "%s0x%016llx", (printed++ ? delim : ""), c->pmt_counter[i]);
+
+ break;
+
+ case PMT_TYPE_XTAL_TIME:
+ value_converted = 100.0 * value_raw / crystal_hz / interval_float;
+ outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), value_converted);
+ break;
+
+ case PMT_TYPE_TCORE_CLOCK:
+ value_converted = 100.0 * value_raw / tcore_clock_freq_hz / interval_float;
+ outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), value_converted);
+ }
+ }
+
fmt8 = "%s%.2f";
if (DO_BIC(BIC_CorWatt) && platform->has_per_core_rapl)
@@ -2317,6 +3222,10 @@ int format_counters(struct thread_data *t, struct core_data *c, struct pkg_data
if (DO_BIC(BIC_Pkgpc10))
outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * p->pc10 / tsc);
+ if (DO_BIC(BIC_Diec6))
+ outp +=
+ sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * p->die_c6 / crystal_hz / interval_float);
+
if (DO_BIC(BIC_CPU_LPI)) {
if (p->cpu_lpi >= 0)
outp +=
@@ -2388,9 +3297,62 @@ int format_counters(struct thread_data *t, struct core_data *c, struct pkg_data
outp += sprintf(outp, "%s%lld", (printed++ ? delim : ""), p->counter[i]);
} else if (mp->format == FORMAT_PERCENT) {
outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * p->counter[i] / tsc);
+ } else if (mp->type == COUNTER_K2M)
+ outp += sprintf(outp, "%s%d", (printed++ ? delim : ""), (unsigned int)p->counter[i] / 1000);
+ }
+
+ for (i = 0, pp = sys.perf_pp; pp; i++, pp = pp->next) {
+ if (pp->format == FORMAT_RAW) {
+ if (pp->width == 32)
+ outp +=
+ sprintf(outp, "%s0x%08x", (printed++ ? delim : ""),
+ (unsigned int)p->perf_counter[i]);
+ else
+ outp += sprintf(outp, "%s0x%016llx", (printed++ ? delim : ""), p->perf_counter[i]);
+ } else if (pp->format == FORMAT_DELTA) {
+ if ((pp->type == COUNTER_ITEMS) && sums_need_wide_columns)
+ outp += sprintf(outp, "%s%8lld", (printed++ ? delim : ""), p->perf_counter[i]);
+ else
+ outp += sprintf(outp, "%s%lld", (printed++ ? delim : ""), p->perf_counter[i]);
+ } else if (pp->format == FORMAT_PERCENT) {
+ outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * p->perf_counter[i] / tsc);
+ } else if (pp->type == COUNTER_K2M) {
+ outp +=
+ sprintf(outp, "%s%d", (printed++ ? delim : ""), (unsigned int)p->perf_counter[i] / 1000);
}
}
+ for (i = 0, ppmt = sys.pmt_pp; ppmt; i++, ppmt = ppmt->next) {
+ const unsigned long value_raw = p->pmt_counter[i];
+ double value_converted;
+ switch (ppmt->type) {
+ case PMT_TYPE_RAW:
+ if (pmt_counter_get_width(ppmt) <= 32)
+ outp += sprintf(outp, "%s0x%08x", (printed++ ? delim : ""),
+ (unsigned int)p->pmt_counter[i]);
+ else
+ outp += sprintf(outp, "%s0x%016llx", (printed++ ? delim : ""), p->pmt_counter[i]);
+
+ break;
+
+ case PMT_TYPE_XTAL_TIME:
+ value_converted = 100.0 * value_raw / crystal_hz / interval_float;
+ outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), value_converted);
+ break;
+
+ case PMT_TYPE_TCORE_CLOCK:
+ value_converted = 100.0 * value_raw / tcore_clock_freq_hz / interval_float;
+ outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), value_converted);
+ }
+ }
+
+ if (DO_BIC(BIC_SysWatt) && (t == &average.threads))
+ outp += sprintf(outp, fmt8, (printed++ ? delim : ""),
+ rapl_counter_get_value(&pplat_cnt->energy_psys, RAPL_UNIT_WATTS, interval_float));
+ if (DO_BIC(BIC_Sys_J) && (t == &average.threads))
+ outp += sprintf(outp, fmt8, (printed++ ? delim : ""),
+ rapl_counter_get_value(&pplat_cnt->energy_psys, RAPL_UNIT_JOULES, interval_float));
+
done:
if (*(outp - 1) != '\n')
outp += sprintf(outp, "\n");
@@ -2444,6 +3406,8 @@ int delta_package(struct pkg_data *new, struct pkg_data *old)
{
int i;
struct msr_counter *mp;
+ struct perf_counter_info *pp;
+ struct pmt_counter *ppmt;
if (DO_BIC(BIC_Totl_c0))
old->pkg_wtd_core_c0 = new->pkg_wtd_core_c0 - old->pkg_wtd_core_c0;
@@ -2464,6 +3428,7 @@ int delta_package(struct pkg_data *new, struct pkg_data *old)
old->pc8 = new->pc8 - old->pc8;
old->pc9 = new->pc9 - old->pc9;
old->pc10 = new->pc10 - old->pc10;
+ old->die_c6 = new->die_c6 - old->die_c6;
old->cpu_lpi = new->cpu_lpi - old->cpu_lpi;
old->sys_lpi = new->sys_lpi - old->sys_lpi;
old->pkg_temp_c = new->pkg_temp_c;
@@ -2498,10 +3463,28 @@ int delta_package(struct pkg_data *new, struct pkg_data *old)
for (i = 0, mp = sys.pp; mp; i++, mp = mp->next) {
if (mp->format == FORMAT_RAW)
old->counter[i] = new->counter[i];
+ else if (mp->format == FORMAT_AVERAGE)
+ old->counter[i] = new->counter[i];
else
old->counter[i] = new->counter[i] - old->counter[i];
}
+ for (i = 0, pp = sys.perf_pp; pp; i++, pp = pp->next) {
+ if (pp->format == FORMAT_RAW)
+ old->perf_counter[i] = new->perf_counter[i];
+ else if (pp->format == FORMAT_AVERAGE)
+ old->perf_counter[i] = new->perf_counter[i];
+ else
+ old->perf_counter[i] = new->perf_counter[i] - old->perf_counter[i];
+ }
+
+ for (i = 0, ppmt = sys.pmt_pp; ppmt; i++, ppmt = ppmt->next) {
+ if (ppmt->format == FORMAT_RAW)
+ old->pmt_counter[i] = new->pmt_counter[i];
+ else
+ old->pmt_counter[i] = new->pmt_counter[i] - old->pmt_counter[i];
+ }
+
return 0;
}
@@ -2509,12 +3492,14 @@ void delta_core(struct core_data *new, struct core_data *old)
{
int i;
struct msr_counter *mp;
+ struct perf_counter_info *pp;
+ struct pmt_counter *ppmt;
old->c3 = new->c3 - old->c3;
old->c6 = new->c6 - old->c6;
old->c7 = new->c7 - old->c7;
old->core_temp_c = new->core_temp_c;
- old->core_throt_cnt = new->core_throt_cnt;
+ old->core_throt_cnt = new->core_throt_cnt - old->core_throt_cnt;
old->mc6_us = new->mc6_us - old->mc6_us;
DELTA_WRAP32(new->core_energy.raw_value, old->core_energy.raw_value);
@@ -2525,6 +3510,20 @@ void delta_core(struct core_data *new, struct core_data *old)
else
old->counter[i] = new->counter[i] - old->counter[i];
}
+
+ for (i = 0, pp = sys.perf_cp; pp; i++, pp = pp->next) {
+ if (pp->format == FORMAT_RAW)
+ old->perf_counter[i] = new->perf_counter[i];
+ else
+ old->perf_counter[i] = new->perf_counter[i] - old->perf_counter[i];
+ }
+
+ for (i = 0, ppmt = sys.pmt_cp; ppmt; i++, ppmt = ppmt->next) {
+ if (ppmt->format == FORMAT_RAW)
+ old->pmt_counter[i] = new->pmt_counter[i];
+ else
+ old->pmt_counter[i] = new->pmt_counter[i] - old->pmt_counter[i];
+ }
}
int soft_c1_residency_display(int bic)
@@ -2542,6 +3541,8 @@ int delta_thread(struct thread_data *new, struct thread_data *old, struct core_d
{
int i;
struct msr_counter *mp;
+ struct perf_counter_info *pp;
+ struct pmt_counter *ppmt;
/* we run cpuid just the 1st time, copy the results */
if (DO_BIC(BIC_APIC))
@@ -2611,6 +3612,9 @@ int delta_thread(struct thread_data *new, struct thread_data *old, struct core_d
if (DO_BIC(BIC_IRQ))
old->irq_count = new->irq_count - old->irq_count;
+ if (DO_BIC(BIC_NMI))
+ old->nmi_count = new->nmi_count - old->nmi_count;
+
if (DO_BIC(BIC_SMI))
old->smi_count = new->smi_count - old->smi_count;
@@ -2620,6 +3624,21 @@ int delta_thread(struct thread_data *new, struct thread_data *old, struct core_d
else
old->counter[i] = new->counter[i] - old->counter[i];
}
+
+ for (i = 0, pp = sys.perf_tp; pp; i++, pp = pp->next) {
+ if (pp->format == FORMAT_RAW)
+ old->perf_counter[i] = new->perf_counter[i];
+ else
+ old->perf_counter[i] = new->perf_counter[i] - old->perf_counter[i];
+ }
+
+ for (i = 0, ppmt = sys.pmt_tp; ppmt; i++, ppmt = ppmt->next) {
+ if (ppmt->format == FORMAT_RAW)
+ old->pmt_counter[i] = new->pmt_counter[i];
+ else
+ old->pmt_counter[i] = new->pmt_counter[i] - old->pmt_counter[i];
+ }
+
return 0;
}
@@ -2634,16 +3653,19 @@ int delta_cpu(struct thread_data *t, struct core_data *c,
/* always calculate thread delta */
retval = delta_thread(t, t2, c2); /* c2 is core delta */
- if (retval)
- return retval;
/* calculate package delta only for 1st core in package */
if (is_cpu_first_core_in_package(t, c, p))
- retval = delta_package(p, p2);
+ retval |= delta_package(p, p2);
return retval;
}
+void delta_platform(struct platform_counters *new, struct platform_counters *old)
+{
+ old->energy_psys.raw_value = new->energy_psys.raw_value - old->energy_psys.raw_value;
+}
+
void rapl_counter_clear(struct rapl_counter *c)
{
c->raw_value = 0;
@@ -2671,6 +3693,7 @@ void clear_counters(struct thread_data *t, struct core_data *c, struct pkg_data
t->instr_count = 0;
t->irq_count = 0;
+ t->nmi_count = 0;
t->smi_count = 0;
c->c3 = 0;
@@ -2696,6 +3719,7 @@ void clear_counters(struct thread_data *t, struct core_data *c, struct pkg_data
p->pc8 = 0;
p->pc9 = 0;
p->pc10 = 0;
+ p->die_c6 = 0;
p->cpu_lpi = 0;
p->sys_lpi = 0;
@@ -2722,6 +3746,14 @@ void clear_counters(struct thread_data *t, struct core_data *c, struct pkg_data
for (i = 0, mp = sys.pp; mp; i++, mp = mp->next)
p->counter[i] = 0;
+
+ memset(&t->perf_counter[0], 0, sizeof(t->perf_counter));
+ memset(&c->perf_counter[0], 0, sizeof(c->perf_counter));
+ memset(&p->perf_counter[0], 0, sizeof(p->perf_counter));
+
+ memset(&t->pmt_counter[0], 0, ARRAY_SIZE(t->pmt_counter));
+ memset(&c->pmt_counter[0], 0, ARRAY_SIZE(c->pmt_counter));
+ memset(&p->pmt_counter[0], 0, ARRAY_SIZE(p->pmt_counter));
}
void rapl_counter_accumulate(struct rapl_counter *dst, const struct rapl_counter *src)
@@ -2742,6 +3774,8 @@ int sum_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p)
{
int i;
struct msr_counter *mp;
+ struct perf_counter_info *pp;
+ struct pmt_counter *ppmt;
/* copy un-changing apic_id's */
if (DO_BIC(BIC_APIC))
@@ -2751,7 +3785,7 @@ int sum_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p)
/* remember first tv_begin */
if (average.threads.tv_begin.tv_sec == 0)
- average.threads.tv_begin = t->tv_begin;
+ average.threads.tv_begin = procsysfs_tv_begin;
/* remember last tv_end */
average.threads.tv_end = t->tv_end;
@@ -2764,6 +3798,7 @@ int sum_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p)
average.threads.instr_count += t->instr_count;
average.threads.irq_count += t->irq_count;
+ average.threads.nmi_count += t->nmi_count;
average.threads.smi_count += t->smi_count;
for (i = 0, mp = sys.tp; mp; i++, mp = mp->next) {
@@ -2772,6 +3807,16 @@ int sum_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p)
average.threads.counter[i] += t->counter[i];
}
+ for (i = 0, pp = sys.perf_tp; pp; i++, pp = pp->next) {
+ if (pp->format == FORMAT_RAW)
+ continue;
+ average.threads.perf_counter[i] += t->perf_counter[i];
+ }
+
+ for (i = 0, ppmt = sys.pmt_tp; ppmt; i++, ppmt = ppmt->next) {
+ average.threads.pmt_counter[i] += t->pmt_counter[i];
+ }
+
/* sum per-core values only for 1st thread in core */
if (!is_cpu_first_thread_in_core(t, c, p))
return 0;
@@ -2792,6 +3837,16 @@ int sum_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p)
average.cores.counter[i] += c->counter[i];
}
+ for (i = 0, pp = sys.perf_cp; pp; i++, pp = pp->next) {
+ if (pp->format == FORMAT_RAW)
+ continue;
+ average.cores.perf_counter[i] += c->perf_counter[i];
+ }
+
+ for (i = 0, ppmt = sys.pmt_cp; ppmt; i++, ppmt = ppmt->next) {
+ average.cores.pmt_counter[i] += c->pmt_counter[i];
+ }
+
/* sum per-pkg values only for 1st core in pkg */
if (!is_cpu_first_core_in_package(t, c, p))
return 0;
@@ -2815,6 +3870,7 @@ int sum_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p)
average.packages.pc8 += p->pc8;
average.packages.pc9 += p->pc9;
average.packages.pc10 += p->pc10;
+ average.packages.die_c6 += p->die_c6;
average.packages.cpu_lpi = p->cpu_lpi;
average.packages.sys_lpi = p->sys_lpi;
@@ -2843,6 +3899,18 @@ int sum_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p)
else
average.packages.counter[i] += p->counter[i];
}
+
+ for (i = 0, pp = sys.perf_pp; pp; i++, pp = pp->next) {
+ if ((pp->format == FORMAT_RAW) && (topo.num_packages == 0))
+ average.packages.perf_counter[i] = p->perf_counter[i];
+ else
+ average.packages.perf_counter[i] += p->perf_counter[i];
+ }
+
+ for (i = 0, ppmt = sys.pmt_pp; ppmt; i++, ppmt = ppmt->next) {
+ average.packages.pmt_counter[i] += p->pmt_counter[i];
+ }
+
return 0;
}
@@ -2854,6 +3922,8 @@ void compute_average(struct thread_data *t, struct core_data *c, struct pkg_data
{
int i;
struct msr_counter *mp;
+ struct perf_counter_info *pp;
+ struct pmt_counter *ppmt;
clear_counters(&average.threads, &average.cores, &average.packages);
@@ -2870,6 +3940,9 @@ void compute_average(struct thread_data *t, struct core_data *c, struct pkg_data
if (average.threads.irq_count > 9999999)
sums_need_wide_columns = 1;
+ if (average.threads.nmi_count > 9999999)
+ sums_need_wide_columns = 1;
+
average.cores.c3 /= topo.allowed_cores;
average.cores.c6 /= topo.allowed_cores;
@@ -2896,6 +3969,7 @@ void compute_average(struct thread_data *t, struct core_data *c, struct pkg_data
average.packages.pc8 /= topo.allowed_packages;
average.packages.pc9 /= topo.allowed_packages;
average.packages.pc10 /= topo.allowed_packages;
+ average.packages.die_c6 /= topo.allowed_packages;
for (i = 0, mp = sys.tp; mp; i++, mp = mp->next) {
if (mp->format == FORMAT_RAW)
@@ -2925,6 +3999,45 @@ void compute_average(struct thread_data *t, struct core_data *c, struct pkg_data
}
average.packages.counter[i] /= topo.allowed_packages;
}
+
+ for (i = 0, pp = sys.perf_tp; pp; i++, pp = pp->next) {
+ if (pp->format == FORMAT_RAW)
+ continue;
+ if (pp->type == COUNTER_ITEMS) {
+ if (average.threads.perf_counter[i] > 9999999)
+ sums_need_wide_columns = 1;
+ continue;
+ }
+ average.threads.perf_counter[i] /= topo.allowed_cpus;
+ }
+ for (i = 0, pp = sys.perf_cp; pp; i++, pp = pp->next) {
+ if (pp->format == FORMAT_RAW)
+ continue;
+ if (pp->type == COUNTER_ITEMS) {
+ if (average.cores.perf_counter[i] > 9999999)
+ sums_need_wide_columns = 1;
+ }
+ average.cores.perf_counter[i] /= topo.allowed_cores;
+ }
+ for (i = 0, pp = sys.perf_pp; pp; i++, pp = pp->next) {
+ if (pp->format == FORMAT_RAW)
+ continue;
+ if (pp->type == COUNTER_ITEMS) {
+ if (average.packages.perf_counter[i] > 9999999)
+ sums_need_wide_columns = 1;
+ }
+ average.packages.perf_counter[i] /= topo.allowed_packages;
+ }
+
+ for (i = 0, ppmt = sys.pmt_tp; ppmt; i++, ppmt = ppmt->next) {
+ average.threads.pmt_counter[i] /= topo.allowed_cpus;
+ }
+ for (i = 0, ppmt = sys.pmt_cp; ppmt; i++, ppmt = ppmt->next) {
+ average.cores.pmt_counter[i] /= topo.allowed_cores;
+ }
+ for (i = 0, ppmt = sys.pmt_pp; ppmt; i++, ppmt = ppmt->next) {
+ average.packages.pmt_counter[i] /= topo.allowed_packages;
+ }
}
static unsigned long long rdtsc(void)
@@ -2970,7 +4083,7 @@ unsigned long long snapshot_sysfs_counter(char *path)
return counter;
}
-int get_mp(int cpu, struct msr_counter *mp, unsigned long long *counterp)
+int get_mp(int cpu, struct msr_counter *mp, unsigned long long *counterp, char *counter_path)
{
if (mp->msr_num != 0) {
assert(!no_msr);
@@ -2980,25 +4093,40 @@ int get_mp(int cpu, struct msr_counter *mp, unsigned long long *counterp)
char path[128 + PATH_BYTES];
if (mp->flags & SYSFS_PERCPU) {
- sprintf(path, "/sys/devices/system/cpu/cpu%d/%s", cpu, mp->path);
+ sprintf(path, "/sys/devices/system/cpu/cpu%d/%s", cpu, mp->sp->path);
*counterp = snapshot_sysfs_counter(path);
} else {
- *counterp = snapshot_sysfs_counter(mp->path);
+ *counterp = snapshot_sysfs_counter(counter_path);
}
}
return 0;
}
-unsigned long long get_uncore_mhz(int package, int die)
+unsigned long long get_legacy_uncore_mhz(int package)
{
char path[128];
+ int die;
+ static int warn_once;
- sprintf(path, "/sys/devices/system/cpu/intel_uncore_frequency/package_%02d_die_%02d/current_freq_khz", package,
- die);
+ /*
+ * for this package, use the first die_id that exists
+ */
+ for (die = 0; die <= topo.max_die_id; ++die) {
+
+ sprintf(path, "/sys/devices/system/cpu/intel_uncore_frequency/package_%02d_die_%02d/current_freq_khz",
+ package, die);
- return (snapshot_sysfs_counter(path) / 1000);
+ if (access(path, R_OK) == 0)
+ return (snapshot_sysfs_counter(path) / 1000);
+ }
+ if (!warn_once) {
+ warnx("BUG: %s: No %s", __func__, path);
+ warn_once = 1;
+ }
+
+ return 0;
}
int get_epb(int cpu)
@@ -3155,30 +4283,6 @@ static unsigned int read_perf_counter_info_n(const char *const path, const char
return v;
}
-static unsigned int read_msr_type(void)
-{
- const char *const path = "/sys/bus/event_source/devices/msr/type";
- const char *const format = "%u";
-
- return read_perf_counter_info_n(path, format);
-}
-
-static unsigned int read_aperf_config(void)
-{
- const char *const path = "/sys/bus/event_source/devices/msr/events/aperf";
- const char *const format = "event=%x";
-
- return read_perf_counter_info_n(path, format);
-}
-
-static unsigned int read_mperf_config(void)
-{
- const char *const path = "/sys/bus/event_source/devices/msr/events/mperf";
- const char *const format = "event=%x";
-
- return read_perf_counter_info_n(path, format);
-}
-
static unsigned int read_perf_type(const char *subsys)
{
const char *const path_format = "/sys/bus/event_source/devices/%s/type";
@@ -3190,15 +4294,55 @@ static unsigned int read_perf_type(const char *subsys)
return read_perf_counter_info_n(path, format);
}
-static unsigned int read_rapl_config(const char *subsys, const char *event_name)
+static unsigned int read_perf_config(const char *subsys, const char *event_name)
{
const char *const path_format = "/sys/bus/event_source/devices/%s/events/%s";
- const char *const format = "event=%x";
+ FILE *fconfig = NULL;
char path[128];
+ char config_str[64];
+ unsigned int config;
+ unsigned int umask;
+ bool has_config = false;
+ bool has_umask = false;
+ unsigned int ret = -1;
snprintf(path, sizeof(path), path_format, subsys, event_name);
- return read_perf_counter_info_n(path, format);
+ fconfig = fopen(path, "r");
+ if (!fconfig)
+ return -1;
+
+ if (fgets(config_str, ARRAY_SIZE(config_str), fconfig) != config_str)
+ goto cleanup_and_exit;
+
+ for (char *pconfig_str = &config_str[0]; pconfig_str;) {
+ if (sscanf(pconfig_str, "event=%x", &config) == 1) {
+ has_config = true;
+ goto next;
+ }
+
+ if (sscanf(pconfig_str, "umask=%x", &umask) == 1) {
+ has_umask = true;
+ goto next;
+ }
+
+next:
+ pconfig_str = strchr(pconfig_str, ',');
+ if (pconfig_str) {
+ *pconfig_str = '\0';
+ ++pconfig_str;
+ }
+ }
+
+ if (!has_umask)
+ umask = 0;
+
+ if (has_config)
+ ret = (umask << 8) | config;
+
+cleanup_and_exit:
+ fclose(fconfig);
+ return ret;
}
static unsigned int read_perf_rapl_unit(const char *subsys, const char *event_name)
@@ -3217,7 +4361,7 @@ static unsigned int read_perf_rapl_unit(const char *subsys, const char *event_na
return RAPL_UNIT_INVALID;
}
-static double read_perf_rapl_scale(const char *subsys, const char *event_name)
+static double read_perf_scale(const char *subsys, const char *event_name)
{
const char *const path_format = "/sys/bus/event_source/devices/%s/events/%s.scale";
const char *const format = "%lf";
@@ -3232,218 +4376,396 @@ static double read_perf_rapl_scale(const char *subsys, const char *event_name)
return scale;
}
-static struct amperf_group_fd open_amperf_fd(int cpu)
+size_t rapl_counter_info_count_perf(const struct rapl_counter_info_t *rci)
{
- const unsigned int msr_type = read_msr_type();
- const unsigned int aperf_config = read_aperf_config();
- const unsigned int mperf_config = read_mperf_config();
- struct amperf_group_fd fds = {.aperf = -1, .mperf = -1 };
+ size_t ret = 0;
- fds.aperf = open_perf_counter(cpu, msr_type, aperf_config, -1, PERF_FORMAT_GROUP);
- fds.mperf = open_perf_counter(cpu, msr_type, mperf_config, fds.aperf, PERF_FORMAT_GROUP);
+ for (int i = 0; i < NUM_RAPL_COUNTERS; ++i)
+ if (rci->source[i] == COUNTER_SOURCE_PERF)
+ ++ret;
- return fds;
+ return ret;
}
-static int get_amperf_fd(int cpu)
+static size_t cstate_counter_info_count_perf(const struct cstate_counter_info_t *cci)
{
- assert(fd_amperf_percpu);
+ size_t ret = 0;
- if (fd_amperf_percpu[cpu].aperf)
- return fd_amperf_percpu[cpu].aperf;
+ for (int i = 0; i < NUM_CSTATE_COUNTERS; ++i)
+ if (cci->source[i] == COUNTER_SOURCE_PERF)
+ ++ret;
+
+ return ret;
+}
- fd_amperf_percpu[cpu] = open_amperf_fd(cpu);
+void write_rapl_counter(struct rapl_counter *rc, struct rapl_counter_info_t *rci, unsigned int idx)
+{
+ if (rci->source[idx] == COUNTER_SOURCE_NONE)
+ return;
- return fd_amperf_percpu[cpu].aperf;
+ rc->raw_value = rci->data[idx];
+ rc->unit = rci->unit[idx];
+ rc->scale = rci->scale[idx];
}
-/* Read APERF, MPERF and TSC using the perf API. */
-static int read_aperf_mperf_tsc_perf(struct thread_data *t, int cpu)
+int get_rapl_counters(int cpu, unsigned int domain, struct core_data *c, struct pkg_data *p)
{
- union {
- struct {
- unsigned long nr_entries;
- unsigned long aperf;
- unsigned long mperf;
- };
+ struct platform_counters *pplat_cnt = p == package_odd ? &platform_counters_odd : &platform_counters_even;
+ unsigned long long perf_data[NUM_RAPL_COUNTERS + 1];
+ struct rapl_counter_info_t *rci;
+
+ if (debug >= 2)
+ fprintf(stderr, "%s: cpu%d domain%d\n", __func__, cpu, domain);
- unsigned long as_array[3];
- } cnt;
+ assert(rapl_counter_info_perdomain);
+ assert(domain < rapl_counter_info_perdomain_size);
- const int fd_amperf = get_amperf_fd(cpu);
+ rci = &rapl_counter_info_perdomain[domain];
/*
- * Read the TSC with rdtsc, because we want the absolute value and not
- * the offset from the start of the counter.
+ * If we have any perf counters to read, read them all now, in bulk
*/
- t->tsc = rdtsc();
+ if (rci->fd_perf != -1) {
+ size_t num_perf_counters = rapl_counter_info_count_perf(rci);
+ const ssize_t expected_read_size = (num_perf_counters + 1) * sizeof(unsigned long long);
+ const ssize_t actual_read_size = read(rci->fd_perf, &perf_data[0], sizeof(perf_data));
- const int n = read(fd_amperf, &cnt.as_array[0], sizeof(cnt.as_array));
+ if (actual_read_size != expected_read_size)
+ err(-1, "%s: failed to read perf_data (%zu %zu)", __func__, expected_read_size,
+ actual_read_size);
+ }
- if (n != sizeof(cnt.as_array))
- return -2;
+ for (unsigned int i = 0, pi = 1; i < NUM_RAPL_COUNTERS; ++i) {
+ switch (rci->source[i]) {
+ case COUNTER_SOURCE_NONE:
+ rci->data[i] = 0;
+ break;
- t->aperf = cnt.aperf * aperf_mperf_multiplier;
- t->mperf = cnt.mperf * aperf_mperf_multiplier;
+ case COUNTER_SOURCE_PERF:
+ assert(pi < ARRAY_SIZE(perf_data));
+ assert(rci->fd_perf != -1);
+
+ if (debug >= 2)
+ fprintf(stderr, "Reading rapl counter via perf at %u (%llu %e %lf)\n",
+ i, perf_data[pi], rci->scale[i], perf_data[pi] * rci->scale[i]);
+
+ rci->data[i] = perf_data[pi];
+
+ ++pi;
+ break;
+
+ case COUNTER_SOURCE_MSR:
+ if (debug >= 2)
+ fprintf(stderr, "Reading rapl counter via msr at %u\n", i);
+
+ assert(!no_msr);
+ if (rci->flags[i] & RAPL_COUNTER_FLAG_USE_MSR_SUM) {
+ if (get_msr_sum(cpu, rci->msr[i], &rci->data[i]))
+ return -13 - i;
+ } else {
+ if (get_msr(cpu, rci->msr[i], &rci->data[i]))
+ return -13 - i;
+ }
+
+ rci->data[i] &= rci->msr_mask[i];
+ if (rci->msr_shift[i] >= 0)
+ rci->data[i] >>= abs(rci->msr_shift[i]);
+ else
+ rci->data[i] <<= abs(rci->msr_shift[i]);
+
+ break;
+ }
+ }
+
+ BUILD_BUG_ON(NUM_RAPL_COUNTERS != 8);
+ write_rapl_counter(&p->energy_pkg, rci, RAPL_RCI_INDEX_ENERGY_PKG);
+ write_rapl_counter(&p->energy_cores, rci, RAPL_RCI_INDEX_ENERGY_CORES);
+ write_rapl_counter(&p->energy_dram, rci, RAPL_RCI_INDEX_DRAM);
+ write_rapl_counter(&p->energy_gfx, rci, RAPL_RCI_INDEX_GFX);
+ write_rapl_counter(&p->rapl_pkg_perf_status, rci, RAPL_RCI_INDEX_PKG_PERF_STATUS);
+ write_rapl_counter(&p->rapl_dram_perf_status, rci, RAPL_RCI_INDEX_DRAM_PERF_STATUS);
+ write_rapl_counter(&c->core_energy, rci, RAPL_RCI_INDEX_CORE_ENERGY);
+ write_rapl_counter(&pplat_cnt->energy_psys, rci, RAPL_RCI_INDEX_ENERGY_PLATFORM);
return 0;
}
-/* Read APERF, MPERF and TSC using the MSR driver and rdtsc instruction. */
-static int read_aperf_mperf_tsc_msr(struct thread_data *t, int cpu)
+char *find_sysfs_path_by_id(struct sysfs_path *sp, int id)
{
- unsigned long long tsc_before, tsc_between, tsc_after, aperf_time, mperf_time;
- int aperf_mperf_retry_count = 0;
+ while (sp) {
+ if (sp->id == id)
+ return (sp->path);
+ sp = sp->next;
+ }
+ if (debug)
+ warnx("%s: id%d not found", __func__, id);
+ return NULL;
+}
+int get_cstate_counters(unsigned int cpu, struct thread_data *t, struct core_data *c, struct pkg_data *p)
+{
/*
- * The TSC, APERF and MPERF must be read together for
- * APERF/MPERF and MPERF/TSC to give accurate results.
- *
- * Unfortunately, APERF and MPERF are read by
- * individual system call, so delays may occur
- * between them. If the time to read them
- * varies by a large amount, we re-read them.
+ * Overcommit memory a little bit here,
+ * but skip calculating exact sizes for the buffers.
*/
+ unsigned long long perf_data[NUM_CSTATE_COUNTERS];
+ unsigned long long perf_data_core[NUM_CSTATE_COUNTERS + 1];
+ unsigned long long perf_data_pkg[NUM_CSTATE_COUNTERS + 1];
+
+ struct cstate_counter_info_t *cci;
+
+ if (debug >= 2)
+ fprintf(stderr, "%s: cpu%d\n", __func__, cpu);
+
+ assert(ccstate_counter_info);
+ assert(cpu <= ccstate_counter_info_size);
+
+ ZERO_ARRAY(perf_data);
+ ZERO_ARRAY(perf_data_core);
+ ZERO_ARRAY(perf_data_pkg);
+
+ cci = &ccstate_counter_info[cpu];
/*
- * This initial dummy APERF read has been seen to
- * reduce jitter in the subsequent reads.
+ * If we have any perf counters to read, read them all now, in bulk
*/
+ const size_t num_perf_counters = cstate_counter_info_count_perf(cci);
+ ssize_t expected_read_size = num_perf_counters * sizeof(unsigned long long);
+ ssize_t actual_read_size_core = 0, actual_read_size_pkg = 0;
- if (get_msr(cpu, MSR_IA32_APERF, &t->aperf))
- return -3;
+ if (cci->fd_perf_core != -1) {
+ /* Each descriptor read begins with number of counters read. */
+ expected_read_size += sizeof(unsigned long long);
-retry:
- t->tsc = rdtsc(); /* re-read close to APERF */
+ actual_read_size_core = read(cci->fd_perf_core, &perf_data_core[0], sizeof(perf_data_core));
- tsc_before = t->tsc;
+ if (actual_read_size_core <= 0)
+ err(-1, "%s: read perf %s: %ld", __func__, "core", actual_read_size_core);
+ }
- if (get_msr(cpu, MSR_IA32_APERF, &t->aperf))
- return -3;
+ if (cci->fd_perf_pkg != -1) {
+ /* Each descriptor read begins with number of counters read. */
+ expected_read_size += sizeof(unsigned long long);
- tsc_between = rdtsc();
+ actual_read_size_pkg = read(cci->fd_perf_pkg, &perf_data_pkg[0], sizeof(perf_data_pkg));
- if (get_msr(cpu, MSR_IA32_MPERF, &t->mperf))
- return -4;
+ if (actual_read_size_pkg <= 0)
+ err(-1, "%s: read perf %s: %ld", __func__, "pkg", actual_read_size_pkg);
+ }
- tsc_after = rdtsc();
+ const ssize_t actual_read_size_total = actual_read_size_core + actual_read_size_pkg;
- aperf_time = tsc_between - tsc_before;
- mperf_time = tsc_after - tsc_between;
+ if (actual_read_size_total != expected_read_size)
+ err(-1, "%s: failed to read perf_data (%zu %zu)", __func__, expected_read_size, actual_read_size_total);
/*
- * If the system call latency to read APERF and MPERF
- * differ by more than 2x, then try again.
+ * Copy ccstate and pcstate data into unified buffer.
+ *
+ * Skip first element from core and pkg buffers.
+ * Kernel puts there how many counters were read.
*/
- if ((aperf_time > (2 * mperf_time)) || (mperf_time > (2 * aperf_time))) {
- aperf_mperf_retry_count++;
- if (aperf_mperf_retry_count < 5)
- goto retry;
- else
- warnx("cpu%d jitter %lld %lld", cpu, aperf_time, mperf_time);
+ const size_t num_core_counters = perf_data_core[0];
+ const size_t num_pkg_counters = perf_data_pkg[0];
+
+ assert(num_perf_counters == num_core_counters + num_pkg_counters);
+
+ /* Copy ccstate perf data */
+ memcpy(&perf_data[0], &perf_data_core[1], num_core_counters * sizeof(unsigned long long));
+
+ /* Copy pcstate perf data */
+ memcpy(&perf_data[num_core_counters], &perf_data_pkg[1], num_pkg_counters * sizeof(unsigned long long));
+
+ for (unsigned int i = 0, pi = 0; i < NUM_CSTATE_COUNTERS; ++i) {
+ switch (cci->source[i]) {
+ case COUNTER_SOURCE_NONE:
+ break;
+
+ case COUNTER_SOURCE_PERF:
+ assert(pi < ARRAY_SIZE(perf_data));
+ assert(cci->fd_perf_core != -1 || cci->fd_perf_pkg != -1);
+
+ if (debug >= 2)
+ fprintf(stderr, "cstate via %s %u: %llu\n", "perf", i, perf_data[pi]);
+
+ cci->data[i] = perf_data[pi];
+
+ ++pi;
+ break;
+
+ case COUNTER_SOURCE_MSR:
+ assert(!no_msr);
+ if (get_msr(cpu, cci->msr[i], &cci->data[i]))
+ return -13 - i;
+
+ if (debug >= 2)
+ fprintf(stderr, "cstate via %s0x%llx %u: %llu\n", "msr", cci->msr[i], i, cci->data[i]);
+
+ break;
+ }
}
- aperf_mperf_retry_count = 0;
- t->aperf = t->aperf * aperf_mperf_multiplier;
- t->mperf = t->mperf * aperf_mperf_multiplier;
+ /*
+ * Helper to write the data only if the source of
+ * the counter for the current cpu is not none.
+ *
+ * Otherwise we would overwrite core data with 0 (default value),
+ * when invoked for the thread sibling.
+ */
+#define PERF_COUNTER_WRITE_DATA(out_counter, index) do { \
+ if (cci->source[index] != COUNTER_SOURCE_NONE) \
+ out_counter = cci->data[index]; \
+} while (0)
+
+ BUILD_BUG_ON(NUM_CSTATE_COUNTERS != 11);
+
+ PERF_COUNTER_WRITE_DATA(t->c1, CCSTATE_RCI_INDEX_C1_RESIDENCY);
+ PERF_COUNTER_WRITE_DATA(c->c3, CCSTATE_RCI_INDEX_C3_RESIDENCY);
+ PERF_COUNTER_WRITE_DATA(c->c6, CCSTATE_RCI_INDEX_C6_RESIDENCY);
+ PERF_COUNTER_WRITE_DATA(c->c7, CCSTATE_RCI_INDEX_C7_RESIDENCY);
+
+ PERF_COUNTER_WRITE_DATA(p->pc2, PCSTATE_RCI_INDEX_C2_RESIDENCY);
+ PERF_COUNTER_WRITE_DATA(p->pc3, PCSTATE_RCI_INDEX_C3_RESIDENCY);
+ PERF_COUNTER_WRITE_DATA(p->pc6, PCSTATE_RCI_INDEX_C6_RESIDENCY);
+ PERF_COUNTER_WRITE_DATA(p->pc7, PCSTATE_RCI_INDEX_C7_RESIDENCY);
+ PERF_COUNTER_WRITE_DATA(p->pc8, PCSTATE_RCI_INDEX_C8_RESIDENCY);
+ PERF_COUNTER_WRITE_DATA(p->pc9, PCSTATE_RCI_INDEX_C9_RESIDENCY);
+ PERF_COUNTER_WRITE_DATA(p->pc10, PCSTATE_RCI_INDEX_C10_RESIDENCY);
+
+#undef PERF_COUNTER_WRITE_DATA
return 0;
}
-size_t rapl_counter_info_count_perf(const struct rapl_counter_info_t *rci)
+size_t msr_counter_info_count_perf(const struct msr_counter_info_t *mci)
{
size_t ret = 0;
- for (int i = 0; i < NUM_RAPL_COUNTERS; ++i)
- if (rci->source[i] == RAPL_SOURCE_PERF)
+ for (int i = 0; i < NUM_MSR_COUNTERS; ++i)
+ if (mci->source[i] == COUNTER_SOURCE_PERF)
++ret;
return ret;
}
-void write_rapl_counter(struct rapl_counter *rc, struct rapl_counter_info_t *rci, unsigned int idx)
+int get_smi_aperf_mperf(unsigned int cpu, struct thread_data *t)
{
- rc->raw_value = rci->data[idx];
- rc->unit = rci->unit[idx];
- rc->scale = rci->scale[idx];
-}
+ unsigned long long perf_data[NUM_MSR_COUNTERS + 1];
-int get_rapl_counters(int cpu, int domain, struct core_data *c, struct pkg_data *p)
-{
- unsigned long long perf_data[NUM_RAPL_COUNTERS + 1];
- struct rapl_counter_info_t *rci = &rapl_counter_info_perdomain[domain];
+ struct msr_counter_info_t *mci;
- if (debug)
- fprintf(stderr, "%s: cpu%d domain%d\n", __func__, cpu, domain);
+ if (debug >= 2)
+ fprintf(stderr, "%s: cpu%d\n", __func__, cpu);
- assert(rapl_counter_info_perdomain);
+ assert(msr_counter_info);
+ assert(cpu <= msr_counter_info_size);
- /*
- * If we have any perf counters to read, read them all now, in bulk
- */
- if (rci->fd_perf != -1) {
- size_t num_perf_counters = rapl_counter_info_count_perf(rci);
+ mci = &msr_counter_info[cpu];
+
+ ZERO_ARRAY(perf_data);
+ ZERO_ARRAY(mci->data);
+
+ if (mci->fd_perf != -1) {
+ const size_t num_perf_counters = msr_counter_info_count_perf(mci);
const ssize_t expected_read_size = (num_perf_counters + 1) * sizeof(unsigned long long);
- const ssize_t actual_read_size = read(rci->fd_perf, &perf_data[0], sizeof(perf_data));
+ const ssize_t actual_read_size = read(mci->fd_perf, &perf_data[0], sizeof(perf_data));
if (actual_read_size != expected_read_size)
err(-1, "%s: failed to read perf_data (%zu %zu)", __func__, expected_read_size,
actual_read_size);
}
- for (unsigned int i = 0, pi = 1; i < NUM_RAPL_COUNTERS; ++i) {
- switch (rci->source[i]) {
- case RAPL_SOURCE_NONE:
+ for (unsigned int i = 0, pi = 1; i < NUM_MSR_COUNTERS; ++i) {
+ switch (mci->source[i]) {
+ case COUNTER_SOURCE_NONE:
break;
- case RAPL_SOURCE_PERF:
+ case COUNTER_SOURCE_PERF:
assert(pi < ARRAY_SIZE(perf_data));
- assert(rci->fd_perf != -1);
+ assert(mci->fd_perf != -1);
- if (debug)
- fprintf(stderr, "Reading rapl counter via perf at %u (%llu %e %lf)\n",
- i, perf_data[pi], rci->scale[i], perf_data[pi] * rci->scale[i]);
+ if (debug >= 2)
+ fprintf(stderr, "Reading msr counter via perf at %u: %llu\n", i, perf_data[pi]);
- rci->data[i] = perf_data[pi];
+ mci->data[i] = perf_data[pi];
++pi;
break;
- case RAPL_SOURCE_MSR:
- if (debug)
- fprintf(stderr, "Reading rapl counter via msr at %u\n", i);
-
+ case COUNTER_SOURCE_MSR:
assert(!no_msr);
- if (rci->flags[i] & RAPL_COUNTER_FLAG_USE_MSR_SUM) {
- if (get_msr_sum(cpu, rci->msr[i], &rci->data[i]))
- return -13 - i;
- } else {
- if (get_msr(cpu, rci->msr[i], &rci->data[i]))
- return -13 - i;
- }
- rci->data[i] &= rci->msr_mask[i];
- if (rci->msr_shift[i] >= 0)
- rci->data[i] >>= abs(rci->msr_shift[i]);
- else
- rci->data[i] <<= abs(rci->msr_shift[i]);
+ if (get_msr(cpu, mci->msr[i], &mci->data[i]))
+ return -2 - i;
+
+ mci->data[i] &= mci->msr_mask[i];
+
+ if (debug >= 2)
+ fprintf(stderr, "Reading msr counter via msr at %u: %llu\n", i, mci->data[i]);
break;
}
}
- _Static_assert(NUM_RAPL_COUNTERS == 7);
- write_rapl_counter(&p->energy_pkg, rci, RAPL_RCI_INDEX_ENERGY_PKG);
- write_rapl_counter(&p->energy_cores, rci, RAPL_RCI_INDEX_ENERGY_CORES);
- write_rapl_counter(&p->energy_dram, rci, RAPL_RCI_INDEX_DRAM);
- write_rapl_counter(&p->energy_gfx, rci, RAPL_RCI_INDEX_GFX);
- write_rapl_counter(&p->rapl_pkg_perf_status, rci, RAPL_RCI_INDEX_PKG_PERF_STATUS);
- write_rapl_counter(&p->rapl_dram_perf_status, rci, RAPL_RCI_INDEX_DRAM_PERF_STATUS);
- write_rapl_counter(&c->core_energy, rci, RAPL_RCI_INDEX_CORE_ENERGY);
+ BUILD_BUG_ON(NUM_MSR_COUNTERS != 3);
+ t->aperf = mci->data[MSR_RCI_INDEX_APERF];
+ t->mperf = mci->data[MSR_RCI_INDEX_MPERF];
+ t->smi_count = mci->data[MSR_RCI_INDEX_SMI];
return 0;
}
+int perf_counter_info_read_values(struct perf_counter_info *pp, int cpu, unsigned long long *out, size_t out_size)
+{
+ unsigned int domain;
+ unsigned long long value;
+ int fd_counter;
+
+ for (size_t i = 0; pp; ++i, pp = pp->next) {
+ domain = cpu_to_domain(pp, cpu);
+ assert(domain < pp->num_domains);
+
+ fd_counter = pp->fd_perf_per_domain[domain];
+
+ if (fd_counter == -1)
+ continue;
+
+ if (read(fd_counter, &value, sizeof(value)) != sizeof(value))
+ return 1;
+
+ assert(i < out_size);
+ out[i] = value * pp->scale;
+ }
+
+ return 0;
+}
+
+unsigned long pmt_gen_value_mask(unsigned int lsb, unsigned int msb)
+{
+ unsigned long mask;
+
+ if (msb == 63)
+ mask = 0xffffffffffffffff;
+ else
+ mask = ((1 << (msb + 1)) - 1);
+
+ mask -= (1 << lsb) - 1;
+
+ return mask;
+}
+
+unsigned long pmt_read_counter(struct pmt_counter *ppmt, unsigned int domain_id)
+{
+ if (domain_id >= ppmt->num_domains)
+ return 0;
+
+ const unsigned long *pmmio = ppmt->domains[domain_id].pcounter;
+ const unsigned long value = pmmio ? *pmmio : 0;
+ const unsigned long value_mask = pmt_gen_value_mask(ppmt->lsb, ppmt->msb);
+ const unsigned long value_shift = ppmt->lsb;
+
+ return (value & value_mask) >> value_shift;
+}
+
/*
* get_counters(...)
* migrate to cpu
@@ -3454,6 +4776,7 @@ int get_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p)
int cpu = t->cpu_id;
unsigned long long msr;
struct msr_counter *mp;
+ struct pmt_counter *pp;
int i;
int status;
@@ -3469,24 +4792,7 @@ int get_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p)
t->tsc = rdtsc(); /* we are running on local CPU of interest */
- if (DO_BIC(BIC_Avg_MHz) || DO_BIC(BIC_Busy) || DO_BIC(BIC_Bzy_MHz) || DO_BIC(BIC_IPC)
- || soft_c1_residency_display(BIC_Avg_MHz)) {
- int status = -1;
-
- assert(!no_perf || !no_msr);
-
- switch (amperf_source) {
- case AMPERF_SOURCE_PERF:
- status = read_aperf_mperf_tsc_perf(t, cpu);
- break;
- case AMPERF_SOURCE_MSR:
- status = read_aperf_mperf_tsc_msr(t, cpu);
- break;
- }
-
- if (status != 0)
- return status;
- }
+ get_smi_aperf_mperf(cpu, t);
if (DO_BIC(BIC_IPC))
if (read(get_instr_count_fd(cpu), &t->instr_count, sizeof(long long)) != sizeof(long long))
@@ -3494,21 +4800,22 @@ int get_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p)
if (DO_BIC(BIC_IRQ))
t->irq_count = irqs_per_cpu[cpu];
- if (DO_BIC(BIC_SMI)) {
- if (get_msr(cpu, MSR_SMI_COUNT, &msr))
- return -5;
- t->smi_count = msr & 0xFFFFFFFF;
- }
- if (DO_BIC(BIC_CPU_c1) && platform->has_msr_core_c1_res) {
- if (get_msr(cpu, MSR_CORE_C1_RES, &t->c1))
- return -6;
- }
+ if (DO_BIC(BIC_NMI))
+ t->nmi_count = nmi_per_cpu[cpu];
+
+ get_cstate_counters(cpu, t, c, p);
for (i = 0, mp = sys.tp; mp; i++, mp = mp->next) {
- if (get_mp(cpu, mp, &t->counter[i]))
+ if (get_mp(cpu, mp, &t->counter[i], mp->sp->path))
return -10;
}
+ if (perf_counter_info_read_values(sys.perf_tp, cpu, t->perf_counter, MAX_ADDED_THREAD_COUNTERS))
+ return -10;
+
+ for (i = 0, pp = sys.pmt_tp; pp; i++, pp = pp->next)
+ t->pmt_counter[i] = pmt_read_counter(pp, t->cpu_id);
+
/* collect core counters only for 1st thread in core */
if (!is_cpu_first_thread_in_core(t, c, p))
goto done;
@@ -3519,31 +4826,14 @@ int get_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p)
return status;
}
- if (DO_BIC(BIC_CPU_c3) || soft_c1_residency_display(BIC_CPU_c3)) {
- if (get_msr(cpu, MSR_CORE_C3_RESIDENCY, &c->c3))
- return -6;
- }
-
- if ((DO_BIC(BIC_CPU_c6) || soft_c1_residency_display(BIC_CPU_c6)) && !platform->has_msr_knl_core_c6_residency) {
- if (get_msr(cpu, MSR_CORE_C6_RESIDENCY, &c->c6))
- return -7;
- } else if (platform->has_msr_knl_core_c6_residency && soft_c1_residency_display(BIC_CPU_c6)) {
- if (get_msr(cpu, MSR_KNL_CORE_C6_RESIDENCY, &c->c6))
- return -7;
- }
-
- if (DO_BIC(BIC_CPU_c7) || soft_c1_residency_display(BIC_CPU_c7)) {
- if (get_msr(cpu, MSR_CORE_C7_RESIDENCY, &c->c7))
- return -8;
- else if (t->is_atom) {
- /*
- * For Atom CPUs that has core cstate deeper than c6,
- * MSR_CORE_C6_RESIDENCY returns residency of cc6 and deeper.
- * Minus CC7 (and deeper cstates) residency to get
- * accturate cc6 residency.
- */
- c->c6 -= c->c7;
- }
+ if (DO_BIC(BIC_CPU_c7) && t->is_atom) {
+ /*
+ * For Atom CPUs that has core cstate deeper than c6,
+ * MSR_CORE_C6_RESIDENCY returns residency of cc6 and deeper.
+ * Minus CC7 (and deeper cstates) residency to get
+ * accturate cc6 residency.
+ */
+ c->c6 -= c->c7;
}
if (DO_BIC(BIC_Mod_c6))
@@ -3560,10 +4850,16 @@ int get_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p)
get_core_throt_cnt(cpu, &c->core_throt_cnt);
for (i = 0, mp = sys.cp; mp; i++, mp = mp->next) {
- if (get_mp(cpu, mp, &c->counter[i]))
+ if (get_mp(cpu, mp, &c->counter[i], mp->sp->path))
return -10;
}
+ if (perf_counter_info_read_values(sys.perf_cp, cpu, c->perf_counter, MAX_ADDED_CORE_COUNTERS))
+ return -10;
+
+ for (i = 0, pp = sys.pmt_cp; pp; i++, pp = pp->next)
+ c->pmt_counter[i] = pmt_read_counter(pp, c->core_id);
+
/* collect package counters only for 1st core in package */
if (!is_cpu_first_core_in_package(t, c, p))
goto done;
@@ -3584,34 +4880,6 @@ int get_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p)
if (get_msr(cpu, MSR_PKG_BOTH_CORE_GFXE_C0_RES, &p->pkg_both_core_gfxe_c0))
return -13;
}
- if (DO_BIC(BIC_Pkgpc3))
- if (get_msr(cpu, MSR_PKG_C3_RESIDENCY, &p->pc3))
- return -9;
- if (DO_BIC(BIC_Pkgpc6)) {
- if (platform->has_msr_atom_pkg_c6_residency) {
- if (get_msr(cpu, MSR_ATOM_PKG_C6_RESIDENCY, &p->pc6))
- return -10;
- } else {
- if (get_msr(cpu, MSR_PKG_C6_RESIDENCY, &p->pc6))
- return -10;
- }
- }
-
- if (DO_BIC(BIC_Pkgpc2))
- if (get_msr(cpu, MSR_PKG_C2_RESIDENCY, &p->pc2))
- return -11;
- if (DO_BIC(BIC_Pkgpc7))
- if (get_msr(cpu, MSR_PKG_C7_RESIDENCY, &p->pc7))
- return -12;
- if (DO_BIC(BIC_Pkgpc8))
- if (get_msr(cpu, MSR_PKG_C8_RESIDENCY, &p->pc8))
- return -13;
- if (DO_BIC(BIC_Pkgpc9))
- if (get_msr(cpu, MSR_PKG_C9_RESIDENCY, &p->pc9))
- return -13;
- if (DO_BIC(BIC_Pkgpc10))
- if (get_msr(cpu, MSR_PKG_C10_RESIDENCY, &p->pc10))
- return -13;
if (DO_BIC(BIC_CPU_LPI))
p->cpu_lpi = cpuidle_cur_cpu_lpi_us;
@@ -3630,9 +4898,8 @@ int get_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p)
p->pkg_temp_c = tj_max - ((msr >> 16) & 0x7F);
}
- /* n.b. assume die0 uncore frequency applies to whole package */
if (DO_BIC(BIC_UNCORE_MHZ))
- p->uncore_mhz = get_uncore_mhz(p->package_id, 0);
+ p->uncore_mhz = get_legacy_uncore_mhz(p->package_id);
if (DO_BIC(BIC_GFX_rc6))
p->gfx_rc6_ms = gfx_info[GFX_rc6].val_ull;
@@ -3653,40 +4920,33 @@ int get_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p)
p->sam_act_mhz = gfx_info[SAM_ACTMHz].val;
for (i = 0, mp = sys.pp; mp; i++, mp = mp->next) {
- if (get_mp(cpu, mp, &p->counter[i]))
+ char *path = NULL;
+
+ if (mp->msr_num == 0) {
+ path = find_sysfs_path_by_id(mp->sp, p->package_id);
+ if (path == NULL) {
+ warnx("%s: package_id %d not found", __func__, p->package_id);
+ return -10;
+ }
+ }
+ if (get_mp(cpu, mp, &p->counter[i], path))
return -10;
}
+
+ if (perf_counter_info_read_values(sys.perf_pp, cpu, p->perf_counter, MAX_ADDED_PACKAGE_COUNTERS))
+ return -10;
+
+ for (i = 0, pp = sys.pmt_pp; pp; i++, pp = pp->next)
+ p->pmt_counter[i] = pmt_read_counter(pp, p->package_id);
+
done:
gettimeofday(&t->tv_end, (struct timezone *)NULL);
return 0;
}
-/*
- * MSR_PKG_CST_CONFIG_CONTROL decoding for pkg_cstate_limit:
- * If you change the values, note they are used both in comparisons
- * (>= PCL__7) and to index pkg_cstate_limit_strings[].
- */
-
-#define PCLUKN 0 /* Unknown */
-#define PCLRSV 1 /* Reserved */
-#define PCL__0 2 /* PC0 */
-#define PCL__1 3 /* PC1 */
-#define PCL__2 4 /* PC2 */
-#define PCL__3 5 /* PC3 */
-#define PCL__4 6 /* PC4 */
-#define PCL__6 7 /* PC6 */
-#define PCL_6N 8 /* PC6 No Retention */
-#define PCL_6R 9 /* PC6 Retention */
-#define PCL__7 10 /* PC7 */
-#define PCL_7S 11 /* PC7 Shrink */
-#define PCL__8 12 /* PC8 */
-#define PCL__9 13 /* PC9 */
-#define PCL_10 14 /* PC10 */
-#define PCLUNL 15 /* Unlimited */
-
int pkg_cstate_limit = PCLUKN;
-char *pkg_cstate_limit_strings[] = { "reserved", "unknown", "pc0", "pc1", "pc2",
+char *pkg_cstate_limit_strings[] = { "unknown", "reserved", "pc0", "pc1", "pc2",
"pc3", "pc4", "pc6", "pc6n", "pc6r", "pc7", "pc7s", "pc8", "pc9", "pc10", "unlimited"
};
@@ -4142,37 +5402,53 @@ void free_fd_percpu(void)
fd_percpu = NULL;
}
-void free_fd_amperf_percpu(void)
+void free_fd_instr_count_percpu(void)
{
- int i;
+ if (!fd_instr_count_percpu)
+ return;
- if (!fd_amperf_percpu)
+ for (int i = 0; i < topo.max_cpu_num + 1; ++i) {
+ if (fd_instr_count_percpu[i] != 0)
+ close(fd_instr_count_percpu[i]);
+ }
+
+ free(fd_instr_count_percpu);
+ fd_instr_count_percpu = NULL;
+}
+
+void free_fd_cstate(void)
+{
+ if (!ccstate_counter_info)
return;
- for (i = 0; i < topo.max_cpu_num + 1; ++i) {
- if (fd_amperf_percpu[i].mperf != 0)
- close(fd_amperf_percpu[i].mperf);
+ const int counter_info_num = ccstate_counter_info_size;
+
+ for (int counter_id = 0; counter_id < counter_info_num; ++counter_id) {
+ if (ccstate_counter_info[counter_id].fd_perf_core != -1)
+ close(ccstate_counter_info[counter_id].fd_perf_core);
- if (fd_amperf_percpu[i].aperf != 0)
- close(fd_amperf_percpu[i].aperf);
+ if (ccstate_counter_info[counter_id].fd_perf_pkg != -1)
+ close(ccstate_counter_info[counter_id].fd_perf_pkg);
}
- free(fd_amperf_percpu);
- fd_amperf_percpu = NULL;
+ free(ccstate_counter_info);
+ ccstate_counter_info = NULL;
+ ccstate_counter_info_size = 0;
}
-void free_fd_instr_count_percpu(void)
+void free_fd_msr(void)
{
- if (!fd_instr_count_percpu)
+ if (!msr_counter_info)
return;
- for (int i = 0; i < topo.max_cpu_num + 1; ++i) {
- if (fd_instr_count_percpu[i] != 0)
- close(fd_instr_count_percpu[i]);
+ for (int cpu = 0; cpu < topo.max_cpu_num; ++cpu) {
+ if (msr_counter_info[cpu].fd_perf != -1)
+ close(msr_counter_info[cpu].fd_perf);
}
- free(fd_instr_count_percpu);
- fd_instr_count_percpu = NULL;
+ free(msr_counter_info);
+ msr_counter_info = NULL;
+ msr_counter_info_size = 0;
}
void free_fd_rapl_percpu(void)
@@ -4180,7 +5456,7 @@ void free_fd_rapl_percpu(void)
if (!rapl_counter_info_perdomain)
return;
- const int num_domains = platform->has_per_core_rapl ? topo.num_cores : topo.num_packages;
+ const int num_domains = rapl_counter_info_perdomain_size;
for (int domain_id = 0; domain_id < num_domains; ++domain_id) {
if (rapl_counter_info_perdomain[domain_id].fd_perf != -1)
@@ -4188,6 +5464,38 @@ void free_fd_rapl_percpu(void)
}
free(rapl_counter_info_perdomain);
+ rapl_counter_info_perdomain = NULL;
+ rapl_counter_info_perdomain_size = 0;
+}
+
+void free_fd_added_perf_counters_(struct perf_counter_info *pp)
+{
+ if (!pp)
+ return;
+
+ if (!pp->fd_perf_per_domain)
+ return;
+
+ while (pp) {
+ for (size_t domain = 0; domain < pp->num_domains; ++domain) {
+ if (pp->fd_perf_per_domain[domain] != -1) {
+ close(pp->fd_perf_per_domain[domain]);
+ pp->fd_perf_per_domain[domain] = -1;
+ }
+ }
+
+ free(pp->fd_perf_per_domain);
+ pp->fd_perf_per_domain = NULL;
+
+ pp = pp->next;
+ }
+}
+
+void free_fd_added_perf_counters(void)
+{
+ free_fd_added_perf_counters_(sys.perf_tp);
+ free_fd_added_perf_counters_(sys.perf_cp);
+ free_fd_added_perf_counters_(sys.perf_pp);
}
void free_all_buffers(void)
@@ -4232,11 +5540,14 @@ void free_all_buffers(void)
free_fd_percpu();
free_fd_instr_count_percpu();
- free_fd_amperf_percpu();
+ free_fd_msr();
free_fd_rapl_percpu();
+ free_fd_cstate();
+ free_fd_added_perf_counters();
free(irq_column_2_cpu);
free(irqs_per_cpu);
+ free(nmi_per_cpu);
for (i = 0; i <= topo.max_cpu_num; ++i) {
if (cpus[i].put_ids)
@@ -4361,6 +5672,9 @@ static int parse_cpu_str(char *cpu_str, cpu_set_t *cpu_set, int cpu_set_size)
if (*next == '-') /* no negative cpu numbers */
return 1;
+ if (*next == '\0' || *next == '\n')
+ break;
+
start = strtoul(next, &next, 10);
if (start >= CPU_SUBSET_MAXCPUS)
@@ -4465,6 +5779,8 @@ int for_all_cpus_2(int (func) (struct thread_data *, struct core_data *,
{
int retval, pkg_no, node_no, core_no, thread_no;
+ retval = 0;
+
for (pkg_no = 0; pkg_no < topo.num_packages; ++pkg_no) {
for (node_no = 0; node_no < topo.nodes_per_pkg; ++node_no) {
for (core_no = 0; core_no < topo.cores_per_node; ++core_no) {
@@ -4486,14 +5802,12 @@ int for_all_cpus_2(int (func) (struct thread_data *, struct core_data *,
p = GET_PKG(pkg_base, pkg_no);
p2 = GET_PKG(pkg_base2, pkg_no);
- retval = func(t, c, p, t2, c2, p2);
- if (retval)
- return retval;
+ retval |= func(t, c, p, t2, c2, p2);
}
}
}
}
- return 0;
+ return retval;
}
/*
@@ -4568,14 +5882,22 @@ static void update_effective_set(bool startup)
}
void linux_perf_init(void);
+void msr_perf_init(void);
void rapl_perf_init(void);
+void cstate_perf_init(void);
+void added_perf_counters_init(void);
+void pmt_init(void);
void re_initialize(void)
{
free_all_buffers();
setup_all_buffers(false);
linux_perf_init();
+ msr_perf_init();
rapl_perf_init();
+ cstate_perf_init();
+ added_perf_counters_init();
+ pmt_init();
fprintf(outf, "turbostat: re-initialized with num_cpus %d, allowed_cpus %d\n", topo.num_cpus,
topo.allowed_cpus);
}
@@ -4624,6 +5946,32 @@ int init_thread_id(int cpu)
return 0;
}
+int set_my_cpu_type(void)
+{
+ unsigned int eax, ebx, ecx, edx;
+ unsigned int max_level;
+
+ __cpuid(0, max_level, ebx, ecx, edx);
+
+ if (max_level < CPUID_LEAF_MODEL_ID)
+ return 0;
+
+ __cpuid(CPUID_LEAF_MODEL_ID, eax, ebx, ecx, edx);
+
+ return (eax >> CPUID_LEAF_MODEL_ID_CORE_TYPE_SHIFT);
+}
+
+int set_cpu_hybrid_type(int cpu)
+{
+ if (cpu_migrate(cpu))
+ return -1;
+
+ int type = set_my_cpu_type();
+
+ cpus[cpu].type = type;
+ return 0;
+}
+
/*
* snapshot_proc_interrupts()
*
@@ -4656,31 +6004,37 @@ int snapshot_proc_interrupts(void)
irq_column_2_cpu[column] = cpu_number;
irqs_per_cpu[cpu_number] = 0;
+ nmi_per_cpu[cpu_number] = 0;
}
/* read /proc/interrupt count lines and sum up irqs per cpu */
while (1) {
int column;
char buf[64];
+ int this_row_is_nmi = 0;
- retval = fscanf(fp, " %s:", buf); /* flush irq# "N:" */
+ retval = fscanf(fp, " %s:", buf); /* irq# "N:" */
if (retval != 1)
break;
+ if (strncmp(buf, "NMI", strlen("NMI")) == 0)
+ this_row_is_nmi = 1;
+
/* read the count per cpu */
for (column = 0; column < topo.num_cpus; ++column) {
int cpu_number, irq_count;
retval = fscanf(fp, " %d", &irq_count);
+
if (retval != 1)
break;
cpu_number = irq_column_2_cpu[column];
irqs_per_cpu[cpu_number] += irq_count;
-
+ if (this_row_is_nmi)
+ nmi_per_cpu[cpu_number] += irq_count;
}
-
while (getc(fp) != '\n') ; /* flush interrupt description */
}
@@ -4696,28 +6050,22 @@ int snapshot_proc_interrupts(void)
*/
int snapshot_graphics(int idx)
{
- FILE *fp;
int retval;
+ rewind(gfx_info[idx].fp);
+ fflush(gfx_info[idx].fp);
+
switch (idx) {
case GFX_rc6:
case SAM_mc6:
- fp = fopen_or_die(gfx_info[idx].path, "r");
- retval = fscanf(fp, "%lld", &gfx_info[idx].val_ull);
+ retval = fscanf(gfx_info[idx].fp, "%lld", &gfx_info[idx].val_ull);
if (retval != 1)
err(1, "rc6");
- fclose(fp);
return 0;
case GFX_MHz:
case GFX_ACTMHz:
case SAM_MHz:
case SAM_ACTMHz:
- if (gfx_info[idx].fp == NULL) {
- gfx_info[idx].fp = fopen_or_die(gfx_info[idx].path, "r");
- } else {
- rewind(gfx_info[idx].fp);
- fflush(gfx_info[idx].fp);
- }
retval = fscanf(gfx_info[idx].fp, "%d", &gfx_info[idx].val);
if (retval != 1)
err(1, "MHz");
@@ -4784,7 +6132,9 @@ int snapshot_sys_lpi_us(void)
*/
int snapshot_proc_sysfs_files(void)
{
- if (DO_BIC(BIC_IRQ))
+ gettimeofday(&procsysfs_tv_begin, (struct timezone *)NULL);
+
+ if (DO_BIC(BIC_IRQ) || DO_BIC(BIC_NMI))
if (snapshot_proc_interrupts())
return 1;
@@ -5084,6 +6434,7 @@ restart:
re_initialize();
goto restart;
}
+ delta_platform(&platform_counters_odd, &platform_counters_even);
compute_average(EVEN_COUNTERS);
format_all_counters(EVEN_COUNTERS);
flush_output_stdout();
@@ -5107,6 +6458,7 @@ restart:
re_initialize();
goto restart;
}
+ delta_platform(&platform_counters_even, &platform_counters_odd);
compute_average(ODD_COUNTERS);
format_all_counters(ODD_COUNTERS);
flush_output_stdout();
@@ -5294,30 +6646,27 @@ static void dump_sysfs_file(char *path)
fprintf(outf, "%s: %s", strrchr(path, '/') + 1, cpuidle_buf);
}
-static void probe_intel_uncore_frequency(void)
+static void probe_intel_uncore_frequency_legacy(void)
{
int i, j;
char path[256];
- if (!genuine_intel)
- return;
-
- if (access("/sys/devices/system/cpu/intel_uncore_frequency/package_00_die_00/current_freq_khz", R_OK))
- goto probe_cluster;
-
- BIC_PRESENT(BIC_UNCORE_MHZ);
-
- if (quiet)
- return;
-
for (i = 0; i < topo.num_packages; ++i) {
- for (j = 0; j < topo.num_die; ++j) {
+ for (j = 0; j <= topo.max_die_id; ++j) {
int k, l;
char path_base[128];
sprintf(path_base, "/sys/devices/system/cpu/intel_uncore_frequency/package_%02d_die_%02d", i,
j);
+ if (access(path_base, R_OK))
+ continue;
+
+ BIC_PRESENT(BIC_UNCORE_MHZ);
+
+ if (quiet)
+ return;
+
sprintf(path, "%s/min_freq_khz", path_base);
k = read_sysfs_int(path);
sprintf(path, "%s/max_freq_khz", path_base);
@@ -5335,24 +6684,36 @@ static void probe_intel_uncore_frequency(void)
fprintf(outf, " %d MHz\n", k / 1000);
}
}
- return;
+}
+
+static void probe_intel_uncore_frequency_cluster(void)
+{
+ int i, uncore_max_id;
+ char path[256];
+ char path_base[128];
-probe_cluster:
if (access("/sys/devices/system/cpu/intel_uncore_frequency/uncore00/current_freq_khz", R_OK))
return;
- if (quiet)
- return;
+ for (uncore_max_id = 0;; ++uncore_max_id) {
+
+ sprintf(path_base, "/sys/devices/system/cpu/intel_uncore_frequency/uncore%02d", uncore_max_id);
- for (i = 0;; ++i) {
+ /* uncore## start at 00 and skips no numbers, so stop upon first missing */
+ if (access(path_base, R_OK)) {
+ uncore_max_id -= 1;
+ break;
+ }
+ }
+ for (i = uncore_max_id; i >= 0; --i) {
int k, l;
- char path_base[128];
int package_id, domain_id, cluster_id;
+ char name_buf[16];
sprintf(path_base, "/sys/devices/system/cpu/intel_uncore_frequency/uncore%02d", i);
if (access(path_base, R_OK))
- break;
+ err(1, "%s: %s\n", __func__, path_base);
sprintf(path, "%s/package_id", path_base);
package_id = read_sysfs_int(path);
@@ -5363,6 +6724,25 @@ probe_cluster:
sprintf(path, "%s/fabric_cluster_id", path_base);
cluster_id = read_sysfs_int(path);
+ sprintf(path, "%s/current_freq_khz", path_base);
+ sprintf(name_buf, "UMHz%d.%d", domain_id, cluster_id);
+
+ /*
+ * Once add_couter() is called, that counter is always read
+ * and reported -- So it is effectively (enabled & present).
+ * Only call add_counter() here if legacy BIC_UNCORE_MHZ (UncMHz)
+ * is (enabled). Since we are in this routine, we
+ * know we will not probe and set (present) the legacy counter.
+ *
+ * This allows "--show/--hide UncMHz" to be effective for
+ * the clustered MHz counters, as a group.
+ */
+ if BIC_IS_ENABLED(BIC_UNCORE_MHZ)
+ add_counter(0, path, name_buf, 0, SCOPE_PACKAGE, COUNTER_K2M, FORMAT_AVERAGE, 0, package_id);
+
+ if (quiet)
+ continue;
+
sprintf(path, "%s/min_freq_khz", path_base);
k = read_sysfs_int(path);
sprintf(path, "%s/max_freq_khz", path_base);
@@ -5382,14 +6762,36 @@ probe_cluster:
}
}
+static void probe_intel_uncore_frequency(void)
+{
+ if (!genuine_intel)
+ return;
+
+ if (access("/sys/devices/system/cpu/intel_uncore_frequency/uncore00", R_OK) == 0)
+ probe_intel_uncore_frequency_cluster();
+ else
+ probe_intel_uncore_frequency_legacy();
+}
+
+static void set_graphics_fp(char *path, int idx)
+{
+ if (!access(path, R_OK))
+ gfx_info[idx].fp = fopen_or_die(path, "r");
+}
+
+/* Enlarge this if there are /sys/class/drm/card2 ... */
+#define GFX_MAX_CARDS 2
+
static void probe_graphics(void)
{
+ char path[PATH_MAX];
+ int i;
+
/* Xe graphics sysfs knobs */
if (!access("/sys/class/drm/card0/device/tile0/gt0/gtidle/idle_residency_ms", R_OK)) {
FILE *fp;
char buf[8];
bool gt0_is_gt;
- int idx;
fp = fopen("/sys/class/drm/card0/device/tile0/gt0/gtidle/name", "r");
if (!fp)
@@ -5408,82 +6810,76 @@ static void probe_graphics(void)
else
goto next;
- idx = gt0_is_gt ? GFX_rc6 : SAM_mc6;
- gfx_info[idx].path = "/sys/class/drm/card0/device/tile0/gt0/gtidle/idle_residency_ms";
+ set_graphics_fp("/sys/class/drm/card0/device/tile0/gt0/gtidle/idle_residency_ms", gt0_is_gt ? GFX_rc6 : SAM_mc6);
- idx = gt0_is_gt ? GFX_MHz : SAM_MHz;
- if (!access("/sys/class/drm/card0/device/tile0/gt0/freq0/cur_freq", R_OK))
- gfx_info[idx].path = "/sys/class/drm/card0/device/tile0/gt0/freq0/cur_freq";
+ set_graphics_fp("/sys/class/drm/card0/device/tile0/gt0/freq0/cur_freq", gt0_is_gt ? GFX_MHz : SAM_MHz);
- idx = gt0_is_gt ? GFX_ACTMHz : SAM_ACTMHz;
- if (!access("/sys/class/drm/card0/device/tile0/gt0/freq0/act_freq", R_OK))
- gfx_info[idx].path = "/sys/class/drm/card0/device/tile0/gt0/freq0/act_freq";
+ set_graphics_fp("/sys/class/drm/card0/device/tile0/gt0/freq0/act_freq", gt0_is_gt ? GFX_ACTMHz : SAM_ACTMHz);
- idx = gt0_is_gt ? SAM_mc6 : GFX_rc6;
- if (!access("/sys/class/drm/card0/device/tile0/gt1/gtidle/idle_residency_ms", R_OK))
- gfx_info[idx].path = "/sys/class/drm/card0/device/tile0/gt1/gtidle/idle_residency_ms";
+ set_graphics_fp("/sys/class/drm/card0/device/tile0/gt1/gtidle/idle_residency_ms", gt0_is_gt ? SAM_mc6 : GFX_rc6);
- idx = gt0_is_gt ? SAM_MHz : GFX_MHz;
- if (!access("/sys/class/drm/card0/device/tile0/gt1/freq0/cur_freq", R_OK))
- gfx_info[idx].path = "/sys/class/drm/card0/device/tile0/gt1/freq0/cur_freq";
+ set_graphics_fp("/sys/class/drm/card0/device/tile0/gt1/freq0/cur_freq", gt0_is_gt ? SAM_MHz : GFX_MHz);
- idx = gt0_is_gt ? SAM_ACTMHz : GFX_ACTMHz;
- if (!access("/sys/class/drm/card0/device/tile0/gt1/freq0/act_freq", R_OK))
- gfx_info[idx].path = "/sys/class/drm/card0/device/tile0/gt1/freq0/act_freq";
+ set_graphics_fp("/sys/class/drm/card0/device/tile0/gt1/freq0/act_freq", gt0_is_gt ? SAM_ACTMHz : GFX_ACTMHz);
goto end;
}
next:
/* New i915 graphics sysfs knobs */
- if (!access("/sys/class/drm/card0/gt/gt0/rc6_residency_ms", R_OK)) {
- gfx_info[GFX_rc6].path = "/sys/class/drm/card0/gt/gt0/rc6_residency_ms";
+ for (i = 0; i < GFX_MAX_CARDS; i++) {
+ snprintf(path, PATH_MAX, "/sys/class/drm/card%d/gt/gt0/rc6_residency_ms", i);
+ if (!access(path, R_OK))
+ break;
+ }
- if (!access("/sys/class/drm/card0/gt/gt0/rps_cur_freq_mhz", R_OK))
- gfx_info[GFX_MHz].path = "/sys/class/drm/card0/gt/gt0/rps_cur_freq_mhz";
+ if (i == GFX_MAX_CARDS)
+ goto legacy_i915;
- if (!access("/sys/class/drm/card0/gt/gt0/rps_act_freq_mhz", R_OK))
- gfx_info[GFX_ACTMHz].path = "/sys/class/drm/card0/gt/gt0/rps_act_freq_mhz";
+ snprintf(path, PATH_MAX, "/sys/class/drm/card%d/gt/gt0/rc6_residency_ms", i);
+ set_graphics_fp(path, GFX_rc6);
- if (!access("/sys/class/drm/card0/gt/gt1/rc6_residency_ms", R_OK))
- gfx_info[SAM_mc6].path = "/sys/class/drm/card0/gt/gt1/rc6_residency_ms";
+ snprintf(path, PATH_MAX, "/sys/class/drm/card%d/gt/gt0/rps_cur_freq_mhz", i);
+ set_graphics_fp(path, GFX_MHz);
- if (!access("/sys/class/drm/card0/gt/gt1/rps_cur_freq_mhz", R_OK))
- gfx_info[SAM_MHz].path = "/sys/class/drm/card0/gt/gt1/rps_cur_freq_mhz";
+ snprintf(path, PATH_MAX, "/sys/class/drm/card%d/gt/gt0/rps_act_freq_mhz", i);
+ set_graphics_fp(path, GFX_ACTMHz);
- if (!access("/sys/class/drm/card0/gt/gt1/rps_act_freq_mhz", R_OK))
- gfx_info[SAM_ACTMHz].path = "/sys/class/drm/card0/gt/gt1/rps_act_freq_mhz";
+ snprintf(path, PATH_MAX, "/sys/class/drm/card%d/gt/gt1/rc6_residency_ms", i);
+ set_graphics_fp(path, SAM_mc6);
- goto end;
- }
+ snprintf(path, PATH_MAX, "/sys/class/drm/card%d/gt/gt1/rps_cur_freq_mhz", i);
+ set_graphics_fp(path, SAM_MHz);
- /* Fall back to traditional i915 graphics sysfs knobs */
- if (!access("/sys/class/drm/card0/power/rc6_residency_ms", R_OK))
- gfx_info[GFX_rc6].path = "/sys/class/drm/card0/power/rc6_residency_ms";
+ snprintf(path, PATH_MAX, "/sys/class/drm/card%d/gt/gt1/rps_act_freq_mhz", i);
+ set_graphics_fp(path, SAM_ACTMHz);
- if (!access("/sys/class/drm/card0/gt_cur_freq_mhz", R_OK))
- gfx_info[GFX_MHz].path = "/sys/class/drm/card0/gt_cur_freq_mhz";
- else if (!access("/sys/class/graphics/fb0/device/drm/card0/gt_cur_freq_mhz", R_OK))
- gfx_info[GFX_MHz].path = "/sys/class/graphics/fb0/device/drm/card0/gt_cur_freq_mhz";
+ goto end;
+legacy_i915:
+ /* Fall back to traditional i915 graphics sysfs knobs */
+ set_graphics_fp("/sys/class/drm/card0/power/rc6_residency_ms", GFX_rc6);
- if (!access("/sys/class/drm/card0/gt_act_freq_mhz", R_OK))
- gfx_info[GFX_ACTMHz].path = "/sys/class/drm/card0/gt_act_freq_mhz";
- else if (!access("/sys/class/graphics/fb0/device/drm/card0/gt_act_freq_mhz", R_OK))
- gfx_info[GFX_ACTMHz].path = "/sys/class/graphics/fb0/device/drm/card0/gt_act_freq_mhz";
+ set_graphics_fp("/sys/class/drm/card0/gt_cur_freq_mhz", GFX_MHz);
+ if (!gfx_info[GFX_MHz].fp)
+ set_graphics_fp("/sys/class/graphics/fb0/device/drm/card0/gt_cur_freq_mhz", GFX_MHz);
+
+ set_graphics_fp("/sys/class/drm/card0/gt_act_freq_mhz", GFX_ACTMHz);
+ if (!gfx_info[GFX_ACTMHz].fp)
+ set_graphics_fp("/sys/class/graphics/fb0/device/drm/card0/gt_act_freq_mhz", GFX_ACTMHz);
end:
- if (gfx_info[GFX_rc6].path)
+ if (gfx_info[GFX_rc6].fp)
BIC_PRESENT(BIC_GFX_rc6);
- if (gfx_info[GFX_MHz].path)
+ if (gfx_info[GFX_MHz].fp)
BIC_PRESENT(BIC_GFXMHz);
- if (gfx_info[GFX_ACTMHz].path)
+ if (gfx_info[GFX_ACTMHz].fp)
BIC_PRESENT(BIC_GFXACTMHz);
- if (gfx_info[SAM_mc6].path)
+ if (gfx_info[SAM_mc6].fp)
BIC_PRESENT(BIC_SAM_mc6);
- if (gfx_info[SAM_MHz].path)
+ if (gfx_info[SAM_MHz].fp)
BIC_PRESENT(BIC_SAMMHz);
- if (gfx_info[SAM_ACTMHz].path)
+ if (gfx_info[SAM_ACTMHz].fp)
BIC_PRESENT(BIC_SAMACTMHz);
}
@@ -5852,8 +7248,8 @@ void rapl_probe_intel(void)
unsigned long long msr;
unsigned int time_unit;
double tdp;
- const unsigned long long bic_watt_bits = BIC_PkgWatt | BIC_CorWatt | BIC_RAMWatt | BIC_GFXWatt;
- const unsigned long long bic_joules_bits = BIC_Pkg_J | BIC_Cor_J | BIC_RAM_J | BIC_GFX_J;
+ const unsigned long long bic_watt_bits = BIC_SysWatt | BIC_PkgWatt | BIC_CorWatt | BIC_RAMWatt | BIC_GFXWatt;
+ const unsigned long long bic_joules_bits = BIC_Sys_J | BIC_Pkg_J | BIC_Cor_J | BIC_RAM_J | BIC_GFX_J;
if (rapl_joules)
bic_enabled &= ~bic_watt_bits;
@@ -5880,6 +7276,11 @@ void rapl_probe_intel(void)
else
rapl_dram_energy_units = rapl_energy_units;
+ if (platform->has_fixed_rapl_psys_unit)
+ rapl_psys_energy_units = 1.0;
+ else
+ rapl_psys_energy_units = rapl_energy_units;
+
time_unit = msr >> 16 & 0xF;
if (time_unit == 0)
time_unit = 0xA;
@@ -6400,21 +7801,13 @@ static int has_instr_count_access(void)
return has_access;
}
-bool is_aperf_access_required(void)
-{
- return BIC_IS_ENABLED(BIC_Avg_MHz)
- || BIC_IS_ENABLED(BIC_Busy)
- || BIC_IS_ENABLED(BIC_Bzy_MHz)
- || BIC_IS_ENABLED(BIC_IPC);
-}
-
int add_rapl_perf_counter_(int cpu, struct rapl_counter_info_t *rci, const struct rapl_counter_arch_info *cai,
double *scale_, enum rapl_unit *unit_)
{
if (no_perf)
return -1;
- const double scale = read_perf_rapl_scale(cai->perf_subsys, cai->perf_name);
+ const double scale = read_perf_scale(cai->perf_subsys, cai->perf_name);
if (scale == 0.0)
return -1;
@@ -6425,7 +7818,7 @@ int add_rapl_perf_counter_(int cpu, struct rapl_counter_info_t *rci, const struc
return -1;
const unsigned int rapl_type = read_perf_type(cai->perf_subsys);
- const unsigned int rapl_energy_pkg_config = read_rapl_config(cai->perf_subsys, cai->perf_name);
+ const unsigned int rapl_energy_pkg_config = read_perf_config(cai->perf_subsys, cai->perf_name);
const int fd_counter =
open_perf_counter(cpu, rapl_type, rapl_energy_pkg_config, rci->fd_perf, PERF_FORMAT_GROUP);
@@ -6446,7 +7839,7 @@ int add_rapl_perf_counter(int cpu, struct rapl_counter_info_t *rci, const struct
{
int ret = add_rapl_perf_counter_(cpu, rci, cai, scale, unit);
- if (debug)
+ if (debug >= 2)
fprintf(stderr, "%s: %d (cpu: %d)\n", __func__, ret, cpu);
return ret;
@@ -6466,35 +7859,28 @@ void linux_perf_init(void)
if (fd_instr_count_percpu == NULL)
err(-1, "calloc fd_instr_count_percpu");
}
-
- const bool aperf_required = is_aperf_access_required();
-
- if (aperf_required && has_aperf && amperf_source == AMPERF_SOURCE_PERF) {
- fd_amperf_percpu = calloc(topo.max_cpu_num + 1, sizeof(*fd_amperf_percpu));
- if (fd_amperf_percpu == NULL)
- err(-1, "calloc fd_amperf_percpu");
- }
}
void rapl_perf_init(void)
{
- const int num_domains = platform->has_per_core_rapl ? topo.num_cores : topo.num_packages;
+ const unsigned int num_domains = (platform->has_per_core_rapl ? topo.max_core_id : topo.max_package_id) + 1;
bool *domain_visited = calloc(num_domains, sizeof(bool));
rapl_counter_info_perdomain = calloc(num_domains, sizeof(*rapl_counter_info_perdomain));
if (rapl_counter_info_perdomain == NULL)
err(-1, "calloc rapl_counter_info_percpu");
+ rapl_counter_info_perdomain_size = num_domains;
/*
* Initialize rapl_counter_info_percpu
*/
- for (int domain_id = 0; domain_id < num_domains; ++domain_id) {
+ for (unsigned int domain_id = 0; domain_id < num_domains; ++domain_id) {
struct rapl_counter_info_t *rci = &rapl_counter_info_perdomain[domain_id];
rci->fd_perf = -1;
for (size_t i = 0; i < NUM_RAPL_COUNTERS; ++i) {
rci->data[i] = 0;
- rci->source[i] = RAPL_SOURCE_NONE;
+ rci->source[i] = COUNTER_SOURCE_NONE;
}
}
@@ -6508,7 +7894,7 @@ void rapl_perf_init(void)
bool has_counter = 0;
double scale;
enum rapl_unit unit;
- int next_domain;
+ unsigned int next_domain;
memset(domain_visited, 0, num_domains * sizeof(*domain_visited));
@@ -6521,11 +7907,16 @@ void rapl_perf_init(void)
next_domain =
platform->has_per_core_rapl ? cpus[cpu].physical_core_id : cpus[cpu].physical_package_id;
+ assert(next_domain < num_domains);
+
if (domain_visited[next_domain])
continue;
domain_visited[next_domain] = 1;
+ if ((cai->flags & RAPL_COUNTER_FLAG_PLATFORM_COUNTER) && (cpu != base_cpu))
+ continue;
+
struct rapl_counter_info_t *rci = &rapl_counter_info_perdomain[next_domain];
/* Check if the counter is enabled and accessible */
@@ -6534,14 +7925,14 @@ void rapl_perf_init(void)
/* Use perf API for this counter */
if (!no_perf && cai->perf_name
&& add_rapl_perf_counter(cpu, rci, cai, &scale, &unit) != -1) {
- rci->source[cai->rci_index] = RAPL_SOURCE_PERF;
+ rci->source[cai->rci_index] = COUNTER_SOURCE_PERF;
rci->scale[cai->rci_index] = scale * cai->compat_scale;
rci->unit[cai->rci_index] = unit;
rci->flags[cai->rci_index] = cai->flags;
/* Use MSR for this counter */
- } else if (!no_msr && cai->msr && probe_msr(cpu, cai->msr) == 0) {
- rci->source[cai->rci_index] = RAPL_SOURCE_MSR;
+ } else if (!no_msr && cai->msr && probe_rapl_msr(cpu, cai->msr, cai->rci_index) == 0) {
+ rci->source[cai->rci_index] = COUNTER_SOURCE_MSR;
rci->msr[cai->rci_index] = cai->msr;
rci->msr_mask[cai->rci_index] = cai->msr_mask;
rci->msr_shift[cai->rci_index] = cai->msr_shift;
@@ -6551,7 +7942,7 @@ void rapl_perf_init(void)
}
}
- if (rci->source[cai->rci_index] != RAPL_SOURCE_NONE)
+ if (rci->source[cai->rci_index] != COUNTER_SOURCE_NONE)
has_counter = 1;
}
@@ -6563,113 +7954,275 @@ void rapl_perf_init(void)
free(domain_visited);
}
-static int has_amperf_access_via_msr(void)
+/* Assumes msr_counter_info is populated */
+static int has_amperf_access(void)
{
- if (no_msr)
- return 0;
+ return msr_counter_arch_infos[MSR_ARCH_INFO_APERF_INDEX].present &&
+ msr_counter_arch_infos[MSR_ARCH_INFO_MPERF_INDEX].present;
+}
- if (probe_msr(base_cpu, MSR_IA32_APERF))
- return 0;
+int *get_cstate_perf_group_fd(struct cstate_counter_info_t *cci, const char *group_name)
+{
+ if (strcmp(group_name, "cstate_core") == 0)
+ return &cci->fd_perf_core;
- if (probe_msr(base_cpu, MSR_IA32_MPERF))
- return 0;
+ if (strcmp(group_name, "cstate_pkg") == 0)
+ return &cci->fd_perf_pkg;
- return 1;
+ return NULL;
}
-static int has_amperf_access_via_perf(void)
+int add_cstate_perf_counter_(int cpu, struct cstate_counter_info_t *cci, const struct cstate_counter_arch_info *cai)
{
- struct amperf_group_fd fds;
+ if (no_perf)
+ return -1;
- /*
- * Cache the last result, so we don't warn the user multiple times
- *
- * Negative means cached, no access
- * Zero means not cached
- * Positive means cached, has access
- */
- static int has_access_cached;
+ int *pfd_group = get_cstate_perf_group_fd(cci, cai->perf_subsys);
+
+ if (pfd_group == NULL)
+ return -1;
+
+ const unsigned int type = read_perf_type(cai->perf_subsys);
+ const unsigned int config = read_perf_config(cai->perf_subsys, cai->perf_name);
+
+ const int fd_counter = open_perf_counter(cpu, type, config, *pfd_group, PERF_FORMAT_GROUP);
+ if (fd_counter == -1)
+ return -1;
+
+ /* If it's the first counter opened, make it a group descriptor */
+ if (*pfd_group == -1)
+ *pfd_group = fd_counter;
+
+ return fd_counter;
+}
+
+int add_cstate_perf_counter(int cpu, struct cstate_counter_info_t *cci, const struct cstate_counter_arch_info *cai)
+{
+ int ret = add_cstate_perf_counter_(cpu, cci, cai);
+
+ if (debug >= 2)
+ fprintf(stderr, "%s: %d (cpu: %d)\n", __func__, ret, cpu);
+
+ return ret;
+}
+
+int add_msr_perf_counter_(int cpu, struct msr_counter_info_t *cci, const struct msr_counter_arch_info *cai)
+{
if (no_perf)
- return 0;
+ return -1;
- if (has_access_cached != 0)
- return has_access_cached > 0;
+ const unsigned int type = read_perf_type(cai->perf_subsys);
+ const unsigned int config = read_perf_config(cai->perf_subsys, cai->perf_name);
- fds = open_amperf_fd(base_cpu);
- has_access_cached = (fds.aperf != -1) && (fds.mperf != -1);
+ const int fd_counter = open_perf_counter(cpu, type, config, cci->fd_perf, PERF_FORMAT_GROUP);
- if (fds.aperf == -1)
- warnx("Failed to access %s. Some of the counters may not be available\n"
- "\tRun as root to enable them or use %s to disable the access explicitly",
- "APERF perf counter", "--no-perf");
- else
- close(fds.aperf);
+ if (fd_counter == -1)
+ return -1;
- if (fds.mperf == -1)
- warnx("Failed to access %s. Some of the counters may not be available\n"
- "\tRun as root to enable them or use %s to disable the access explicitly",
- "MPERF perf counter", "--no-perf");
- else
- close(fds.mperf);
+ /* If it's the first counter opened, make it a group descriptor */
+ if (cci->fd_perf == -1)
+ cci->fd_perf = fd_counter;
+
+ return fd_counter;
+}
+
+int add_msr_perf_counter(int cpu, struct msr_counter_info_t *cci, const struct msr_counter_arch_info *cai)
+{
+ int ret = add_msr_perf_counter_(cpu, cci, cai);
- if (has_access_cached == 0)
- has_access_cached = -1;
+ if (debug)
+ fprintf(stderr, "%s: %s/%s: %d (cpu: %d)\n", __func__, cai->perf_subsys, cai->perf_name, ret, cpu);
- return has_access_cached > 0;
+ return ret;
}
-/* Check if we can access APERF and MPERF */
-static int has_amperf_access(void)
+void msr_perf_init_(void)
{
- if (!is_aperf_access_required())
- return 0;
+ const int mci_num = topo.max_cpu_num + 1;
- if (!no_msr && has_amperf_access_via_msr())
- return 1;
+ msr_counter_info = calloc(mci_num, sizeof(*msr_counter_info));
+ if (!msr_counter_info)
+ err(1, "calloc msr_counter_info");
+ msr_counter_info_size = mci_num;
- if (!no_perf && has_amperf_access_via_perf())
- return 1;
+ for (int cpu = 0; cpu < mci_num; ++cpu)
+ msr_counter_info[cpu].fd_perf = -1;
- return 0;
+ for (int cidx = 0; cidx < NUM_MSR_COUNTERS; ++cidx) {
+
+ struct msr_counter_arch_info *cai = &msr_counter_arch_infos[cidx];
+
+ cai->present = false;
+
+ for (int cpu = 0; cpu < mci_num; ++cpu) {
+
+ struct msr_counter_info_t *const cci = &msr_counter_info[cpu];
+
+ if (cpu_is_not_allowed(cpu))
+ continue;
+
+ if (cai->needed) {
+ /* Use perf API for this counter */
+ if (!no_perf && cai->perf_name && add_msr_perf_counter(cpu, cci, cai) != -1) {
+ cci->source[cai->rci_index] = COUNTER_SOURCE_PERF;
+ cai->present = true;
+
+ /* User MSR for this counter */
+ } else if (!no_msr && cai->msr && probe_rapl_msr(cpu, cai->msr, cai->rci_index) == 0) {
+ cci->source[cai->rci_index] = COUNTER_SOURCE_MSR;
+ cci->msr[cai->rci_index] = cai->msr;
+ cci->msr_mask[cai->rci_index] = cai->msr_mask;
+ cai->present = true;
+ }
+ }
+ }
+ }
}
-void probe_cstates(void)
+/* Initialize data for reading perf counters from the MSR group. */
+void msr_perf_init(void)
{
- probe_cst_limit();
+ bool need_amperf = false, need_smi = false;
+ const bool need_soft_c1 = (!platform->has_msr_core_c1_res) && (platform->supported_cstates & CC1);
- if (platform->supported_cstates & CC1)
- BIC_PRESENT(BIC_CPU_c1);
+ need_amperf = BIC_IS_ENABLED(BIC_Avg_MHz) || BIC_IS_ENABLED(BIC_Busy) || BIC_IS_ENABLED(BIC_Bzy_MHz)
+ || BIC_IS_ENABLED(BIC_IPC) || need_soft_c1;
+
+ if (BIC_IS_ENABLED(BIC_SMI))
+ need_smi = true;
+
+ /* Enable needed counters */
+ msr_counter_arch_infos[MSR_ARCH_INFO_APERF_INDEX].needed = need_amperf;
+ msr_counter_arch_infos[MSR_ARCH_INFO_MPERF_INDEX].needed = need_amperf;
+ msr_counter_arch_infos[MSR_ARCH_INFO_SMI_INDEX].needed = need_smi;
+
+ msr_perf_init_();
+
+ const bool has_amperf = has_amperf_access();
+ const bool has_smi = msr_counter_arch_infos[MSR_ARCH_INFO_SMI_INDEX].present;
+
+ has_aperf_access = has_amperf;
+
+ if (has_amperf) {
+ BIC_PRESENT(BIC_Avg_MHz);
+ BIC_PRESENT(BIC_Busy);
+ BIC_PRESENT(BIC_Bzy_MHz);
+ BIC_PRESENT(BIC_SMI);
+ }
+
+ if (has_smi)
+ BIC_PRESENT(BIC_SMI);
+}
+
+void cstate_perf_init_(bool soft_c1)
+{
+ bool has_counter;
+ bool *cores_visited = NULL, *pkg_visited = NULL;
+ const int cores_visited_elems = topo.max_core_id + 1;
+ const int pkg_visited_elems = topo.max_package_id + 1;
+ const int cci_num = topo.max_cpu_num + 1;
+
+ ccstate_counter_info = calloc(cci_num, sizeof(*ccstate_counter_info));
+ if (!ccstate_counter_info)
+ err(1, "calloc ccstate_counter_arch_info");
+ ccstate_counter_info_size = cci_num;
+
+ cores_visited = calloc(cores_visited_elems, sizeof(*cores_visited));
+ if (!cores_visited)
+ err(1, "calloc cores_visited");
+
+ pkg_visited = calloc(pkg_visited_elems, sizeof(*pkg_visited));
+ if (!pkg_visited)
+ err(1, "calloc pkg_visited");
+
+ /* Initialize cstate_counter_info_percpu */
+ for (int cpu = 0; cpu < cci_num; ++cpu) {
+ ccstate_counter_info[cpu].fd_perf_core = -1;
+ ccstate_counter_info[cpu].fd_perf_pkg = -1;
+ }
+
+ for (int cidx = 0; cidx < NUM_CSTATE_COUNTERS; ++cidx) {
+ has_counter = false;
+ memset(cores_visited, 0, cores_visited_elems * sizeof(*cores_visited));
+ memset(pkg_visited, 0, pkg_visited_elems * sizeof(*pkg_visited));
+
+ const struct cstate_counter_arch_info *cai = &ccstate_counter_arch_infos[cidx];
+
+ for (int cpu = 0; cpu < cci_num; ++cpu) {
+
+ struct cstate_counter_info_t *const cci = &ccstate_counter_info[cpu];
+
+ if (cpu_is_not_allowed(cpu))
+ continue;
+
+ const int core_id = cpus[cpu].physical_core_id;
+ const int pkg_id = cpus[cpu].physical_package_id;
+
+ assert(core_id < cores_visited_elems);
+ assert(pkg_id < pkg_visited_elems);
+
+ const bool per_thread = cai->flags & CSTATE_COUNTER_FLAG_COLLECT_PER_THREAD;
+ const bool per_core = cai->flags & CSTATE_COUNTER_FLAG_COLLECT_PER_CORE;
+
+ if (!per_thread && cores_visited[core_id])
+ continue;
- if (platform->supported_cstates & CC3)
- BIC_PRESENT(BIC_CPU_c3);
+ if (!per_core && pkg_visited[pkg_id])
+ continue;
- if (platform->supported_cstates & CC6)
- BIC_PRESENT(BIC_CPU_c6);
+ const bool counter_needed = BIC_IS_ENABLED(cai->bic) ||
+ (soft_c1 && (cai->flags & CSTATE_COUNTER_FLAG_SOFT_C1_DEPENDENCY));
+ const bool counter_supported = (platform->supported_cstates & cai->feature_mask);
- if (platform->supported_cstates & CC7)
- BIC_PRESENT(BIC_CPU_c7);
+ if (counter_needed && counter_supported) {
+ /* Use perf API for this counter */
+ if (!no_perf && cai->perf_name && add_cstate_perf_counter(cpu, cci, cai) != -1) {
- if (platform->supported_cstates & PC2 && (pkg_cstate_limit >= PCL__2))
- BIC_PRESENT(BIC_Pkgpc2);
+ cci->source[cai->rci_index] = COUNTER_SOURCE_PERF;
- if (platform->supported_cstates & PC3 && (pkg_cstate_limit >= PCL__3))
- BIC_PRESENT(BIC_Pkgpc3);
+ /* User MSR for this counter */
+ } else if (!no_msr && cai->msr && pkg_cstate_limit >= cai->pkg_cstate_limit
+ && probe_rapl_msr(cpu, cai->msr, cai->rci_index) == 0) {
+ cci->source[cai->rci_index] = COUNTER_SOURCE_MSR;
+ cci->msr[cai->rci_index] = cai->msr;
+ }
+ }
+
+ if (cci->source[cai->rci_index] != COUNTER_SOURCE_NONE) {
+ has_counter = true;
+ cores_visited[core_id] = true;
+ pkg_visited[pkg_id] = true;
+ }
+ }
- if (platform->supported_cstates & PC6 && (pkg_cstate_limit >= PCL__6))
- BIC_PRESENT(BIC_Pkgpc6);
+ /* If any CPU has access to the counter, make it present */
+ if (has_counter)
+ BIC_PRESENT(cai->bic);
+ }
+
+ free(cores_visited);
+ free(pkg_visited);
+}
- if (platform->supported_cstates & PC7 && (pkg_cstate_limit >= PCL__7))
- BIC_PRESENT(BIC_Pkgpc7);
+void cstate_perf_init(void)
+{
+ /*
+ * If we don't have a C1 residency MSR, we calculate it "in software",
+ * but we need APERF, MPERF too.
+ */
+ const bool soft_c1 = !platform->has_msr_core_c1_res && has_amperf_access()
+ && platform->supported_cstates & CC1;
- if (platform->supported_cstates & PC8 && (pkg_cstate_limit >= PCL__8))
- BIC_PRESENT(BIC_Pkgpc8);
+ if (soft_c1)
+ BIC_PRESENT(BIC_CPU_c1);
- if (platform->supported_cstates & PC9 && (pkg_cstate_limit >= PCL__9))
- BIC_PRESENT(BIC_Pkgpc9);
+ cstate_perf_init_(soft_c1);
+}
- if (platform->supported_cstates & PC10 && (pkg_cstate_limit >= PCL_10))
- BIC_PRESENT(BIC_Pkgpc10);
+void probe_cstates(void)
+{
+ probe_cst_limit();
if (platform->has_msr_module_c6_res_ms)
BIC_PRESENT(BIC_Mod_c6);
@@ -6819,12 +8372,6 @@ void process_cpuid()
__cpuid(0x6, eax, ebx, ecx, edx);
has_aperf = ecx & (1 << 0);
- if (has_aperf && has_amperf_access()) {
- BIC_PRESENT(BIC_Avg_MHz);
- BIC_PRESENT(BIC_Busy);
- BIC_PRESENT(BIC_Bzy_MHz);
- BIC_PRESENT(BIC_IPC);
- }
do_dts = eax & (1 << 0);
if (do_dts)
BIC_PRESENT(BIC_CoreTmp);
@@ -6924,9 +8471,31 @@ void process_cpuid()
aperf_mperf_multiplier = platform->need_perf_multiplier ? 1024 : 1;
BIC_PRESENT(BIC_IRQ);
+ BIC_PRESENT(BIC_NMI);
BIC_PRESENT(BIC_TSC_MHz);
}
+static void counter_info_init(void)
+{
+ for (int i = 0; i < NUM_CSTATE_COUNTERS; ++i) {
+ struct cstate_counter_arch_info *const cai = &ccstate_counter_arch_infos[i];
+
+ if (platform->has_msr_knl_core_c6_residency && cai->msr == MSR_CORE_C6_RESIDENCY)
+ cai->msr = MSR_KNL_CORE_C6_RESIDENCY;
+
+ if (!platform->has_msr_core_c1_res && cai->msr == MSR_CORE_C1_RES)
+ cai->msr = 0;
+
+ if (platform->has_msr_atom_pkg_c6_residency && cai->msr == MSR_PKG_C6_RESIDENCY)
+ cai->msr = MSR_ATOM_PKG_C6_RESIDENCY;
+ }
+
+ for (int i = 0; i < NUM_MSR_COUNTERS; ++i) {
+ msr_counter_arch_infos[i].present = false;
+ msr_counter_arch_infos[i].needed = false;
+ }
+}
+
void probe_pm_features(void)
{
probe_pstates();
@@ -6962,19 +8531,45 @@ int dir_filter(const struct dirent *dirp)
return 0;
}
+char *possible_file = "/sys/devices/system/cpu/possible";
+char possible_buf[1024];
+
+int initialize_cpu_possible_set(void)
+{
+ FILE *fp;
+
+ fp = fopen(possible_file, "r");
+ if (!fp) {
+ warn("open %s", possible_file);
+ return -1;
+ }
+ if (fread(possible_buf, sizeof(char), 1024, fp) == 0) {
+ warn("read %s", possible_file);
+ goto err;
+ }
+ if (parse_cpu_str(possible_buf, cpu_possible_set, cpu_possible_setsize)) {
+ warnx("%s: cpu str malformat %s\n", possible_file, cpu_effective_str);
+ goto err;
+ }
+ return 0;
+
+err:
+ fclose(fp);
+ return -1;
+}
+
void topology_probe(bool startup)
{
int i;
int max_core_id = 0;
int max_package_id = 0;
- int max_die_id = 0;
int max_siblings = 0;
/* Initialize num_cpus, max_cpu_num */
set_max_cpu_num();
topo.num_cpus = 0;
for_all_proc_cpus(count_cpus);
- if (!summary_only && topo.num_cpus > 1)
+ if (!summary_only)
BIC_PRESENT(BIC_CPU);
if (debug > 1)
@@ -6995,6 +8590,16 @@ void topology_probe(bool startup)
for_all_proc_cpus(mark_cpu_present);
/*
+ * Allocate and initialize cpu_possible_set
+ */
+ cpu_possible_set = CPU_ALLOC((topo.max_cpu_num + 1));
+ if (cpu_possible_set == NULL)
+ err(3, "CPU_ALLOC");
+ cpu_possible_setsize = CPU_ALLOC_SIZE((topo.max_cpu_num + 1));
+ CPU_ZERO_S(cpu_possible_setsize, cpu_possible_set);
+ initialize_cpu_possible_set();
+
+ /*
* Allocate and initialize cpu_effective_set
*/
cpu_effective_set = CPU_ALLOC((topo.max_cpu_num + 1));
@@ -7062,6 +8667,8 @@ void topology_probe(bool startup)
for_all_proc_cpus(init_thread_id);
+ for_all_proc_cpus(set_cpu_hybrid_type);
+
/*
* For online cpus
* find max_core_id, max_package_id
@@ -7084,8 +8691,8 @@ void topology_probe(bool startup)
/* get die information */
cpus[i].die_id = get_die_id(i);
- if (cpus[i].die_id > max_die_id)
- max_die_id = cpus[i].die_id;
+ if (cpus[i].die_id > topo.max_die_id)
+ topo.max_die_id = cpus[i].die_id;
/* get numa node information */
cpus[i].physical_node_id = get_physical_node_id(&cpus[i]);
@@ -7104,16 +8711,18 @@ void topology_probe(bool startup)
if (cpus[i].thread_id == 0)
topo.num_cores++;
}
+ topo.max_core_id = max_core_id;
+ topo.max_package_id = max_package_id;
topo.cores_per_node = max_core_id + 1;
if (debug > 1)
fprintf(outf, "max_core_id %d, sizing for %d cores per package\n", max_core_id, topo.cores_per_node);
- if (!summary_only && topo.cores_per_node > 1)
+ if (!summary_only)
BIC_PRESENT(BIC_Core);
- topo.num_die = max_die_id + 1;
+ topo.num_die = topo.max_die_id + 1;
if (debug > 1)
- fprintf(outf, "max_die_id %d, sizing for %d die\n", max_die_id, topo.num_die);
+ fprintf(outf, "max_die_id %d, sizing for %d die\n", topo.max_die_id, topo.num_die);
if (!summary_only && topo.num_die > 1)
BIC_PRESENT(BIC_Die);
@@ -7250,7 +8859,11 @@ void allocate_irq_buffers(void)
irqs_per_cpu = calloc(topo.max_cpu_num + 1, sizeof(int));
if (irqs_per_cpu == NULL)
- err(-1, "calloc %d", topo.max_cpu_num + 1);
+ err(-1, "calloc %d IRQ", topo.max_cpu_num + 1);
+
+ nmi_per_cpu = calloc(topo.max_cpu_num + 1, sizeof(int));
+ if (nmi_per_cpu == NULL)
+ err(-1, "calloc %d NMI", topo.max_cpu_num + 1);
}
int update_topo(struct thread_data *t, struct core_data *c, struct pkg_data *p)
@@ -7299,100 +8912,568 @@ void set_base_cpu(void)
err(-ENODEV, "No valid cpus found");
}
-static void set_amperf_source(void)
+bool has_added_counters(void)
{
- amperf_source = AMPERF_SOURCE_PERF;
+ /*
+ * It only makes sense to call this after the command line is parsed,
+ * otherwise sys structure is not populated.
+ */
- const bool aperf_required = is_aperf_access_required();
+ return sys.added_core_counters | sys.added_thread_counters | sys.added_package_counters;
+}
- if (no_perf || !aperf_required || !has_amperf_access_via_perf())
- amperf_source = AMPERF_SOURCE_MSR;
+void check_msr_access(void)
+{
+ check_dev_msr();
+ check_msr_permission();
- if (quiet || !debug)
- return;
+ if (no_msr)
+ bic_disable_msr_access();
+}
- fprintf(outf, "aperf/mperf source preference: %s\n", amperf_source == AMPERF_SOURCE_MSR ? "msr" : "perf");
+void check_perf_access(void)
+{
+ if (no_perf || !BIC_IS_ENABLED(BIC_IPC) || !has_instr_count_access())
+ bic_enabled &= ~BIC_IPC;
}
-bool has_added_counters(void)
+bool perf_has_hybrid_devices(void)
{
/*
- * It only makes sense to call this after the command line is parsed,
- * otherwise sys structure is not populated.
+ * 0: unknown
+ * 1: has separate perf device for p and e core
+ * -1: doesn't have separate perf device for p and e core
*/
+ static int cached;
- return sys.added_core_counters | sys.added_thread_counters | sys.added_package_counters;
+ if (cached > 0)
+ return true;
+
+ if (cached < 0)
+ return false;
+
+ if (access("/sys/bus/event_source/devices/cpu_core", F_OK)) {
+ cached = -1;
+ return false;
+ }
+
+ if (access("/sys/bus/event_source/devices/cpu_atom", F_OK)) {
+ cached = -1;
+ return false;
+ }
+
+ cached = 1;
+ return true;
}
-bool is_msr_access_required(void)
+int added_perf_counters_init_(struct perf_counter_info *pinfo)
{
- if (no_msr)
- return false;
+ size_t num_domains = 0;
+ unsigned int next_domain;
+ bool *domain_visited;
+ unsigned int perf_type, perf_config;
+ double perf_scale;
+ int fd_perf;
- if (has_added_counters())
- return true;
+ if (!pinfo)
+ return 0;
+
+ const size_t max_num_domains = MAX(topo.max_cpu_num + 1, MAX(topo.max_core_id + 1, topo.max_package_id + 1));
+
+ domain_visited = calloc(max_num_domains, sizeof(*domain_visited));
+
+ while (pinfo) {
+ switch (pinfo->scope) {
+ case SCOPE_CPU:
+ num_domains = topo.max_cpu_num + 1;
+ break;
+
+ case SCOPE_CORE:
+ num_domains = topo.max_core_id + 1;
+ break;
+
+ case SCOPE_PACKAGE:
+ num_domains = topo.max_package_id + 1;
+ break;
+ }
- return BIC_IS_ENABLED(BIC_SMI)
- || BIC_IS_ENABLED(BIC_CPU_c1)
- || BIC_IS_ENABLED(BIC_CPU_c3)
- || BIC_IS_ENABLED(BIC_CPU_c6)
- || BIC_IS_ENABLED(BIC_CPU_c7)
- || BIC_IS_ENABLED(BIC_Mod_c6)
- || BIC_IS_ENABLED(BIC_CoreTmp)
- || BIC_IS_ENABLED(BIC_Totl_c0)
- || BIC_IS_ENABLED(BIC_Any_c0)
- || BIC_IS_ENABLED(BIC_GFX_c0)
- || BIC_IS_ENABLED(BIC_CPUGFX)
- || BIC_IS_ENABLED(BIC_Pkgpc3)
- || BIC_IS_ENABLED(BIC_Pkgpc6)
- || BIC_IS_ENABLED(BIC_Pkgpc2)
- || BIC_IS_ENABLED(BIC_Pkgpc7)
- || BIC_IS_ENABLED(BIC_Pkgpc8)
- || BIC_IS_ENABLED(BIC_Pkgpc9)
- || BIC_IS_ENABLED(BIC_Pkgpc10)
- /* TODO: Multiplex access with perf */
- || BIC_IS_ENABLED(BIC_CorWatt)
- || BIC_IS_ENABLED(BIC_Cor_J)
- || BIC_IS_ENABLED(BIC_PkgWatt)
- || BIC_IS_ENABLED(BIC_CorWatt)
- || BIC_IS_ENABLED(BIC_GFXWatt)
- || BIC_IS_ENABLED(BIC_RAMWatt)
- || BIC_IS_ENABLED(BIC_Pkg_J)
- || BIC_IS_ENABLED(BIC_Cor_J)
- || BIC_IS_ENABLED(BIC_GFX_J)
- || BIC_IS_ENABLED(BIC_RAM_J)
- || BIC_IS_ENABLED(BIC_PKG__)
- || BIC_IS_ENABLED(BIC_RAM__)
- || BIC_IS_ENABLED(BIC_PkgTmp)
- || (is_aperf_access_required() && !has_amperf_access_via_perf());
+ /* Allocate buffer for file descriptor for each domain. */
+ pinfo->fd_perf_per_domain = calloc(num_domains, sizeof(*pinfo->fd_perf_per_domain));
+ if (!pinfo->fd_perf_per_domain)
+ errx(1, "%s: alloc %s", __func__, "fd_perf_per_domain");
+
+ for (size_t i = 0; i < num_domains; ++i)
+ pinfo->fd_perf_per_domain[i] = -1;
+
+ pinfo->num_domains = num_domains;
+ pinfo->scale = 1.0;
+
+ memset(domain_visited, 0, max_num_domains * sizeof(*domain_visited));
+
+ for (int cpu = 0; cpu < topo.max_cpu_num + 1; ++cpu) {
+
+ next_domain = cpu_to_domain(pinfo, cpu);
+
+ assert(next_domain < num_domains);
+
+ if (cpu_is_not_allowed(cpu))
+ continue;
+
+ if (domain_visited[next_domain])
+ continue;
+
+ /*
+ * Intel hybrid platforms expose different perf devices for P and E cores.
+ * Instead of one, "/sys/bus/event_source/devices/cpu" device, there are
+ * "/sys/bus/event_source/devices/{cpu_core,cpu_atom}".
+ *
+ * This makes it more complicated to the user, because most of the counters
+ * are available on both and have to be handled manually, otherwise.
+ *
+ * Code below, allow user to use the old "cpu" name, which is translated accordingly.
+ */
+ const char *perf_device = pinfo->device;
+
+ if (strcmp(perf_device, "cpu") == 0 && perf_has_hybrid_devices()) {
+ switch (cpus[cpu].type) {
+ case INTEL_PCORE_TYPE:
+ perf_device = "cpu_core";
+ break;
+
+ case INTEL_ECORE_TYPE:
+ perf_device = "cpu_atom";
+ break;
+
+ default: /* Don't change, we will probably fail and report a problem soon. */
+ break;
+ }
+ }
+
+ perf_type = read_perf_type(perf_device);
+ if (perf_type == (unsigned int)-1) {
+ warnx("%s: perf/%s/%s: failed to read %s",
+ __func__, perf_device, pinfo->event, "type");
+ continue;
+ }
+
+ perf_config = read_perf_config(perf_device, pinfo->event);
+ if (perf_config == (unsigned int)-1) {
+ warnx("%s: perf/%s/%s: failed to read %s",
+ __func__, perf_device, pinfo->event, "config");
+ continue;
+ }
+
+ /* Scale is not required, some counters just don't have it. */
+ perf_scale = read_perf_scale(perf_device, pinfo->event);
+ if (perf_scale == 0.0)
+ perf_scale = 1.0;
+
+ fd_perf = open_perf_counter(cpu, perf_type, perf_config, -1, 0);
+ if (fd_perf == -1) {
+ warnx("%s: perf/%s/%s: failed to open counter on cpu%d",
+ __func__, perf_device, pinfo->event, cpu);
+ continue;
+ }
+
+ domain_visited[next_domain] = 1;
+ pinfo->fd_perf_per_domain[next_domain] = fd_perf;
+ pinfo->scale = perf_scale;
+
+ if (debug)
+ fprintf(stderr, "Add perf/%s/%s cpu%d: %d\n",
+ perf_device, pinfo->event, cpu, pinfo->fd_perf_per_domain[next_domain]);
+ }
+
+ pinfo = pinfo->next;
+ }
+
+ free(domain_visited);
+
+ return 0;
}
-void check_msr_access(void)
+void added_perf_counters_init(void)
{
- if (!is_msr_access_required())
- no_msr = 1;
+ if (added_perf_counters_init_(sys.perf_tp))
+ errx(1, "%s: %s", __func__, "thread");
- check_dev_msr();
- check_msr_permission();
+ if (added_perf_counters_init_(sys.perf_cp))
+ errx(1, "%s: %s", __func__, "core");
- if (no_msr)
- bic_disable_msr_access();
+ if (added_perf_counters_init_(sys.perf_pp))
+ errx(1, "%s: %s", __func__, "package");
}
-void check_perf_access(void)
+int parse_telem_info_file(int fd_dir, const char *info_filename, const char *format, unsigned long *output)
{
- const bool intrcount_required = BIC_IS_ENABLED(BIC_IPC);
+ int fd_telem_info;
+ FILE *file_telem_info;
+ unsigned long value;
- if (no_perf || !intrcount_required || !has_instr_count_access())
- bic_enabled &= ~BIC_IPC;
+ fd_telem_info = openat(fd_dir, info_filename, O_RDONLY);
+ if (fd_telem_info == -1)
+ return -1;
- const bool aperf_required = is_aperf_access_required();
+ file_telem_info = fdopen(fd_telem_info, "r");
+ if (file_telem_info == NULL) {
+ close(fd_telem_info);
+ return -1;
+ }
- if (!aperf_required || !has_amperf_access()) {
- bic_enabled &= ~BIC_Avg_MHz;
- bic_enabled &= ~BIC_Busy;
- bic_enabled &= ~BIC_Bzy_MHz;
- bic_enabled &= ~BIC_IPC;
+ if (fscanf(file_telem_info, format, &value) != 1) {
+ fclose(file_telem_info);
+ return -1;
+ }
+
+ fclose(file_telem_info);
+
+ *output = value;
+
+ return 0;
+}
+
+struct pmt_mmio *pmt_mmio_open(unsigned int target_guid)
+{
+ struct pmt_diriter_t pmt_iter;
+ const struct dirent *entry;
+ struct stat st;
+ int fd_telem_dir, fd_pmt;
+ unsigned long guid, size, offset;
+ size_t mmap_size;
+ void *mmio;
+ struct pmt_mmio *head = NULL, *last = NULL;
+ struct pmt_mmio *new_pmt = NULL;
+
+ if (stat(SYSFS_TELEM_PATH, &st) == -1)
+ return NULL;
+
+ pmt_diriter_init(&pmt_iter);
+ entry = pmt_diriter_begin(&pmt_iter, SYSFS_TELEM_PATH);
+ if (!entry) {
+ pmt_diriter_remove(&pmt_iter);
+ return NULL;
+ }
+
+ for ( ; entry != NULL; entry = pmt_diriter_next(&pmt_iter)) {
+ if (fstatat(dirfd(pmt_iter.dir), entry->d_name, &st, 0) == -1)
+ break;
+
+ if (!S_ISDIR(st.st_mode))
+ continue;
+
+ fd_telem_dir = openat(dirfd(pmt_iter.dir), entry->d_name, O_RDONLY);
+ if (fd_telem_dir == -1)
+ break;
+
+ if (parse_telem_info_file(fd_telem_dir, "guid", "%lx", &guid)) {
+ close(fd_telem_dir);
+ break;
+ }
+
+ if (parse_telem_info_file(fd_telem_dir, "size", "%lu", &size)) {
+ close(fd_telem_dir);
+ break;
+ }
+
+ if (guid != target_guid) {
+ close(fd_telem_dir);
+ continue;
+ }
+
+ if (parse_telem_info_file(fd_telem_dir, "offset", "%lu", &offset)) {
+ close(fd_telem_dir);
+ break;
+ }
+
+ assert(offset == 0);
+
+ fd_pmt = openat(fd_telem_dir, "telem", O_RDONLY);
+ if (fd_pmt == -1)
+ goto loop_cleanup_and_break;
+
+ mmap_size = ROUND_UP_TO_PAGE_SIZE(size);
+ mmio = mmap(0, mmap_size, PROT_READ, MAP_SHARED, fd_pmt, 0);
+ if (mmio != MAP_FAILED) {
+ if (debug)
+ fprintf(stderr, "%s: 0x%lx mmaped at: %p\n", __func__, guid, mmio);
+
+ new_pmt = calloc(1, sizeof(*new_pmt));
+
+ if (!new_pmt) {
+ fprintf(stderr, "%s: Failed to allocate pmt_mmio\n", __func__);
+ exit(1);
+ }
+
+ /*
+ * Create linked list of mmaped regions,
+ * but preserve the ordering from sysfs.
+ * Ordering is important for the user to
+ * use the seq=%u parameter when adding a counter.
+ */
+ new_pmt->guid = guid;
+ new_pmt->mmio_base = mmio;
+ new_pmt->pmt_offset = offset;
+ new_pmt->size = size;
+ new_pmt->next = pmt_mmios;
+
+ if (last)
+ last->next = new_pmt;
+ else
+ head = new_pmt;
+
+ last = new_pmt;
+ }
+
+loop_cleanup_and_break:
+ close(fd_pmt);
+ close(fd_telem_dir);
+ }
+
+ pmt_diriter_remove(&pmt_iter);
+
+ /*
+ * If we found something, stick just
+ * created linked list to the front.
+ */
+ if (head)
+ pmt_mmios = head;
+
+ return head;
+}
+
+struct pmt_mmio *pmt_mmio_find(unsigned int guid)
+{
+ struct pmt_mmio *pmmio = pmt_mmios;
+
+ while (pmmio) {
+ if (pmmio->guid == guid)
+ return pmmio;
+
+ pmmio = pmmio->next;
+ }
+
+ return NULL;
+}
+
+void *pmt_get_counter_pointer(struct pmt_mmio *pmmio, unsigned long counter_offset)
+{
+ char *ret;
+
+ /* Get base of mmaped PMT file. */
+ ret = (char *)pmmio->mmio_base;
+
+ /*
+ * Apply PMT MMIO offset to obtain beginning of the mmaped telemetry data.
+ * It's not guaranteed that the mmaped memory begins with the telemetry data
+ * - we might have to apply the offset first.
+ */
+ ret += pmmio->pmt_offset;
+
+ /* Apply the counter offset to get the address to the mmaped counter. */
+ ret += counter_offset;
+
+ return ret;
+}
+
+struct pmt_mmio *pmt_add_guid(unsigned int guid, unsigned int seq)
+{
+ struct pmt_mmio *ret;
+
+ ret = pmt_mmio_find(guid);
+ if (!ret)
+ ret = pmt_mmio_open(guid);
+
+ while (ret && seq) {
+ ret = ret->next;
+ --seq;
+ }
+
+ return ret;
+}
+
+enum pmt_open_mode {
+ PMT_OPEN_TRY, /* Open failure is not an error. */
+ PMT_OPEN_REQUIRED, /* Open failure is a fatal error. */
+};
+
+struct pmt_counter *pmt_find_counter(struct pmt_counter *pcounter, const char *name)
+{
+ while (pcounter) {
+ if (strcmp(pcounter->name, name) == 0)
+ break;
+
+ pcounter = pcounter->next;
+ }
+
+ return pcounter;
+}
+
+struct pmt_counter **pmt_get_scope_root(enum counter_scope scope)
+{
+ switch (scope) {
+ case SCOPE_CPU:
+ return &sys.pmt_tp;
+ case SCOPE_CORE:
+ return &sys.pmt_cp;
+ case SCOPE_PACKAGE:
+ return &sys.pmt_pp;
+ }
+
+ __builtin_unreachable();
+}
+
+void pmt_counter_add_domain(struct pmt_counter *pcounter, unsigned long *pmmio, unsigned int domain_id)
+{
+ /* Make sure the new domain fits. */
+ if (domain_id >= pcounter->num_domains)
+ pmt_counter_resize(pcounter, domain_id + 1);
+
+ assert(pcounter->domains);
+ assert(domain_id < pcounter->num_domains);
+
+ pcounter->domains[domain_id].pcounter = pmmio;
+}
+
+int pmt_add_counter(unsigned int guid, unsigned int seq, const char *name, enum pmt_datatype type,
+ unsigned int lsb, unsigned int msb, unsigned int offset, enum counter_scope scope,
+ enum counter_format format, unsigned int domain_id, enum pmt_open_mode mode)
+{
+ struct pmt_mmio *mmio;
+ struct pmt_counter *pcounter;
+ struct pmt_counter **const pmt_root = pmt_get_scope_root(scope);
+ bool new_counter = false;
+ int conflict = 0;
+
+ if (lsb > msb) {
+ fprintf(stderr, "%s: %s: `%s` must be satisfied\n", __func__, "lsb <= msb", name);
+ exit(1);
+ }
+
+ if (msb >= 64) {
+ fprintf(stderr, "%s: %s: `%s` must be satisfied\n", __func__, "msb < 64", name);
+ exit(1);
+ }
+
+ mmio = pmt_add_guid(guid, seq);
+ if (!mmio) {
+ if (mode != PMT_OPEN_TRY) {
+ fprintf(stderr, "%s: failed to map PMT MMIO for guid %x, seq %u\n", __func__, guid, seq);
+ exit(1);
+ }
+
+ return 1;
+ }
+
+ if (offset >= mmio->size) {
+ if (mode != PMT_OPEN_TRY) {
+ fprintf(stderr, "%s: offset %u outside of PMT MMIO size %u\n", __func__, offset, mmio->size);
+ exit(1);
+ }
+
+ return 1;
+ }
+
+ pcounter = pmt_find_counter(*pmt_root, name);
+ if (!pcounter) {
+ pcounter = calloc(1, sizeof(*pcounter));
+ new_counter = true;
+ }
+
+ if (new_counter) {
+ strncpy(pcounter->name, name, ARRAY_SIZE(pcounter->name) - 1);
+ pcounter->type = type;
+ pcounter->scope = scope;
+ pcounter->lsb = lsb;
+ pcounter->msb = msb;
+ pcounter->format = format;
+ } else {
+ conflict += pcounter->type != type;
+ conflict += pcounter->scope != scope;
+ conflict += pcounter->lsb != lsb;
+ conflict += pcounter->msb != msb;
+ conflict += pcounter->format != format;
+ }
+
+ if (conflict) {
+ fprintf(stderr, "%s: conflicting parameters for the PMT counter with the same name %s\n",
+ __func__, name);
+ exit(1);
+ }
+
+ pmt_counter_add_domain(pcounter, pmt_get_counter_pointer(mmio, offset), domain_id);
+
+ if (new_counter) {
+ pcounter->next = *pmt_root;
+ *pmt_root = pcounter;
+ }
+
+ return 0;
+}
+
+void pmt_init(void)
+{
+ int cpu_num;
+ unsigned long seq, offset, mod_num;
+
+ if (BIC_IS_ENABLED(BIC_Diec6)) {
+ pmt_add_counter(PMT_MTL_DC6_GUID, PMT_MTL_DC6_SEQ, "Die%c6", PMT_TYPE_XTAL_TIME,
+ PMT_COUNTER_MTL_DC6_LSB, PMT_COUNTER_MTL_DC6_MSB, PMT_COUNTER_MTL_DC6_OFFSET,
+ SCOPE_PACKAGE, FORMAT_DELTA, 0, PMT_OPEN_TRY);
+ }
+
+ if (BIC_IS_ENABLED(BIC_CPU_c1e)) {
+ seq = 0;
+ offset = PMT_COUNTER_CWF_MC1E_OFFSET_BASE;
+ mod_num = 0; /* Relative module number for current PMT file. */
+
+ /* Open the counter for each CPU. */
+ for (cpu_num = 0; cpu_num < topo.max_cpu_num;) {
+
+ if (cpu_is_not_allowed(cpu_num))
+ goto next_loop_iter;
+
+ /*
+ * Set the scope to CPU, even though CWF report the counter per module.
+ * CPUs inside the same module will read from the same location, instead of reporting zeros.
+ *
+ * CWF with newer firmware might require a PMT_TYPE_XTAL_TIME intead of PMT_TYPE_TCORE_CLOCK.
+ */
+ pmt_add_counter(PMT_CWF_MC1E_GUID, seq, "CPU%c1e", PMT_TYPE_TCORE_CLOCK,
+ PMT_COUNTER_CWF_MC1E_LSB, PMT_COUNTER_CWF_MC1E_MSB, offset, SCOPE_CPU,
+ FORMAT_DELTA, cpu_num, PMT_OPEN_TRY);
+
+ /*
+ * Rather complex logic for each time we go to the next loop iteration,
+ * so keep it as a label.
+ */
+next_loop_iter:
+ /*
+ * Advance the cpu number and check if we should also advance offset to
+ * the next counter inside the PMT file.
+ *
+ * On Clearwater Forest platform, the counter is reported per module,
+ * so open the same counter for all of the CPUs inside the module.
+ * That way, reported table show the correct value for all of the CPUs inside the module,
+ * instead of zeros.
+ */
+ ++cpu_num;
+ if (cpu_num % PMT_COUNTER_CWF_CPUS_PER_MODULE == 0) {
+ offset += PMT_COUNTER_CWF_MC1E_OFFSET_INCREMENT;
+ ++mod_num;
+ }
+
+ /*
+ * There are PMT_COUNTER_CWF_MC1E_NUM_MODULES_PER_FILE in each PMT file.
+ *
+ * If that number is reached, seq must be incremented to advance to the next file in a sequence.
+ * Offset inside that file and a module counter has to be reset.
+ */
+ if (mod_num == PMT_COUNTER_CWF_MC1E_NUM_MODULES_PER_FILE) {
+ ++seq;
+ offset = PMT_COUNTER_CWF_MC1E_OFFSET_BASE;
+ mod_num = 0;
+ }
+ }
}
}
@@ -7403,16 +9484,20 @@ void turbostat_init()
check_msr_access();
check_perf_access();
process_cpuid();
+ counter_info_init();
probe_pm_features();
- set_amperf_source();
+ msr_perf_init();
linux_perf_init();
rapl_perf_init();
+ cstate_perf_init();
+ added_perf_counters_init();
+ pmt_init();
for_all_cpus(get_cpu_type, ODD_COUNTERS);
for_all_cpus(get_cpu_type, EVEN_COUNTERS);
- if (DO_BIC(BIC_IPC))
- (void)get_instr_count_fd(base_cpu);
+ if (BIC_IS_ENABLED(BIC_IPC) && has_aperf_access && get_instr_count_fd(base_cpu) != -1)
+ BIC_PRESENT(BIC_IPC);
/*
* If TSC tweak is needed, but couldn't get it,
@@ -7424,6 +9509,18 @@ void turbostat_init()
}
}
+void affinitize_child(void)
+{
+ /* Prefer cpu_possible_set, if available */
+ if (sched_setaffinity(0, cpu_possible_setsize, cpu_possible_set)) {
+ warn("sched_setaffinity cpu_possible_set");
+
+ /* Otherwise, allow child to run on same cpu set as turbostat */
+ if (sched_setaffinity(0, cpu_allowed_setsize, cpu_allowed_set))
+ warn("sched_setaffinity cpu_allowed_set");
+ }
+}
+
int fork_it(char **argv)
{
pid_t child_pid;
@@ -7439,6 +9536,7 @@ int fork_it(char **argv)
child_pid = fork();
if (!child_pid) {
/* child */
+ affinitize_child();
execvp(argv[0], argv);
err(errno, "exec %s", argv[0]);
} else {
@@ -7465,10 +9563,9 @@ int fork_it(char **argv)
timersub(&tv_odd, &tv_even, &tv_delta);
if (for_all_cpus_2(delta_cpu, ODD_COUNTERS, EVEN_COUNTERS))
fprintf(outf, "%s: Counter reset detected\n", progname);
- else {
- compute_average(EVEN_COUNTERS);
- format_all_counters(EVEN_COUNTERS);
- }
+
+ compute_average(EVEN_COUNTERS);
+ format_all_counters(EVEN_COUNTERS);
fprintf(outf, "%.6f sec\n", tv_delta.tv_sec + tv_delta.tv_usec / 1000000.0);
@@ -7497,7 +9594,7 @@ int get_and_dump_counters(void)
void print_version()
{
- fprintf(outf, "turbostat version 2024.04.08 - Len Brown <lenb@kernel.org>\n");
+ fprintf(outf, "turbostat version 2025.04.06 - Len Brown <lenb@kernel.org>\n");
}
#define COMMAND_LINE_SIZE 2048
@@ -7523,71 +9620,220 @@ void print_bootcmd(void)
fclose(fp);
}
+struct msr_counter *find_msrp_by_name(struct msr_counter *head, char *name)
+{
+ struct msr_counter *mp;
+
+ for (mp = head; mp; mp = mp->next) {
+ if (debug)
+ fprintf(stderr, "%s: %s %s\n", __func__, name, mp->name);
+ if (!strcmp(name, mp->name))
+ return mp;
+ }
+ return NULL;
+}
+
int add_counter(unsigned int msr_num, char *path, char *name,
unsigned int width, enum counter_scope scope,
- enum counter_type type, enum counter_format format, int flags)
+ enum counter_type type, enum counter_format format, int flags, int id)
{
struct msr_counter *msrp;
if (no_msr && msr_num)
errx(1, "Requested MSR counter 0x%x, but in --no-msr mode", msr_num);
- msrp = calloc(1, sizeof(struct msr_counter));
+ if (debug)
+ fprintf(stderr, "%s(msr%d, %s, %s, width%d, scope%d, type%d, format%d, flags%x, id%d)\n",
+ __func__, msr_num, path, name, width, scope, type, format, flags, id);
+
+ switch (scope) {
+
+ case SCOPE_CPU:
+ msrp = find_msrp_by_name(sys.tp, name);
+ if (msrp) {
+ if (debug)
+ fprintf(stderr, "%s: %s FOUND\n", __func__, name);
+ break;
+ }
+ if (sys.added_thread_counters++ >= MAX_ADDED_THREAD_COUNTERS) {
+ warnx("ignoring thread counter %s", name);
+ return -1;
+ }
+ break;
+ case SCOPE_CORE:
+ msrp = find_msrp_by_name(sys.cp, name);
+ if (msrp) {
+ if (debug)
+ fprintf(stderr, "%s: %s FOUND\n", __func__, name);
+ break;
+ }
+ if (sys.added_core_counters++ >= MAX_ADDED_CORE_COUNTERS) {
+ warnx("ignoring core counter %s", name);
+ return -1;
+ }
+ break;
+ case SCOPE_PACKAGE:
+ msrp = find_msrp_by_name(sys.pp, name);
+ if (msrp) {
+ if (debug)
+ fprintf(stderr, "%s: %s FOUND\n", __func__, name);
+ break;
+ }
+ if (sys.added_package_counters++ >= MAX_ADDED_PACKAGE_COUNTERS) {
+ warnx("ignoring package counter %s", name);
+ return -1;
+ }
+ break;
+ default:
+ warnx("ignoring counter %s with unknown scope", name);
+ return -1;
+ }
+
if (msrp == NULL) {
- perror("calloc");
- exit(1);
+ msrp = calloc(1, sizeof(struct msr_counter));
+ if (msrp == NULL)
+ err(-1, "calloc msr_counter");
+
+ msrp->msr_num = msr_num;
+ strncpy(msrp->name, name, NAME_BYTES - 1);
+ msrp->width = width;
+ msrp->type = type;
+ msrp->format = format;
+ msrp->flags = flags;
+
+ switch (scope) {
+ case SCOPE_CPU:
+ msrp->next = sys.tp;
+ sys.tp = msrp;
+ break;
+ case SCOPE_CORE:
+ msrp->next = sys.cp;
+ sys.cp = msrp;
+ break;
+ case SCOPE_PACKAGE:
+ msrp->next = sys.pp;
+ sys.pp = msrp;
+ break;
+ }
}
- msrp->msr_num = msr_num;
- strncpy(msrp->name, name, NAME_BYTES - 1);
- if (path)
- strncpy(msrp->path, path, PATH_BYTES - 1);
- msrp->width = width;
- msrp->type = type;
- msrp->format = format;
- msrp->flags = flags;
+ if (path) {
+ struct sysfs_path *sp;
- switch (scope) {
+ sp = calloc(1, sizeof(struct sysfs_path));
+ if (sp == NULL) {
+ perror("calloc");
+ exit(1);
+ }
+ strncpy(sp->path, path, PATH_BYTES - 1);
+ sp->id = id;
+ sp->next = msrp->sp;
+ msrp->sp = sp;
+ }
+
+ return 0;
+}
+
+/*
+ * Initialize the fields used for identifying and opening the counter.
+ *
+ * Defer the initialization of any runtime buffers for actually reading
+ * the counters for when we initialize all perf counters, so we can later
+ * easily call re_initialize().
+ */
+struct perf_counter_info *make_perf_counter_info(const char *perf_device,
+ const char *perf_event,
+ const char *name,
+ unsigned int width,
+ enum counter_scope scope,
+ enum counter_type type, enum counter_format format)
+{
+ struct perf_counter_info *pinfo;
+
+ pinfo = calloc(1, sizeof(*pinfo));
+ if (!pinfo)
+ errx(1, "%s: Failed to allocate %s/%s\n", __func__, perf_device, perf_event);
+
+ strncpy(pinfo->device, perf_device, ARRAY_SIZE(pinfo->device) - 1);
+ strncpy(pinfo->event, perf_event, ARRAY_SIZE(pinfo->event) - 1);
+ strncpy(pinfo->name, name, ARRAY_SIZE(pinfo->name) - 1);
+ pinfo->width = width;
+ pinfo->scope = scope;
+ pinfo->type = type;
+ pinfo->format = format;
+
+ return pinfo;
+}
+
+int add_perf_counter(const char *perf_device, const char *perf_event, const char *name_buffer, unsigned int width,
+ enum counter_scope scope, enum counter_type type, enum counter_format format)
+{
+ struct perf_counter_info *pinfo;
+
+ switch (scope) {
case SCOPE_CPU:
- msrp->next = sys.tp;
- sys.tp = msrp;
- sys.added_thread_counters++;
- if (sys.added_thread_counters > MAX_ADDED_THREAD_COUNTERS) {
- fprintf(stderr, "exceeded max %d added thread counters\n", MAX_ADDED_COUNTERS);
- exit(-1);
+ if (sys.added_thread_perf_counters >= MAX_ADDED_THREAD_COUNTERS) {
+ warnx("ignoring thread counter perf/%s/%s", perf_device, perf_event);
+ return -1;
}
break;
case SCOPE_CORE:
- msrp->next = sys.cp;
- sys.cp = msrp;
- sys.added_core_counters++;
- if (sys.added_core_counters > MAX_ADDED_COUNTERS) {
- fprintf(stderr, "exceeded max %d added core counters\n", MAX_ADDED_COUNTERS);
- exit(-1);
+ if (sys.added_core_perf_counters >= MAX_ADDED_CORE_COUNTERS) {
+ warnx("ignoring core counter perf/%s/%s", perf_device, perf_event);
+ return -1;
}
break;
case SCOPE_PACKAGE:
- msrp->next = sys.pp;
- sys.pp = msrp;
- sys.added_package_counters++;
- if (sys.added_package_counters > MAX_ADDED_COUNTERS) {
- fprintf(stderr, "exceeded max %d added package counters\n", MAX_ADDED_COUNTERS);
- exit(-1);
+ if (sys.added_package_perf_counters >= MAX_ADDED_PACKAGE_COUNTERS) {
+ warnx("ignoring package counter perf/%s/%s", perf_device, perf_event);
+ return -1;
}
break;
}
+ pinfo = make_perf_counter_info(perf_device, perf_event, name_buffer, width, scope, type, format);
+
+ if (!pinfo)
+ return -1;
+
+ switch (scope) {
+ case SCOPE_CPU:
+ pinfo->next = sys.perf_tp;
+ sys.perf_tp = pinfo;
+ ++sys.added_thread_perf_counters;
+ break;
+
+ case SCOPE_CORE:
+ pinfo->next = sys.perf_cp;
+ sys.perf_cp = pinfo;
+ ++sys.added_core_perf_counters;
+ break;
+
+ case SCOPE_PACKAGE:
+ pinfo->next = sys.perf_pp;
+ sys.perf_pp = pinfo;
+ ++sys.added_package_perf_counters;
+ break;
+ }
+
+ // FIXME: we might not have debug here yet
+ if (debug)
+ fprintf(stderr, "%s: %s/%s, name: %s, scope%d\n",
+ __func__, pinfo->device, pinfo->event, pinfo->name, pinfo->scope);
+
return 0;
}
-void parse_add_command(char *add_command)
+void parse_add_command_msr(char *add_command)
{
int msr_num = 0;
char *path = NULL;
- char name_buffer[NAME_BYTES] = "";
+ char perf_device[PERF_DEV_NAME_BYTES] = "";
+ char perf_event[PERF_EVT_NAME_BYTES] = "";
+ char name_buffer[PERF_NAME_BYTES] = "";
int width = 64;
int fail = 0;
enum counter_scope scope = SCOPE_CPU;
@@ -7602,6 +9848,11 @@ void parse_add_command(char *add_command)
if (sscanf(add_command, "msr%d", &msr_num) == 1)
goto next;
+ BUILD_BUG_ON(ARRAY_SIZE(perf_device) <= 31);
+ BUILD_BUG_ON(ARRAY_SIZE(perf_event) <= 31);
+ if (sscanf(add_command, "perf/%31[^/]/%31[^,]", &perf_device[0], &perf_event[0]) == 2)
+ goto next;
+
if (*add_command == '/') {
path = add_command;
goto next;
@@ -7649,7 +9900,8 @@ void parse_add_command(char *add_command)
goto next;
}
- if (sscanf(add_command, "%18s,%*s", name_buffer) == 1) { /* 18 < NAME_BYTES */
+ BUILD_BUG_ON(ARRAY_SIZE(name_buffer) <= 18);
+ if (sscanf(add_command, "%18s,%*s", name_buffer) == 1) {
char *eos;
eos = strchr(name_buffer, ',');
@@ -7666,21 +9918,33 @@ next:
}
}
- if ((msr_num == 0) && (path == NULL)) {
- fprintf(stderr, "--add: (msrDDD | msr0xXXX | /path_to_counter ) required\n");
+ if ((msr_num == 0) && (path == NULL) && (perf_device[0] == '\0' || perf_event[0] == '\0')) {
+ fprintf(stderr, "--add: (msrDDD | msr0xXXX | /path_to_counter | perf/device/event) required\n");
fail++;
}
+ /* Test for non-empty perf_device and perf_event */
+ const bool is_perf_counter = perf_device[0] && perf_event[0];
+
/* generate default column header */
if (*name_buffer == '\0') {
- if (width == 32)
- sprintf(name_buffer, "M0x%x%s", msr_num, format == FORMAT_PERCENT ? "%" : "");
- else
- sprintf(name_buffer, "M0X%x%s", msr_num, format == FORMAT_PERCENT ? "%" : "");
+ if (is_perf_counter) {
+ snprintf(name_buffer, ARRAY_SIZE(name_buffer), "perf/%s", perf_event);
+ } else {
+ if (width == 32)
+ sprintf(name_buffer, "M0x%x%s", msr_num, format == FORMAT_PERCENT ? "%" : "");
+ else
+ sprintf(name_buffer, "M0X%x%s", msr_num, format == FORMAT_PERCENT ? "%" : "");
+ }
}
- if (add_counter(msr_num, path, name_buffer, width, scope, type, format, 0))
- fail++;
+ if (is_perf_counter) {
+ if (add_perf_counter(perf_device, perf_event, name_buffer, width, scope, type, format))
+ fail++;
+ } else {
+ if (add_counter(msr_num, path, name_buffer, width, scope, type, format, 0, 0))
+ fail++;
+ }
if (fail) {
help();
@@ -7688,6 +9952,308 @@ next:
}
}
+bool starts_with(const char *str, const char *prefix)
+{
+ return strncmp(prefix, str, strlen(prefix)) == 0;
+}
+
+int pmt_parse_from_path(const char *target_path, unsigned int *out_guid, unsigned int *out_seq)
+{
+ struct pmt_diriter_t pmt_iter;
+ const struct dirent *dirname;
+ struct stat stat, target_stat;
+ int fd_telem_dir = -1;
+ int fd_target_dir;
+ unsigned int seq = 0;
+ unsigned long guid, target_guid;
+ int ret = -1;
+
+ fd_target_dir = open(target_path, O_RDONLY | O_DIRECTORY);
+ if (fd_target_dir == -1) {
+ return -1;
+ }
+
+ if (fstat(fd_target_dir, &target_stat) == -1) {
+ fprintf(stderr, "%s: Failed to stat the target: %s", __func__, strerror(errno));
+ exit(1);
+ }
+
+ if (parse_telem_info_file(fd_target_dir, "guid", "%lx", &target_guid)) {
+ fprintf(stderr, "%s: Failed to parse the target guid file: %s", __func__, strerror(errno));
+ exit(1);
+ }
+
+ close(fd_target_dir);
+
+ pmt_diriter_init(&pmt_iter);
+
+ for (dirname = pmt_diriter_begin(&pmt_iter, SYSFS_TELEM_PATH); dirname != NULL;
+ dirname = pmt_diriter_next(&pmt_iter)) {
+
+ fd_telem_dir = openat(dirfd(pmt_iter.dir), dirname->d_name, O_RDONLY | O_DIRECTORY);
+ if (fd_telem_dir == -1)
+ continue;
+
+ if (parse_telem_info_file(fd_telem_dir, "guid", "%lx", &guid)) {
+ fprintf(stderr, "%s: Failed to parse the guid file: %s", __func__, strerror(errno));
+ continue;
+ }
+
+ if (fstat(fd_telem_dir, &stat) == -1) {
+ fprintf(stderr, "%s: Failed to stat %s directory: %s", __func__,
+ dirname->d_name, strerror(errno));
+ continue;
+ }
+
+ /*
+ * If reached the same directory as target, exit the loop.
+ * Seq has the correct value now.
+ */
+ if (stat.st_dev == target_stat.st_dev && stat.st_ino == target_stat.st_ino) {
+ ret = 0;
+ break;
+ }
+
+ /*
+ * If reached directory with the same guid,
+ * but it's not the target directory yet,
+ * increment seq and continue the search.
+ */
+ if (guid == target_guid)
+ ++seq;
+
+ close(fd_telem_dir);
+ fd_telem_dir = -1;
+ }
+
+ pmt_diriter_remove(&pmt_iter);
+
+ if (fd_telem_dir != -1)
+ close(fd_telem_dir);
+
+ if (!ret) {
+ *out_guid = target_guid;
+ *out_seq = seq;
+ }
+
+ return ret;
+}
+
+void parse_add_command_pmt(char *add_command)
+{
+ char *name = NULL;
+ char *type_name = NULL;
+ char *format_name = NULL;
+ char *direct_path = NULL;
+ static const char direct_path_prefix[] = "path=";
+ unsigned int offset;
+ unsigned int lsb;
+ unsigned int msb;
+ unsigned int guid;
+ unsigned int seq = 0; /* By default, pick first file in a sequence with a given GUID. */
+ unsigned int domain_id;
+ enum counter_scope scope = 0;
+ enum pmt_datatype type = PMT_TYPE_RAW;
+ enum counter_format format = FORMAT_RAW;
+ bool has_offset = false;
+ bool has_lsb = false;
+ bool has_msb = false;
+ bool has_format = true; /* Format has a default value. */
+ bool has_guid = false;
+ bool has_scope = false;
+ bool has_type = true; /* Type has a default value. */
+
+ /* Consume the "pmt," prefix. */
+ add_command = strchr(add_command, ',');
+ if (!add_command) {
+ help();
+ exit(1);
+ }
+ ++add_command;
+
+ while (add_command) {
+ if (starts_with(add_command, "name=")) {
+ name = add_command + strlen("name=");
+ goto next;
+ }
+
+ if (starts_with(add_command, "type=")) {
+ type_name = add_command + strlen("type=");
+ goto next;
+ }
+
+ if (starts_with(add_command, "domain=")) {
+ const size_t prefix_len = strlen("domain=");
+
+ if (sscanf(add_command + prefix_len, "cpu%u", &domain_id) == 1) {
+ scope = SCOPE_CPU;
+ has_scope = true;
+ } else if (sscanf(add_command + prefix_len, "core%u", &domain_id) == 1) {
+ scope = SCOPE_CORE;
+ has_scope = true;
+ } else if (sscanf(add_command + prefix_len, "package%u", &domain_id) == 1) {
+ scope = SCOPE_PACKAGE;
+ has_scope = true;
+ }
+
+ if (!has_scope) {
+ printf("%s: invalid value for scope. Expected cpu%%u, core%%u or package%%u.\n",
+ __func__);
+ exit(1);
+ }
+
+ goto next;
+ }
+
+ if (starts_with(add_command, "format=")) {
+ format_name = add_command + strlen("format=");
+ goto next;
+ }
+
+ if (sscanf(add_command, "offset=%u", &offset) == 1) {
+ has_offset = true;
+ goto next;
+ }
+
+ if (sscanf(add_command, "lsb=%u", &lsb) == 1) {
+ has_lsb = true;
+ goto next;
+ }
+
+ if (sscanf(add_command, "msb=%u", &msb) == 1) {
+ has_msb = true;
+ goto next;
+ }
+
+ if (sscanf(add_command, "guid=%x", &guid) == 1) {
+ has_guid = true;
+ goto next;
+ }
+
+ if (sscanf(add_command, "seq=%x", &seq) == 1)
+ goto next;
+
+ if (strncmp(add_command, direct_path_prefix, strlen(direct_path_prefix)) == 0) {
+ direct_path = add_command + strlen(direct_path_prefix);
+ goto next;
+ }
+next:
+ add_command = strchr(add_command, ',');
+ if (add_command) {
+ *add_command = '\0';
+ add_command++;
+ }
+ }
+
+ if (!name) {
+ printf("%s: missing %s\n", __func__, "name");
+ exit(1);
+ }
+
+ if (strlen(name) >= PMT_COUNTER_NAME_SIZE_BYTES) {
+ printf("%s: name has to be at most %d characters long\n", __func__, PMT_COUNTER_NAME_SIZE_BYTES);
+ exit(1);
+ }
+
+ if (format_name) {
+ has_format = false;
+
+ if (strcmp("raw", format_name) == 0) {
+ format = FORMAT_RAW;
+ has_format = true;
+ }
+
+ if (strcmp("delta", format_name) == 0) {
+ format = FORMAT_DELTA;
+ has_format = true;
+ }
+
+ if (!has_format) {
+ fprintf(stderr, "%s: Invalid format %s. Expected raw or delta\n", __func__, format_name);
+ exit(1);
+ }
+ }
+
+ if (type_name) {
+ has_type = false;
+
+ if (strcmp("raw", type_name) == 0) {
+ type = PMT_TYPE_RAW;
+ has_type = true;
+ }
+
+ if (strcmp("txtal_time", type_name) == 0) {
+ type = PMT_TYPE_XTAL_TIME;
+ has_type = true;
+ }
+
+ if (strcmp("tcore_clock", type_name) == 0) {
+ type = PMT_TYPE_TCORE_CLOCK;
+ has_type = true;
+ }
+
+ if (!has_type) {
+ printf("%s: invalid %s: %s\n", __func__, "type", type_name);
+ exit(1);
+ }
+ }
+
+ if (!has_offset) {
+ printf("%s : missing %s\n", __func__, "offset");
+ exit(1);
+ }
+
+ if (!has_lsb) {
+ printf("%s: missing %s\n", __func__, "lsb");
+ exit(1);
+ }
+
+ if (!has_msb) {
+ printf("%s: missing %s\n", __func__, "msb");
+ exit(1);
+ }
+
+ if (direct_path && has_guid) {
+ printf("%s: path and guid+seq parameters are mutually exclusive\n"
+ "notice: passed guid=0x%x and path=%s\n", __func__, guid, direct_path);
+ exit(1);
+ }
+
+ if (direct_path) {
+ if (pmt_parse_from_path(direct_path, &guid, &seq)) {
+ printf("%s: failed to parse PMT file from %s\n", __func__, direct_path);
+ exit(1);
+ }
+
+ /* GUID was just infered from the direct path. */
+ has_guid = true;
+ }
+
+ if (!has_guid) {
+ printf("%s: missing %s\n", __func__, "guid or path");
+ exit(1);
+ }
+
+ if (!has_scope) {
+ printf("%s: missing %s\n", __func__, "scope");
+ exit(1);
+ }
+
+ if (lsb > msb) {
+ printf("%s: lsb > msb doesn't make sense\n", __func__);
+ exit(1);
+ }
+
+ pmt_add_counter(guid, seq, name, type, lsb, msb, offset, scope, format, domain_id, PMT_OPEN_REQUIRED);
+}
+
+void parse_add_command(char *add_command)
+{
+ if (strncmp(add_command, "pmt", strlen("pmt")) == 0)
+ return parse_add_command_pmt(add_command);
+ return parse_add_command_msr(add_command);
+}
+
int is_deferred_add(char *name)
{
int i;
@@ -7708,14 +10274,18 @@ int is_deferred_skip(char *name)
return 0;
}
-void probe_sysfs(void)
+void probe_cpuidle_residency(void)
{
char path[64];
char name_buf[16];
FILE *input;
int state;
+ int min_state = 1024, max_state = 0;
char *sp;
+ if (!DO_BIC(BIC_pct_idle))
+ return;
+
for (state = 10; state >= 0; --state) {
sprintf(path, "/sys/devices/system/cpu/cpu%d/cpuidle/state%d/name", base_cpu, state);
@@ -7738,14 +10308,32 @@ void probe_sysfs(void)
sprintf(path, "cpuidle/state%d/time", state);
- if (!DO_BIC(BIC_sysfs) && !is_deferred_add(name_buf))
+ if (!DO_BIC(BIC_pct_idle) && !is_deferred_add(name_buf))
continue;
if (is_deferred_skip(name_buf))
continue;
- add_counter(0, path, name_buf, 64, SCOPE_CPU, COUNTER_USEC, FORMAT_PERCENT, SYSFS_PERCPU);
+ add_counter(0, path, name_buf, 64, SCOPE_CPU, COUNTER_USEC, FORMAT_PERCENT, SYSFS_PERCPU, 0);
+
+ if (state > max_state)
+ max_state = state;
+ if (state < min_state)
+ min_state = state;
}
+}
+
+void probe_cpuidle_counts(void)
+{
+ char path[64];
+ char name_buf[16];
+ FILE *input;
+ int state;
+ int min_state = 1024, max_state = 0;
+ char *sp;
+
+ if (!DO_BIC(BIC_cpuidle))
+ return;
for (state = 10; state >= 0; --state) {
@@ -7755,26 +10343,52 @@ void probe_sysfs(void)
continue;
if (!fgets(name_buf, sizeof(name_buf), input))
err(1, "%s: failed to read file", path);
- /* truncate "C1-HSW\n" to "C1", or truncate "C1\n" to "C1" */
- sp = strchr(name_buf, '-');
- if (!sp)
- sp = strchrnul(name_buf, '\n');
- *sp = '\0';
fclose(input);
remove_underbar(name_buf);
- sprintf(path, "cpuidle/state%d/usage", state);
-
- if (!DO_BIC(BIC_sysfs) && !is_deferred_add(name_buf))
+ if (!DO_BIC(BIC_cpuidle) && !is_deferred_add(name_buf))
continue;
if (is_deferred_skip(name_buf))
continue;
- add_counter(0, path, name_buf, 64, SCOPE_CPU, COUNTER_ITEMS, FORMAT_DELTA, SYSFS_PERCPU);
- }
+ /* truncate "C1-HSW\n" to "C1", or truncate "C1\n" to "C1" */
+ sp = strchr(name_buf, '-');
+ if (!sp)
+ sp = strchrnul(name_buf, '\n');
+ /*
+ * The 'below' sysfs file always contains 0 for the deepest state (largest index),
+ * do not add it.
+ */
+ if (state != max_state) {
+ /*
+ * Add 'C1+' for C1, and so on. The 'below' sysfs file always contains 0 for
+ * the last state, so do not add it.
+ */
+
+ *sp = '+';
+ *(sp + 1) = '\0';
+ sprintf(path, "cpuidle/state%d/below", state);
+ add_counter(0, path, name_buf, 64, SCOPE_CPU, COUNTER_ITEMS, FORMAT_DELTA, SYSFS_PERCPU, 0);
+ }
+
+ *sp = '\0';
+ sprintf(path, "cpuidle/state%d/usage", state);
+ add_counter(0, path, name_buf, 64, SCOPE_CPU, COUNTER_ITEMS, FORMAT_DELTA, SYSFS_PERCPU, 0);
+
+ /*
+ * The 'above' sysfs file always contains 0 for the shallowest state (smallest
+ * index), do not add it.
+ */
+ if (state != min_state) {
+ *sp = '-';
+ *(sp + 1) = '\0';
+ sprintf(path, "cpuidle/state%d/above", state);
+ add_counter(0, path, name_buf, 64, SCOPE_CPU, COUNTER_ITEMS, FORMAT_DELTA, SYSFS_PERCPU, 0);
+ }
+ }
}
/*
@@ -7826,6 +10440,7 @@ void cmdline(int argc, char **argv)
{ "Dump", no_argument, 0, 'D' },
{ "debug", no_argument, 0, 'd' }, /* internal, not documented */
{ "enable", required_argument, 0, 'e' },
+ { "force", no_argument, 0, 'f' },
{ "interval", required_argument, 0, 'i' },
{ "IPC", no_argument, 0, 'I' },
{ "num_iterations", required_argument, 0, 'n' },
@@ -7851,7 +10466,7 @@ void cmdline(int argc, char **argv)
* Parse some options early, because they may make other options invalid,
* like adding the MSR counter with --add and at the same time using --no-msr.
*/
- while ((opt = getopt_long_only(argc, argv, "MP", long_options, &option_index)) != -1) {
+ while ((opt = getopt_long_only(argc, argv, "+MPn:", long_options, &option_index)) != -1) {
switch (opt) {
case 'M':
no_msr = 1;
@@ -7875,11 +10490,20 @@ void cmdline(int argc, char **argv)
break;
case 'D':
dump_only++;
+ /*
+ * Force the no_perf early to prevent using it as a source.
+ * User asks for raw values, but perf returns them relative
+ * to the opening of the file descriptor.
+ */
+ no_perf = 1;
break;
case 'e':
/* --enable specified counter */
bic_enabled = bic_enabled | bic_lookup(optarg, SHOW_LIST);
break;
+ case 'f':
+ force_load++;
+ break;
case 'd':
debug++;
ENABLE_BIC(BIC_DISABLED_BY_DEFAULT);
@@ -8008,7 +10632,8 @@ skip_cgroup_setting:
print_bootcmd();
}
- probe_sysfs();
+ probe_cpuidle_residency();
+ probe_cpuidle_counts();
if (!getuid())
set_rlimit();