1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * perf events self profiling example test case for hw breakpoints. |
4 | * |
5 | * This tests perf PERF_TYPE_BREAKPOINT parameters |
6 | * 1) tests all variants of the break on read/write flags |
7 | * 2) tests exclude_user == 0 and 1 |
8 | * 3) test array matches (if DAWR is supported)) |
9 | * 4) test different numbers of breakpoints matches |
10 | * |
11 | * Configure this breakpoint, then read and write the data a number of |
12 | * times. Then check the output count from perf is as expected. |
13 | * |
14 | * Based on: |
15 | * http://ozlabs.org/~anton/junkcode/perf_events_example1.c |
16 | * |
17 | * Copyright (C) 2018 Michael Neuling, IBM Corporation. |
18 | */ |
19 | |
20 | #define _GNU_SOURCE |
21 | |
22 | #include <unistd.h> |
23 | #include <assert.h> |
24 | #include <sched.h> |
25 | #include <stdio.h> |
26 | #include <stdlib.h> |
27 | #include <signal.h> |
28 | #include <string.h> |
29 | #include <sys/ioctl.h> |
30 | #include <sys/wait.h> |
31 | #include <sys/ptrace.h> |
32 | #include <sys/resource.h> |
33 | #include <sys/sysinfo.h> |
34 | #include <asm/ptrace.h> |
35 | #include <elf.h> |
36 | #include <pthread.h> |
37 | #include <sys/syscall.h> |
38 | #include <linux/perf_event.h> |
39 | #include <linux/hw_breakpoint.h> |
40 | #include "utils.h" |
41 | |
42 | #ifndef PPC_DEBUG_FEATURE_DATA_BP_ARCH_31 |
43 | #define PPC_DEBUG_FEATURE_DATA_BP_ARCH_31 0x20 |
44 | #endif |
45 | |
46 | #define MAX_LOOPS 10000 |
47 | |
48 | #define DAWR_LENGTH_MAX ((0x3f + 1) * 8) |
49 | |
50 | int nprocs; |
51 | |
52 | static volatile int a = 10; |
53 | static volatile int b = 10; |
54 | static volatile char c[512 + 8] __attribute__((aligned(512))); |
55 | |
56 | static void perf_event_attr_set(struct perf_event_attr *attr, |
57 | __u32 type, __u64 addr, __u64 len, |
58 | bool exclude_user) |
59 | { |
60 | memset(attr, 0, sizeof(struct perf_event_attr)); |
61 | attr->type = PERF_TYPE_BREAKPOINT; |
62 | attr->size = sizeof(struct perf_event_attr); |
63 | attr->bp_type = type; |
64 | attr->bp_addr = addr; |
65 | attr->bp_len = len; |
66 | attr->exclude_kernel = 1; |
67 | attr->exclude_hv = 1; |
68 | attr->exclude_guest = 1; |
69 | attr->exclude_user = exclude_user; |
70 | attr->disabled = 1; |
71 | } |
72 | |
73 | static int |
74 | perf_process_event_open_exclude_user(__u32 type, __u64 addr, __u64 len, bool exclude_user) |
75 | { |
76 | struct perf_event_attr attr; |
77 | |
78 | perf_event_attr_set(attr: &attr, type, addr, len, exclude_user); |
79 | return syscall(__NR_perf_event_open, &attr, getpid(), -1, -1, 0); |
80 | } |
81 | |
82 | static int perf_process_event_open(__u32 type, __u64 addr, __u64 len) |
83 | { |
84 | struct perf_event_attr attr; |
85 | |
86 | perf_event_attr_set(attr: &attr, type, addr, len, exclude_user: 0); |
87 | return syscall(__NR_perf_event_open, &attr, getpid(), -1, -1, 0); |
88 | } |
89 | |
90 | static int perf_cpu_event_open(long cpu, __u32 type, __u64 addr, __u64 len) |
91 | { |
92 | struct perf_event_attr attr; |
93 | |
94 | perf_event_attr_set(attr: &attr, type, addr, len, exclude_user: 0); |
95 | return syscall(__NR_perf_event_open, &attr, -1, cpu, -1, 0); |
96 | } |
97 | |
98 | static void close_fds(int *fd, int n) |
99 | { |
100 | int i; |
101 | |
102 | for (i = 0; i < n; i++) |
103 | close(fd[i]); |
104 | } |
105 | |
106 | static unsigned long read_fds(int *fd, int n) |
107 | { |
108 | int i; |
109 | unsigned long c = 0; |
110 | unsigned long count = 0; |
111 | size_t res; |
112 | |
113 | for (i = 0; i < n; i++) { |
114 | res = read(fd[i], &c, sizeof(c)); |
115 | assert(res == sizeof(unsigned long long)); |
116 | count += c; |
117 | } |
118 | return count; |
119 | } |
120 | |
121 | static void reset_fds(int *fd, int n) |
122 | { |
123 | int i; |
124 | |
125 | for (i = 0; i < n; i++) |
126 | ioctl(fd[i], PERF_EVENT_IOC_RESET); |
127 | } |
128 | |
129 | static void enable_fds(int *fd, int n) |
130 | { |
131 | int i; |
132 | |
133 | for (i = 0; i < n; i++) |
134 | ioctl(fd[i], PERF_EVENT_IOC_ENABLE); |
135 | } |
136 | |
137 | static void disable_fds(int *fd, int n) |
138 | { |
139 | int i; |
140 | |
141 | for (i = 0; i < n; i++) |
142 | ioctl(fd[i], PERF_EVENT_IOC_DISABLE); |
143 | } |
144 | |
145 | static int perf_systemwide_event_open(int *fd, __u32 type, __u64 addr, __u64 len) |
146 | { |
147 | int i, ncpus, cpu, ret = 0; |
148 | struct rlimit rlim; |
149 | cpu_set_t *mask; |
150 | size_t size; |
151 | |
152 | if (getrlimit(RLIMIT_NOFILE, &rlim)) { |
153 | perror("getrlimit" ); |
154 | return -1; |
155 | } |
156 | rlim.rlim_cur = 65536; |
157 | if (setrlimit(RLIMIT_NOFILE, &rlim)) { |
158 | perror("setrlimit" ); |
159 | return -1; |
160 | } |
161 | |
162 | ncpus = get_nprocs_conf(); |
163 | size = CPU_ALLOC_SIZE(ncpus); |
164 | mask = CPU_ALLOC(ncpus); |
165 | if (!mask) { |
166 | perror("malloc" ); |
167 | return -1; |
168 | } |
169 | |
170 | CPU_ZERO_S(size, mask); |
171 | |
172 | if (sched_getaffinity(pid: 0, mask: size, mask)) { |
173 | perror("sched_getaffinity" ); |
174 | ret = -1; |
175 | goto done; |
176 | } |
177 | |
178 | for (i = 0, cpu = 0; i < nprocs && cpu < ncpus; cpu++) { |
179 | if (!CPU_ISSET_S(cpu, size, mask)) |
180 | continue; |
181 | fd[i] = perf_cpu_event_open(cpu, type, addr, len); |
182 | if (fd[i] < 0) { |
183 | perror("perf_systemwide_event_open" ); |
184 | close_fds(fd, n: i); |
185 | ret = fd[i]; |
186 | goto done; |
187 | } |
188 | i++; |
189 | } |
190 | |
191 | if (i < nprocs) { |
192 | printf("Error: Number of online cpus reduced since start of test: %d < %d\n" , i, nprocs); |
193 | close_fds(fd, n: i); |
194 | ret = -1; |
195 | } |
196 | |
197 | done: |
198 | CPU_FREE(mask); |
199 | return ret; |
200 | } |
201 | |
202 | static inline bool breakpoint_test(int len) |
203 | { |
204 | int fd; |
205 | |
206 | /* bp_addr can point anywhere but needs to be aligned */ |
207 | fd = perf_process_event_open(type: HW_BREAKPOINT_R, addr: (__u64)(&fd) & 0xfffffffffffff800, len); |
208 | if (fd < 0) |
209 | return false; |
210 | close(fd); |
211 | return true; |
212 | } |
213 | |
214 | static inline bool perf_breakpoint_supported(void) |
215 | { |
216 | return breakpoint_test(len: 4); |
217 | } |
218 | |
219 | static inline bool dawr_supported(void) |
220 | { |
221 | return breakpoint_test(DAWR_LENGTH_MAX); |
222 | } |
223 | |
224 | static int runtestsingle(int readwriteflag, int exclude_user, int arraytest) |
225 | { |
226 | int i,j; |
227 | size_t res; |
228 | unsigned long long breaks, needed; |
229 | int readint; |
230 | int readintarraybig[2*DAWR_LENGTH_MAX/sizeof(int)]; |
231 | int *readintalign; |
232 | volatile int *ptr; |
233 | int break_fd; |
234 | int loop_num = MAX_LOOPS - (rand() % 100); /* provide some variability */ |
235 | volatile int *k; |
236 | __u64 len; |
237 | |
238 | /* align to 0x400 boundary as required by DAWR */ |
239 | readintalign = (int *)(((unsigned long)readintarraybig + 0x7ff) & |
240 | 0xfffffffffffff800); |
241 | |
242 | ptr = &readint; |
243 | if (arraytest) |
244 | ptr = &readintalign[0]; |
245 | |
246 | len = arraytest ? DAWR_LENGTH_MAX : sizeof(int); |
247 | break_fd = perf_process_event_open_exclude_user(type: readwriteflag, addr: (__u64)ptr, |
248 | len, exclude_user); |
249 | if (break_fd < 0) { |
250 | perror("perf_process_event_open_exclude_user" ); |
251 | exit(1); |
252 | } |
253 | |
254 | /* start counters */ |
255 | ioctl(break_fd, PERF_EVENT_IOC_ENABLE); |
256 | |
257 | /* Test a bunch of reads and writes */ |
258 | k = &readint; |
259 | for (i = 0; i < loop_num; i++) { |
260 | if (arraytest) |
261 | k = &(readintalign[i % (DAWR_LENGTH_MAX/sizeof(int))]); |
262 | |
263 | j = *k; |
264 | *k = j; |
265 | } |
266 | |
267 | /* stop counters */ |
268 | ioctl(break_fd, PERF_EVENT_IOC_DISABLE); |
269 | |
270 | /* read and check counters */ |
271 | res = read(break_fd, &breaks, sizeof(unsigned long long)); |
272 | assert(res == sizeof(unsigned long long)); |
273 | /* we read and write each loop, so subtract the ones we are counting */ |
274 | needed = 0; |
275 | if (readwriteflag & HW_BREAKPOINT_R) |
276 | needed += loop_num; |
277 | if (readwriteflag & HW_BREAKPOINT_W) |
278 | needed += loop_num; |
279 | needed = needed * (1 - exclude_user); |
280 | printf("TESTED: addr:0x%lx brks:% 8lld loops:% 8i rw:%i !user:%i array:%i\n" , |
281 | (unsigned long int)ptr, breaks, loop_num, readwriteflag, exclude_user, arraytest); |
282 | if (breaks != needed) { |
283 | printf("FAILED: 0x%lx brks:%lld needed:%lli %i %i %i\n\n" , |
284 | (unsigned long int)ptr, breaks, needed, loop_num, readwriteflag, exclude_user); |
285 | return 1; |
286 | } |
287 | close(break_fd); |
288 | |
289 | return 0; |
290 | } |
291 | |
292 | static int runtest_dar_outside(void) |
293 | { |
294 | void *target; |
295 | volatile __u16 temp16; |
296 | volatile __u64 temp64; |
297 | int break_fd; |
298 | unsigned long long breaks; |
299 | int fail = 0; |
300 | size_t res; |
301 | |
302 | target = malloc(8); |
303 | if (!target) { |
304 | perror("malloc failed" ); |
305 | exit(EXIT_FAILURE); |
306 | } |
307 | |
308 | /* watch middle half of target array */ |
309 | break_fd = perf_process_event_open(type: HW_BREAKPOINT_RW, addr: (__u64)(target + 2), len: 4); |
310 | if (break_fd < 0) { |
311 | free(target); |
312 | perror("perf_process_event_open" ); |
313 | exit(EXIT_FAILURE); |
314 | } |
315 | |
316 | /* Shouldn't hit. */ |
317 | ioctl(break_fd, PERF_EVENT_IOC_RESET); |
318 | ioctl(break_fd, PERF_EVENT_IOC_ENABLE); |
319 | temp16 = *((__u16 *)target); |
320 | *((__u16 *)target) = temp16; |
321 | ioctl(break_fd, PERF_EVENT_IOC_DISABLE); |
322 | res = read(break_fd, &breaks, sizeof(unsigned long long)); |
323 | assert(res == sizeof(unsigned long long)); |
324 | if (breaks == 0) { |
325 | printf("TESTED: No overlap\n" ); |
326 | } else { |
327 | printf("FAILED: No overlap: %lld != 0\n" , breaks); |
328 | fail = 1; |
329 | } |
330 | |
331 | /* Hit */ |
332 | ioctl(break_fd, PERF_EVENT_IOC_RESET); |
333 | ioctl(break_fd, PERF_EVENT_IOC_ENABLE); |
334 | temp16 = *((__u16 *)(target + 1)); |
335 | *((__u16 *)(target + 1)) = temp16; |
336 | ioctl(break_fd, PERF_EVENT_IOC_DISABLE); |
337 | res = read(break_fd, &breaks, sizeof(unsigned long long)); |
338 | assert(res == sizeof(unsigned long long)); |
339 | if (breaks == 2) { |
340 | printf("TESTED: Partial overlap\n" ); |
341 | } else { |
342 | printf("FAILED: Partial overlap: %lld != 2\n" , breaks); |
343 | fail = 1; |
344 | } |
345 | |
346 | /* Hit */ |
347 | ioctl(break_fd, PERF_EVENT_IOC_RESET); |
348 | ioctl(break_fd, PERF_EVENT_IOC_ENABLE); |
349 | temp16 = *((__u16 *)(target + 5)); |
350 | *((__u16 *)(target + 5)) = temp16; |
351 | ioctl(break_fd, PERF_EVENT_IOC_DISABLE); |
352 | res = read(break_fd, &breaks, sizeof(unsigned long long)); |
353 | assert(res == sizeof(unsigned long long)); |
354 | if (breaks == 2) { |
355 | printf("TESTED: Partial overlap\n" ); |
356 | } else { |
357 | printf("FAILED: Partial overlap: %lld != 2\n" , breaks); |
358 | fail = 1; |
359 | } |
360 | |
361 | /* Shouldn't Hit */ |
362 | ioctl(break_fd, PERF_EVENT_IOC_RESET); |
363 | ioctl(break_fd, PERF_EVENT_IOC_ENABLE); |
364 | temp16 = *((__u16 *)(target + 6)); |
365 | *((__u16 *)(target + 6)) = temp16; |
366 | ioctl(break_fd, PERF_EVENT_IOC_DISABLE); |
367 | res = read(break_fd, &breaks, sizeof(unsigned long long)); |
368 | assert(res == sizeof(unsigned long long)); |
369 | if (breaks == 0) { |
370 | printf("TESTED: No overlap\n" ); |
371 | } else { |
372 | printf("FAILED: No overlap: %lld != 0\n" , breaks); |
373 | fail = 1; |
374 | } |
375 | |
376 | /* Hit */ |
377 | ioctl(break_fd, PERF_EVENT_IOC_RESET); |
378 | ioctl(break_fd, PERF_EVENT_IOC_ENABLE); |
379 | temp64 = *((__u64 *)target); |
380 | *((__u64 *)target) = temp64; |
381 | ioctl(break_fd, PERF_EVENT_IOC_DISABLE); |
382 | res = read(break_fd, &breaks, sizeof(unsigned long long)); |
383 | assert(res == sizeof(unsigned long long)); |
384 | if (breaks == 2) { |
385 | printf("TESTED: Full overlap\n" ); |
386 | } else { |
387 | printf("FAILED: Full overlap: %lld != 2\n" , breaks); |
388 | fail = 1; |
389 | } |
390 | |
391 | free(target); |
392 | close(break_fd); |
393 | return fail; |
394 | } |
395 | |
396 | static void multi_dawr_workload(void) |
397 | { |
398 | a += 10; |
399 | b += 10; |
400 | c[512 + 1] += 'a'; |
401 | } |
402 | |
403 | static int test_process_multi_diff_addr(void) |
404 | { |
405 | unsigned long long breaks1 = 0, breaks2 = 0; |
406 | int fd1, fd2; |
407 | char *desc = "Process specific, Two events, diff addr" ; |
408 | size_t res; |
409 | |
410 | fd1 = perf_process_event_open(type: HW_BREAKPOINT_RW, addr: (__u64)&a, len: (__u64)sizeof(a)); |
411 | if (fd1 < 0) { |
412 | perror("perf_process_event_open" ); |
413 | exit(EXIT_FAILURE); |
414 | } |
415 | |
416 | fd2 = perf_process_event_open(type: HW_BREAKPOINT_RW, addr: (__u64)&b, len: (__u64)sizeof(b)); |
417 | if (fd2 < 0) { |
418 | close(fd1); |
419 | perror("perf_process_event_open" ); |
420 | exit(EXIT_FAILURE); |
421 | } |
422 | |
423 | ioctl(fd1, PERF_EVENT_IOC_RESET); |
424 | ioctl(fd2, PERF_EVENT_IOC_RESET); |
425 | ioctl(fd1, PERF_EVENT_IOC_ENABLE); |
426 | ioctl(fd2, PERF_EVENT_IOC_ENABLE); |
427 | multi_dawr_workload(); |
428 | ioctl(fd1, PERF_EVENT_IOC_DISABLE); |
429 | ioctl(fd2, PERF_EVENT_IOC_DISABLE); |
430 | |
431 | res = read(fd1, &breaks1, sizeof(breaks1)); |
432 | assert(res == sizeof(unsigned long long)); |
433 | res = read(fd2, &breaks2, sizeof(breaks2)); |
434 | assert(res == sizeof(unsigned long long)); |
435 | |
436 | close(fd1); |
437 | close(fd2); |
438 | |
439 | if (breaks1 != 2 || breaks2 != 2) { |
440 | printf("FAILED: %s: %lld != 2 || %lld != 2\n" , desc, breaks1, breaks2); |
441 | return 1; |
442 | } |
443 | |
444 | printf("TESTED: %s\n" , desc); |
445 | return 0; |
446 | } |
447 | |
448 | static int test_process_multi_same_addr(void) |
449 | { |
450 | unsigned long long breaks1 = 0, breaks2 = 0; |
451 | int fd1, fd2; |
452 | char *desc = "Process specific, Two events, same addr" ; |
453 | size_t res; |
454 | |
455 | fd1 = perf_process_event_open(type: HW_BREAKPOINT_RW, addr: (__u64)&a, len: (__u64)sizeof(a)); |
456 | if (fd1 < 0) { |
457 | perror("perf_process_event_open" ); |
458 | exit(EXIT_FAILURE); |
459 | } |
460 | |
461 | fd2 = perf_process_event_open(type: HW_BREAKPOINT_RW, addr: (__u64)&a, len: (__u64)sizeof(a)); |
462 | if (fd2 < 0) { |
463 | close(fd1); |
464 | perror("perf_process_event_open" ); |
465 | exit(EXIT_FAILURE); |
466 | } |
467 | |
468 | ioctl(fd1, PERF_EVENT_IOC_RESET); |
469 | ioctl(fd2, PERF_EVENT_IOC_RESET); |
470 | ioctl(fd1, PERF_EVENT_IOC_ENABLE); |
471 | ioctl(fd2, PERF_EVENT_IOC_ENABLE); |
472 | multi_dawr_workload(); |
473 | ioctl(fd1, PERF_EVENT_IOC_DISABLE); |
474 | ioctl(fd2, PERF_EVENT_IOC_DISABLE); |
475 | |
476 | res = read(fd1, &breaks1, sizeof(breaks1)); |
477 | assert(res == sizeof(unsigned long long)); |
478 | res = read(fd2, &breaks2, sizeof(breaks2)); |
479 | assert(res == sizeof(unsigned long long)); |
480 | |
481 | close(fd1); |
482 | close(fd2); |
483 | |
484 | if (breaks1 != 2 || breaks2 != 2) { |
485 | printf("FAILED: %s: %lld != 2 || %lld != 2\n" , desc, breaks1, breaks2); |
486 | return 1; |
487 | } |
488 | |
489 | printf("TESTED: %s\n" , desc); |
490 | return 0; |
491 | } |
492 | |
493 | static int test_process_multi_diff_addr_ro_wo(void) |
494 | { |
495 | unsigned long long breaks1 = 0, breaks2 = 0; |
496 | int fd1, fd2; |
497 | char *desc = "Process specific, Two events, diff addr, one is RO, other is WO" ; |
498 | size_t res; |
499 | |
500 | fd1 = perf_process_event_open(type: HW_BREAKPOINT_W, addr: (__u64)&a, len: (__u64)sizeof(a)); |
501 | if (fd1 < 0) { |
502 | perror("perf_process_event_open" ); |
503 | exit(EXIT_FAILURE); |
504 | } |
505 | |
506 | fd2 = perf_process_event_open(type: HW_BREAKPOINT_R, addr: (__u64)&b, len: (__u64)sizeof(b)); |
507 | if (fd2 < 0) { |
508 | close(fd1); |
509 | perror("perf_process_event_open" ); |
510 | exit(EXIT_FAILURE); |
511 | } |
512 | |
513 | ioctl(fd1, PERF_EVENT_IOC_RESET); |
514 | ioctl(fd2, PERF_EVENT_IOC_RESET); |
515 | ioctl(fd1, PERF_EVENT_IOC_ENABLE); |
516 | ioctl(fd2, PERF_EVENT_IOC_ENABLE); |
517 | multi_dawr_workload(); |
518 | ioctl(fd1, PERF_EVENT_IOC_DISABLE); |
519 | ioctl(fd2, PERF_EVENT_IOC_DISABLE); |
520 | |
521 | res = read(fd1, &breaks1, sizeof(breaks1)); |
522 | assert(res == sizeof(unsigned long long)); |
523 | res = read(fd2, &breaks2, sizeof(breaks2)); |
524 | assert(res == sizeof(unsigned long long)); |
525 | |
526 | close(fd1); |
527 | close(fd2); |
528 | |
529 | if (breaks1 != 1 || breaks2 != 1) { |
530 | printf("FAILED: %s: %lld != 1 || %lld != 1\n" , desc, breaks1, breaks2); |
531 | return 1; |
532 | } |
533 | |
534 | printf("TESTED: %s\n" , desc); |
535 | return 0; |
536 | } |
537 | |
538 | static int test_process_multi_same_addr_ro_wo(void) |
539 | { |
540 | unsigned long long breaks1 = 0, breaks2 = 0; |
541 | int fd1, fd2; |
542 | char *desc = "Process specific, Two events, same addr, one is RO, other is WO" ; |
543 | size_t res; |
544 | |
545 | fd1 = perf_process_event_open(type: HW_BREAKPOINT_R, addr: (__u64)&a, len: (__u64)sizeof(a)); |
546 | if (fd1 < 0) { |
547 | perror("perf_process_event_open" ); |
548 | exit(EXIT_FAILURE); |
549 | } |
550 | |
551 | fd2 = perf_process_event_open(type: HW_BREAKPOINT_W, addr: (__u64)&a, len: (__u64)sizeof(a)); |
552 | if (fd2 < 0) { |
553 | close(fd1); |
554 | perror("perf_process_event_open" ); |
555 | exit(EXIT_FAILURE); |
556 | } |
557 | |
558 | ioctl(fd1, PERF_EVENT_IOC_RESET); |
559 | ioctl(fd2, PERF_EVENT_IOC_RESET); |
560 | ioctl(fd1, PERF_EVENT_IOC_ENABLE); |
561 | ioctl(fd2, PERF_EVENT_IOC_ENABLE); |
562 | multi_dawr_workload(); |
563 | ioctl(fd1, PERF_EVENT_IOC_DISABLE); |
564 | ioctl(fd2, PERF_EVENT_IOC_DISABLE); |
565 | |
566 | res = read(fd1, &breaks1, sizeof(breaks1)); |
567 | assert(res == sizeof(unsigned long long)); |
568 | res = read(fd2, &breaks2, sizeof(breaks2)); |
569 | assert(res == sizeof(unsigned long long)); |
570 | |
571 | close(fd1); |
572 | close(fd2); |
573 | |
574 | if (breaks1 != 1 || breaks2 != 1) { |
575 | printf("FAILED: %s: %lld != 1 || %lld != 1\n" , desc, breaks1, breaks2); |
576 | return 1; |
577 | } |
578 | |
579 | printf("TESTED: %s\n" , desc); |
580 | return 0; |
581 | } |
582 | |
583 | static int test_syswide_multi_diff_addr(void) |
584 | { |
585 | unsigned long long breaks1 = 0, breaks2 = 0; |
586 | int *fd1 = malloc(nprocs * sizeof(int)); |
587 | int *fd2 = malloc(nprocs * sizeof(int)); |
588 | char *desc = "Systemwide, Two events, diff addr" ; |
589 | int ret; |
590 | |
591 | ret = perf_systemwide_event_open(fd: fd1, type: HW_BREAKPOINT_RW, addr: (__u64)&a, len: (__u64)sizeof(a)); |
592 | if (ret) |
593 | exit(EXIT_FAILURE); |
594 | |
595 | ret = perf_systemwide_event_open(fd: fd2, type: HW_BREAKPOINT_RW, addr: (__u64)&b, len: (__u64)sizeof(b)); |
596 | if (ret) { |
597 | close_fds(fd: fd1, n: nprocs); |
598 | exit(EXIT_FAILURE); |
599 | } |
600 | |
601 | reset_fds(fd: fd1, n: nprocs); |
602 | reset_fds(fd: fd2, n: nprocs); |
603 | enable_fds(fd: fd1, n: nprocs); |
604 | enable_fds(fd: fd2, n: nprocs); |
605 | multi_dawr_workload(); |
606 | disable_fds(fd: fd1, n: nprocs); |
607 | disable_fds(fd: fd2, n: nprocs); |
608 | |
609 | breaks1 = read_fds(fd: fd1, n: nprocs); |
610 | breaks2 = read_fds(fd: fd2, n: nprocs); |
611 | |
612 | close_fds(fd: fd1, n: nprocs); |
613 | close_fds(fd: fd2, n: nprocs); |
614 | |
615 | free(fd1); |
616 | free(fd2); |
617 | |
618 | if (breaks1 != 2 || breaks2 != 2) { |
619 | printf("FAILED: %s: %lld != 2 || %lld != 2\n" , desc, breaks1, breaks2); |
620 | return 1; |
621 | } |
622 | |
623 | printf("TESTED: %s\n" , desc); |
624 | return 0; |
625 | } |
626 | |
627 | static int test_syswide_multi_same_addr(void) |
628 | { |
629 | unsigned long long breaks1 = 0, breaks2 = 0; |
630 | int *fd1 = malloc(nprocs * sizeof(int)); |
631 | int *fd2 = malloc(nprocs * sizeof(int)); |
632 | char *desc = "Systemwide, Two events, same addr" ; |
633 | int ret; |
634 | |
635 | ret = perf_systemwide_event_open(fd: fd1, type: HW_BREAKPOINT_RW, addr: (__u64)&a, len: (__u64)sizeof(a)); |
636 | if (ret) |
637 | exit(EXIT_FAILURE); |
638 | |
639 | ret = perf_systemwide_event_open(fd: fd2, type: HW_BREAKPOINT_RW, addr: (__u64)&a, len: (__u64)sizeof(a)); |
640 | if (ret) { |
641 | close_fds(fd: fd1, n: nprocs); |
642 | exit(EXIT_FAILURE); |
643 | } |
644 | |
645 | reset_fds(fd: fd1, n: nprocs); |
646 | reset_fds(fd: fd2, n: nprocs); |
647 | enable_fds(fd: fd1, n: nprocs); |
648 | enable_fds(fd: fd2, n: nprocs); |
649 | multi_dawr_workload(); |
650 | disable_fds(fd: fd1, n: nprocs); |
651 | disable_fds(fd: fd2, n: nprocs); |
652 | |
653 | breaks1 = read_fds(fd: fd1, n: nprocs); |
654 | breaks2 = read_fds(fd: fd2, n: nprocs); |
655 | |
656 | close_fds(fd: fd1, n: nprocs); |
657 | close_fds(fd: fd2, n: nprocs); |
658 | |
659 | free(fd1); |
660 | free(fd2); |
661 | |
662 | if (breaks1 != 2 || breaks2 != 2) { |
663 | printf("FAILED: %s: %lld != 2 || %lld != 2\n" , desc, breaks1, breaks2); |
664 | return 1; |
665 | } |
666 | |
667 | printf("TESTED: %s\n" , desc); |
668 | return 0; |
669 | } |
670 | |
671 | static int test_syswide_multi_diff_addr_ro_wo(void) |
672 | { |
673 | unsigned long long breaks1 = 0, breaks2 = 0; |
674 | int *fd1 = malloc(nprocs * sizeof(int)); |
675 | int *fd2 = malloc(nprocs * sizeof(int)); |
676 | char *desc = "Systemwide, Two events, diff addr, one is RO, other is WO" ; |
677 | int ret; |
678 | |
679 | ret = perf_systemwide_event_open(fd: fd1, type: HW_BREAKPOINT_W, addr: (__u64)&a, len: (__u64)sizeof(a)); |
680 | if (ret) |
681 | exit(EXIT_FAILURE); |
682 | |
683 | ret = perf_systemwide_event_open(fd: fd2, type: HW_BREAKPOINT_R, addr: (__u64)&b, len: (__u64)sizeof(b)); |
684 | if (ret) { |
685 | close_fds(fd: fd1, n: nprocs); |
686 | exit(EXIT_FAILURE); |
687 | } |
688 | |
689 | reset_fds(fd: fd1, n: nprocs); |
690 | reset_fds(fd: fd2, n: nprocs); |
691 | enable_fds(fd: fd1, n: nprocs); |
692 | enable_fds(fd: fd2, n: nprocs); |
693 | multi_dawr_workload(); |
694 | disable_fds(fd: fd1, n: nprocs); |
695 | disable_fds(fd: fd2, n: nprocs); |
696 | |
697 | breaks1 = read_fds(fd: fd1, n: nprocs); |
698 | breaks2 = read_fds(fd: fd2, n: nprocs); |
699 | |
700 | close_fds(fd: fd1, n: nprocs); |
701 | close_fds(fd: fd2, n: nprocs); |
702 | |
703 | free(fd1); |
704 | free(fd2); |
705 | |
706 | if (breaks1 != 1 || breaks2 != 1) { |
707 | printf("FAILED: %s: %lld != 1 || %lld != 1\n" , desc, breaks1, breaks2); |
708 | return 1; |
709 | } |
710 | |
711 | printf("TESTED: %s\n" , desc); |
712 | return 0; |
713 | } |
714 | |
715 | static int test_syswide_multi_same_addr_ro_wo(void) |
716 | { |
717 | unsigned long long breaks1 = 0, breaks2 = 0; |
718 | int *fd1 = malloc(nprocs * sizeof(int)); |
719 | int *fd2 = malloc(nprocs * sizeof(int)); |
720 | char *desc = "Systemwide, Two events, same addr, one is RO, other is WO" ; |
721 | int ret; |
722 | |
723 | ret = perf_systemwide_event_open(fd: fd1, type: HW_BREAKPOINT_W, addr: (__u64)&a, len: (__u64)sizeof(a)); |
724 | if (ret) |
725 | exit(EXIT_FAILURE); |
726 | |
727 | ret = perf_systemwide_event_open(fd: fd2, type: HW_BREAKPOINT_R, addr: (__u64)&a, len: (__u64)sizeof(a)); |
728 | if (ret) { |
729 | close_fds(fd: fd1, n: nprocs); |
730 | exit(EXIT_FAILURE); |
731 | } |
732 | |
733 | reset_fds(fd: fd1, n: nprocs); |
734 | reset_fds(fd: fd2, n: nprocs); |
735 | enable_fds(fd: fd1, n: nprocs); |
736 | enable_fds(fd: fd2, n: nprocs); |
737 | multi_dawr_workload(); |
738 | disable_fds(fd: fd1, n: nprocs); |
739 | disable_fds(fd: fd2, n: nprocs); |
740 | |
741 | breaks1 = read_fds(fd: fd1, n: nprocs); |
742 | breaks2 = read_fds(fd: fd2, n: nprocs); |
743 | |
744 | close_fds(fd: fd1, n: nprocs); |
745 | close_fds(fd: fd2, n: nprocs); |
746 | |
747 | free(fd1); |
748 | free(fd2); |
749 | |
750 | if (breaks1 != 1 || breaks2 != 1) { |
751 | printf("FAILED: %s: %lld != 1 || %lld != 1\n" , desc, breaks1, breaks2); |
752 | return 1; |
753 | } |
754 | |
755 | printf("TESTED: %s\n" , desc); |
756 | return 0; |
757 | } |
758 | |
759 | static int runtest_multi_dawr(void) |
760 | { |
761 | int ret = 0; |
762 | |
763 | ret |= test_process_multi_diff_addr(); |
764 | ret |= test_process_multi_same_addr(); |
765 | ret |= test_process_multi_diff_addr_ro_wo(); |
766 | ret |= test_process_multi_same_addr_ro_wo(); |
767 | ret |= test_syswide_multi_diff_addr(); |
768 | ret |= test_syswide_multi_same_addr(); |
769 | ret |= test_syswide_multi_diff_addr_ro_wo(); |
770 | ret |= test_syswide_multi_same_addr_ro_wo(); |
771 | |
772 | return ret; |
773 | } |
774 | |
775 | static int runtest_unaligned_512bytes(void) |
776 | { |
777 | unsigned long long breaks = 0; |
778 | int fd; |
779 | char *desc = "Process specific, 512 bytes, unaligned" ; |
780 | __u64 addr = (__u64)&c + 8; |
781 | size_t res; |
782 | |
783 | fd = perf_process_event_open(type: HW_BREAKPOINT_RW, addr, len: 512); |
784 | if (fd < 0) { |
785 | perror("perf_process_event_open" ); |
786 | exit(EXIT_FAILURE); |
787 | } |
788 | |
789 | ioctl(fd, PERF_EVENT_IOC_RESET); |
790 | ioctl(fd, PERF_EVENT_IOC_ENABLE); |
791 | multi_dawr_workload(); |
792 | ioctl(fd, PERF_EVENT_IOC_DISABLE); |
793 | |
794 | res = read(fd, &breaks, sizeof(breaks)); |
795 | assert(res == sizeof(unsigned long long)); |
796 | |
797 | close(fd); |
798 | |
799 | if (breaks != 2) { |
800 | printf("FAILED: %s: %lld != 2\n" , desc, breaks); |
801 | return 1; |
802 | } |
803 | |
804 | printf("TESTED: %s\n" , desc); |
805 | return 0; |
806 | } |
807 | |
808 | /* There is no perf api to find number of available watchpoints. Use ptrace. */ |
809 | static int get_nr_wps(bool *arch_31) |
810 | { |
811 | struct ppc_debug_info dbginfo; |
812 | int child_pid; |
813 | |
814 | child_pid = fork(); |
815 | if (!child_pid) { |
816 | int ret = ptrace(PTRACE_TRACEME, 0, NULL, 0); |
817 | if (ret) { |
818 | perror("PTRACE_TRACEME failed\n" ); |
819 | exit(EXIT_FAILURE); |
820 | } |
821 | kill(getpid(), SIGUSR1); |
822 | |
823 | sleep(1); |
824 | exit(EXIT_SUCCESS); |
825 | } |
826 | |
827 | wait(NULL); |
828 | if (ptrace(PPC_PTRACE_GETHWDBGINFO, child_pid, NULL, &dbginfo)) { |
829 | perror("Can't get breakpoint info" ); |
830 | exit(EXIT_FAILURE); |
831 | } |
832 | |
833 | *arch_31 = !!(dbginfo.features & PPC_DEBUG_FEATURE_DATA_BP_ARCH_31); |
834 | return dbginfo.num_data_bps; |
835 | } |
836 | |
837 | static int runtest(void) |
838 | { |
839 | int rwflag; |
840 | int exclude_user; |
841 | int ret; |
842 | bool dawr = dawr_supported(); |
843 | bool arch_31 = false; |
844 | int nr_wps = get_nr_wps(arch_31: &arch_31); |
845 | |
846 | /* |
847 | * perf defines rwflag as two bits read and write and at least |
848 | * one must be set. So range 1-3. |
849 | */ |
850 | for (rwflag = 1 ; rwflag < 4; rwflag++) { |
851 | for (exclude_user = 0 ; exclude_user < 2; exclude_user++) { |
852 | ret = runtestsingle(readwriteflag: rwflag, exclude_user, arraytest: 0); |
853 | if (ret) |
854 | return ret; |
855 | |
856 | /* if we have the dawr, we can do an array test */ |
857 | if (!dawr) |
858 | continue; |
859 | ret = runtestsingle(readwriteflag: rwflag, exclude_user, arraytest: 1); |
860 | if (ret) |
861 | return ret; |
862 | } |
863 | } |
864 | |
865 | ret = runtest_dar_outside(); |
866 | if (ret) |
867 | return ret; |
868 | |
869 | if (dawr && nr_wps > 1) { |
870 | nprocs = get_nprocs(); |
871 | ret = runtest_multi_dawr(); |
872 | if (ret) |
873 | return ret; |
874 | } |
875 | |
876 | if (dawr && arch_31) |
877 | ret = runtest_unaligned_512bytes(); |
878 | |
879 | return ret; |
880 | } |
881 | |
882 | |
883 | static int perf_hwbreak(void) |
884 | { |
885 | srand ( time(NULL) ); |
886 | |
887 | SKIP_IF_MSG(!perf_breakpoint_supported(), "Perf breakpoints not supported" ); |
888 | |
889 | return runtest(); |
890 | } |
891 | |
892 | int main(int argc, char *argv[], char **envp) |
893 | { |
894 | return test_harness(perf_hwbreak, "perf_hwbreak" ); |
895 | } |
896 | |