| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * User Events Dyn Events Test Program
- *
- * Copyright (c) 2021 Beau Belgrave <beaub@linux.microsoft.com>
- */
- #include <errno.h>
- #include <linux/user_events.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <fcntl.h>
- #include <sys/ioctl.h>
- #include <sys/stat.h>
- #include <unistd.h>
- #include "kselftest_harness.h"
- #include "user_events_selftests.h"
- const char *dyn_file = "/sys/kernel/tracing/dynamic_events";
- const char *abi_file = "/sys/kernel/tracing/user_events_data";
- const char *enable_file = "/sys/kernel/tracing/events/user_events/__test_event/enable";
- static int event_delete(void)
- {
- int fd = open(abi_file, O_RDWR);
- int ret;
- if (fd < 0)
- return -1;
- ret = ioctl(fd, DIAG_IOCSDEL, "__test_event");
- close(fd);
- return ret;
- }
- static bool wait_for_delete(void)
- {
- int i;
- for (i = 0; i < 1000; ++i) {
- int fd = open(enable_file, O_RDONLY);
- if (fd == -1)
- return true;
- close(fd);
- usleep(1000);
- }
- return false;
- }
- static int reg_event(int fd, int *check, int bit, const char *value)
- {
- struct user_reg reg = {0};
- reg.size = sizeof(reg);
- reg.name_args = (__u64)value;
- reg.enable_bit = bit;
- reg.enable_addr = (__u64)check;
- reg.enable_size = sizeof(*check);
- if (ioctl(fd, DIAG_IOCSREG, ®) == -1)
- return -1;
- return 0;
- }
- static int unreg_event(int fd, int *check, int bit)
- {
- struct user_unreg unreg = {0};
- unreg.size = sizeof(unreg);
- unreg.disable_bit = bit;
- unreg.disable_addr = (__u64)check;
- return ioctl(fd, DIAG_IOCSUNREG, &unreg);
- }
- static int parse_dyn(const char *value)
- {
- int fd = open(dyn_file, O_RDWR | O_APPEND);
- int len = strlen(value);
- int ret;
- if (fd == -1)
- return -1;
- ret = write(fd, value, len);
- if (ret == len)
- ret = 0;
- else
- ret = -1;
- close(fd);
- if (ret == 0)
- event_delete();
- return ret;
- }
- static int parse_abi(int *check, const char *value)
- {
- int fd = open(abi_file, O_RDWR);
- int ret;
- if (fd == -1)
- return -1;
- /* Until we have persist flags via dynamic events, use the base name */
- if (value[0] != 'u' || value[1] != ':') {
- close(fd);
- return -1;
- }
- ret = reg_event(fd, check, 31, value + 2);
- if (ret != -1) {
- if (unreg_event(fd, check, 31) == -1)
- printf("WARN: Couldn't unreg event\n");
- }
- close(fd);
- wait_for_delete();
- return ret;
- }
- static int parse(int *check, const char *value)
- {
- int abi_ret = parse_abi(check, value);
- int dyn_ret = parse_dyn(value);
- /* Ensure both ABI and DYN parse the same way */
- if (dyn_ret != abi_ret)
- return -1;
- return dyn_ret;
- }
- static int check_match(int *check, const char *first, const char *second, bool *match)
- {
- int fd = open(abi_file, O_RDWR);
- int ret = -1;
- if (fd == -1)
- return -1;
- if (reg_event(fd, check, 31, first) == -1)
- goto cleanup;
- if (reg_event(fd, check, 30, second) == -1) {
- if (errno == EADDRINUSE) {
- /* Name is in use, with different fields */
- *match = false;
- ret = 0;
- }
- goto cleanup;
- }
- *match = true;
- ret = 0;
- cleanup:
- unreg_event(fd, check, 31);
- unreg_event(fd, check, 30);
- close(fd);
- wait_for_delete();
- return ret;
- }
- #define TEST_MATCH(x, y) \
- do { \
- bool match; \
- ASSERT_NE(-1, check_match(&self->check, x, y, &match)); \
- ASSERT_EQ(true, match); \
- } while (0)
- #define TEST_NMATCH(x, y) \
- do { \
- bool match; \
- ASSERT_NE(-1, check_match(&self->check, x, y, &match)); \
- ASSERT_EQ(false, match); \
- } while (0)
- #define TEST_PARSE(x) ASSERT_NE(-1, parse(&self->check, x))
- #define TEST_NPARSE(x) ASSERT_EQ(-1, parse(&self->check, x))
- FIXTURE(user) {
- int check;
- bool umount;
- };
- FIXTURE_SETUP(user) {
- USER_EVENT_FIXTURE_SETUP(return, self->umount);
- }
- FIXTURE_TEARDOWN(user) {
- USER_EVENT_FIXTURE_TEARDOWN(self->umount);
- wait_for_delete();
- }
- TEST_F(user, basic_types) {
- /* All should work */
- TEST_PARSE("u:__test_event u64 a");
- TEST_PARSE("u:__test_event u32 a");
- TEST_PARSE("u:__test_event u16 a");
- TEST_PARSE("u:__test_event u8 a");
- TEST_PARSE("u:__test_event char a");
- TEST_PARSE("u:__test_event unsigned char a");
- TEST_PARSE("u:__test_event int a");
- TEST_PARSE("u:__test_event unsigned int a");
- TEST_PARSE("u:__test_event short a");
- TEST_PARSE("u:__test_event unsigned short a");
- TEST_PARSE("u:__test_event char[20] a");
- TEST_PARSE("u:__test_event unsigned char[20] a");
- TEST_PARSE("u:__test_event char[0x14] a");
- TEST_PARSE("u:__test_event unsigned char[0x14] a");
- /* Bad size format should fail */
- TEST_NPARSE("u:__test_event char[aa] a");
- /* Large size should fail */
- TEST_NPARSE("u:__test_event char[9999] a");
- /* Long size string should fail */
- TEST_NPARSE("u:__test_event char[0x0000000000001] a");
- }
- TEST_F(user, loc_types) {
- /* All should work */
- TEST_PARSE("u:__test_event __data_loc char[] a");
- TEST_PARSE("u:__test_event __data_loc unsigned char[] a");
- TEST_PARSE("u:__test_event __rel_loc char[] a");
- TEST_PARSE("u:__test_event __rel_loc unsigned char[] a");
- }
- TEST_F(user, size_types) {
- /* Should work */
- TEST_PARSE("u:__test_event struct custom a 20");
- /* Size not specified on struct should fail */
- TEST_NPARSE("u:__test_event struct custom a");
- /* Size specified on non-struct should fail */
- TEST_NPARSE("u:__test_event char a 20");
- }
- TEST_F(user, matching) {
- /* Single name matches */
- TEST_MATCH("__test_event u32 a",
- "__test_event u32 a");
- /* Multiple names match */
- TEST_MATCH("__test_event u32 a; u32 b",
- "__test_event u32 a; u32 b");
- /* Multiple names match with dangling ; */
- TEST_MATCH("__test_event u32 a; u32 b",
- "__test_event u32 a; u32 b;");
- /* Single name doesn't match */
- TEST_NMATCH("__test_event u32 a",
- "__test_event u32 b");
- /* Multiple names don't match */
- TEST_NMATCH("__test_event u32 a; u32 b",
- "__test_event u32 b; u32 a");
- /* Types don't match */
- TEST_NMATCH("__test_event u64 a; u64 b",
- "__test_event u32 a; u32 b");
- /* Struct name and size matches */
- TEST_MATCH("__test_event struct my_struct a 20",
- "__test_event struct my_struct a 20");
- /* Struct name don't match */
- TEST_NMATCH("__test_event struct my_struct a 20",
- "__test_event struct my_struct b 20");
- /* Struct size don't match */
- TEST_NMATCH("__test_event struct my_struct a 20",
- "__test_event struct my_struct a 21");
- }
- int main(int argc, char **argv)
- {
- return test_harness_run(argc, argv);
- }
|