我在我的 c 分析应用程序中使用
perf_event_open
来利用 perf 来获取事件数据。为了提高性能,我直接读取硬件寄存器,按照Perf Userspace PMU Hardware Counter Access Documentation使用mrs
指令直接读取PMU寄存器。
我使用以下代码:
static struct perf_event_attr attr;
attr.type = PERF_TYPE_HARDWARE;
attr.config = PERF_COUNT_HW_CPU_CYCLES;
attr.exclude_kernel = 1;
attr.exclude_hv = 1;
attr.config1 = 3; // user access enabled
int fd = syscall(SYS_perf_event_open, &attr, 0, -1, -1, 0);
ioctl(fd, PERF_EVENT_IOC_RESET, 0);
ioctl(fd, PERF_EVENT_IOC_ENABLE, 0);
// Code where we want to measure performance. At certain points we call read_register_directly()
ioctl(fd, PERF_EVENT_IOC_DISABLE, 0);
close(fd);
uint64_t read_register_directly() {
uint64_t value = 0;
asm volatile("mrs %0, PMCCNTR_EL0 " : "=r" (value));
return value;
}
上面直接读取寄存器的代码在 perf 配置下可以正常工作。问题是,在读取寄存器大约 25 次之后,我收到了“非法指令”错误,尽管我不确定为什么。
我查看了 ARM 文档中的 PMCCNTR_EL0 和其他一些资源,但我没有找到任何可以解释此非法指令错误的内容。
这最终是由于在
perf_event_open
系统调用中传递的属性造成的。进行系统调用的线程能够直接读取寄存器,但其他线程导致“非法指令”错误。
有一个
perf_event_attr
标志 inherit
,它允许用户分析进程中的所有线程,而不仅仅是由 perf_event_open
执行的线程。所以在上面的代码中我添加了两件事来修复代码流程:
attr.inherit = 1;
asm volatile("isb;");
读取PMU寄存器后