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