有谁知道如何设置perf_event_attr可触发PMU通过以下方式监视多个(类型)事件的结构perf_event_open()?
perf_event_attr
perf_event_open()
像一样perf record -e cycles,faults ls,它具有两种不同的事件类型(PERF_TYPE_HARDWARE和PERF_TYPE_SOFTWARE),但是在perf_event_open的联机帮助页上的示例中,perf_event_attr.type只能分配单个值。
perf record -e cycles,faults ls
perf_event_attr.type
任何建议将不胜感激,谢谢!
20170208更新 感谢@gudok为我指明方向,但结果似乎有些异常。如下所示的演示程序(用于测量整个系统的CPU周期和缓存未命中):
#define _GNU_SOURCE #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <sys/syscall.h> #include <string.h> #include <sys/ioctl.h> #include <linux/perf_event.h> #include <linux/hw_breakpoint.h> #include <asm/unistd.h> #include <errno.h> #include <stdint.h> #include <inttypes.h> #include <time.h> struct read_format { uint64_t nr; struct { uint64_t value; uint64_t id; } values[]; }; int main(int argc, char* argv[]) { struct perf_event_attr pea; int fd1, fd2; uint64_t id1, id2; uint64_t val1, val2; char buf[4096]; struct read_format* rf = (struct read_format*) buf; int i,j; struct timespec time, time2; time.tv_sec = 1; time.tv_nsec = 0; memset(&pea, 0, sizeof(struct perf_event_attr)); pea.type = PERF_TYPE_HARDWARE; pea.size = sizeof(struct perf_event_attr); pea.config = PERF_COUNT_HW_CPU_CYCLES; pea.disabled = 1; pea.exclude_kernel = 1; pea.exclude_hv = 1; pea.read_format = PERF_FORMAT_GROUP | PERF_FORMAT_ID; fd1 = syscall(__NR_perf_event_open, &pea, 0, -1, -1, 0); ioctl(fd1, PERF_EVENT_IOC_ID, &id1); memset(&pea, 0, sizeof(struct perf_event_attr)); pea.type = PERF_TYPE_HARDWARE; pea.size = sizeof(struct perf_event_attr); pea.config = PERF_COUNT_HW_CACHE_MISSES; pea.disabled = 1; pea.exclude_kernel = 1; pea.exclude_hv = 1; pea.precise_ip = 2; // want to using PEBS pea.read_format = PERF_FORMAT_GROUP | PERF_FORMAT_ID; fd2 = syscall(__NR_perf_event_open, &pea, 0, -1, fd1 /*!!!*/, 0); ioctl(fd2, PERF_EVENT_IOC_ID, &id2); ioctl(fd1, PERF_EVENT_IOC_RESET, PERF_IOC_FLAG_GROUP); ioctl(fd1, PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP); while (1) { nanosleep(&time, &time2); //ioctl(fd1, PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP); read(fd1, buf, sizeof(buf)); for (i = 0; i < rf->nr; i++) { if (rf->values[i].id == id1) { val1 = rf->values[i].value; } else if (rf->values[i].id == id2) { val2 = rf->values[i].value; } } printf("cpu cycles: %"PRIu64"\n", val1); printf("cache misses: %"PRIu64"\n", val2); } return 0; }
输出为:
cpu cycles: 120 // Just have about 120 CPU cycles in a second cache misses: 0 // and doesn't have any cache miss? cpu cycles: 233 cache misses: 0 cpu cycles: 352 cache misses: 0 cpu cycles: 455 cache misses: 0 cpu cycles: 562 cache misses: 0 cpu cycles: 673 cache misses: 0 cpu cycles: 794 cache misses: 0 cpu cycles: 907 cache misses: 0 cpu cycles: 1011 cache misses: 0 cpu cycles: 1129 cache misses: 3 cpu cycles: 1269 cache misses: 4 cpu cycles: 1423
有点棘手。
我们照常创建第一个计数器。此外,我们通过PERF_FORMAT_GROUP并PERF_FORMAT_ID能够同时使用多个计数器。这个柜台将是我们的小组组长。
PERF_FORMAT_GROUP
PERF_FORMAT_ID
struct perf_event_attr pea; int fd1, fd2; uint64_t id1, id2; memset(&pea, 0, sizeof(struct perf_event_attr)); pea.type = PERF_TYPE_HARDWARE; pea.size = sizeof(struct perf_event_attr); pea.config = PERF_COUNT_HW_CPU_CYCLES; pea.disabled = 1; pea.exclude_kernel = 1; pea.exclude_hv = 1; pea.read_format = PERF_FORMAT_GROUP | PERF_FORMAT_ID; fd1 = syscall(__NR_perf_event_open, &pea, 0, -1, -1, 0);
接下来,我们检索第一个计数器的标识符:
ioctl(fd1, PERF_EVENT_IOC_ID, &id1);
第二个(以及所有其他计数器)以相同的方式创建,只有一个例外:我们将fd1值作为组长参数传递:
fd1
memset(&pea, 0, sizeof(struct perf_event_attr)); pea.type = PERF_TYPE_SOFTWARE; pea.size = sizeof(struct perf_event_attr); pea.config = PERF_COUNT_SW_PAGE_FAULTS; pea.disabled = 1; pea.exclude_kernel = 1; pea.exclude_hv = 1; pea.read_format = PERF_FORMAT_GROUP | PERF_FORMAT_ID; fd2 = syscall(__NR_perf_event_open, &pea, 0, -1, fd1, 0); // <-- here ioctl(fd2, PERF_EVENT_IOC_ID, &id2);
接下来,我们需要声明一个数据结构以一次读取多个计数器。您必须根据传递给的标志声明不同的字段集perf_event_open。手册页提到了所有可能的字段。在我们的例子中,我们通过PERF_FORMAT_ID了添加id字段的标志。这将使我们能够区分不同的计数器。
perf_event_open
id
struct read_format { uint64_t nr; struct { uint64_t value; uint64_t id; } values[/*2*/]; };
现在我们将标准配置文件称为ioctl:
ioctl(fd1, PERF_EVENT_IOC_RESET, PERF_IOC_FLAG_GROUP); ioctl(fd1, PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP); do_something(); ioctl(fd1, PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP);
最后,我们从组长文件描述符中读取计数器。这两个计数器都以read_format我们声明的单一结构返回:
read_format
char buf[4096]; struct read_format* rf = (struct read_format*) buf; uint64_t val1, val2; read(fd1, buf, sizeof(buf)); for (i = 0; i < rf->nr; i++) { if (rf->values[i].id == id1) { val1 = rf->values[i].value; } else if (rf->values[i].id == id2) { val2 = rf->values[i].value; } } printf("cpu cycles: %"PRIu64"\n", val1); printf("page faults: %"PRIu64"\n", val2);
以下是完整的程序清单:
#define _GNU_SOURCE #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <sys/syscall.h> #include <string.h> #include <sys/ioctl.h> #include <linux/perf_event.h> #include <linux/hw_breakpoint.h> #include <asm/unistd.h> #include <errno.h> #include <stdint.h> #include <inttypes.h> struct read_format { uint64_t nr; struct { uint64_t value; uint64_t id; } values[]; }; void do_something() { int i; char* ptr; ptr = malloc(100*1024*1024); for (i = 0; i < 100*1024*1024; i++) { ptr[i] = (char) (i & 0xff); // pagefault } free(ptr); } int main(int argc, char* argv[]) { struct perf_event_attr pea; int fd1, fd2; uint64_t id1, id2; uint64_t val1, val2; char buf[4096]; struct read_format* rf = (struct read_format*) buf; int i; memset(&pea, 0, sizeof(struct perf_event_attr)); pea.type = PERF_TYPE_HARDWARE; pea.size = sizeof(struct perf_event_attr); pea.config = PERF_COUNT_HW_CPU_CYCLES; pea.disabled = 1; pea.exclude_kernel = 1; pea.exclude_hv = 1; pea.read_format = PERF_FORMAT_GROUP | PERF_FORMAT_ID; fd1 = syscall(__NR_perf_event_open, &pea, 0, -1, -1, 0); ioctl(fd1, PERF_EVENT_IOC_ID, &id1); memset(&pea, 0, sizeof(struct perf_event_attr)); pea.type = PERF_TYPE_SOFTWARE; pea.size = sizeof(struct perf_event_attr); pea.config = PERF_COUNT_SW_PAGE_FAULTS; pea.disabled = 1; pea.exclude_kernel = 1; pea.exclude_hv = 1; pea.read_format = PERF_FORMAT_GROUP | PERF_FORMAT_ID; fd2 = syscall(__NR_perf_event_open, &pea, 0, -1, fd1 /*!!!*/, 0); ioctl(fd2, PERF_EVENT_IOC_ID, &id2); ioctl(fd1, PERF_EVENT_IOC_RESET, PERF_IOC_FLAG_GROUP); ioctl(fd1, PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP); do_something(); ioctl(fd1, PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP); read(fd1, buf, sizeof(buf)); for (i = 0; i < rf->nr; i++) { if (rf->values[i].id == id1) { val1 = rf->values[i].value; } else if (rf->values[i].id == id2) { val2 = rf->values[i].value; } } printf("cpu cycles: %"PRIu64"\n", val1); printf("page faults: %"PRIu64"\n", val2); return 0; }