1// SPDX-License-Identifier: GPL-2.0
2/*
3 * User Events Dyn Events Test Program
4 *
5 * Copyright (c) 2021 Beau Belgrave <beaub@linux.microsoft.com>
6 */
7
8#include <errno.h>
9#include <linux/user_events.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <fcntl.h>
13#include <sys/ioctl.h>
14#include <sys/stat.h>
15#include <unistd.h>
16
17#include "../kselftest_harness.h"
18#include "user_events_selftests.h"
19
20const char *dyn_file = "/sys/kernel/tracing/dynamic_events";
21const char *abi_file = "/sys/kernel/tracing/user_events_data";
22const char *enable_file = "/sys/kernel/tracing/events/user_events/__test_event/enable";
23
24static int event_delete(void)
25{
26	int fd = open(abi_file, O_RDWR);
27	int ret;
28
29	if (fd < 0)
30		return -1;
31
32	ret = ioctl(fd, DIAG_IOCSDEL, "__test_event");
33
34	close(fd);
35
36	return ret;
37}
38
39static bool wait_for_delete(void)
40{
41	int i;
42
43	for (i = 0; i < 1000; ++i) {
44		int fd = open(enable_file, O_RDONLY);
45
46		if (fd == -1)
47			return true;
48
49		close(fd);
50		usleep(1000);
51	}
52
53	return false;
54}
55
56static int reg_event(int fd, int *check, int bit, const char *value)
57{
58	struct user_reg reg = {0};
59
60	reg.size = sizeof(reg);
61	reg.name_args = (__u64)value;
62	reg.enable_bit = bit;
63	reg.enable_addr = (__u64)check;
64	reg.enable_size = sizeof(*check);
65
66	if (ioctl(fd, DIAG_IOCSREG, &reg) == -1)
67		return -1;
68
69	return 0;
70}
71
72static int unreg_event(int fd, int *check, int bit)
73{
74	struct user_unreg unreg = {0};
75
76	unreg.size = sizeof(unreg);
77	unreg.disable_bit = bit;
78	unreg.disable_addr = (__u64)check;
79
80	return ioctl(fd, DIAG_IOCSUNREG, &unreg);
81}
82
83static int parse_dyn(const char *value)
84{
85	int fd = open(dyn_file, O_RDWR | O_APPEND);
86	int len = strlen(value);
87	int ret;
88
89	if (fd == -1)
90		return -1;
91
92	ret = write(fd, value, len);
93
94	if (ret == len)
95		ret = 0;
96	else
97		ret = -1;
98
99	close(fd);
100
101	if (ret == 0)
102		event_delete();
103
104	return ret;
105}
106
107static int parse_abi(int *check, const char *value)
108{
109	int fd = open(abi_file, O_RDWR);
110	int ret;
111
112	if (fd == -1)
113		return -1;
114
115	/* Until we have persist flags via dynamic events, use the base name */
116	if (value[0] != 'u' || value[1] != ':') {
117		close(fd);
118		return -1;
119	}
120
121	ret = reg_event(fd, check, 31, value + 2);
122
123	if (ret != -1) {
124		if (unreg_event(fd, check, 31) == -1)
125			printf("WARN: Couldn't unreg event\n");
126	}
127
128	close(fd);
129
130	return ret;
131}
132
133static int parse(int *check, const char *value)
134{
135	int abi_ret = parse_abi(check, value);
136	int dyn_ret = parse_dyn(value);
137
138	/* Ensure both ABI and DYN parse the same way */
139	if (dyn_ret != abi_ret)
140		return -1;
141
142	return dyn_ret;
143}
144
145static int check_match(int *check, const char *first, const char *second, bool *match)
146{
147	int fd = open(abi_file, O_RDWR);
148	int ret = -1;
149
150	if (fd == -1)
151		return -1;
152
153	if (reg_event(fd, check, 31, first) == -1)
154		goto cleanup;
155
156	if (reg_event(fd, check, 30, second) == -1) {
157		if (errno == EADDRINUSE) {
158			/* Name is in use, with different fields */
159			*match = false;
160			ret = 0;
161		}
162
163		goto cleanup;
164	}
165
166	*match = true;
167	ret = 0;
168cleanup:
169	unreg_event(fd, check, 31);
170	unreg_event(fd, check, 30);
171
172	close(fd);
173
174	wait_for_delete();
175
176	return ret;
177}
178
179#define TEST_MATCH(x, y) \
180do { \
181	bool match; \
182	ASSERT_NE(-1, check_match(&self->check, x, y, &match)); \
183	ASSERT_EQ(true, match); \
184} while (0)
185
186#define TEST_NMATCH(x, y) \
187do { \
188	bool match; \
189	ASSERT_NE(-1, check_match(&self->check, x, y, &match)); \
190	ASSERT_EQ(false, match); \
191} while (0)
192
193#define TEST_PARSE(x) ASSERT_NE(-1, parse(&self->check, x))
194
195#define TEST_NPARSE(x) ASSERT_EQ(-1, parse(&self->check, x))
196
197FIXTURE(user) {
198	int check;
199	bool umount;
200};
201
202FIXTURE_SETUP(user) {
203	USER_EVENT_FIXTURE_SETUP(return, self->umount);
204}
205
206FIXTURE_TEARDOWN(user) {
207	USER_EVENT_FIXTURE_TEARDOWN(self->umount);
208
209	wait_for_delete();
210}
211
212TEST_F(user, basic_types) {
213	/* All should work */
214	TEST_PARSE("u:__test_event u64 a");
215	TEST_PARSE("u:__test_event u32 a");
216	TEST_PARSE("u:__test_event u16 a");
217	TEST_PARSE("u:__test_event u8 a");
218	TEST_PARSE("u:__test_event char a");
219	TEST_PARSE("u:__test_event unsigned char a");
220	TEST_PARSE("u:__test_event int a");
221	TEST_PARSE("u:__test_event unsigned int a");
222	TEST_PARSE("u:__test_event short a");
223	TEST_PARSE("u:__test_event unsigned short a");
224	TEST_PARSE("u:__test_event char[20] a");
225	TEST_PARSE("u:__test_event unsigned char[20] a");
226	TEST_PARSE("u:__test_event char[0x14] a");
227	TEST_PARSE("u:__test_event unsigned char[0x14] a");
228	/* Bad size format should fail */
229	TEST_NPARSE("u:__test_event char[aa] a");
230	/* Large size should fail */
231	TEST_NPARSE("u:__test_event char[9999] a");
232	/* Long size string should fail */
233	TEST_NPARSE("u:__test_event char[0x0000000000001] a");
234}
235
236TEST_F(user, loc_types) {
237	/* All should work */
238	TEST_PARSE("u:__test_event __data_loc char[] a");
239	TEST_PARSE("u:__test_event __data_loc unsigned char[] a");
240	TEST_PARSE("u:__test_event __rel_loc char[] a");
241	TEST_PARSE("u:__test_event __rel_loc unsigned char[] a");
242}
243
244TEST_F(user, size_types) {
245	/* Should work */
246	TEST_PARSE("u:__test_event struct custom a 20");
247	/* Size not specified on struct should fail */
248	TEST_NPARSE("u:__test_event struct custom a");
249	/* Size specified on non-struct should fail */
250	TEST_NPARSE("u:__test_event char a 20");
251}
252
253TEST_F(user, matching) {
254	/* Single name matches */
255	TEST_MATCH("__test_event u32 a",
256		   "__test_event u32 a");
257
258	/* Multiple names match */
259	TEST_MATCH("__test_event u32 a; u32 b",
260		   "__test_event u32 a; u32 b");
261
262	/* Multiple names match with dangling ; */
263	TEST_MATCH("__test_event u32 a; u32 b",
264		   "__test_event u32 a; u32 b;");
265
266	/* Single name doesn't match */
267	TEST_NMATCH("__test_event u32 a",
268		    "__test_event u32 b");
269
270	/* Multiple names don't match */
271	TEST_NMATCH("__test_event u32 a; u32 b",
272		    "__test_event u32 b; u32 a");
273
274	/* Types don't match */
275	TEST_NMATCH("__test_event u64 a; u64 b",
276		    "__test_event u32 a; u32 b");
277
278	/* Struct name and size matches */
279	TEST_MATCH("__test_event struct my_struct a 20",
280		   "__test_event struct my_struct a 20");
281
282	/* Struct name don't match */
283	TEST_NMATCH("__test_event struct my_struct a 20",
284		    "__test_event struct my_struct b 20");
285
286	/* Struct size don't match */
287	TEST_NMATCH("__test_event struct my_struct a 20",
288		    "__test_event struct my_struct a 21");
289}
290
291int main(int argc, char **argv)
292{
293	return test_harness_run(argc, argv);
294}
295