xref: /openbmc/linux/tools/perf/tests/bp_account.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
1  // SPDX-License-Identifier: GPL-2.0
2  /*
3   * Powerpc needs __SANE_USERSPACE_TYPES__ before <linux/types.h> to select
4   * 'int-ll64.h' and avoid compile warnings when printing __u64 with %llu.
5   */
6  #define __SANE_USERSPACE_TYPES__
7  
8  #include <stdlib.h>
9  #include <stdio.h>
10  #include <unistd.h>
11  #include <string.h>
12  #include <sys/ioctl.h>
13  #include <fcntl.h>
14  #include <linux/hw_breakpoint.h>
15  
16  #include "tests.h"
17  #include "debug.h"
18  #include "event.h"
19  #include "../perf-sys.h"
20  #include "cloexec.h"
21  
22  /*
23   * PowerPC and S390 do not support creation of instruction breakpoints using the
24   * perf_event interface.
25   *
26   * Just disable the test for these architectures until these issues are
27   * resolved.
28   */
29  #if defined(__powerpc__) || defined(__s390x__)
30  #define BP_ACCOUNT_IS_SUPPORTED 0
31  #else
32  #define BP_ACCOUNT_IS_SUPPORTED 1
33  #endif
34  
35  static volatile long the_var;
36  
test_function(void)37  static noinline int test_function(void)
38  {
39  	return 0;
40  }
41  
__event(bool is_x,void * addr,struct perf_event_attr * attr)42  static int __event(bool is_x, void *addr, struct perf_event_attr *attr)
43  {
44  	int fd;
45  
46  	memset(attr, 0, sizeof(struct perf_event_attr));
47  	attr->type = PERF_TYPE_BREAKPOINT;
48  	attr->size = sizeof(struct perf_event_attr);
49  
50  	attr->config = 0;
51  	attr->bp_type = is_x ? HW_BREAKPOINT_X : HW_BREAKPOINT_W;
52  	attr->bp_addr = (unsigned long) addr;
53  	attr->bp_len = sizeof(long);
54  
55  	attr->sample_period = 1;
56  	attr->sample_type = PERF_SAMPLE_IP;
57  
58  	attr->exclude_kernel = 1;
59  	attr->exclude_hv = 1;
60  
61  	fd = sys_perf_event_open(attr, -1, 0, -1,
62  				 perf_event_open_cloexec_flag());
63  	if (fd < 0) {
64  		pr_debug("failed opening event %llx\n", attr->config);
65  		return TEST_FAIL;
66  	}
67  
68  	return fd;
69  }
70  
wp_event(void * addr,struct perf_event_attr * attr)71  static int wp_event(void *addr, struct perf_event_attr *attr)
72  {
73  	return __event(false, addr, attr);
74  }
75  
bp_event(void * addr,struct perf_event_attr * attr)76  static int bp_event(void *addr, struct perf_event_attr *attr)
77  {
78  	return __event(true, addr, attr);
79  }
80  
bp_accounting(int wp_cnt,int share)81  static int bp_accounting(int wp_cnt, int share)
82  {
83  	struct perf_event_attr attr, attr_mod, attr_new;
84  	int i, fd[wp_cnt], fd_wp, ret;
85  
86  	for (i = 0; i < wp_cnt; i++) {
87  		fd[i] = wp_event((void *)&the_var, &attr);
88  		TEST_ASSERT_VAL("failed to create wp\n", fd[i] != -1);
89  		pr_debug("wp %d created\n", i);
90  	}
91  
92  	attr_mod = attr;
93  	attr_mod.bp_type = HW_BREAKPOINT_X;
94  	attr_mod.bp_addr = (unsigned long) test_function;
95  
96  	ret = ioctl(fd[0], PERF_EVENT_IOC_MODIFY_ATTRIBUTES, &attr_mod);
97  	TEST_ASSERT_VAL("failed to modify wp\n", ret == 0);
98  
99  	pr_debug("wp 0 modified to bp\n");
100  
101  	if (!share) {
102  		fd_wp = wp_event((void *)&the_var, &attr_new);
103  		TEST_ASSERT_VAL("failed to create max wp\n", fd_wp != -1);
104  		pr_debug("wp max created\n");
105  	}
106  
107  	for (i = 0; i < wp_cnt; i++)
108  		close(fd[i]);
109  
110  	return 0;
111  }
112  
detect_cnt(bool is_x)113  static int detect_cnt(bool is_x)
114  {
115  	struct perf_event_attr attr;
116  	void *addr = is_x ? (void *)test_function : (void *)&the_var;
117  	int fd[100], cnt = 0, i;
118  
119  	while (1) {
120  		if (cnt == 100) {
121  			pr_debug("way too many debug registers, fix the test\n");
122  			return 0;
123  		}
124  		fd[cnt] = __event(is_x, addr, &attr);
125  
126  		if (fd[cnt] < 0)
127  			break;
128  		cnt++;
129  	}
130  
131  	for (i = 0; i < cnt; i++)
132  		close(fd[i]);
133  
134  	return cnt;
135  }
136  
detect_ioctl(void)137  static int detect_ioctl(void)
138  {
139  	struct perf_event_attr attr;
140  	int fd, ret = 1;
141  
142  	fd = wp_event((void *) &the_var, &attr);
143  	if (fd > 0) {
144  		ret = ioctl(fd, PERF_EVENT_IOC_MODIFY_ATTRIBUTES, &attr);
145  		close(fd);
146  	}
147  
148  	return ret ? 0 : 1;
149  }
150  
detect_share(int wp_cnt,int bp_cnt)151  static int detect_share(int wp_cnt, int bp_cnt)
152  {
153  	struct perf_event_attr attr;
154  	int i, *fd = NULL, ret = -1;
155  
156  	if (wp_cnt + bp_cnt == 0)
157  		return 0;
158  
159  	fd = malloc(sizeof(int) * (wp_cnt + bp_cnt));
160  	if (!fd)
161  		return -1;
162  
163  	for (i = 0; i < wp_cnt; i++) {
164  		fd[i] = wp_event((void *)&the_var, &attr);
165  		if (fd[i] == -1) {
166  			pr_err("failed to create wp\n");
167  			goto out;
168  		}
169  	}
170  
171  	for (; i < (bp_cnt + wp_cnt); i++) {
172  		fd[i] = bp_event((void *)test_function, &attr);
173  		if (fd[i] == -1)
174  			break;
175  	}
176  
177  	ret = i != (bp_cnt + wp_cnt);
178  
179  out:
180  	while (i--)
181  		close(fd[i]);
182  
183  	free(fd);
184  	return ret;
185  }
186  
187  /*
188   * This test does following:
189   *   - detects the number of watch/break-points,
190   *     skip test if any is missing
191   *   - detects PERF_EVENT_IOC_MODIFY_ATTRIBUTES ioctl,
192   *     skip test if it's missing
193   *   - detects if watchpoints and breakpoints share
194   *     same slots
195   *   - create all possible watchpoints on cpu 0
196   *   - change one of it to breakpoint
197   *   - in case wp and bp do not share slots,
198   *     we create another watchpoint to ensure
199   *     the slot accounting is correct
200   */
test__bp_accounting(struct test_suite * test __maybe_unused,int subtest __maybe_unused)201  static int test__bp_accounting(struct test_suite *test __maybe_unused, int subtest __maybe_unused)
202  {
203  	int has_ioctl = detect_ioctl();
204  	int wp_cnt = detect_cnt(false);
205  	int bp_cnt = detect_cnt(true);
206  	int share  = detect_share(wp_cnt, bp_cnt);
207  
208  	if (!BP_ACCOUNT_IS_SUPPORTED) {
209  		pr_debug("Test not supported on this architecture");
210  		return TEST_SKIP;
211  	}
212  
213  	pr_debug("watchpoints count %d, breakpoints count %d, has_ioctl %d, share %d\n",
214  		 wp_cnt, bp_cnt, has_ioctl, share);
215  
216  	if (!wp_cnt || !bp_cnt || !has_ioctl)
217  		return TEST_SKIP;
218  
219  	return bp_accounting(wp_cnt, share);
220  }
221  
222  DEFINE_SUITE("Breakpoint accounting", bp_accounting);
223