Skip to content

Commit ae2cdf8

Browse files
Patryk Wlazlynlenb
authored andcommitted
tools/power turbostat: Allow using cpu device in perf counters on hybrid platforms
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, however makes it more complicated for the user, because most of the counters are available on both and had to be handled manually. This patch allows users to use "virtual" cpu device that is seemingly translated to cpu_core and cpu_atom perf devices, depending on the type of a CPU we are opening the counter for. Signed-off-by: Patryk Wlazlyn <[email protected]> Signed-off-by: Len Brown <[email protected]>
1 parent ea8614c commit ae2cdf8

File tree

2 files changed

+123
-7
lines changed

2 files changed

+123
-7
lines changed

tools/power/x86/turbostat/turbostat.8

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ name as necessary to disambiguate it from others is necessary. Note that option
3333
msr0xXXX is a hex offset, eg. msr0x10
3434
/sys/path... is an absolute path to a sysfs attribute
3535
<device> is a perf device from /sys/bus/event_source/devices/<device> eg. cstate_core
36+
On Intel hybrid platforms, instead of one "cpu" perf device there are two, "cpu_core" and "cpu_atom" devices for P and E cores respectively.
37+
Turbostat, in this case, allow user to use "cpu" device and will automatically detect the type of a CPU and translate it to "cpu_core" and "cpu_atom" accordingly.
38+
For a complete example see "ADD PERF COUNTER EXAMPLE #2 (using virtual "cpu" device)".
3639
<event> is a perf event for given device from /sys/bus/event_source/devices/<device>/events/<event> eg. c1-residency
3740
perf/cstate_core/c1-residency would then use /sys/bus/event_source/devices/cstate_core/events/c1-residency
3841

@@ -387,6 +390,28 @@ CPU pCPU%c1 CPU%c1
387390

388391
.fi
389392

393+
.SH ADD PERF COUNTER EXAMPLE #2 (using virtual cpu device)
394+
Here we run on hybrid, Raptor Lake platform.
395+
We limit turbostat to show output for just cpu0 (pcore) and cpu12 (ecore).
396+
We add a counter showing number of L3 cache misses, using virtual "cpu" device,
397+
labeling it with the column header, "VCMISS".
398+
We add a counter showing number of L3 cache misses, using virtual "cpu_core" device,
399+
labeling it with the column header, "PCMISS". This will fail on ecore cpu12.
400+
We add a counter showing number of L3 cache misses, using virtual "cpu_atom" device,
401+
labeling it with the column header, "ECMISS". This will fail on pcore cpu0.
402+
We display it only once, after the conclusion of 0.1 second sleep.
403+
.nf
404+
sudo ./turbostat --quiet --cpu 0,12 --show CPU --add perf/cpu/cache-misses,cpu,delta,raw,VCMISS --add perf/cpu_core/cache-misses,cpu,delta,raw,PCMISS --add perf/cpu_atom/cache-misses,cpu,delta,raw,ECMISS sleep .1
405+
turbostat: added_perf_counters_init_: perf/cpu_atom/cache-misses: failed to open counter on cpu0
406+
turbostat: added_perf_counters_init_: perf/cpu_core/cache-misses: failed to open counter on cpu12
407+
0.104630 sec
408+
CPU ECMISS PCMISS VCMISS
409+
- 0x0000000000000000 0x0000000000000000 0x0000000000000000
410+
0 0x0000000000000000 0x0000000000007951 0x0000000000007796
411+
12 0x000000000001137a 0x0000000000000000 0x0000000000011392
412+
413+
.fi
414+
390415
.SH ADD PMT COUNTER EXAMPLE
391416
Here we limit turbostat to showing just the CPU number 0.
392417
We add two counters, showing crystal clock count and the DC6 residency.

tools/power/x86/turbostat/turbostat.c

Lines changed: 98 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@
3131
)
3232
// end copied section
3333

34+
#define CPUID_LEAF_MODEL_ID 0x1A
35+
#define CPUID_LEAF_MODEL_ID_CORE_TYPE_SHIFT 24
36+
3437
#define X86_VENDOR_INTEL 0
3538

3639
#include INTEL_FAMILY_HEADER
@@ -89,6 +92,9 @@
8992
#define PERF_DEV_NAME_BYTES 32
9093
#define PERF_EVT_NAME_BYTES 32
9194

95+
#define INTEL_ECORE_TYPE 0x20
96+
#define INTEL_PCORE_TYPE 0x40
97+
9298
enum counter_scope { SCOPE_CPU, SCOPE_CORE, SCOPE_PACKAGE };
9399
enum counter_type { COUNTER_ITEMS, COUNTER_CYCLES, COUNTER_SECONDS, COUNTER_USEC, COUNTER_K2M };
94100
enum counter_format { FORMAT_RAW, FORMAT_DELTA, FORMAT_PERCENT, FORMAT_AVERAGE };
@@ -1848,6 +1854,7 @@ struct cpu_topology {
18481854
int logical_node_id; /* 0-based count within the package */
18491855
int physical_core_id;
18501856
int thread_id;
1857+
int type;
18511858
cpu_set_t *put_ids; /* Processing Unit/Thread IDs */
18521859
} *cpus;
18531860

@@ -5653,6 +5660,32 @@ int init_thread_id(int cpu)
56535660
return 0;
56545661
}
56555662

