/* * perf events self profiling example test case for hw breakpoints. * * This tests perf PERF_TYPE_BREAKPOINT parameters * 1) tests all variants of the break on read/write flags * 2) tests exclude_user == 0 and 1 * 3) test array matches (if DAWR is supported)) * 4) test different numbers of breakpoints matches * * Configure this breakpoint, then read and write the data a number of * times. Then check the output count from perf is as expected. * * Based on: * http://ozlabs.org/~anton/junkcode/perf_events_example1.c * * Copyright (C) 2018 Michael Neuling, IBM Corporation. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ #include #include #include #include #include #include #include #include #include #include #include #include "utils.h" #define MAX_LOOPS 10000 #define DAWR_LENGTH_MAX ((0x3f + 1) * 8) 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); } static inline bool breakpoint_test(int len) { struct perf_event_attr attr; int fd; /* setup counters */ memset(&attr, 0, sizeof(attr)); attr.disabled = 1; attr.type = PERF_TYPE_BREAKPOINT; attr.bp_type = HW_BREAKPOINT_R; /* bp_addr can point anywhere but needs to be aligned */ attr.bp_addr = (__u64)(&attr) & 0xfffffffffffff800; attr.bp_len = len; fd = sys_perf_event_open(&attr, 0, -1, -1, 0); if (fd < 0) return false; close(fd); return true; } static inline bool perf_breakpoint_supported(void) { return breakpoint_test(4); } static inline bool dawr_supported(void) { return breakpoint_test(DAWR_LENGTH_MAX); } static int runtestsingle(int readwriteflag, int exclude_user, int arraytest) { int i,j; struct perf_event_attr attr; size_t res; unsigned long long breaks, needed; int readint; int readintarraybig[2*DAWR_LENGTH_MAX/sizeof(int)]; int *readintalign; volatile int *ptr; int break_fd; int loop_num = MAX_LOOPS - (rand() % 100); /* provide some variability */ volatile int *k; /* align to 0x400 boundary as required by DAWR */ readintalign = (int *)(((unsigned long)readintarraybig + 0x7ff) & 0xfffffffffffff800); ptr = &readint; if (arraytest) ptr = &readintalign[0]; /* setup counters */ memset(&attr, 0, sizeof(attr)); attr.disabled = 1; attr.type = PERF_TYPE_BREAKPOINT; attr.bp_type = readwriteflag; attr.bp_addr = (__u64)ptr; attr.bp_len = sizeof(int); if (arraytest) attr.bp_len = DAWR_LENGTH_MAX; attr.exclude_user = exclude_user; break_fd = sys_perf_event_open(&attr, 0, -1, -1, 0); if (break_fd < 0) { perror("sys_perf_event_open"); exit(1); } /* start counters */ ioctl(break_fd, PERF_EVENT_IOC_ENABLE); /* Test a bunch of reads and writes */ k = &readint; for (i = 0; i < loop_num; i++) { if (arraytest) k = &(readintalign[i % (DAWR_LENGTH_MAX/sizeof(int))]); j = *k; *k = j; } /* stop counters */ ioctl(break_fd, PERF_EVENT_IOC_DISABLE); /* read and check counters */ res = read(break_fd, &breaks, sizeof(unsigned long long)); assert(res == sizeof(unsigned long long)); /* we read and write each loop, so subtract the ones we are counting */ needed = 0; if (readwriteflag & HW_BREAKPOINT_R) needed += loop_num; if (readwriteflag & HW_BREAKPOINT_W) needed += loop_num; needed = needed * (1 - exclude_user); printf("TESTED: addr:0x%lx brks:% 8lld loops:% 8i rw:%i !user:%i array:%i\n", (unsigned long int)ptr, breaks, loop_num, readwriteflag, exclude_user, arraytest); if (breaks != needed) { printf("FAILED: 0x%lx brks:%lld needed:%lli %i %i %i\n\n", (unsigned long int)ptr, breaks, needed, loop_num, readwriteflag, exclude_user); return 1; } close(break_fd); return 0; } static int runtest(void) { int rwflag; int exclude_user; int ret; /* * perf defines rwflag as two bits read and write and at least * one must be set. So range 1-3. */ for (rwflag = 1 ; rwflag < 4; rwflag++) { for (exclude_user = 0 ; exclude_user < 2; exclude_user++) { ret = runtestsingle(rwflag, exclude_user, 0); if (ret) return ret; /* if we have the dawr, we can do an array test */ if (!dawr_supported()) continue; ret = runtestsingle(rwflag, exclude_user, 1); if (ret) return ret; } } return 0; } static int perf_hwbreak(void) { srand ( time(NULL) ); SKIP_IF(!perf_breakpoint_supported()); return runtest(); } int main(int argc, char *argv[], char **envp) { return test_harness(perf_hwbreak, "perf_hwbreak"); }