summaryrefslogtreecommitdiff
path: root/tools/testing/selftests/user_events/perf_test.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2022-03-23 21:40:25 +0300
committerLinus Torvalds <torvalds@linux-foundation.org>2022-03-23 21:40:25 +0300
commit1bc191051dca28fa6d20fd1dc34a1903e7d4fb62 (patch)
tree788fc5896c343ee3d84af7a2e3c6d78f5d55bca6 /tools/testing/selftests/user_events/perf_test.c
parent20f463fb38686dd3fe7e6903cab56bdbbf756238 (diff)
parent795301d3c28996219d555023ac6863401b6076bc (diff)
downloadlinux-1bc191051dca28fa6d20fd1dc34a1903e7d4fb62.tar.xz
Merge tag 'trace-v5.18' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace
Pull tracing updates from Steven Rostedt: - New user_events interface. User space can register an event with the kernel describing the format of the event. Then it will receive a byte in a page mapping that it can check against. A privileged task can then enable that event like any other event, which will change the mapped byte to true, telling the user space application to start writing the event to the tracing buffer. - Add new "ftrace_boot_snapshot" kernel command line parameter. When set, the tracing buffer will be saved in the snapshot buffer at boot up when the kernel hands things over to user space. This will keep the traces that happened at boot up available even if user space boot up has tracing as well. - Have TRACE_EVENT_ENUM() also update trace event field type descriptions. Thus if a static array defines its size with an enum, the user space trace event parsers can still know how to parse that array. - Add new TRACE_CUSTOM_EVENT() macro. This acts the same as the TRACE_EVENT() macro, but will attach to an existing tracepoint. This will make one tracepoint be able to trace different content and not be stuck at only what the original TRACE_EVENT() macro exports. - Fixes to tracing error logging. - Better saving of cmdlines to PIDs when tracing (use the wakeup events for mapping). * tag 'trace-v5.18' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace: (30 commits) tracing: Have type enum modifications copy the strings user_events: Add trace event call as root for low permission cases tracing/user_events: Use alloc_pages instead of kzalloc() for register pages tracing: Add snapshot at end of kernel boot up tracing: Have TRACE_DEFINE_ENUM affect trace event types as well tracing: Fix strncpy warning in trace_events_synth.c user_events: Prevent dyn_event delete racing with ioctl add/delete tracing: Add TRACE_CUSTOM_EVENT() macro tracing: Move the defines to create TRACE_EVENTS into their own files tracing: Add sample code for custom trace events tracing: Allow custom events to be added to the tracefs directory tracing: Fix last_cmd_set() string management in histogram code user_events: Fix potential uninitialized pointer while parsing field tracing: Fix allocation of last_cmd in last_cmd_set() user_events: Add documentation file user_events: Add sample code for typical usage user_events: Add self-test for validator boundaries user_events: Add self-test for perf_event integration user_events: Add self-test for dynamic_events integration user_events: Add self-test for ftrace integration ...
Diffstat (limited to 'tools/testing/selftests/user_events/perf_test.c')
-rw-r--r--tools/testing/selftests/user_events/perf_test.c168
1 files changed, 168 insertions, 0 deletions
diff --git a/tools/testing/selftests/user_events/perf_test.c b/tools/testing/selftests/user_events/perf_test.c
new file mode 100644
index 000000000000..26851d51d6bb
--- /dev/null
+++ b/tools/testing/selftests/user_events/perf_test.c
@@ -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);
+}