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 20 static int go_away; 21 22 struct thread_data { 23 pthread_t pt; 24 pid_t tid; 25 void *map; 26 int ready[2]; 27 }; 28 29 static struct thread_data threads[THREADS]; 30 31 static 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 51 static 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 76 static 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 96 static 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 113 static 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 129 typedef int (*synth_cb)(struct machine *machine); 130 131 static 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 138 static 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 153 static 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 */ 225 int 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