5663+
int set_my_cpu_type(void)
5664+
{
5665+
unsigned int eax, ebx, ecx, edx;
5666+
unsigned int max_level;
5667+
5668+
__cpuid(0, max_level, ebx, ecx, edx);
5669+
5670+
if (max_level < CPUID_LEAF_MODEL_ID)
5671+
return 0;
5672+
5673+
__cpuid(CPUID_LEAF_MODEL_ID, eax, ebx, ecx, edx);
5674+
5675+
return (eax >> CPUID_LEAF_MODEL_ID_CORE_TYPE_SHIFT);
5676+
}
5677+
5678+
int set_cpu_hybrid_type(int cpu)
5679+
{
5680+
if (cpu_migrate(cpu))
5681+
return -1;
5682+
5683+
int type = set_my_cpu_type();
5684+
5685+
cpus[cpu].type = type;
5686+
return 0;
5687+
}
5688+
56565689
/*
56575690
* snapshot_proc_interrupts()
56585691
*
@@ -8281,6 +8314,8 @@ void topology_probe(bool startup)
82818314

82828315
for_all_proc_cpus(init_thread_id);
82838316

8317+
for_all_proc_cpus(set_cpu_hybrid_type);
8318+
82848319
/*
82858320
* For online cpus
82868321
* find max_core_id, max_package_id
@@ -8545,6 +8580,35 @@ void check_perf_access(void)
85458580
bic_enabled &= ~BIC_IPC;
85468581
}
85478582

8583+
bool perf_has_hybrid_devices(void)
8584+
{
8585+
/*
8586+
* 0: unknown
8587+
* 1: has separate perf device for p and e core
8588+
* -1: doesn't have separate perf device for p and e core
8589+
*/
8590+
static int cached;
8591+
8592+
if (cached > 0)
8593+
return true;
8594+
8595+
if (cached < 0)
8596+
return false;
8597+
8598+
if (access("/sys/bus/event_source/devices/cpu_core", F_OK)) {
8599+
cached = -1;
8600+
return false;
8601+
}
8602+
8603+
if (access("/sys/bus/event_source/devices/cpu_atom", F_OK)) {
8604+
cached = -1;
8605+
return false;
8606+
}
8607+
8608+
cached = 1;
8609+
return true;
8610+
}
8611+
85488612
int added_perf_counters_init_(struct perf_counter_info *pinfo)
85498613
{
85508614
size_t num_domains = 0;
@@ -8601,29 +8665,56 @@ int added_perf_counters_init_(struct perf_counter_info *pinfo)
86018665
if (domain_visited[next_domain])
86028666
continue;
86038667

8604-
perf_type = read_perf_type(pinfo->device);
8668+
/*
8669+
* Intel hybrid platforms expose different perf devices for P and E cores.
8670+
* Instead of one, "/sys/bus/event_source/devices/cpu" device, there are
8671+
* "/sys/bus/event_source/devices/{cpu_core,cpu_atom}".
8672+
*
8673+
* This makes it more complicated to the user, because most of the counters
8674+
* are available on both and have to be handled manually, otherwise.
8675+
*
8676+
* Code below, allow user to use the old "cpu" name, which is translated accordingly.
8677+
*/
8678+
const char *perf_device = pinfo->device;
8679+
8680+
if (strcmp(perf_device, "cpu") == 0 && perf_has_hybrid_devices()) {
8681+
switch (cpus[cpu].type) {
8682+
case INTEL_PCORE_TYPE:
8683+
perf_device = "cpu_core";
8684+
break;
8685+
8686+
case INTEL_ECORE_TYPE:
8687+
perf_device = "cpu_atom";
8688+
break;
8689+
8690+
default: /* Don't change, we will probably fail and report a problem soon. */
8691+
break;
8692+
}
8693+
}
8694+
8695+
perf_type = read_perf_type(perf_device);
86058696
if (perf_type == (unsigned int)-1) {
86068697
warnx("%s: perf/%s/%s: failed to read %s",
8607-
__func__, pinfo->device, pinfo->event, "type");
8698+
__func__, perf_device, pinfo->event, "type");
86088699
continue;
86098700
}
86108701

8611-
perf_config = read_perf_config(pinfo->device, pinfo->event);
8702+
perf_config = read_perf_config(perf_device, pinfo->event);
86128703
if (perf_config == (unsigned int)-1) {
86138704
warnx("%s: perf/%s/%s: failed to read %s",
8614-
__func__, pinfo->device, pinfo->event, "config");
8705+
__func__, perf_device, pinfo->event, "config");
86158706
continue;
86168707
}
86178708

86188709
/* Scale is not required, some counters just don't have it. */
8619-
perf_scale = read_perf_scale(pinfo->device, pinfo->event);
8710+
perf_scale = read_perf_scale(perf_device, pinfo->event);
86208711
if (perf_scale == 0.0)
86218712
perf_scale = 1.0;
86228713

86238714
fd_perf = open_perf_counter(cpu, perf_type, perf_config, -1, 0);
86248715
if (fd_perf == -1) {
86258716
warnx("%s: perf/%s/%s: failed to open counter on cpu%d",
8626-
__func__, pinfo->device, pinfo->event, cpu);
8717+
__func__, perf_device, pinfo->event, cpu);
86278718
continue;
86288719
}
86298720

@@ -8633,7 +8724,7 @@ int added_perf_counters_init_(struct perf_counter_info *pinfo)
86338724

86348725
if (debug)
86358726
fprintf(stderr, "Add perf/%s/%s cpu%d: %d\n",
8636-
pinfo->device, pinfo->event, cpu, pinfo->fd_perf_per_domain[next_domain]);
8727+
perf_device, pinfo->event, cpu, pinfo->fd_perf_per_domain[next_domain]);
86378728
}
86388729

86398730
pinfo = pinfo->next;

0 commit comments

Comments
 (0)