Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

perf tests: Add thread maps lookup automated tests

Adding automated test for memory maps lookup within multiple machines
threads.

The test creates 4 threads and separated memory maps. It checks that we
could use thread__find_addr_map function with thread object based on TID
to find memory maps.

Acked-by: Namhyung Kim <namhyung@kernel.org>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
Cc: Corey Ashford <cjashfor@linux.vnet.ibm.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Don Zickus <dzickus@redhat.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Stephane Eranian <eranian@google.com>
Link: http://lkml.kernel.org/r/1397490723-1992-2-git-send-email-jolsa@redhat.com
Signed-off-by: Jiri Olsa <jolsa@kernel.org>

Jiri Olsa 4e85edfc 3c3cfd99

+245
+1
tools/perf/Makefile.perf
··· 416 416 LIB_OBJS += $(OUTPUT)tests/dwarf-unwind.o 417 417 endif 418 418 endif 419 + LIB_OBJS += $(OUTPUT)tests/mmap-thread-lookup.o 419 420 420 421 BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o 421 422 BUILTIN_OBJS += $(OUTPUT)builtin-bench.o
+6
tools/perf/perf.h
··· 15 15 #ifndef __NR_futex 16 16 # define __NR_futex 240 17 17 #endif 18 + #ifndef __NR_gettid 19 + # define __NR_gettid 224 20 + #endif 18 21 #endif 19 22 20 23 #if defined(__x86_64__) ··· 31 28 #endif 32 29 #ifndef __NR_futex 33 30 # define __NR_futex 202 31 + #endif 32 + #ifndef __NR_gettid 33 + # define __NR_gettid 186 34 34 #endif 35 35 #endif 36 36
+4
tools/perf/tests/builtin-test.c
··· 128 128 .func = test__hists_filter, 129 129 }, 130 130 { 131 + .desc = "Test mmap thread lookup", 132 + .func = test__mmap_thread_lookup, 133 + }, 134 + { 131 135 .func = NULL, 132 136 }, 133 137 };
+233
tools/perf/tests/mmap-thread-lookup.c
··· 1 + #include <unistd.h> 2 + #include <sys/syscall.h> 3 + #include <sys/types.h> 4 + #include <sys/mman.h> 5 + #include <pthread.h> 6 + #include <stdlib.h> 7 + #include <stdio.h> 8 + #include "debug.h" 9 + #include "tests.h" 10 + #include "machine.h" 11 + #include "thread_map.h" 12 + #include "symbol.h" 13 + #include "thread.h" 14 + 15 + #define THREADS 4 16 + 17 + static int go_away; 18 + 19 + struct thread_data { 20 + pthread_t pt; 21 + pid_t tid; 22 + void *map; 23 + int ready[2]; 24 + }; 25 + 26 + static struct thread_data threads[THREADS]; 27 + 28 + static int thread_init(struct thread_data *td) 29 + { 30 + void *map; 31 + 32 + map = mmap(NULL, page_size, 33 + PROT_READ|PROT_WRITE|PROT_EXEC, 34 + MAP_SHARED|MAP_ANONYMOUS, -1, 0); 35 + 36 + if (map == MAP_FAILED) { 37 + perror("mmap failed"); 38 + return -1; 39 + } 40 + 41 + td->map = map; 42 + td->tid = syscall(SYS_gettid); 43 + 44 + pr_debug("tid = %d, map = %p\n", td->tid, map); 45 + return 0; 46 + } 47 + 48 + static void *thread_fn(void *arg) 49 + { 50 + struct thread_data *td = arg; 51 + ssize_t ret; 52 + int go; 53 + 54 + if (thread_init(td)) 55 + return NULL; 56 + 57 + /* Signal thread_create thread is initialized. */ 58 + ret = write(td->ready[1], &go, sizeof(int)); 59 + if (ret != sizeof(int)) { 60 + pr_err("failed to notify\n"); 61 + return NULL; 62 + } 63 + 64 + while (!go_away) { 65 + /* Waiting for main thread to kill us. */ 66 + usleep(100); 67 + } 68 + 69 + munmap(td->map, page_size); 70 + return NULL; 71 + } 72 + 73 + static int thread_create(int i) 74 + { 75 + struct thread_data *td = &threads[i]; 76 + int err, go; 77 + 78 + if (pipe(td->ready)) 79 + return -1; 80 + 81 + err = pthread_create(&td->pt, NULL, thread_fn, td); 82 + if (!err) { 83 + /* Wait for thread initialization. */ 84 + ssize_t ret = read(td->ready[0], &go, sizeof(int)); 85 + err = ret != sizeof(int); 86 + } 87 + 88 + close(td->ready[0]); 89 + close(td->ready[1]); 90 + return err; 91 + } 92 + 93 + static int threads_create(void) 94 + { 95 + struct thread_data *td0 = &threads[0]; 96 + int i, err = 0; 97 + 98 + go_away = 0; 99 + 100 + /* 0 is main thread */ 101 + if (thread_init(td0)) 102 + return -1; 103 + 104 + for (i = 1; !err && i < THREADS; i++) 105 + err = thread_create(i); 106 + 107 + return err; 108 + } 109 + 110 + static int threads_destroy(void) 111 + { 112 + struct thread_data *td0 = &threads[0]; 113 + int i, err = 0; 114 + 115 + /* cleanup the main thread */ 116 + munmap(td0->map, page_size); 117 + 118 + go_away = 1; 119 + 120 + for (i = 1; !err && i < THREADS; i++) 121 + err = pthread_join(threads[i].pt, NULL); 122 + 123 + return err; 124 + } 125 + 126 + typedef int (*synth_cb)(struct machine *machine); 127 + 128 + static int synth_all(struct machine *machine) 129 + { 130 + return perf_event__synthesize_threads(NULL, 131 + perf_event__process, 132 + machine, 0); 133 + } 134 + 135 + static int synth_process(struct machine *machine) 136 + { 137 + struct thread_map *map; 138 + int err; 139 + 140 + map = thread_map__new_by_pid(getpid()); 141 + 142 + err = perf_event__synthesize_thread_map(NULL, map, 143 + perf_event__process, 144 + machine, 0); 145 + 146 + thread_map__delete(map); 147 + return err; 148 + } 149 + 150 + static int mmap_events(synth_cb synth) 151 + { 152 + struct machines machines; 153 + struct machine *machine; 154 + int err, i; 155 + 156 + /* 157 + * The threads_create will not return before all threads 158 + * are spawned and all created memory map. 159 + * 160 + * They will loop until threads_destroy is called, so we 161 + * can safely run synthesizing function. 162 + */ 163 + TEST_ASSERT_VAL("failed to create threads", !threads_create()); 164 + 165 + machines__init(&machines); 166 + machine = &machines.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, machine, 191 + PERF_RECORD_MISC_USER, MAP__FUNCTION, 192 + (unsigned long) (td->map + 1), &al); 193 + 194 + if (!al.map) { 195 + pr_debug("failed, couldn't find map\n"); 196 + err = -1; 197 + break; 198 + } 199 + 200 + pr_debug("map %p, addr %" PRIx64 "\n", al.map, al.map->start); 201 + } 202 + 203 + machine__delete_threads(machine); 204 + machines__exit(&machines); 205 + return err; 206 + } 207 + 208 + /* 209 + * This test creates 'THREADS' number of threads (including 210 + * main thread) and each thread creates memory map. 211 + * 212 + * When threads are created, we synthesize them with both 213 + * (separate tests): 214 + * perf_event__synthesize_thread_map (process based) 215 + * perf_event__synthesize_threads (global) 216 + * 217 + * We test we can find all memory maps via: 218 + * thread__find_addr_map 219 + * 220 + * by using all thread objects. 221 + */ 222 + int test__mmap_thread_lookup(void) 223 + { 224 + /* perf_event__synthesize_threads synthesize */ 225 + TEST_ASSERT_VAL("failed with sythesizing all", 226 + !mmap_events(synth_all)); 227 + 228 + /* perf_event__synthesize_thread_map synthesize */ 229 + TEST_ASSERT_VAL("failed with sythesizing process", 230 + !mmap_events(synth_process)); 231 + 232 + return 0; 233 + }
+1
tools/perf/tests/tests.h
··· 42 42 int test__parse_no_sample_id_all(void); 43 43 int test__dwarf_unwind(void); 44 44 int test__hists_filter(void); 45 + int test__mmap_thread_lookup(void); 45 46 46 47 #if defined(__x86_64__) || defined(__i386__) 47 48 #ifdef HAVE_DWARF_UNWIND_SUPPORT