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 *abi_file = "/sys/kernel/tracing/user_events_data";
21const char *enable_file = "/sys/kernel/tracing/events/user_events/__test_event/enable";
22
23static bool wait_for_delete(void)
24{
25	int i;
26
27	for (i = 0; i < 1000; ++i) {
28		int fd = open(enable_file, O_RDONLY);
29
30		if (fd == -1)
31			return true;
32
33		close(fd);
34		usleep(1000);
35	}
36
37	return false;
38}
39
40static int reg_event(int fd, int *check, int bit, const char *value)
41{
42	struct user_reg reg = {0};
43
44	reg.size = sizeof(reg);
45	reg.name_args = (__u64)value;
46	reg.enable_bit = bit;
47	reg.enable_addr = (__u64)check;
48	reg.enable_size = sizeof(*check);
49
50	if (ioctl(fd, DIAG_IOCSREG, &reg) == -1)
51		return -1;
52
53	return 0;
54}
55
56static int unreg_event(int fd, int *check, int bit)
57{
58	struct user_unreg unreg = {0};
59
60	unreg.size = sizeof(unreg);
61	unreg.disable_bit = bit;
62	unreg.disable_addr = (__u64)check;
63
64	return ioctl(fd, DIAG_IOCSUNREG, &unreg);
65}
66
67static int parse(int *check, const char *value)
68{
69	int fd = open(abi_file, O_RDWR);
70	int ret;
71
72	if (fd == -1)
73		return -1;
74
75	/* Until we have persist flags via dynamic events, use the base name */
76	if (value[0] != 'u' || value[1] != ':') {
77		close(fd);
78		return -1;
79	}
80
81	ret = reg_event(fd, check, 31, value + 2);
82
83	if (ret != -1) {
84		if (unreg_event(fd, check, 31) == -1)
85			printf("WARN: Couldn't unreg event\n");
86	}
87
88	close(fd);
89
90	return ret;
91}
92
93static int check_match(int *check, const char *first, const char *second, bool *match)
94{
95	int fd = open(abi_file, O_RDWR);
96	int ret = -1;
97
98	if (fd == -1)
99		return -1;
100
101	if (reg_event(fd, check, 31, first) == -1)
102		goto cleanup;
103
104	if (reg_event(fd, check, 30, second) == -1) {
105		if (errno == EADDRINUSE) {
106			/* Name is in use, with different fields */
107			*match = false;
108			ret = 0;
109		}
110
111		goto cleanup;
112	}
113
114	*match = true;
115	ret = 0;
116cleanup:
117	unreg_event(fd, check, 31);
118	unreg_event(fd, check, 30);
119
120	close(fd);
121
122	wait_for_delete();
123
124	return ret;
125}
126
127#define TEST_MATCH(x, y) \
128do { \
129	bool match; \
130	ASSERT_NE(-1, check_match(&self->check, x, y, &match)); \
131	ASSERT_EQ(true, match); \
132} while (0)
133
134#define TEST_NMATCH(x, y) \
135do { \
136	bool match; \
137	ASSERT_NE(-1, check_match(&self->check, x, y, &match)); \
138	ASSERT_EQ(false, match); \
139} while (0)
140
141#define TEST_PARSE(x) ASSERT_NE(-1, parse(&self->check, x))
142
143#define TEST_NPARSE(x) ASSERT_EQ(-1, parse(&self->check, x))
144
145FIXTURE(user) {
146	int check;
147	bool umount;
148};
149
150FIXTURE_SETUP(user) {
151	USER_EVENT_FIXTURE_SETUP(return, self->umount);
152}
153
154FIXTURE_TEARDOWN(user) {
155	USER_EVENT_FIXTURE_TEARDOWN(self->umount);
156
157	wait_for_delete();
158}
159
160TEST_F(user, basic_types) {
161	/* All should work */
162	TEST_PARSE("u:__test_event u64 a");
163	TEST_PARSE("u:__test_event u32 a");
164	TEST_PARSE("u:__test_event u16 a");
165	TEST_PARSE("u:__test_event u8 a");
166	TEST_PARSE("u:__test_event char a");
167	TEST_PARSE("u:__test_event unsigned char a");
168	TEST_PARSE("u:__test_event int a");
169	TEST_PARSE("u:__test_event unsigned int a");
170	TEST_PARSE("u:__test_event short a");
171	TEST_PARSE("u:__test_event unsigned short a");
172	TEST_PARSE("u:__test_event char[20] a");
173	TEST_PARSE("u:__test_event unsigned char[20] a");
174	TEST_PARSE("u:__test_event char[0x14] a");
175	TEST_PARSE("u:__test_event unsigned char[0x14] a");
176	/* Bad size format should fail */
177	TEST_NPARSE("u:__test_event char[aa] a");
178	/* Large size should fail */
179	TEST_NPARSE("u:__test_event char[9999] a");
180	/* Long size string should fail */
181	TEST_NPARSE("u:__test_event char[0x0000000000001] a");
182}
183
184TEST_F(user, loc_types) {
185	/* All should work */
186	TEST_PARSE("u:__test_event __data_loc char[] a");
187	TEST_PARSE("u:__test_event __data_loc unsigned char[] a");
188	TEST_PARSE("u:__test_event __rel_loc char[] a");
189	TEST_PARSE("u:__test_event __rel_loc unsigned char[] a");
190}
191
192TEST_F(user, size_types) {
193	/* Should work */
194	TEST_PARSE("u:__test_event struct custom a 20");
195	/* Size not specified on struct should fail */
196	TEST_NPARSE("u:__test_event struct custom a");
197	/* Size specified on non-struct should fail */
198	TEST_NPARSE("u:__test_event char a 20");
199}
200
201TEST_F(user, matching) {
202	/* Single name matches */
203	TEST_MATCH("__test_event u32 a",
204		   "__test_event u32 a");
205
206	/* Multiple names match */
207	TEST_MATCH("__test_event u32 a; u32 b",
208		   "__test_event u32 a; u32 b");
209
210	/* Multiple names match with dangling ; */
211	TEST_MATCH("__test_event u32 a; u32 b",
212		   "__test_event u32 a; u32 b;");
213
214	/* Single name doesn't match */
215	TEST_NMATCH("__test_event u32 a",
216		    "__test_event u32 b");
217
218	/* Multiple names don't match */
219	TEST_NMATCH("__test_event u32 a; u32 b",
220		    "__test_event u32 b; u32 a");
221
222	/* Types don't match */
223	TEST_NMATCH("__test_event u64 a; u64 b",
224		    "__test_event u32 a; u32 b");
225
226	/* Struct name and size matches */
227	TEST_MATCH("__test_event struct my_struct a 20",
228		   "__test_event struct my_struct a 20");
229
230	/* Struct name don't match */
231	TEST_NMATCH("__test_event struct my_struct a 20",
232		    "__test_event struct my_struct b 20");
233
234	/* Struct size don't match */
235	TEST_NMATCH("__test_event struct my_struct a 20",
236		    "__test_event struct my_struct a 21");
237}
238
239int main(int argc, char **argv)
240{
241	return test_harness_run(argc, argv);
242}
243