xref: /openbmc/linux/tools/perf/tests/bp_account.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
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