diff options
author | Michael Neuling <mikey@neuling.org> | 2018-05-28 19:22:38 -0400 |
---|---|---|
committer | Michael Ellerman <mpe@ellerman.id.au> | 2018-06-03 07:16:44 -0400 |
commit | 9c2d72d497a32788bf90f05610319a217258129a (patch) | |
tree | ab9dbda6daf8e74ec37f9917afe3cb5a408b2ed1 /tools | |
parent | a377514519b9a20fa1ea9adddbb4129573129cef (diff) |
selftests/powerpc: Add perf breakpoint test
This tests perf hardware breakpoints (ie PERF_TYPE_BREAKPOINT) on
powerpc.
Signed-off-by: Michael Neuling <mikey@neuling.org>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Diffstat (limited to 'tools')
-rw-r--r-- | tools/testing/selftests/powerpc/ptrace/.gitignore | 1 | ||||
-rw-r--r-- | tools/testing/selftests/powerpc/ptrace/Makefile | 3 | ||||
-rw-r--r-- | tools/testing/selftests/powerpc/ptrace/perf-hwbreak.c | 195 |
3 files changed, 198 insertions, 1 deletions
diff --git a/tools/testing/selftests/powerpc/ptrace/.gitignore b/tools/testing/selftests/powerpc/ptrace/.gitignore index 9dcc16ea8179..07ec449a2767 100644 --- a/tools/testing/selftests/powerpc/ptrace/.gitignore +++ b/tools/testing/selftests/powerpc/ptrace/.gitignore | |||
@@ -9,3 +9,4 @@ ptrace-tm-vsx | |||
9 | ptrace-tm-spd-vsx | 9 | ptrace-tm-spd-vsx |
10 | ptrace-tm-spr | 10 | ptrace-tm-spr |
11 | ptrace-hwbreak | 11 | ptrace-hwbreak |
12 | perf-hwbreak | ||
diff --git a/tools/testing/selftests/powerpc/ptrace/Makefile b/tools/testing/selftests/powerpc/ptrace/Makefile index 4f5957538908..28f5b781a553 100644 --- a/tools/testing/selftests/powerpc/ptrace/Makefile +++ b/tools/testing/selftests/powerpc/ptrace/Makefile | |||
@@ -1,7 +1,8 @@ | |||
1 | # SPDX-License-Identifier: GPL-2.0 | 1 | # SPDX-License-Identifier: GPL-2.0 |
2 | TEST_PROGS := ptrace-gpr ptrace-tm-gpr ptrace-tm-spd-gpr \ | 2 | TEST_PROGS := ptrace-gpr ptrace-tm-gpr ptrace-tm-spd-gpr \ |
3 | ptrace-tar ptrace-tm-tar ptrace-tm-spd-tar ptrace-vsx ptrace-tm-vsx \ | 3 | ptrace-tar ptrace-tm-tar ptrace-tm-spd-tar ptrace-vsx ptrace-tm-vsx \ |
4 | ptrace-tm-spd-vsx ptrace-tm-spr ptrace-hwbreak ptrace-pkey core-pkey | 4 | ptrace-tm-spd-vsx ptrace-tm-spr ptrace-hwbreak ptrace-pkey core-pkey \ |
5 | perf-hwbreak | ||
5 | 6 | ||
6 | include ../../lib.mk | 7 | include ../../lib.mk |
7 | 8 | ||
diff --git a/tools/testing/selftests/powerpc/ptrace/perf-hwbreak.c b/tools/testing/selftests/powerpc/ptrace/perf-hwbreak.c new file mode 100644 index 000000000000..60df0b5e628a --- /dev/null +++ b/tools/testing/selftests/powerpc/ptrace/perf-hwbreak.c | |||
@@ -0,0 +1,195 @@ | |||
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 | } | ||