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 21 static int go_away; 22 23 struct thread_data { 24 pthread_t pt; 25 pid_t tid; 26 void *map; 27 int ready[2]; 28 }; 29 30 static struct thread_data threads[THREADS]; 31 32 static 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 52 static void *thread_fn(void *arg) 53 { 54 struct thread_data *td = arg; 55 ssize_t ret; 56 int go; 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 77 static 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 97 static 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 114 static 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 130 typedef int (*synth_cb)(struct machine *machine); 131 132 static int synth_all(struct machine *machine) 133 { 134 return perf_event__synthesize_threads(NULL, 135 perf_event__process, 136 machine, 0, 1); 137 } 138 139 static 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 154 static 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 */ 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