Я пытаюсь выполнить некоторую аналитику событий оборудования PMU / кеширования на моем Pi, используя perf_event.h
. Я получаю сообщение об ошибке всякий раз, когда пытаюсь добавить более 7 событий в группу событий. Есть несколько вещей, которые я не понимаю.
Основные вопросы заключаются в следующем:
Есть ли ограничение на количество событий, которые могут быть мультиплексированы на Pi (я полагаю, ARM cortex A53)? Как я уже сказал, у меня не получается 8, и это кажется низким.
При мультиплексировании событий perf_event.h
определяет количество счетчиков PMU и использует их все?
Если есть ограничения на количество событий в группах событий, могу ли я получить доступ к этой информации с помощью возможностей perf_event.h
?
Знаете ли вы какие-либо хорошие ресурсы для помогите мне лучше понять функциональность perf_event.h
?
Я собираюсь включить весь файл, который пытаюсь запустить, потому что я новичок в этом, и я не уверен, какая часть будет быть значительным. Макросы N и M в верхней части кода - это количество используемых событий hw и событий кэша hw соответственно. Ошибка возникает всякий раз, когда N + M> 7
Я считаю, что этот код будет работать в любой системе Linux (используется стресс-пакет apt-get install stress
)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
#include <linux/perf_event.h>
#include <linux/hw_breakpoint.h>
#include <asm/unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#define N 0 // number of hw events to monitor
#define M 8 // number of hw events to monitor
int global_sigchld_trip = 0; // catch end child
void sighandler(int);
// function to add perf event to event list
// based on example from:
// http://web.eece.maine.edu/~vweaver/projects/perf_events/perf_event_open.html
static long
perf_event_open(struct perf_event_attr *hw_event, pid_t pid,
int cpu, int group_fd, unsigned long flags)
{
int ret;
ret = syscall(__NR_perf_event_open, hw_event,
pid, cpu, group_fd, flags);
return ret;
}
// no function for seeing if we can monitor kernel events
int no_function(int seconds)
{
sleep(seconds);
printf("\n\n Start stress \n\n");
system("stress -c 4 -t 10");
return 0;
}
int
main(int argc, char **argv){
signal(SIGCHLD, sighandler);
int num_hw_events = N;
uint pe_hw[7] = {
PERF_COUNT_HW_CPU_CYCLES,
PERF_COUNT_HW_INSTRUCTIONS,
PERF_COUNT_HW_CACHE_REFERENCES,
PERF_COUNT_HW_CACHE_MISSES,
PERF_COUNT_HW_BRANCH_INSTRUCTIONS,
PERF_COUNT_HW_BRANCH_MISSES,
PERF_COUNT_HW_BUS_CYCLES,
};
char hw_name_arr[7][50] = {
"PERF_COUNT_HW_CPU_CYCLES",
"PERF_COUNT_HW_INSTRUCTIONS",
"PERF_COUNT_HW_CACHE_REFERENCES",
"PERF_COUNT_HW_CACHE_MISSES",
"PERF_COUNT_HW_BRANCH_INSTRUCTIONS",
"PERF_COUNT_HW_BRANCH_MISSES",
"PERF_COUNT_HW_BUS_CYCLES",
};
// cache events
int num_hw_cache_events = M;
uint pe_hw_cache[12] = {
(PERF_COUNT_HW_CACHE_L1D) | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16), //
(PERF_COUNT_HW_CACHE_L1I) | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16),
(PERF_COUNT_HW_CACHE_LL) | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16),
(PERF_COUNT_HW_CACHE_BPU) | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16),
(PERF_COUNT_HW_CACHE_L1D) | (PERF_COUNT_HW_CACHE_OP_WRITE<< 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16), //
(PERF_COUNT_HW_CACHE_LL) | (PERF_COUNT_HW_CACHE_OP_WRITE<< 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16),
(PERF_COUNT_HW_CACHE_BPU) | (PERF_COUNT_HW_CACHE_OP_WRITE<< 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16),
};
char hw_cache_name_arr[12][50] = {
"PERF_COUNT_HW_CACHE_L1D_read_miss",
"PERF_COUNT_HW_CACHE_L1I_read_miss",
"PERF_COUNT_HW_CACHE_LL_read_miss",
"PERF_COUNT_HW_CACHE_BPU_read_miss",
"PERF_COUNT_HW_CACHE_L1D_write_miss",
"PERF_COUNT_HW_CACHE_LL_write_miss",
"PERF_COUNT_HW_CACHE_BPU_write_miss",
};
struct perf_event_attr pat_arr[num_hw_events+num_hw_cache_events];
long long counts[num_hw_events+num_hw_cache_events];
int fd_arr[num_hw_events+num_hw_cache_events];
// initialize hw events
for(int i=0; i<num_hw_events; i++)
{
memset(&pat_arr[i], 0, sizeof(struct perf_event_attr));
pat_arr[i].type = PERF_TYPE_HARDWARE;
pat_arr[i].size = sizeof(struct perf_event_attr);
pat_arr[i].config = pe_hw[i];
if(i==0){pat_arr[i].disabled = 1;}
else{pat_arr[i].disabled = 0;}
pat_arr[i].exclude_kernel = 1;
pat_arr[i].exclude_hv = 1;
pat_arr[i].inherit = 1;
if(i==0){fd_arr[i] = perf_event_open(&pat_arr[i],0,-1,-1,0);}
else{fd_arr[i] = perf_event_open(&pat_arr[i],0,-1,fd_arr[0],0);}
if (fd_arr[i] == -1){
fprintf(stderr, "Error opening leader %llx\n", pat_arr[i].config);
exit(EXIT_FAILURE);
}
printf("FD%d: %d \t ITEM: %s\n",i,fd_arr[i], hw_name_arr[i]);
}
// initialize hw cache events
for(int i=0; i<num_hw_cache_events; i++)
{
memset(&pat_arr[i+num_hw_events], 0, sizeof(struct perf_event_attr));
pat_arr[i+num_hw_events].type = PERF_TYPE_HW_CACHE;
pat_arr[i+num_hw_events].size = sizeof(struct perf_event_attr);
pat_arr[i+num_hw_events].config = pe_hw_cache[i];
if(i+num_hw_events==0){printf("dis=1");pat_arr[i+num_hw_events].disabled = 1;}
else{printf("dis=0");pat_arr[i+num_hw_events].disabled = 0;}
pat_arr[i+num_hw_events].exclude_kernel = 1;
pat_arr[i+num_hw_events].exclude_hv = 1;
pat_arr[i+num_hw_events].inherit = 1;
if(i+num_hw_events==0){fd_arr[i+num_hw_events] = perf_event_open(&pat_arr[i+num_hw_events],0,-1,-1,0);}
else{fd_arr[i+num_hw_events] = perf_event_open(&pat_arr[i+num_hw_events],0,-1,fd_arr[0],0);}
if (fd_arr[i+num_hw_events] == -1){
printf("\ni: %d\nnhe:%d\n",i,num_hw_events);
fprintf(stderr, "Error opening leader %llx\n", pat_arr[i+num_hw_events].config);
exit(EXIT_FAILURE);
}
printf("FD%d: %d \t ITEM: %s\n",i+num_hw_events,fd_arr[i+num_hw_events], hw_cache_name_arr[i]);
}
// reset and enable counters
for(int i=0; i<num_hw_events+num_hw_cache_events; i++){
ioctl(fd_arr[i], PERF_EVENT_IOC_RESET,0);
ioctl(fd_arr[i], PERF_EVENT_IOC_ENABLE,0);
}
/////////////////// ACTION ///////////////////////////
/*-------- CHILD PROCESS BEING REDORDED -------*/
printf("\nSHOULD FORK RIGHTE HERE\n");
pid_t proc = fork();
if(proc==0){
printf("entered child process\n");
int no_sleep = 3;
no_function(no_sleep);
//x = silly_events(loop);
printf("exiting child process\n");
return 0;
}
/*-------- ACTION TAKEN DURRING REDORDING-------*/
else{
while(!global_sigchld_trip){
sleep(1);
for(int i=0;i<num_hw_events+num_hw_cache_events;i++){read(fd_arr[i], &counts[i], sizeof(long long));}
for(int i=0;i<num_hw_events+num_hw_cache_events;i++){ioctl(fd_arr[i], PERF_EVENT_IOC_RESET,0);}
for(int i=0;i<num_hw_events;i++){printf("%lld %s\t\n", counts[i], hw_name_arr[i]);}
printf("--------------------------------------\n");
for(int i=0;i<num_hw_cache_events;i++){printf("%lld %s\t\n", counts[i+num_hw_events], hw_cache_name_arr[i]);}
printf("\n\n");
}
}
for(int i=0;i<num_hw_events+num_hw_cache_events;i++){ioctl(fd_arr[i], PERF_EVENT_IOC_DISABLE,0);}
for(int i=0;i<num_hw_events+num_hw_cache_events;i++){read(fd_arr[i], &counts[i], sizeof(long long));}
for(int i=0;i<num_hw_events;i++){printf("Used %lld %s\t", counts[i], hw_name_arr[i]);}
for(int i=0;i<num_hw_cache_events;i++){printf("Used %lld %s\t", counts[i+num_hw_events], hw_cache_name_arr[i]);}
for(int i=0;i<num_hw_events;i++){close(fd_arr[i]);}
return 0;
}
void sighandler(int signum) {
printf("Caught signal %d, coming out...\n", signum);
global_sigchld_trip = 1;
}