linux – perf_event_open溢出信号
我想计算某些代码的(或多或少)准确数量的指令.此外,我希望在通过特定数量的指令后收到信号.
为此,我使用了提供的溢出信号行为 我正在使用manpage建议实现溢出信号的第二种方式:
PERF_EVENT_IOC_REFRESH ioctl的进一步说明:
一个非常小的例子看起来像这样: #define _GNU_SOURCE 1 #include <asm/unistd.h> #include <fcntl.h> #include <linux/perf_event.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> long perf_event_open(struct perf_event_attr* event_attr,pid_t pid,int cpu,int group_fd,unsigned long flags) { return syscall(__NR_perf_event_open,event_attr,pid,cpu,group_fd,flags); } static void perf_event_handler(int signum,siginfo_t* info,void* ucontext) { if(info->si_code != POLL_HUP) { // Only POLL_HUP should happen. exit(EXIT_FAILURE); } ioctl(info->si_fd,PERF_EVENT_IOC_REFRESH,1); } int main(int argc,char** argv) { // Configure signal handler struct sigaction sa; memset(&sa,sizeof(struct sigaction)); sa.sa_sigaction = perf_event_handler; sa.sa_flags = SA_SIGINFO; // Setup signal handler if (sigaction(SIGIO,&sa,NULL) < 0) { fprintf(stderr,"Error setting up signal handlern"); perror("sigaction"); exit(EXIT_FAILURE); } // Configure perf_event_attr struct struct perf_event_attr pe; memset(&pe,sizeof(struct perf_event_attr)); pe.type = PERF_TYPE_HARDWARE; pe.size = sizeof(struct perf_event_attr); pe.config = PERF_COUNT_HW_INSTRUCTIONS; // Count retired hardware instructions pe.disabled = 1; // Event is initially disabled pe.sample_type = PERF_SAMPLE_IP; pe.sample_period = 1000; pe.exclude_kernel = 1; // excluding events that happen in the kernel-space pe.exclude_hv = 1; // excluding events that happen in the hypervisor pid_t pid = 0; // measure the current process/thread int cpu = -1; // measure on any cpu int group_fd = -1; unsigned long flags = 0; int fd = perf_event_open(&pe,flags); if (fd == -1) { fprintf(stderr,"Error opening leader %llxn",pe.config); perror("perf_event_open"); exit(EXIT_FAILURE); } // Setup event handler for overflow signals fcntl(fd,F_SETFL,O_NONBLOCK|O_ASYNC); fcntl(fd,F_SETSIG,SIGIO); fcntl(fd,F_SETOWN,getpid()); ioctl(fd,PERF_EVENT_IOC_RESET,0); // Reset event counter to 0 ioctl(fd,1); // // Start monitoring long loopCount = 1000000; long c = 0; long i = 0; // Some sample payload. for(i = 0; i < loopCount; i++) { c += 1; } // End monitoring ioctl(fd,PERF_EVENT_IOC_DISABLE,0); // Disable event long long counter; read(fd,&counter,sizeof(long long)); // Read event counter value printf("Used %lld instructionsn",counter); close(fd); } 所以基本上我正在做以下事情: >为SIGIO信号设置信号处理程序 执行有效负载循环时,在某些时候将执行1000条指令(sample_interval).根据perf_event_open manpage,这会触发溢出,然后递减内部计数器. 当发送信号时,停止当前进程/线程的控制流程,并执行信号处理程序.场景: >已执行1000条指令. 这种情况意味着两件事: >计数指令的最终数量总是等于根本不使用信号的示例. 基本上你可以说,信号行为可以看作是同步的. 这是我想要实现的完美语义. 但是,就我而言,我配置的信号通常是异步的,并且可能会经过一段时间,直到它最终被传递并执行信号处理程序.这可能对我造成问题. 例如,请考虑以下情形: >已执行1000条指令. 这种情况意味着两件事: >计算指令的最终数量将小于根本不使用信号的示例. 到目前为止,我已经对上面的示例进行了大量测试,并且没有遇到支持第一个场景的错过指令. 但是,我真的很想知道,我是否可以依赖这个假设. 解决方法
你有两个任务可能会相互冲突.当您想要计数(某些硬件事件的确切数量)时,只需在计数模式下使用CPU的性能监视单元(不要设置所使用的perf_event_attr结构的sample_period / sample_freq)并将测量代码放在目标程序中(如它是在你的例子中完成的).在根据man page of
要计算程序的一部分,请使用返回fd的perf_event_open的ioctls,如man page所述
您可以使用rdpmc(在x86上)或在fd上读取系统调用来读取当前值,如the man page中的简短示例所示: #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <string.h> #include <sys/ioctl.h> #include <linux/perf_event.h> #include <asm/unistd.h> static long perf_event_open(struct perf_event_attr *hw_event,unsigned long flags) { int ret; ret = syscall(__NR_perf_event_open,hw_event,flags); return ret; } int main(int argc,char **argv) { struct perf_event_attr pe; long long count; int fd; memset(&pe,sizeof(struct perf_event_attr)); pe.type = PERF_TYPE_HARDWARE; pe.size = sizeof(struct perf_event_attr); pe.config = PERF_COUNT_HW_INSTRUCTIONS; pe.disabled = 1; pe.exclude_kernel = 1; pe.exclude_hv = 1; fd = perf_event_open(&pe,-1,0); if (fd == -1) { fprintf(stderr,pe.config); exit(EXIT_FAILURE); } ioctl(fd,0); ioctl(fd,PERF_EVENT_IOC_ENABLE,0); printf("Measuring instruction count for this printfn"); /* Place target code here instead of printf */ ioctl(fd,0); read(fd,&count,sizeof(long long)); printf("Used %lld instructionsn",count); close(fd); }
你真的想获得信号,或者你只需??要执行1000条指令的指令指针吗?如果要收集指针,请使用带采样模式的perf_even_open,但可以从其他程序执行此操作以禁用对事件收集代码的测量.此外,它对您的目标程序的负面影响较小,如果您不使用每个溢出的信号(具有大量的内核跟踪器交互和从内核切换),而是使用perf_events的功能来收集多个溢出事件进入单个mmap缓冲区并在此缓冲区上进行轮询.来自PMU的溢出中断将调用中断处理程序将指令指针保存到缓冲区中,然后重置计数,程序将返回执行状态.在您的示例中,perf中断处理程序将唤醒您的程序,它将执行多个系统调用,返回内核,然后内核将重新启动目标代码(因此每个示例的开销大于使用mmap并解析它).使用precise_ip标志,您可以激活PMU的高级采样(如果它具有这样的模式,如针对some counters的intel x86 / em64t中的PEBS和PREC_DIST,如INST_RETIRED,UOPS_RETIRED,BR_INST_RETIRED,BR_MISP_RETIRED,MEM_UOPS_RETIRED,MEM_LOAD_UOPS_RETIRED,MEM_LOAD_UOPS_LLC_HIT_RETIRED以及simple hack也可以循环;或者像AMD x86 / amd64的IBS;关于PEBS and IBS的论文),当指令地址由具有低滑动的硬件直接保存时.一些非常先进的PMU能够在硬件中进行采样,在行中存储多个事件的溢出信息,并且无需软件中断即可自动复位计数器(precision_ip上的一些描述为in the same paper). 我不知道perf_events子系统和CPU中是否有可能同时激活两个perf_event任务:目标进程中的计数事件和同时从其他进程中采样.使用高级PMU,这可以在硬件中实现,现代内核中的perf_events可以允许它.但是,您没有提供有关内核版本以及CPU供应商和系列的详细信息,因此我们无法回答这一部分. 您也可以尝试其他API来访问PMU,如PAPI或likwid(https://github.com/RRZE-HPC/likwid).其中一些可以直接读取PMU寄存器(有时是MSR),并且可以在启用计数时同时进行采样. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |