162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * User Events Dyn Events Test Program
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2021 Beau Belgrave <beaub@linux.microsoft.com>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <errno.h>
962306a36Sopenharmony_ci#include <linux/user_events.h>
1062306a36Sopenharmony_ci#include <stdio.h>
1162306a36Sopenharmony_ci#include <stdlib.h>
1262306a36Sopenharmony_ci#include <fcntl.h>
1362306a36Sopenharmony_ci#include <sys/ioctl.h>
1462306a36Sopenharmony_ci#include <sys/stat.h>
1562306a36Sopenharmony_ci#include <unistd.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include "../kselftest_harness.h"
1862306a36Sopenharmony_ci#include "user_events_selftests.h"
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ciconst char *abi_file = "/sys/kernel/tracing/user_events_data";
2162306a36Sopenharmony_ciconst char *enable_file = "/sys/kernel/tracing/events/user_events/__test_event/enable";
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistatic bool wait_for_delete(void)
2462306a36Sopenharmony_ci{
2562306a36Sopenharmony_ci	int i;
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	for (i = 0; i < 1000; ++i) {
2862306a36Sopenharmony_ci		int fd = open(enable_file, O_RDONLY);
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci		if (fd == -1)
3162306a36Sopenharmony_ci			return true;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci		close(fd);
3462306a36Sopenharmony_ci		usleep(1000);
3562306a36Sopenharmony_ci	}
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	return false;
3862306a36Sopenharmony_ci}
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistatic int reg_event(int fd, int *check, int bit, const char *value)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	struct user_reg reg = {0};
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	reg.size = sizeof(reg);
4562306a36Sopenharmony_ci	reg.name_args = (__u64)value;
4662306a36Sopenharmony_ci	reg.enable_bit = bit;
4762306a36Sopenharmony_ci	reg.enable_addr = (__u64)check;
4862306a36Sopenharmony_ci	reg.enable_size = sizeof(*check);
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	if (ioctl(fd, DIAG_IOCSREG, &reg) == -1)
5162306a36Sopenharmony_ci		return -1;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	return 0;
5462306a36Sopenharmony_ci}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic int unreg_event(int fd, int *check, int bit)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	struct user_unreg unreg = {0};
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	unreg.size = sizeof(unreg);
6162306a36Sopenharmony_ci	unreg.disable_bit = bit;
6262306a36Sopenharmony_ci	unreg.disable_addr = (__u64)check;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	return ioctl(fd, DIAG_IOCSUNREG, &unreg);
6562306a36Sopenharmony_ci}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic int parse(int *check, const char *value)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	int fd = open(abi_file, O_RDWR);
7062306a36Sopenharmony_ci	int ret;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	if (fd == -1)
7362306a36Sopenharmony_ci		return -1;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	/* Until we have persist flags via dynamic events, use the base name */
7662306a36Sopenharmony_ci	if (value[0] != 'u' || value[1] != ':') {
7762306a36Sopenharmony_ci		close(fd);
7862306a36Sopenharmony_ci		return -1;
7962306a36Sopenharmony_ci	}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	ret = reg_event(fd, check, 31, value + 2);
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	if (ret != -1) {
8462306a36Sopenharmony_ci		if (unreg_event(fd, check, 31) == -1)
8562306a36Sopenharmony_ci			printf("WARN: Couldn't unreg event\n");
8662306a36Sopenharmony_ci	}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	close(fd);
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	return ret;
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic int check_match(int *check, const char *first, const char *second, bool *match)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	int fd = open(abi_file, O_RDWR);
9662306a36Sopenharmony_ci	int ret = -1;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	if (fd == -1)
9962306a36Sopenharmony_ci		return -1;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	if (reg_event(fd, check, 31, first) == -1)
10262306a36Sopenharmony_ci		goto cleanup;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	if (reg_event(fd, check, 30, second) == -1) {
10562306a36Sopenharmony_ci		if (errno == EADDRINUSE) {
10662306a36Sopenharmony_ci			/* Name is in use, with different fields */
10762306a36Sopenharmony_ci			*match = false;
10862306a36Sopenharmony_ci			ret = 0;
10962306a36Sopenharmony_ci		}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci		goto cleanup;
11262306a36Sopenharmony_ci	}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	*match = true;
11562306a36Sopenharmony_ci	ret = 0;
11662306a36Sopenharmony_cicleanup:
11762306a36Sopenharmony_ci	unreg_event(fd, check, 31);
11862306a36Sopenharmony_ci	unreg_event(fd, check, 30);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	close(fd);
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	wait_for_delete();
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	return ret;
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci#define TEST_MATCH(x, y) \
12862306a36Sopenharmony_cido { \
12962306a36Sopenharmony_ci	bool match; \
13062306a36Sopenharmony_ci	ASSERT_NE(-1, check_match(&self->check, x, y, &match)); \
13162306a36Sopenharmony_ci	ASSERT_EQ(true, match); \
13262306a36Sopenharmony_ci} while (0)
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci#define TEST_NMATCH(x, y) \
13562306a36Sopenharmony_cido { \
13662306a36Sopenharmony_ci	bool match; \
13762306a36Sopenharmony_ci	ASSERT_NE(-1, check_match(&self->check, x, y, &match)); \
13862306a36Sopenharmony_ci	ASSERT_EQ(false, match); \
13962306a36Sopenharmony_ci} while (0)
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci#define TEST_PARSE(x) ASSERT_NE(-1, parse(&self->check, x))
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci#define TEST_NPARSE(x) ASSERT_EQ(-1, parse(&self->check, x))
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ciFIXTURE(user) {
14662306a36Sopenharmony_ci	int check;
14762306a36Sopenharmony_ci	bool umount;
14862306a36Sopenharmony_ci};
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ciFIXTURE_SETUP(user) {
15162306a36Sopenharmony_ci	USER_EVENT_FIXTURE_SETUP(return, self->umount);
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ciFIXTURE_TEARDOWN(user) {
15562306a36Sopenharmony_ci	USER_EVENT_FIXTURE_TEARDOWN(self->umount);
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	wait_for_delete();
15862306a36Sopenharmony_ci}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ciTEST_F(user, basic_types) {
16162306a36Sopenharmony_ci	/* All should work */
16262306a36Sopenharmony_ci	TEST_PARSE("u:__test_event u64 a");
16362306a36Sopenharmony_ci	TEST_PARSE("u:__test_event u32 a");
16462306a36Sopenharmony_ci	TEST_PARSE("u:__test_event u16 a");
16562306a36Sopenharmony_ci	TEST_PARSE("u:__test_event u8 a");
16662306a36Sopenharmony_ci	TEST_PARSE("u:__test_event char a");
16762306a36Sopenharmony_ci	TEST_PARSE("u:__test_event unsigned char a");
16862306a36Sopenharmony_ci	TEST_PARSE("u:__test_event int a");
16962306a36Sopenharmony_ci	TEST_PARSE("u:__test_event unsigned int a");
17062306a36Sopenharmony_ci	TEST_PARSE("u:__test_event short a");
17162306a36Sopenharmony_ci	TEST_PARSE("u:__test_event unsigned short a");
17262306a36Sopenharmony_ci	TEST_PARSE("u:__test_event char[20] a");
17362306a36Sopenharmony_ci	TEST_PARSE("u:__test_event unsigned char[20] a");
17462306a36Sopenharmony_ci	TEST_PARSE("u:__test_event char[0x14] a");
17562306a36Sopenharmony_ci	TEST_PARSE("u:__test_event unsigned char[0x14] a");
17662306a36Sopenharmony_ci	/* Bad size format should fail */
17762306a36Sopenharmony_ci	TEST_NPARSE("u:__test_event char[aa] a");
17862306a36Sopenharmony_ci	/* Large size should fail */
17962306a36Sopenharmony_ci	TEST_NPARSE("u:__test_event char[9999] a");
18062306a36Sopenharmony_ci	/* Long size string should fail */
18162306a36Sopenharmony_ci	TEST_NPARSE("u:__test_event char[0x0000000000001] a");
18262306a36Sopenharmony_ci}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ciTEST_F(user, loc_types) {
18562306a36Sopenharmony_ci	/* All should work */
18662306a36Sopenharmony_ci	TEST_PARSE("u:__test_event __data_loc char[] a");
18762306a36Sopenharmony_ci	TEST_PARSE("u:__test_event __data_loc unsigned char[] a");
18862306a36Sopenharmony_ci	TEST_PARSE("u:__test_event __rel_loc char[] a");
18962306a36Sopenharmony_ci	TEST_PARSE("u:__test_event __rel_loc unsigned char[] a");
19062306a36Sopenharmony_ci}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ciTEST_F(user, size_types) {
19362306a36Sopenharmony_ci	/* Should work */
19462306a36Sopenharmony_ci	TEST_PARSE("u:__test_event struct custom a 20");
19562306a36Sopenharmony_ci	/* Size not specified on struct should fail */
19662306a36Sopenharmony_ci	TEST_NPARSE("u:__test_event struct custom a");
19762306a36Sopenharmony_ci	/* Size specified on non-struct should fail */
19862306a36Sopenharmony_ci	TEST_NPARSE("u:__test_event char a 20");
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ciTEST_F(user, matching) {
20262306a36Sopenharmony_ci	/* Single name matches */
20362306a36Sopenharmony_ci	TEST_MATCH("__test_event u32 a",
20462306a36Sopenharmony_ci		   "__test_event u32 a");
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	/* Multiple names match */
20762306a36Sopenharmony_ci	TEST_MATCH("__test_event u32 a; u32 b",
20862306a36Sopenharmony_ci		   "__test_event u32 a; u32 b");
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	/* Multiple names match with dangling ; */
21162306a36Sopenharmony_ci	TEST_MATCH("__test_event u32 a; u32 b",
21262306a36Sopenharmony_ci		   "__test_event u32 a; u32 b;");
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	/* Single name doesn't match */
21562306a36Sopenharmony_ci	TEST_NMATCH("__test_event u32 a",
21662306a36Sopenharmony_ci		    "__test_event u32 b");
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	/* Multiple names don't match */
21962306a36Sopenharmony_ci	TEST_NMATCH("__test_event u32 a; u32 b",
22062306a36Sopenharmony_ci		    "__test_event u32 b; u32 a");
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	/* Types don't match */
22362306a36Sopenharmony_ci	TEST_NMATCH("__test_event u64 a; u64 b",
22462306a36Sopenharmony_ci		    "__test_event u32 a; u32 b");
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	/* Struct name and size matches */
22762306a36Sopenharmony_ci	TEST_MATCH("__test_event struct my_struct a 20",
22862306a36Sopenharmony_ci		   "__test_event struct my_struct a 20");
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	/* Struct name don't match */
23162306a36Sopenharmony_ci	TEST_NMATCH("__test_event struct my_struct a 20",
23262306a36Sopenharmony_ci		    "__test_event struct my_struct b 20");
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	/* Struct size don't match */
23562306a36Sopenharmony_ci	TEST_NMATCH("__test_event struct my_struct a 20",
23662306a36Sopenharmony_ci		    "__test_event struct my_struct a 21");
23762306a36Sopenharmony_ci}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ciint main(int argc, char **argv)
24062306a36Sopenharmony_ci{
24162306a36Sopenharmony_ci	return test_harness_run(argc, argv);
24262306a36Sopenharmony_ci}
243