Daw*_*odo 9 linux signals perf
我想计算某些代码的(或多或少)确切数量的指令.此外,我希望在通过特定数量的指令后收到信号.
为此,我使用perf_event_open提供的溢出信号行为 .
我正在使用manpage建议实现溢出信号的第二种方式:
信号溢出
可以设置事件以在超过阈值时传递信号.使用poll(2),select(2),epoll(2)和fcntl(2),系统调用来设置信号处理程序.
[...]
另一种方法是使用PERF_EVENT_IOC_REFRESH ioctl.这个ioctl增加了一个计数器,每次事件溢出时减少.当非零时,溢出时发送POLL_IN信号,但一旦值达到0,就会发送类型为POLL_HUP的信号,并禁用基础事件.
PERF_EVENT_IOC_REFRESH ioctl的进一步说明:
PERF_EVENT_IOC_REFRESH
非继承溢出计数器可以使用它来为参数指定的多个溢出启用计数器,之后禁用它.此ioctl的后续调用将参数值添加到当前计数.设置POLL_IN的信号将在每次溢出时发生,直到计数达到0; 当发生这种情况时,发送设置了POLL_HUP的信号并禁用该事件.使用0的参数被视为未定义的行为.
一个非常小的例子看起来像这样:
#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, 0, 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 handler\n");
perror("sigaction");
exit(EXIT_FAILURE);
}
// Configure perf_event_attr struct
struct perf_event_attr pe;
memset(&pe, 0, 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, pid, cpu, group_fd, flags);
if (fd == -1) {
fprintf(stderr, "Error opening leader %llx\n", 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, PERF_EVENT_IOC_REFRESH, 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 instructions\n", counter);
close(fd);
}
Run Code Online (Sandbox Code Playgroud)
所以基本上我正在做以下事情:
perf_event_open(返回文件描述符)创建一个新的性能计数器fcntl到的信号发送行为添加到该文件描述符.执行有效负载循环时,在某些时候sample_interval将执行1000条指令().根据perf_event_open联机帮助页,这会触发溢出,然后会减少内部计数器.一旦此计数器达到零,"发送类型为POLL_HUP的信号,并且基础事件被禁用."
当发送信号时,停止当前进程/线程的控制流程,并执行信号处理程序.场景:
这种情况意味着两件事:
ucontext)将直接指向导致溢出的指令.基本上你可以说,信号行为可以看作是同步的.
这是我想要实现的完美语义.
但是,就我而言,我配置的信号通常是异步的,有些时间可能会通过,直到最终传递并执行信号处理程序.这可能对我来说是个问题.
例如,请考虑以下情形:
这种情况意味着两件事:
到目前为止,我已经对上面的示例进行了大量测试,并且没有遇到支持第一个场景的错过指令.
但是,我真的很想知道,我是否可以依赖这个假设.内核会发生什么?
我想计算某段代码的(或多或少)确切的指令数量。此外,我希望在通过特定数量的指令后收到信号。
您有两项可能相互冲突的任务。当您想要计数(某些硬件事件的确切数量)时,只需在计数模式下使用 CPU 的性能监控单元(不要设置sample_period/使用sample_freq的perf_event_attr结构)并将测量代码放入目标程序中(就像完成的那样)在你的例子中)。在这种模式下,根据手册页perf_event_open不会产生溢出(CPU的PMU通常是64位宽,并且在使用采样模式时不设置为小负值时不会溢出):
溢出仅由采样事件生成(sample_period 必须为非零值)。
要对程序的一部分进行计数,请使用ioctlperf_event_open 返回的 fd 的 s,如手册页中所述
perf_event ioctl 调用 - 各种 ioctl 作用于 perf_event_open() 文件描述符: PERF_EVENT_IOC_ENABLE ... PERF_EVENT_IOC_DISABLE ... PERF_EVENT_IOC_RESET
您可以使用rdpmc(在 x86 上)或通过readfd 上的系统调用来读取当前值,如手册页中的简短示例所示:
#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, 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;
}
int
main(int argc, char **argv)
{
struct perf_event_attr pe;
long long count;
int fd;
memset(&pe, 0, 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, 0, -1, -1, 0);
if (fd == -1) {
fprintf(stderr, "Error opening leader %llx\n", pe.config);
exit(EXIT_FAILURE);
}
ioctl(fd, PERF_EVENT_IOC_RESET, 0);
ioctl(fd, PERF_EVENT_IOC_ENABLE, 0);
printf("Measuring instruction count for this printf\n");
/* Place target code here instead of printf */
ioctl(fd, PERF_EVENT_IOC_DISABLE, 0);
read(fd, &count, sizeof(long long));
printf("Used %lld instructions\n", count);
close(fd);
}
Run Code Online (Sandbox Code Playgroud)
此外,我希望在通过特定数量的指令后收到信号。
您真的想获得信号还是只需要每执行 1000 条指令时就有指令指针?如果要收集指针,请使用perf_even_open采样模式,但从其他程序中执行此操作以禁用事件收集代码的测量。此外,如果您不为每个溢出使用信号(具有大量的内核跟踪器交互以及从/到内核的切换),而是使用 perf_events 的功能来收集多个溢出事件,那么它对目标程序的负面影响会更小进入单个 mmap 缓冲区并轮询该缓冲区。当 PMU 溢出中断时,将调用 perf 中断处理程序将指令指针保存到缓冲区中,然后计数将被重置,程序将返回执行。在您的示例中,perf 中断处理程序将唤醒您的程序,它将执行多个系统调用,返回内核,然后内核将重新启动目标代码(因此每个样本的开销大于使用 mmap 并解析它)。使用precise_ip标志,您可以激活 PMU 的高级采样(如果它具有此类模式,例如 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 并使用简单的 hack也cycles;或者像AMD x86/amd64的IBS;关于PEBS和IBS的论文),当指令地址直接由低速硬件保存时。一些非常先进的 PMU 能够在硬件中进行采样,连续存储多个事件的溢出信息,并在没有软件中断的情况下自动重置计数器(一些描述在precise_ip同一篇论文中)。
我不知道在 perf_events 子系统和 CPU 中是否可以同时激活两个 perf_event 任务:两个任务都对目标进程中的事件进行计数,同时从其他进程中进行采样。借助先进的 PMU,这在硬件中是可能的,并且现代内核中的 perf_events 可能允许它。但您没有提供有关您的内核版本以及 CPU 供应商和系列的详细信息,因此我们无法回答这部分。
您还可以尝试其他 API 来访问 PMU,例如 PAPI 或 likwid ( https://github.com/RRZE-HPC/likwid )。其中一些可以直接读取 PMU 寄存器(有时是 MSR),并且可以在启用计数时允许同时采样。
| 归档时间: |
|
| 查看次数: |
3103 次 |
| 最近记录: |