From 367df352d32b7115fbfd90cc07b6c35dbaff7d64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ciro=20Santilli=20=E5=85=AD=E5=9B=9B=E4=BA=8B=E4=BB=B6=20?= =?UTF-8?q?=E6=B3=95=E8=BD=AE=E5=8A=9F?= Date: Wed, 18 Nov 2020 00:00:01 +0000 Subject: [PATCH] perf_event_open: generalize to multiple events --- path_properties.py | 9 ++- userland/linux/perf_event_open.c | 102 +++++++++++++++++++++---------- 2 files changed, 77 insertions(+), 34 deletions(-) diff --git a/path_properties.py b/path_properties.py index ff8804a..82882c6 100644 --- a/path_properties.py +++ b/path_properties.py @@ -787,7 +787,14 @@ path_properties_tuples = ( 'gem5_unimplemented_syscall': True }, 'pagemap_dump.c': {'requires_argument': True}, - 'perf_event_open.c': {'extra_objs_lkmc_common': True}, + 'perf_event_open.c': { + # QEMU the syscall just fails on QEMU, presumably because QEMU + # does not have a microarchitecture model, and so it must just set + # CPU bits that inform the kernel that the feature is not available. + # https://cirosantilli.com/linux-kernel-module-cheat#gem5-vs-qemu + 'allowed_emulators': {'gem5'}, + 'extra_objs_lkmc_common': True, + }, 'poweroff.c': {'requires_sudo': True}, 'proc_events.c': {'requires_sudo': True}, 'proc_events.c': {'requires_sudo': True}, diff --git a/userland/linux/perf_event_open.c b/userland/linux/perf_event_open.c index afad774..c3deac3 100644 --- a/userland/linux/perf_event_open.c +++ b/userland/linux/perf_event_open.c @@ -1,7 +1,5 @@ /* https://cirosantilli.com/linux-kernel-module-cheat#perf-event-open - * - * Malloc n bytes as given from the command line. - */ + * Adapted from `man perf_event_open` in manpages 5.05-1. */ #include #include @@ -11,57 +9,95 @@ #include #include +#define LKMC_M5OPS_ENABLE 1 #include -static long -perf_event_open(struct perf_event_attr *hw_event, pid_t pid, - int cpu, int group_fd, unsigned long flags) -{ +static long perf_event_open(struct perf_event_attr *hw_event, + uint32_t type, uint64_t config, pid_t pid, + int cpu, int group_fd, unsigned long flags +) { int ret; + memset(hw_event, 0, sizeof(struct perf_event_attr)); + hw_event->type = type; + hw_event->size = sizeof(struct perf_event_attr); + hw_event->config = config; + hw_event->disabled = 1; + hw_event->exclude_kernel = 1; + /* Don't count hypervisor events. */ + hw_event->exclude_hv = 1; ret = syscall(__NR_perf_event_open, hw_event, pid, cpu, group_fd, flags); + if (ret == -1) { + fprintf(stderr, "Error opening leader %llx\n", hw_event->config); + exit(EXIT_FAILURE); + } return ret; } -int -main(int argc, char **argv) -{ - struct perf_event_attr pe; - long long count; - int fd; +typedef struct { + char *name; + uint32_t type; + uint64_t config; +} Desc; +#define DESC(type,config) {#config, type, config} + +int +main(int argc, char **argv) { + size_t i; + int gem5; + long long count; uint64_t n; + Desc descs[] = { + DESC(PERF_TYPE_HARDWARE, PERF_COUNT_HW_BRANCH_INSTRUCTIONS), + DESC(PERF_TYPE_HARDWARE, PERF_COUNT_HW_BRANCH_MISSES), + DESC(PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES), + DESC(PERF_TYPE_HARDWARE, PERF_COUNT_HW_INSTRUCTIONS), + DESC(PERF_TYPE_HW_CACHE, PERF_COUNT_HW_CACHE_L1D | PERF_COUNT_HW_CACHE_OP_READ << 8 | PERF_COUNT_HW_CACHE_RESULT_MISS << 16), + }; + struct perf_event_attr pes[LKMC_ARRAY_SIZE(descs)]; + int fds[LKMC_ARRAY_SIZE(descs)]; + if (argc > 1) { n = strtoll(argv[1], NULL, 0); } else { - n = 100; + n = 10000; + } + if (argc > 2) { + gem5 = argv[2][0] == '1'; + } else { + gem5 = 0; } - 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; - // Don't count hypervisor events. - pe.exclude_hv = 1; + for (i = 0; i < LKMC_ARRAY_SIZE(descs); i++) + fds[i] = perf_event_open(&pes[i], + descs[i].type, descs[i].config, 0, -1, -1, 0); - fd = perf_event_open(&pe, 0, -1, -1, 0); - if (fd == -1) { - fprintf(stderr, "Error opening leader %llx\n", pe.config); - exit(EXIT_FAILURE); + /* Start the counts. */ + for (i = 0; i < LKMC_ARRAY_SIZE(descs); i++) + ioctl(fds[i], PERF_EVENT_IOC_RESET, 0); + for (i = 0; i < LKMC_ARRAY_SIZE(descs); i++) + ioctl(fds[i], PERF_EVENT_IOC_ENABLE, 0); + if (gem5) { + /* Cross check with gem5 stats to see if things make sense.. */ + LKMC_M5OPS_RESETSTATS; } - ioctl(fd, PERF_EVENT_IOC_RESET, 0); - ioctl(fd, PERF_EVENT_IOC_ENABLE, 0); - + /* Mesure this function. */ lkmc_busy_loop(n, 1); - ioctl(fd, PERF_EVENT_IOC_DISABLE, 0); - read(fd, &count, sizeof(long long)); + /* Stop the count. */ + if (gem5) { LKMC_M5OPS_DUMPSTATS; } + for (i = 0; i < LKMC_ARRAY_SIZE(descs); i++) + ioctl(fds[i], PERF_EVENT_IOC_DISABLE, 0); - printf("Used %lld instructions\n", count); + /* Print results. */ + for (i = 0; i < LKMC_ARRAY_SIZE(descs); i++) { + read(fds[i], &count, sizeof(long long)); + printf("%s %lld\n", descs[i].name, count); + } - close(fd); + for (i = 0; i < LKMC_ARRAY_SIZE(descs); i++) + close(fds[i]); }