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 "map.h"
  15#include "symbol.h"
  16#include "thread.h"
  17#include "util.h"
  18
  19#define THREADS 4
  20
  21static int go_away;
  22
  23struct thread_data {
  24        pthread_t       pt;
  25        pid_t           tid;
  26        void            *map;
  27        int             ready[2];
  28};
  29
  30static struct thread_data threads[THREADS];
  31
  32static int thread_init(struct thread_data *td)
  33{
  34        void *map;
  35
  36        map = mmap(NULL, page_size,
  37                   PROT_READ|PROT_WRITE|PROT_EXEC,
  38                   MAP_SHARED|MAP_ANONYMOUS, -1, 0);
  39
  40        if (map == MAP_FAILED) {
  41                perror("mmap failed");
  42                return -1;
  43        }
  44
  45        td->map = map;
  46        td->tid = syscall(SYS_gettid);
  47
  48        pr_debug("tid = %d, map = %p\n", td->tid, map);
  49        return 0;
  50}
  51
  52static void *thread_fn(void *arg)
  53{
  54        struct thread_data *td = arg;
  55        ssize_t ret;
  56        int go = 0;
  57
  58        if (thread_init(td))
  59                return NULL;
  60
  61        /* Signal thread_create thread is initialized. */
  62        ret = write(td->ready[1], &go, sizeof(int));
  63        if (ret != sizeof(int)) {
  64                pr_err("failed to notify\n");
  65                return NULL;
  66        }
  67
  68        while (!go_away) {
  69                /* Waiting for main thread to kill us. */
  70                usleep(100);
  71        }
  72
  73        munmap(td->map, page_size);
  74        return NULL;
  75}
  76
  77static int thread_create(int i)
  78{
  79        struct thread_data *td = &threads[i];
  80        int err, go;
  81
  82        if (pipe(td->ready))
  83                return -1;
  84
  85        err = pthread_create(&td->pt, NULL, thread_fn, td);
  86        if (!err) {
  87                /* Wait for thread initialization. */
  88                ssize_t ret = read(td->ready[0], &go, sizeof(int));
  89                err = ret != sizeof(int);
  90        }
  91
  92        close(td->ready[0]);
  93        close(td->ready[1]);
  94        return err;
  95}
  96
  97static int threads_create(void)
  98{
  99        struct thread_data *td0 = &threads[0];
 100        int i, err = 0;
 101
 102        go_away = 0;
 103
 104        /* 0 is main thread */
 105        if (thread_init(td0))
 106                return -1;
 107
 108        for (i = 1; !err && i < THREADS; i++)
 109                err = thread_create(i);
 110
 111        return err;
 112}
 113
 114static int threads_destroy(void)
 115{
 116        struct thread_data *td0 = &threads[0];
 117        int i, err = 0;
 118
 119        /* cleanup the main thread */
 120        munmap(td0->map, page_size);
 121
 122        go_away = 1;
 123
 124        for (i = 1; !err && i < THREADS; i++)
 125                err = pthread_join(threads[i].pt, NULL);
 126
 127        return err;
 128}
 129
 130typedef int (*synth_cb)(struct machine *machine);
 131
 132static int synth_all(struct machine *machine)
 133{
 134        return perf_event__synthesize_threads(NULL,
 135                                              perf_event__process,
 136                                              machine, 0, 1);
 137}
 138
 139static int synth_process(struct machine *machine)
 140{
 141        struct thread_map *map;
 142        int err;
 143
 144        map = thread_map__new_by_pid(getpid());
 145
 146        err = perf_event__synthesize_thread_map(NULL, map,
 147                                                perf_event__process,
 148                                                machine, 0);
 149
 150        thread_map__put(map);
 151        return err;
 152}
 153
 154static int mmap_events(synth_cb synth)
 155{
 156        struct machine *machine;
 157        int err, i;
 158
 159        /*
 160         * The threads_create will not return before all threads
 161         * are spawned and all created memory map.
 162         *
 163         * They will loop until threads_destroy is called, so we
 164         * can safely run synthesizing function.
 165         */
 166        TEST_ASSERT_VAL("failed to create threads", !threads_create());
 167
 168        machine = machine__new_host();
 169
 170        dump_trace = verbose > 1 ? 1 : 0;
 171
 172        err = synth(machine);
 173
 174        dump_trace = 0;
 175
 176        TEST_ASSERT_VAL("failed to destroy threads", !threads_destroy());
 177        TEST_ASSERT_VAL("failed to synthesize maps", !err);
 178
 179        /*
 180         * All data is synthesized, try to find map for each
 181         * thread object.
 182         */
 183        for (i = 0; i < THREADS; i++) {
 184                struct thread_data *td = &threads[i];
 185                struct addr_location al;
 186                struct thread *thread;
 187
 188                thread = machine__findnew_thread(machine, getpid(), td->tid);
 189
 190                pr_debug("looking for map %p\n", td->map);
 191
 192                thread__find_map(thread, PERF_RECORD_MISC_USER,
 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_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