// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2022 , Athira Rajeev , IBM Corp .
* Copyright 2022 , Madhavan Srinivasan , IBM Corp .
* Copyright 2022 , Kajol Jain , IBM Corp .
*/
#include <unistd.h>
#include <sys/syscall.h>
#include <string.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <ctype.h>
#include "misc.h"
#define PAGE_SIZE sysconf(_SC_PAGESIZE)
/* Storage for platform version */
int pvr;
u64 platform_extended_mask;
/* Mask and Shift for Event code fields */
int ev_mask_pmcxsel, ev_shift_pmcxsel; //pmcxsel field
int ev_mask_marked, ev_shift_marked; //marked filed
int ev_mask_comb, ev_shift_comb; //combine field
int ev_mask_unit, ev_shift_unit; //unit field
int ev_mask_pmc, ev_shift_pmc; //pmc field
int ev_mask_cache, ev_shift_cache; //Cache sel field
int ev_mask_sample, ev_shift_sample; //Random sampling field
int ev_mask_thd_sel, ev_shift_thd_sel; //thresh_sel field
int ev_mask_thd_start, ev_shift_thd_start; //thresh_start field
int ev_mask_thd_stop, ev_shift_thd_stop; //thresh_stop field
int ev_mask_thd_cmp, ev_shift_thd_cmp; //thresh cmp field
int ev_mask_sm, ev_shift_sm; //SDAR mode field
int ev_mask_rsq, ev_shift_rsq; //radix scope qual field
int ev_mask_l2l3, ev_shift_l2l3; //l2l3 sel field
int ev_mask_mmcr3_src, ev_shift_mmcr3_src; //mmcr3 field
static void init_ev_encodes(void )
{
ev_mask_pmcxsel = 0 xff;
ev_shift_pmcxsel = 0 ;
ev_mask_marked = 1 ;
ev_shift_marked = 8 ;
ev_mask_unit = 0 xf;
ev_shift_unit = 12 ;
ev_mask_pmc = 0 xf;
ev_shift_pmc = 16 ;
ev_mask_sample = 0 x1f;
ev_shift_sample = 24 ;
ev_mask_thd_sel = 0 x7;
ev_shift_thd_sel = 29 ;
ev_mask_thd_start = 0 xf;
ev_shift_thd_start = 36 ;
ev_mask_thd_stop = 0 xf;
ev_shift_thd_stop = 32 ;
switch (pvr) {
case POWER11:
case POWER10:
ev_mask_thd_cmp = 0 x3ffff;
ev_shift_thd_cmp = 0 ;
ev_mask_rsq = 1 ;
ev_shift_rsq = 9 ;
ev_mask_comb = 3 ;
ev_shift_comb = 10 ;
ev_mask_cache = 3 ;
ev_shift_cache = 20 ;
ev_mask_sm = 0 x3;
ev_shift_sm = 22 ;
ev_mask_l2l3 = 0 x1f;
ev_shift_l2l3 = 40 ;
ev_mask_mmcr3_src = 0 x7fff;
ev_shift_mmcr3_src = 45 ;
break ;
case POWER9:
ev_mask_comb = 3 ;
ev_shift_comb = 10 ;
ev_mask_cache = 0 xf;
ev_shift_cache = 20 ;
ev_mask_thd_cmp = 0 x3ff;
ev_shift_thd_cmp = 40 ;
ev_mask_sm = 0 x3;
ev_shift_sm = 50 ;
break ;
default :
FAIL_IF_EXIT(1 );
}
}
/* Return the extended regs mask value */
u64 perf_get_platform_reg_mask(void )
{
if (have_hwcap2(PPC_FEATURE2_ARCH_3_1))
return PERF_POWER10_MASK;
if (have_hwcap2(PPC_FEATURE2_ARCH_3_00))
return PERF_POWER9_MASK;
return -1 ;
}
int check_extended_regs_support(void )
{
int fd;
struct event event;
event_init(&event, 0 x1001e);
event.attr.type = 4 ;
event.attr.sample_period = 1 ;
event.attr.disabled = 1 ;
event.attr.sample_type = PERF_SAMPLE_REGS_INTR;
event.attr.sample_regs_intr = platform_extended_mask;
fd = event_open(&event);
if (fd != -1 )
return 0 ;
return -1 ;
}
int platform_check_for_tests(void )
{
pvr = PVR_VER(mfspr(SPRN_PVR));
/*
* Check for supported platforms
* for sampling test
*/
switch (pvr) {
case POWER11:
case POWER10:
case POWER9:
break ;
default :
goto out;
}
/*
* Check PMU driver registered by looking for
* PPC_FEATURE2_EBB bit in AT_HWCAP2
*/
if (!have_hwcap2(PPC_FEATURE2_EBB) || !have_hwcap2(PPC_FEATURE2_ARCH_3_00))
goto out;
return 0 ;
out:
printf("%s: Tests unsupported for this platform\n" , __func__);
return -1 ;
}
int check_pvr_for_sampling_tests(void )
{
SKIP_IF(platform_check_for_tests());
platform_extended_mask = perf_get_platform_reg_mask();
/* check if platform supports extended regs */
if (check_extended_regs_support())
goto out;
init_ev_encodes();
return 0 ;
out:
printf("%s: Sampling tests un-supported\n" , __func__);
return -1 ;
}
/*
* Allocate mmap buffer of " mmap_pages " number of
* pages .
*/
void *event_sample_buf_mmap(int fd, int mmap_pages)
{
size_t page_size = sysconf(_SC_PAGESIZE);
size_t mmap_size;
void *buff;
if (mmap_pages <= 0 )
return NULL;
if (fd <= 0 )
return NULL;
mmap_size = page_size * (1 + mmap_pages);
buff = mmap(NULL, mmap_size,
PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0 );
if (buff == MAP_FAILED) {
perror("mmap() failed." );
return NULL;
}
return buff;
}
/*
* Post process the mmap buffer .
* - If sample_count ! = NULL then return count of total
* number of samples present in the mmap buffer .
* - If sample_count = = NULL then return the address
* of first sample from the mmap buffer
*/
void *__event_read_samples(void *sample_buff, size_t *size, u64 *sample_count)
{
size_t page_size = sysconf(_SC_PAGESIZE);
struct perf_event_header *header = sample_buff + page_size;
struct perf_event_mmap_page *metadata_page = sample_buff;
unsigned long data_head, data_tail;
/*
* PERF_RECORD_SAMPLE :
* struct {
* struct perf_event_header hdr ;
* u64 data [ ] ;
* } ;
*/
data_head = metadata_page->data_head;
/* sync memory before reading sample */
mb();
data_tail = metadata_page->data_tail;
/* Check for sample_count */
if (sample_count)
*sample_count = 0 ;
while (1 ) {
/*
* Reads the mmap data buffer by moving
* the data_tail to know the last read data .
* data_head points to head in data buffer .
* refer " struct perf_event_mmap_page " in
* " include / uapi / linux / perf_event . h " .
*/
if (data_head - data_tail < sizeof (header))
return NULL;
data_tail += sizeof (header);
if (header->type == PERF_RECORD_SAMPLE) {
*size = (header->size - sizeof (header));
if (!sample_count)
return sample_buff + page_size + data_tail;
data_tail += *size;
*sample_count += 1 ;
} else {
*size = (header->size - sizeof (header));
if ((metadata_page->data_tail + *size) > metadata_page->data_head)
data_tail = metadata_page->data_head;
else
data_tail += *size;
}
header = (struct perf_event_header *)((void *)header + header->size);
}
return NULL;
}
int collect_samples(void *sample_buff)
{
u64 sample_count;
size_t size = 0 ;
__event_read_samples(sample_buff, &size, &sample_count);
return sample_count;
}
static void *perf_read_first_sample(void *sample_buff, size_t *size)
{
return __event_read_samples(sample_buff, size, NULL);
}
u64 *get_intr_regs(struct event *event, void *sample_buff)
{
u64 type = event->attr.sample_type;
u64 *intr_regs;
size_t size = 0 ;
if ((type ^ (PERF_SAMPLE_REGS_INTR | PERF_SAMPLE_BRANCH_STACK)) &&
(type ^ PERF_SAMPLE_REGS_INTR))
return NULL;
intr_regs = (u64 *)perf_read_first_sample(sample_buff, &size);
if (!intr_regs)
return NULL;
if (type & PERF_SAMPLE_BRANCH_STACK) {
/*
* PERF_RECORD_SAMPLE and PERF_SAMPLE_BRANCH_STACK :
* struct {
* struct perf_event_header hdr ;
* u64 number_of_branches ;
* struct perf_branch_entry [ number_of_branches ] ;
* u64 data [ ] ;
* } ;
* struct perf_branch_entry {
* u64 from ;
* u64 to ;
* u64 misc ;
* } ;
*/
intr_regs += ((*intr_regs) * 3 ) + 1 ;
}
/*
* First entry in the sample buffer used to specify
* PERF_SAMPLE_REGS_ABI_64 , skip perf regs abi to access
* interrupt registers .
*/
++intr_regs;
return intr_regs;
}
static const int __perf_reg_mask(const char *register_name)
{
if (!strcmp(register_name, "R0" ))
return 0 ;
else if (!strcmp(register_name, "R1" ))
return 1 ;
else if (!strcmp(register_name, "R2" ))
return 2 ;
else if (!strcmp(register_name, "R3" ))
return 3 ;
else if (!strcmp(register_name, "R4" ))
return 4 ;
else if (!strcmp(register_name, "R5" ))
return 5 ;
else if (!strcmp(register_name, "R6" ))
return 6 ;
else if (!strcmp(register_name, "R7" ))
return 7 ;
else if (!strcmp(register_name, "R8" ))
return 8 ;
else if (!strcmp(register_name, "R9" ))
return 9 ;
else if (!strcmp(register_name, "R10" ))
return 10 ;
else if (!strcmp(register_name, "R11" ))
return 11 ;
else if (!strcmp(register_name, "R12" ))
return 12 ;
else if (!strcmp(register_name, "R13" ))
return 13 ;
else if (!strcmp(register_name, "R14" ))
return 14 ;
else if (!strcmp(register_name, "R15" ))
return 15 ;
else if (!strcmp(register_name, "R16" ))
return 16 ;
else if (!strcmp(register_name, "R17" ))
return 17 ;
else if (!strcmp(register_name, "R18" ))
return 18 ;
else if (!strcmp(register_name, "R19" ))
return 19 ;
else if (!strcmp(register_name, "R20" ))
return 20 ;
else if (!strcmp(register_name, "R21" ))
return 21 ;
else if (!strcmp(register_name, "R22" ))
return 22 ;
else if (!strcmp(register_name, "R23" ))
return 23 ;
else if (!strcmp(register_name, "R24" ))
return 24 ;
else if (!strcmp(register_name, "R25" ))
return 25 ;
else if (!strcmp(register_name, "R26" ))
return 26 ;
else if (!strcmp(register_name, "R27" ))
return 27 ;
else if (!strcmp(register_name, "R28" ))
return 28 ;
else if (!strcmp(register_name, "R29" ))
return 29 ;
else if (!strcmp(register_name, "R30" ))
return 30 ;
else if (!strcmp(register_name, "R31" ))
return 31 ;
else if (!strcmp(register_name, "NIP" ))
return 32 ;
else if (!strcmp(register_name, "MSR" ))
return 33 ;
else if (!strcmp(register_name, "ORIG_R3" ))
return 34 ;
else if (!strcmp(register_name, "CTR" ))
return 35 ;
else if (!strcmp(register_name, "LINK" ))
return 36 ;
else if (!strcmp(register_name, "XER" ))
return 37 ;
else if (!strcmp(register_name, "CCR" ))
return 38 ;
else if (!strcmp(register_name, "SOFTE" ))
return 39 ;
else if (!strcmp(register_name, "TRAP" ))
return 40 ;
else if (!strcmp(register_name, "DAR" ))
return 41 ;
else if (!strcmp(register_name, "DSISR" ))
return 42 ;
else if (!strcmp(register_name, "SIER" ))
return 43 ;
else if (!strcmp(register_name, "MMCRA" ))
return 44 ;
else if (!strcmp(register_name, "MMCR0" ))
return 45 ;
else if (!strcmp(register_name, "MMCR1" ))
return 46 ;
else if (!strcmp(register_name, "MMCR2" ))
return 47 ;
else if (!strcmp(register_name, "MMCR3" ))
return 48 ;
else if (!strcmp(register_name, "SIER2" ))
return 49 ;
else if (!strcmp(register_name, "SIER3" ))
return 50 ;
else if (!strcmp(register_name, "PMC1" ))
return 51 ;
else if (!strcmp(register_name, "PMC2" ))
return 52 ;
else if (!strcmp(register_name, "PMC3" ))
return 53 ;
else if (!strcmp(register_name, "PMC4" ))
return 54 ;
else if (!strcmp(register_name, "PMC5" ))
return 55 ;
else if (!strcmp(register_name, "PMC6" ))
return 56 ;
else if (!strcmp(register_name, "SDAR" ))
return 57 ;
else if (!strcmp(register_name, "SIAR" ))
return 58 ;
else
return -1 ;
}
u64 get_reg_value(u64 *intr_regs, char *register_name)
{
int register_bit_position;
register_bit_position = __perf_reg_mask(register_name);
if (register_bit_position < 0 || (!((platform_extended_mask >>
(register_bit_position - 1 )) & 1 )))
return -1 ;
return *(intr_regs + register_bit_position);
}
int get_thresh_cmp_val(struct event event)
{
int exp = 0 ;
u64 result = 0 ;
u64 value;
if (!have_hwcap2(PPC_FEATURE2_ARCH_3_1))
return EV_CODE_EXTRACT(event.attr.config, thd_cmp);
value = EV_CODE_EXTRACT(event.attr.config1, thd_cmp);
if (!value)
return value;
/*
* Incase of P10 , thresh_cmp value is not part of raw event code
* and provided via attr . config1 parameter . To program threshold in MMCRA ,
* take a 18 bit number N and shift right 2 places and increment
* the exponent E by 1 until the upper 10 bits of N are zero .
* Write E to the threshold exponent and write the lower 8 bits of N
* to the threshold mantissa .
* The max threshold that can be written is 261120 .
*/
if (value > 261120 )
value = 261120 ;
while ((64 - __builtin_clzl(value)) > 8 ) {
exp++;
value >>= 2 ;
}
/*
* Note that it is invalid to write a mantissa with the
* upper 2 bits of mantissa being zero , unless the
* exponent is also zero .
*/
if (!(value & 0 xC0) && exp)
result = -1 ;
else
result = (exp << 8 ) | value;
return result;
}
/*
* Utility function to check for generic compat PMU
* by comparing base_platform value from auxv and real
* PVR value .
* auxv_base_platform ( ) func gives information of " base platform "
* corresponding to PVR value . Incase , if the distro doesn ' t
* support platform PVR ( missing cputable support ) , base platform
* in auxv will have a default value other than the real PVR ' s .
* In this case , ISAv3 PMU ( generic compat PMU ) will be registered
* in the system . auxv_generic_compat_pmu ( ) makes use of the base
* platform value from auxv to do this check .
*/
static bool auxv_generic_compat_pmu(void )
{
int base_pvr = 0 ;
if (!strcmp(auxv_base_platform(), "power9" ))
base_pvr = POWER9;
else if (!strcmp(auxv_base_platform(), "power10" ))
base_pvr = POWER10;
else if (!strcmp(auxv_base_platform(), "power11" ))
base_pvr = POWER11;
return (!base_pvr);
}
/*
* Check for generic compat PMU .
* First check for presence of pmu_name from
* " / sys / bus / event_source / devices / cpu / caps " .
* If doesn ' t exist , fallback to using value
* auxv .
*/
bool check_for_generic_compat_pmu(void )
{
char pmu_name[256 ];
memset(pmu_name, 0 , sizeof (pmu_name));
if (read_sysfs_file("bus/event_source/devices/cpu/caps/pmu_name" ,
pmu_name, sizeof (pmu_name)) < 0 )
return auxv_generic_compat_pmu();
if (!strcmp(pmu_name, "ISAv3" ))
return true ;
else
return false ;
}
/*
* Check if system is booted in compat mode .
*/
bool check_for_compat_mode(void )
{
char *platform = auxv_platform();
char *base_platform = auxv_base_platform();
return strcmp(platform, base_platform);
}
Messung V0.5 in Prozent C=84 H=90 G=86
¤ Dauer der Verarbeitung: 0.2 Sekunden
(vorverarbeitet am 2026-10-12)
¤
*© Formatika GbR, Deutschland