1 /* 2 * perf events self profiling example test case for hw breakpoints. 3 * 4 * This tests perf PERF_TYPE_BREAKPOINT parameters 5 * 1) tests all variants of the break on read/write flags 6 * 2) tests exclude_user == 0 and 1 7 * 3) test array matches (if DAWR is supported)) 8 * 4) test different numbers of breakpoints matches 9 * 10 * Configure this breakpoint, then read and write the data a number of 11 * times. Then check the output count from perf is as expected. 12 * 13 * Based on: 14 * http://ozlabs.org/~anton/junkcode/perf_events_example1.c 15 * 16 * Copyright (C) 2018 Michael Neuling, IBM Corporation. 17 * 18 * This program is free software; you can redistribute it and/or 19 * modify it under the terms of the GNU General Public License 20 * as published by the Free Software Foundation; either version 21 * 2 of the License, or (at your option) any later version. 22 */ 23 24 #include <unistd.h> 25 #include <assert.h> 26 #include <stdio.h> 27 #include <stdlib.h> 28 #include <string.h> 29 #include <sys/ioctl.h> 30 #include <elf.h> 31 #include <pthread.h> 32 #include <sys/syscall.h> 33 #include <linux/perf_event.h> 34 #include <linux/hw_breakpoint.h> 35 #include "utils.h" 36 37 #define MAX_LOOPS 10000 38 39 #define DAWR_LENGTH_MAX ((0x3f + 1) * 8) 40 41 static inline int sys_perf_event_open(struct perf_event_attr *attr, pid_t pid, 42 int cpu, int group_fd, 43 unsigned long flags) 44 { 45 attr->size = sizeof(*attr); 46 return syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags); 47 } 48 49 static inline bool breakpoint_test(int len) 50 { 51 struct perf_event_attr attr; 52 int fd; 53 54 /* setup counters */ 55 memset(&attr, 0, sizeof(attr)); 56 attr.disabled = 1; 57 attr.type = PERF_TYPE_BREAKPOINT; 58 attr.bp_type = HW_BREAKPOINT_R; 59 /* bp_addr can point anywhere but needs to be aligned */ 60 attr.bp_addr = (__u64)(&attr) & 0xfffffffffffff800; 61 attr.bp_len = len; 62 fd = sys_perf_event_open(&attr, 0, -1, -1, 0); 63 if (fd < 0) 64 return false; 65 close(fd); 66 return true; 67 } 68 69 static inline bool perf_breakpoint_supported(void) 70 { 71 return breakpoint_test(4); 72 } 73 74 static inline bool dawr_supported(void) 75 { 76 return breakpoint_test(DAWR_LENGTH_MAX); 77 } 78 79 static int runtestsingle(int readwriteflag, int exclude_user, int arraytest) 80 { 81 int i,j; 82 struct perf_event_attr attr; 83 size_t res; 84 unsigned long long breaks, needed; 85 int readint; 86 int readintarraybig[2*DAWR_LENGTH_MAX/sizeof(int)]; 87 int *readintalign; 88 volatile int *ptr; 89 int break_fd; 90 int loop_num = MAX_LOOPS - (rand() % 100); /* provide some variability */ 91 volatile int *k; 92 93 /* align to 0x400 boundary as required by DAWR */ 94 readintalign = (int *)(((unsigned long)readintarraybig + 0x7ff) & 95 0xfffffffffffff800); 96 97 ptr = &readint; 98 if (arraytest) 99 ptr = &readintalign[0]; 100 101 /* setup counters */ 102 memset(&attr, 0, sizeof(attr)); 103 attr.disabled = 1; 104 attr.type = PERF_TYPE_BREAKPOINT; 105 attr.bp_type = readwriteflag; 106 attr.bp_addr = (__u64)ptr; 107 attr.bp_len = sizeof(int); 108 if (arraytest) 109 attr.bp_len = DAWR_LENGTH_MAX; 110 attr.exclude_user = exclude_user; 111 break_fd = sys_perf_event_open(&attr, 0, -1, -1, 0); 112 if (break_fd < 0) { 113 perror("sys_perf_event_open"); 114 exit(1); 115 } 116 117 /* start counters */ 118 ioctl(break_fd, PERF_EVENT_IOC_ENABLE); 119 120 /* Test a bunch of reads and writes */ 121 k = &readint; 122 for (i = 0; i < loop_num; i++) { 123 if (arraytest) 124 k = &(readintalign[i % (DAWR_LENGTH_MAX/sizeof(int))]); 125 126 j = *k; 127 *k = j; 128 } 129 130 /* stop counters */ 131 ioctl(break_fd, PERF_EVENT_IOC_DISABLE); 132 133 /* read and check counters */ 134 res = read(break_fd, &breaks, sizeof(unsigned long long)); 135 assert(res == sizeof(unsigned long long)); 136 /* we read and write each loop, so subtract the ones we are counting */ 137 needed = 0; 138 if (readwriteflag & HW_BREAKPOINT_R) 139 needed += loop_num; 140 if (readwriteflag & HW_BREAKPOINT_W) 141 needed += loop_num; 142 needed = needed * (1 - exclude_user); 143 printf("TESTED: addr:0x%lx brks:% 8lld loops:% 8i rw:%i !user:%i array:%i\n", 144 (unsigned long int)ptr, breaks, loop_num, readwriteflag, exclude_user, arraytest); 145 if (breaks != needed) { 146 printf("FAILED: 0x%lx brks:%lld needed:%lli %i %i %i\n\n", 147 (unsigned long int)ptr, breaks, needed, loop_num, readwriteflag, exclude_user); 148 return 1; 149 } 150 close(break_fd); 151 152 return 0; 153 } 154 155 static int runtest(void) 156 { 157 int rwflag; 158 int exclude_user; 159 int ret; 160 161 /* 162 * perf defines rwflag as two bits read and write and at least 163 * one must be set. So range 1-3. 164 */ 165 for (rwflag = 1 ; rwflag < 4; rwflag++) { 166 for (exclude_user = 0 ; exclude_user < 2; exclude_user++) { 167 ret = runtestsingle(rwflag, exclude_user, 0); 168 if (ret) 169 return ret; 170 171 /* if we have the dawr, we can do an array test */ 172 if (!dawr_supported()) 173 continue; 174 ret = runtestsingle(rwflag, exclude_user, 1); 175 if (ret) 176 return ret; 177 } 178 } 179 return 0; 180 } 181 182 183 static int perf_hwbreak(void) 184 { 185 srand ( time(NULL) ); 186 187 SKIP_IF(!perf_breakpoint_supported()); 188 189 return runtest(); 190 } 191 192 int main(int argc, char *argv[], char **envp) 193 { 194 return test_harness(perf_hwbreak, "perf_hwbreak"); 195 } 196