linux/tools/perf/tests/mmap-thread-lookup.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2#include <inttypes.h>
   3#include <unistd.h>
   4#include <sys/syscall.h>
   5#include <sys/types.h>
   6#include <sys/mman.h>
   7#include <pthread.h>
   8#include <stdlib.h>
   9#include <stdio.h>
  10#include "debug.h"
  11#include "event.h"
  12#include "tests.h"
  13#include "machine.h"
  14#include "thread_map.h"
  15#include "map.h"
  16#include "symbol.h"
  17#include "util/synthetic-events.h"
  18#include "thread.h"
  19#include <internal/lib.h> // page_size
  20
  21#define THREADS 4
  22
  23static int go_away;
  24
  25struct thread_data {
  26        pthread_t       pt;
  27        pid_t           tid;
  28        void            *map;
  29        int             ready[2];
  30};
  31
  32static struct thread_data threads[THREADS];
  33
  34static int thread_init(struct thread_data *td)
  35{
  36        void *map;
  37
  38        map = mmap(NULL, page_size,
  39                   PROT_READ|PROT_WRITE|PROT_EXEC,
  40                   MAP_SHARED|MAP_ANONYMOUS, -1, 0);
  41
  42        if (map == MAP_FAILED) {
  43                perror("mmap failed");
  44                return -1;
  45        }
  46
  47        td->map = map;
  48        td->tid = syscall(SYS_gettid);
  49
  50        pr_debug("tid = %d, map = %p\n", td->tid, map);
  51        return 0;
  52}
  53
  54static void *thread_fn(void *arg)
  55{
  56        struct thread_data *td = arg;
  57        ssize_t ret;
  58        int go = 0;
  59
  60        if (thread_init(td))
  61                return NULL;
  62
  63        /* Signal thread_create thread is initialized. */
  64        ret = write(td->ready[1], &go, sizeof(int));
  65        if (ret != sizeof(int)) {
  66                pr_err("failed to notify\n");
  67                return NULL;
  68        }
  69
  70        while (!go_away) {
  71                /* Waiting for main thread to kill us. */
  72                usleep(100);
  73        }
  74
  75        munmap(td->map, page_size);
  76        return NULL;
  77}
  78
  79static int thread_create(int i)
  80{
  81        struct thread_data *td = &threads[i];
  82        int err, go;
  83
  84        if (pipe(td->ready))
  85                return -1;
  86
  87        err = pthread_create(&td->pt, NULL, thread_fn, td);
  88        if (!err) {
  89                /* Wait for thread initialization. */
  90                ssize_t ret = read(td->ready[0], &go, sizeof(int));
  91                err = ret != sizeof(int);
  92        }
  93
  94        close(td->ready[0]);
  95        close(td->ready[1]);
  96        return err;
  97}
  98
  99static int threads_create(void)
 100{
 101        struct thread_data *td0 = &threads[0];
 102        int i, err = 0;
 103
 104        go_away = 0;
 105
 106        /* 0 is main thread */
 107        if (thread_init(td0))
 108                return -1;
 109
 110        for (i = 1; !err && i < THREADS; i++)
 111                err = thread_create(i);
 112
 113        return err;
 114}
 115
 116static int threads_destroy(void)
 117{
 118        struct thread_data *td0 = &threads[0];
 119        int i, err = 0;
 120
 121        /* cleanup the main thread */
 122        munmap(td0->map, page_size);
 123
 124        go_away = 1;
 125
 126        for (i = 1; !err && i < THREADS; i++)
 127                err = pthread_join(threads[i].pt, NULL);
 128
 129        return err;
 130}
 131
 132typedef int (*synth_cb)(struct machine *machine);
 133
 134static int synth_all(struct machine *machine)
 135{
 136        return perf_event__synthesize_threads(NULL,
 137                                              perf_event__process,
 138                                              machine, 0, 1);
 139}
 140
 141static int synth_process(struct machine *machine)
 142{
 143        struct perf_thread_map *map;
 144        int err;
 145
 146        map = thread_map__new_by_pid(getpid());
 147
 148        err = perf_event__synthesize_thread_map(NULL, map,
 149                                                perf_event__process,
 150                                                machine, 0);
 151
 152        perf_thread_map__put(map);
 153        return err;
 154}
 155
 156static int mmap_events(synth_cb synth)
 157{
 158        struct machine *machine;
 159        int err, i;
 160
 161        /*
 162         * The threads_create will not return before all threads
 163         * are spawned and all created memory map.
 164         *
 165         * They will loop until threads_destroy is called, so we
 166         * can safely run synthesizing function.
 167         */
 168        TEST_ASSERT_VAL("failed to create threads", !threads_create());
 169
 170        machine = machine__new_host();
 171
 172        dump_trace = verbose > 1 ? 1 : 0;
 173
 174        err = synth(machine);
 175
 176        dump_trace = 0;
 177
 178        TEST_ASSERT_VAL("failed to destroy threads", !threads_destroy());
 179        TEST_ASSERT_VAL("failed to synthesize maps", !err);
 180
 181        /*
 182         * All data is synthesized, try to find map for each
 183         * thread object.
 184         */
 185        for (i = 0; i < THREADS; i++) {
 186                struct thread_data *td = &threads[i];
 187                struct addr_location al;
 188                struct thread *thread;
 189
 190                thread = machine__findnew_thread(machine, getpid(), td->tid);
 191
 192                pr_debug("looking for map %p\n", td->map);
 193
 194                thread__find_map(thread, PERF_RECORD_MISC_USER,
 195                                 (unsigned long) (td->map + 1), &al);
 196
 197                thread__put(thread);
 198
 199                if (!al.map) {
 200                        pr_debug("failed, couldn't find map\n");
 201                        err = -1;
 202                        break;
 203                }
 204
 205                pr_debug("map %p, addr %" PRIx64 "\n", al.map, al.map->start);
 206        }
 207
 208        machine__delete_threads(machine);
 209        machine__delete(machine);
 210        return err;
 211}
 212
 213/*
 214 * This test creates 'THREADS' number of threads (including
 215 * main thread) and each thread creates memory map.
 216 *
 217 * When threads are created, we synthesize them with both
 218 * (separate tests):
 219 *   perf_event__synthesize_thread_map (process based)
 220 *   perf_event__synthesize_threads    (global)
 221 *
 222 * We test we can find all memory maps via:
 223 *   thread__find_map
 224 *
 225 * by using all thread objects.
 226 */
 227int test__mmap_thread_lookup(struct test *test __maybe_unused, int subtest __maybe_unused)
 228{
 229        /* perf_event__synthesize_threads synthesize */
 230        TEST_ASSERT_VAL("failed with sythesizing all",
 231                        !mmap_events(synth_all));
 232
 233        /* perf_event__synthesize_thread_map synthesize */
 234        TEST_ASSERT_VAL("failed with sythesizing process",
 235                        !mmap_events(synth_process));
 236
 237        return 0;
 238}
 239