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