15875cf4cSArnaldo Carvalho de Melo // SPDX-License-Identifier: GPL-2.0
2032db28eSJiri Olsa /*
3032db28eSJiri Olsa * Powerpc needs __SANE_USERSPACE_TYPES__ before <linux/types.h> to select
4032db28eSJiri Olsa * 'int-ll64.h' and avoid compile warnings when printing __u64 with %llu.
5032db28eSJiri Olsa */
6032db28eSJiri Olsa #define __SANE_USERSPACE_TYPES__
7032db28eSJiri Olsa
8032db28eSJiri Olsa #include <stdlib.h>
9032db28eSJiri Olsa #include <stdio.h>
10032db28eSJiri Olsa #include <unistd.h>
11032db28eSJiri Olsa #include <string.h>
12032db28eSJiri Olsa #include <sys/ioctl.h>
13032db28eSJiri Olsa #include <fcntl.h>
14032db28eSJiri Olsa #include <linux/hw_breakpoint.h>
15032db28eSJiri Olsa
16032db28eSJiri Olsa #include "tests.h"
17032db28eSJiri Olsa #include "debug.h"
188520a98dSArnaldo Carvalho de Melo #include "event.h"
1991854f9aSArnaldo Carvalho de Melo #include "../perf-sys.h"
20032db28eSJiri Olsa #include "cloexec.h"
21032db28eSJiri Olsa
224935e2cdSIan Rogers /*
234935e2cdSIan Rogers * PowerPC and S390 do not support creation of instruction breakpoints using the
244935e2cdSIan Rogers * perf_event interface.
254935e2cdSIan Rogers *
264935e2cdSIan Rogers * Just disable the test for these architectures until these issues are
274935e2cdSIan Rogers * resolved.
284935e2cdSIan Rogers */
294935e2cdSIan Rogers #if defined(__powerpc__) || defined(__s390x__)
304935e2cdSIan Rogers #define BP_ACCOUNT_IS_SUPPORTED 0
314935e2cdSIan Rogers #else
324935e2cdSIan Rogers #define BP_ACCOUNT_IS_SUPPORTED 1
334935e2cdSIan Rogers #endif
344935e2cdSIan Rogers
35cff20b31SArnaldo Carvalho de Melo static volatile long the_var;
36032db28eSJiri Olsa
test_function(void)37032db28eSJiri Olsa static noinline int test_function(void)
38032db28eSJiri Olsa {
39032db28eSJiri Olsa return 0;
40032db28eSJiri Olsa }
41032db28eSJiri Olsa
__event(bool is_x,void * addr,struct perf_event_attr * attr)42032db28eSJiri Olsa static int __event(bool is_x, void *addr, struct perf_event_attr *attr)
43032db28eSJiri Olsa {
44032db28eSJiri Olsa int fd;
45032db28eSJiri Olsa
46032db28eSJiri Olsa memset(attr, 0, sizeof(struct perf_event_attr));
47032db28eSJiri Olsa attr->type = PERF_TYPE_BREAKPOINT;
48032db28eSJiri Olsa attr->size = sizeof(struct perf_event_attr);
49032db28eSJiri Olsa
50032db28eSJiri Olsa attr->config = 0;
51032db28eSJiri Olsa attr->bp_type = is_x ? HW_BREAKPOINT_X : HW_BREAKPOINT_W;
52032db28eSJiri Olsa attr->bp_addr = (unsigned long) addr;
53032db28eSJiri Olsa attr->bp_len = sizeof(long);
54032db28eSJiri Olsa
55032db28eSJiri Olsa attr->sample_period = 1;
56032db28eSJiri Olsa attr->sample_type = PERF_SAMPLE_IP;
57032db28eSJiri Olsa
58032db28eSJiri Olsa attr->exclude_kernel = 1;
59032db28eSJiri Olsa attr->exclude_hv = 1;
60032db28eSJiri Olsa
61032db28eSJiri Olsa fd = sys_perf_event_open(attr, -1, 0, -1,
62032db28eSJiri Olsa perf_event_open_cloexec_flag());
63032db28eSJiri Olsa if (fd < 0) {
64032db28eSJiri Olsa pr_debug("failed opening event %llx\n", attr->config);
65032db28eSJiri Olsa return TEST_FAIL;
66032db28eSJiri Olsa }
67032db28eSJiri Olsa
68032db28eSJiri Olsa return fd;
69032db28eSJiri Olsa }
70032db28eSJiri Olsa
wp_event(void * addr,struct perf_event_attr * attr)71032db28eSJiri Olsa static int wp_event(void *addr, struct perf_event_attr *attr)
72032db28eSJiri Olsa {
73032db28eSJiri Olsa return __event(false, addr, attr);
74032db28eSJiri Olsa }
75032db28eSJiri Olsa
bp_event(void * addr,struct perf_event_attr * attr)76032db28eSJiri Olsa static int bp_event(void *addr, struct perf_event_attr *attr)
77032db28eSJiri Olsa {
78032db28eSJiri Olsa return __event(true, addr, attr);
79032db28eSJiri Olsa }
80032db28eSJiri Olsa
bp_accounting(int wp_cnt,int share)81032db28eSJiri Olsa static int bp_accounting(int wp_cnt, int share)
82032db28eSJiri Olsa {
83032db28eSJiri Olsa struct perf_event_attr attr, attr_mod, attr_new;
84032db28eSJiri Olsa int i, fd[wp_cnt], fd_wp, ret;
85032db28eSJiri Olsa
86032db28eSJiri Olsa for (i = 0; i < wp_cnt; i++) {
87032db28eSJiri Olsa fd[i] = wp_event((void *)&the_var, &attr);
88032db28eSJiri Olsa TEST_ASSERT_VAL("failed to create wp\n", fd[i] != -1);
89032db28eSJiri Olsa pr_debug("wp %d created\n", i);
90032db28eSJiri Olsa }
91032db28eSJiri Olsa
92032db28eSJiri Olsa attr_mod = attr;
93032db28eSJiri Olsa attr_mod.bp_type = HW_BREAKPOINT_X;
94032db28eSJiri Olsa attr_mod.bp_addr = (unsigned long) test_function;
95032db28eSJiri Olsa
96032db28eSJiri Olsa ret = ioctl(fd[0], PERF_EVENT_IOC_MODIFY_ATTRIBUTES, &attr_mod);
97032db28eSJiri Olsa TEST_ASSERT_VAL("failed to modify wp\n", ret == 0);
98032db28eSJiri Olsa
99032db28eSJiri Olsa pr_debug("wp 0 modified to bp\n");
100032db28eSJiri Olsa
101032db28eSJiri Olsa if (!share) {
102032db28eSJiri Olsa fd_wp = wp_event((void *)&the_var, &attr_new);
103032db28eSJiri Olsa TEST_ASSERT_VAL("failed to create max wp\n", fd_wp != -1);
104032db28eSJiri Olsa pr_debug("wp max created\n");
105032db28eSJiri Olsa }
106032db28eSJiri Olsa
107032db28eSJiri Olsa for (i = 0; i < wp_cnt; i++)
108032db28eSJiri Olsa close(fd[i]);
109032db28eSJiri Olsa
110032db28eSJiri Olsa return 0;
111032db28eSJiri Olsa }
112032db28eSJiri Olsa
detect_cnt(bool is_x)113032db28eSJiri Olsa static int detect_cnt(bool is_x)
114032db28eSJiri Olsa {
115032db28eSJiri Olsa struct perf_event_attr attr;
1161cd61883SArnaldo Carvalho de Melo void *addr = is_x ? (void *)test_function : (void *)&the_var;
117032db28eSJiri Olsa int fd[100], cnt = 0, i;
118032db28eSJiri Olsa
119032db28eSJiri Olsa while (1) {
120032db28eSJiri Olsa if (cnt == 100) {
121032db28eSJiri Olsa pr_debug("way too many debug registers, fix the test\n");
122032db28eSJiri Olsa return 0;
123032db28eSJiri Olsa }
12466790bc8SColin Ian King fd[cnt] = __event(is_x, addr, &attr);
125032db28eSJiri Olsa
12666790bc8SColin Ian King if (fd[cnt] < 0)
12766790bc8SColin Ian King break;
128032db28eSJiri Olsa cnt++;
129032db28eSJiri Olsa }
130032db28eSJiri Olsa
131032db28eSJiri Olsa for (i = 0; i < cnt; i++)
132032db28eSJiri Olsa close(fd[i]);
133032db28eSJiri Olsa
134032db28eSJiri Olsa return cnt;
135032db28eSJiri Olsa }
136032db28eSJiri Olsa
detect_ioctl(void)137032db28eSJiri Olsa static int detect_ioctl(void)
138032db28eSJiri Olsa {
139032db28eSJiri Olsa struct perf_event_attr attr;
140032db28eSJiri Olsa int fd, ret = 1;
141032db28eSJiri Olsa
142032db28eSJiri Olsa fd = wp_event((void *) &the_var, &attr);
143032db28eSJiri Olsa if (fd > 0) {
144032db28eSJiri Olsa ret = ioctl(fd, PERF_EVENT_IOC_MODIFY_ATTRIBUTES, &attr);
145032db28eSJiri Olsa close(fd);
146032db28eSJiri Olsa }
147032db28eSJiri Olsa
148032db28eSJiri Olsa return ret ? 0 : 1;
149032db28eSJiri Olsa }
150032db28eSJiri Olsa
detect_share(int wp_cnt,int bp_cnt)151032db28eSJiri Olsa static int detect_share(int wp_cnt, int bp_cnt)
152032db28eSJiri Olsa {
153032db28eSJiri Olsa struct perf_event_attr attr;
154*cc214552SIan Rogers int i, *fd = NULL, ret = -1;
155*cc214552SIan Rogers
156*cc214552SIan Rogers if (wp_cnt + bp_cnt == 0)
157*cc214552SIan Rogers return 0;
158*cc214552SIan Rogers
159*cc214552SIan Rogers fd = malloc(sizeof(int) * (wp_cnt + bp_cnt));
160*cc214552SIan Rogers if (!fd)
161*cc214552SIan Rogers return -1;
162032db28eSJiri Olsa
163032db28eSJiri Olsa for (i = 0; i < wp_cnt; i++) {
164032db28eSJiri Olsa fd[i] = wp_event((void *)&the_var, &attr);
165*cc214552SIan Rogers if (fd[i] == -1) {
166*cc214552SIan Rogers pr_err("failed to create wp\n");
167*cc214552SIan Rogers goto out;
168*cc214552SIan Rogers }
169032db28eSJiri Olsa }
170032db28eSJiri Olsa
171032db28eSJiri Olsa for (; i < (bp_cnt + wp_cnt); i++) {
172032db28eSJiri Olsa fd[i] = bp_event((void *)test_function, &attr);
173032db28eSJiri Olsa if (fd[i] == -1)
174032db28eSJiri Olsa break;
175032db28eSJiri Olsa }
176032db28eSJiri Olsa
177032db28eSJiri Olsa ret = i != (bp_cnt + wp_cnt);
178032db28eSJiri Olsa
179*cc214552SIan Rogers out:
180032db28eSJiri Olsa while (i--)
181032db28eSJiri Olsa close(fd[i]);
182032db28eSJiri Olsa
183*cc214552SIan Rogers free(fd);
184032db28eSJiri Olsa return ret;
185032db28eSJiri Olsa }
186032db28eSJiri Olsa
187032db28eSJiri Olsa /*
188032db28eSJiri Olsa * This test does following:
189032db28eSJiri Olsa * - detects the number of watch/break-points,
190032db28eSJiri Olsa * skip test if any is missing
191032db28eSJiri Olsa * - detects PERF_EVENT_IOC_MODIFY_ATTRIBUTES ioctl,
192032db28eSJiri Olsa * skip test if it's missing
193032db28eSJiri Olsa * - detects if watchpoints and breakpoints share
194032db28eSJiri Olsa * same slots
195032db28eSJiri Olsa * - create all possible watchpoints on cpu 0
196032db28eSJiri Olsa * - change one of it to breakpoint
197032db28eSJiri Olsa * - in case wp and bp do not share slots,
198032db28eSJiri Olsa * we create another watchpoint to ensure
199032db28eSJiri Olsa * the slot accounting is correct
200032db28eSJiri Olsa */
test__bp_accounting(struct test_suite * test __maybe_unused,int subtest __maybe_unused)20133f44bfdSIan Rogers static int test__bp_accounting(struct test_suite *test __maybe_unused, int subtest __maybe_unused)
202032db28eSJiri Olsa {
203032db28eSJiri Olsa int has_ioctl = detect_ioctl();
204032db28eSJiri Olsa int wp_cnt = detect_cnt(false);
205032db28eSJiri Olsa int bp_cnt = detect_cnt(true);
206032db28eSJiri Olsa int share = detect_share(wp_cnt, bp_cnt);
207032db28eSJiri Olsa
2084935e2cdSIan Rogers if (!BP_ACCOUNT_IS_SUPPORTED) {
2094935e2cdSIan Rogers pr_debug("Test not supported on this architecture");
2104935e2cdSIan Rogers return TEST_SKIP;
2114935e2cdSIan Rogers }
2124935e2cdSIan Rogers
213032db28eSJiri Olsa pr_debug("watchpoints count %d, breakpoints count %d, has_ioctl %d, share %d\n",
214032db28eSJiri Olsa wp_cnt, bp_cnt, has_ioctl, share);
215032db28eSJiri Olsa
216032db28eSJiri Olsa if (!wp_cnt || !bp_cnt || !has_ioctl)
217032db28eSJiri Olsa return TEST_SKIP;
218032db28eSJiri Olsa
219032db28eSJiri Olsa return bp_accounting(wp_cnt, share);
220032db28eSJiri Olsa }
221e533eadfSLeo Yan
2224935e2cdSIan Rogers DEFINE_SUITE("Breakpoint accounting", bp_accounting);
223