// SPDX-License-Identifier: GPL-2.0 /* * User Events Perf Events Test Program * * Copyright (c) 2021 Beau Belgrave */ #include #include #include #include #include #include #include #include #include #include #include "../kselftest_harness.h" const char *data_file = "/sys/kernel/debug/tracing/user_events_data"; const char *status_file = "/sys/kernel/debug/tracing/user_events_status"; const char *id_file = "/sys/kernel/debug/tracing/events/user_events/__test_event/id"; const char *fmt_file = "/sys/kernel/debug/tracing/events/user_events/__test_event/format"; struct event { __u32 index; __u32 field1; __u32 field2; }; static long perf_event_open(struct perf_event_attr *pe, pid_t pid, int cpu, int group_fd, unsigned long flags) { return syscall(__NR_perf_event_open, pe, pid, cpu, group_fd, flags); } static int get_id(void) { FILE *fp = fopen(id_file, "r"); int ret, id = 0; if (!fp) return -1; ret = fscanf(fp, "%d", &id); fclose(fp); if (ret != 1) return -1; return id; } static int get_offset(void) { FILE *fp = fopen(fmt_file, "r"); int ret, c, last = 0, offset = 0; if (!fp) return -1; /* Read until empty line */ while (true) { c = getc(fp); if (c == EOF) break; if (last == '\n' && c == '\n') break; last = c; } ret = fscanf(fp, "\tfield:u32 field1;\toffset:%d;", &offset); fclose(fp); if (ret != 1) return -1; return offset; } FIXTURE(user) { int status_fd; int data_fd; }; FIXTURE_SETUP(user) { self->status_fd = open(status_file, O_RDONLY); ASSERT_NE(-1, self->status_fd); self->data_fd = open(data_file, O_RDWR); ASSERT_NE(-1, self->data_fd); } FIXTURE_TEARDOWN(user) { close(self->status_fd); close(self->data_fd); } TEST_F(user, perf_write) { struct perf_event_attr pe = {0}; struct user_reg reg = {0}; int page_size = sysconf(_SC_PAGESIZE); char *status_page; struct event event; struct perf_event_mmap_page *perf_page; int id, fd, offset; __u32 *val; reg.size = sizeof(reg); reg.name_args = (__u64)"__test_event u32 field1; u32 field2"; status_page = mmap(NULL, page_size, PROT_READ, MAP_SHARED, self->status_fd, 0); ASSERT_NE(MAP_FAILED, status_page); /* Register should work */ ASSERT_EQ(0, ioctl(self->data_fd, DIAG_IOCSREG, ®)); ASSERT_EQ(0, reg.write_index); ASSERT_NE(0, reg.status_index); ASSERT_EQ(0, status_page[reg.status_index]); /* Id should be there */ id = get_id(); ASSERT_NE(-1, id); offset = get_offset(); ASSERT_NE(-1, offset); pe.type = PERF_TYPE_TRACEPOINT; pe.size = sizeof(pe); pe.config = id; pe.sample_type = PERF_SAMPLE_RAW; pe.sample_period = 1; pe.wakeup_events = 1; /* Tracepoint attach should work */ fd = perf_event_open(&pe, 0, -1, -1, 0); ASSERT_NE(-1, fd); perf_page = mmap(NULL, page_size * 2, PROT_READ, MAP_SHARED, fd, 0); ASSERT_NE(MAP_FAILED, perf_page); /* Status should be updated */ ASSERT_EQ(EVENT_STATUS_PERF, status_page[reg.status_index]); event.index = reg.write_index; event.field1 = 0xc001; event.field2 = 0xc01a; /* Ensure write shows up at correct offset */ ASSERT_NE(-1, write(self->data_fd, &event, sizeof(event))); val = (void *)(((char *)perf_page) + perf_page->data_offset); ASSERT_EQ(PERF_RECORD_SAMPLE, *val); /* Skip over header and size, move to offset */ val += 3; val = (void *)((char *)val) + offset; /* Ensure correct */ ASSERT_EQ(event.field1, *val++); ASSERT_EQ(event.field2, *val++); } int main(int argc, char **argv) { return test_harness_run(argc, argv); }