/* vim: set expandtab tabstop=4 softtabstop=4 shiftwidth=4: */ #include #include #include #include #include #include #include #include #include #include #ifndef __NR_perf_event_open #error __NR_perf_event_open not defined. #endif #define PAGE_SIZE 8192 #define NUM_TIMES 100000 static const char *event_name[] = { "cycles", "instructions", "cache-misses", "mbox-replays" }; static inline int sys_perf_event_open(struct perf_event_attr *attr, pid_t pid, int cpu, int group_fd, unsigned long flags) { attr->size = sizeof(*attr); return syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags); } void clear_page(void *page); char *src __attribute__((used)); int main(int argc, char *argv[]) { struct perf_event_attr *attr; int fd[4]; char *ptr; int num_pages; if (argc != 2) { printf("Usage: %s \n", argv[0]); return 1; } num_pages = atoi(argv[1]); /* Sanity test to ensure a whole page is cleared, and nothing more */ if (posix_memalign((void **)&src, PAGE_SIZE, 3 * PAGE_SIZE)) return 1; memset(src, 0xff, 3 * PAGE_SIZE); mprotect(src + 0 * PAGE_SIZE, PAGE_SIZE, PROT_NONE); mprotect(src + 2 * PAGE_SIZE, PAGE_SIZE, PROT_NONE); clear_page(src + PAGE_SIZE); mprotect(src + 0 * PAGE_SIZE, PAGE_SIZE, PROT_READ); mprotect(src + 2 * PAGE_SIZE, PAGE_SIZE, PROT_READ); if ((ptr = memchr(src + 0 * PAGE_SIZE, 0x00, PAGE_SIZE)) != NULL) { fprintf(stderr, "Sanity test failed: page 0 cleared at byte %lu\n", ptr - (src + 0 * PAGE_SIZE)); return 1; } if ((ptr = memchr(src + 1 * PAGE_SIZE, 0xff, PAGE_SIZE)) != NULL) { fprintf(stderr, "Sanity test failed: page 1 not cleared at byte %lu\n", ptr - (src + 1 * PAGE_SIZE)); return 1; } if ((ptr = memchr(src + 2 * PAGE_SIZE, 0x00, PAGE_SIZE)) != NULL) { fprintf(stderr, "Sanity test failed: page 2 cleared at byte %lu\n", ptr - (src + 2 * PAGE_SIZE)); return 1; } free(src); /* Setup */ if (posix_memalign((void **)&src, PAGE_SIZE, num_pages * PAGE_SIZE + 1)) return 1; memset(src, 0xff, num_pages * PAGE_SIZE + 1); attr = calloc(4, sizeof(*attr)); if (src == NULL || attr == NULL) return 1; attr[0].type = PERF_TYPE_HARDWARE; attr[0].config = PERF_COUNT_HW_CPU_CYCLES; attr[0].read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING; attr[0].disabled = 1; attr[1].type = PERF_TYPE_HARDWARE; attr[1].config = PERF_COUNT_HW_INSTRUCTIONS; attr[1].read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING; attr[1].disabled = 1; attr[2].type = PERF_TYPE_HARDWARE; attr[2].read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING; attr[2].config = PERF_COUNT_HW_CACHE_MISSES; attr[2].disabled = 1; attr[3].type = PERF_TYPE_RAW; attr[3].config = 4; attr[3].read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING; attr[3].disabled = 1; for (int i = 0; i < 4; i++) { fd[i] = sys_perf_event_open(attr + i, 0, -1, -1, 0); if (fd[i] < 0) { perror("perf_open event"); return 1; } } /* Benchmark */ prctl(PR_TASK_PERF_EVENTS_ENABLE); for (int i = 0; i < NUM_TIMES; i++) { for (int j = 0; j < num_pages; j++) { clear_page(src + j * PAGE_SIZE); } } prctl(PR_TASK_PERF_EVENTS_DISABLE); /* Verify */ if ((ptr = memchr(src, 0xff, num_pages * PAGE_SIZE + 1)) != (src + num_pages * PAGE_SIZE)) { fprintf(stderr, "Failed to clear at byte %lu\n", ptr - src); return 1; } for (int i = 0; i < 4; i++) { unsigned long count[3]; if (read(fd[i], count, 3 * sizeof(__u64)) != 3 * sizeof(__u64)) perror("read of event failed"); printf("%20s: ", event_name[i]); if (count[2] == 0) { printf("not counted\n"); } else if (count[2] < count[1]) { printf("%10lu (scaled from %3.1f%%)\n", (unsigned long)((double)count[0] * count[1] / count[2] / NUM_TIMES + 0.5), 100.0 * (double)count[2] / count[1]); } else { printf("%lu\n", count[0] / NUM_TIMES); } close(fd[i]); } free(src); free(attr); return 0; }