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