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 19 static int go_away; 20 21 struct thread_data { 22 pthread_t pt; 23 pid_t tid; 24 void *map; 25 int ready[2]; 26 }; 27 28 static struct thread_data threads[THREADS]; 29 30 static 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 50 static 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 75 static 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 95 static 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 112 static 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 128 typedef int (*synth_cb)(struct machine *machine); 129 130 static int synth_all(struct machine *machine) 131 { 132 return perf_event__synthesize_threads(NULL, 133 perf_event__process, 134 machine, 0, 500); 135 } 136 137 static 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 152 static 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 */ 224 int 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