diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 10:05:51 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 10:05:51 +0000 |
commit | 5d1646d90e1f2cceb9f0828f4b28318cd0ec7744 (patch) | |
tree | a94efe259b9009378be6d90eb30d2b019d95c194 /tools/perf/tests/mmap-thread-lookup.c | |
parent | Initial commit. (diff) | |
download | linux-upstream.tar.xz linux-upstream.zip |
Adding upstream version 5.10.209.upstream/5.10.209upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'tools/perf/tests/mmap-thread-lookup.c')
-rw-r--r-- | tools/perf/tests/mmap-thread-lookup.c | 238 |
1 files changed, 238 insertions, 0 deletions
diff --git a/tools/perf/tests/mmap-thread-lookup.c b/tools/perf/tests/mmap-thread-lookup.c new file mode 100644 index 000000000..8d9d4cbff --- /dev/null +++ b/tools/perf/tests/mmap-thread-lookup.c @@ -0,0 +1,238 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <inttypes.h> +#include <unistd.h> +#include <sys/syscall.h> +#include <sys/types.h> +#include <sys/mman.h> +#include <pthread.h> +#include <stdlib.h> +#include <stdio.h> +#include "debug.h" +#include "event.h" +#include "tests.h" +#include "machine.h" +#include "thread_map.h" +#include "map.h" +#include "symbol.h" +#include "util/synthetic-events.h" +#include "thread.h" +#include <internal/lib.h> // page_size + +#define THREADS 4 + +static int go_away; + +struct thread_data { + pthread_t pt; + pid_t tid; + void *map; + int ready[2]; +}; + +static struct thread_data threads[THREADS]; + +static int thread_init(struct thread_data *td) +{ + void *map; + + map = mmap(NULL, page_size, + PROT_READ|PROT_WRITE|PROT_EXEC, + MAP_SHARED|MAP_ANONYMOUS, -1, 0); + + if (map == MAP_FAILED) { + perror("mmap failed"); + return -1; + } + + td->map = map; + td->tid = syscall(SYS_gettid); + + pr_debug("tid = %d, map = %p\n", td->tid, map); + return 0; +} + +static void *thread_fn(void *arg) +{ + struct thread_data *td = arg; + ssize_t ret; + int go = 0; + + if (thread_init(td)) + return NULL; + + /* Signal thread_create thread is initialized. */ + ret = write(td->ready[1], &go, sizeof(int)); + if (ret != sizeof(int)) { + pr_err("failed to notify\n"); + return NULL; + } + + while (!go_away) { + /* Waiting for main thread to kill us. */ + usleep(100); + } + + munmap(td->map, page_size); + return NULL; +} + +static int thread_create(int i) +{ + struct thread_data *td = &threads[i]; + int err, go; + + if (pipe(td->ready)) + return -1; + + err = pthread_create(&td->pt, NULL, thread_fn, td); + if (!err) { + /* Wait for thread initialization. */ + ssize_t ret = read(td->ready[0], &go, sizeof(int)); + err = ret != sizeof(int); + } + + close(td->ready[0]); + close(td->ready[1]); + return err; +} + +static int threads_create(void) +{ + struct thread_data *td0 = &threads[0]; + int i, err = 0; + + go_away = 0; + + /* 0 is main thread */ + if (thread_init(td0)) + return -1; + + for (i = 1; !err && i < THREADS; i++) + err = thread_create(i); + + return err; +} + +static int threads_destroy(void) +{ + struct thread_data *td0 = &threads[0]; + int i, err = 0; + + /* cleanup the main thread */ + munmap(td0->map, page_size); + + go_away = 1; + + for (i = 1; !err && i < THREADS; i++) + err = pthread_join(threads[i].pt, NULL); + + return err; +} + +typedef int (*synth_cb)(struct machine *machine); + +static int synth_all(struct machine *machine) +{ + return perf_event__synthesize_threads(NULL, + perf_event__process, + machine, 0, 1); +} + +static int synth_process(struct machine *machine) +{ + struct perf_thread_map *map; + int err; + + map = thread_map__new_by_pid(getpid()); + + err = perf_event__synthesize_thread_map(NULL, map, + perf_event__process, + machine, 0); + + perf_thread_map__put(map); + return err; +} + +static int mmap_events(synth_cb synth) +{ + struct machine *machine; + int err, i; + + /* + * The threads_create will not return before all threads + * are spawned and all created memory map. + * + * They will loop until threads_destroy is called, so we + * can safely run synthesizing function. + */ + TEST_ASSERT_VAL("failed to create threads", !threads_create()); + + machine = machine__new_host(); + + dump_trace = verbose > 1 ? 1 : 0; + + err = synth(machine); + + dump_trace = 0; + + TEST_ASSERT_VAL("failed to destroy threads", !threads_destroy()); + TEST_ASSERT_VAL("failed to synthesize maps", !err); + + /* + * All data is synthesized, try to find map for each + * thread object. + */ + for (i = 0; i < THREADS; i++) { + struct thread_data *td = &threads[i]; + struct addr_location al; + struct thread *thread; + + thread = machine__findnew_thread(machine, getpid(), td->tid); + + pr_debug("looking for map %p\n", td->map); + + thread__find_map(thread, PERF_RECORD_MISC_USER, + (unsigned long) (td->map + 1), &al); + + thread__put(thread); + + if (!al.map) { + pr_debug("failed, couldn't find map\n"); + err = -1; + break; + } + + pr_debug("map %p, addr %" PRIx64 "\n", al.map, al.map->start); + } + + machine__delete_threads(machine); + machine__delete(machine); + return err; +} + +/* + * This test creates 'THREADS' number of threads (including + * main thread) and each thread creates memory map. + * + * When threads are created, we synthesize them with both + * (separate tests): + * perf_event__synthesize_thread_map (process based) + * perf_event__synthesize_threads (global) + * + * We test we can find all memory maps via: + * thread__find_map + * + * by using all thread objects. + */ +int test__mmap_thread_lookup(struct test *test __maybe_unused, int subtest __maybe_unused) +{ + /* perf_event__synthesize_threads synthesize */ + TEST_ASSERT_VAL("failed with sythesizing all", + !mmap_events(synth_all)); + + /* perf_event__synthesize_thread_map synthesize */ + TEST_ASSERT_VAL("failed with sythesizing process", + !mmap_events(synth_process)); + + return 0; +} |