user_events: Add self-test for perf_event integration
authorBeau Belgrave <beaub@linux.microsoft.com>
Tue, 18 Jan 2022 20:43:23 +0000 (12:43 -0800)
committerSteven Rostedt (Google) <rostedt@goodmis.org>
Fri, 11 Feb 2022 03:37:59 +0000 (22:37 -0500)
Tests perf can be attached to and written out correctly. Ensures attach
updates status bits in user programs.

Link: https://lkml.kernel.org/r/20220118204326.2169-10-beaub@linux.microsoft.com
Acked-by: Masami Hiramatsu <mhiramat@kernel.org>
Signed-off-by: Beau Belgrave <beaub@linux.microsoft.com>
Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
tools/testing/selftests/user_events/Makefile
tools/testing/selftests/user_events/perf_test.c [new file with mode: 0644]

index e824b9c2cae7f3081ab1daf9023d0f5957940b0f..c765d8635d9afedc508d2372848be4d9caf655e5 100644 (file)
@@ -2,7 +2,7 @@
 CFLAGS += -Wl,-no-as-needed -Wall -I../../../../usr/include
 LDLIBS += -lrt -lpthread -lm
 
-TEST_GEN_PROGS = ftrace_test dyn_test
+TEST_GEN_PROGS = ftrace_test dyn_test perf_test
 
 TEST_FILES := settings
 
diff --git a/tools/testing/selftests/user_events/perf_test.c b/tools/testing/selftests/user_events/perf_test.c
new file mode 100644 (file)
index 0000000..26851d5
--- /dev/null
@@ -0,0 +1,168 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * User Events Perf Events Test Program
+ *
+ * Copyright (c) 2021 Beau Belgrave <beaub@linux.microsoft.com>
+ */
+
+#include <errno.h>
+#include <linux/user_events.h>
+#include <linux/perf_event.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <asm/unistd.h>
+
+#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, &reg));
+       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);
+}