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