diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2022-03-23 21:40:25 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2022-03-23 21:40:25 +0300 |
commit | 1bc191051dca28fa6d20fd1dc34a1903e7d4fb62 (patch) | |
tree | 788fc5896c343ee3d84af7a2e3c6d78f5d55bca6 /tools/testing/selftests/user_events/perf_test.c | |
parent | 20f463fb38686dd3fe7e6903cab56bdbbf756238 (diff) | |
parent | 795301d3c28996219d555023ac6863401b6076bc (diff) | |
download | linux-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.c | 168 |
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, ®)); + 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); +} |