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