diff options
author | Jiri Olsa <jolsa@kernel.org> | 2014-03-05 11:20:31 -0500 |
---|---|---|
committer | Jiri Olsa <jolsa@kernel.org> | 2014-04-28 07:42:52 -0400 |
commit | 4e85edfc3f5c0e016a960c1dcbe0217e86602525 (patch) | |
tree | 100dfc86e6caa87980be07ef95b816fbc06273a6 /tools/perf | |
parent | 3c3cfd99c8988e568a5243f38c600a6a03d1b148 (diff) |
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>
Diffstat (limited to 'tools/perf')
-rw-r--r-- | tools/perf/Makefile.perf | 1 | ||||
-rw-r--r-- | tools/perf/perf.h | 6 | ||||
-rw-r--r-- | tools/perf/tests/builtin-test.c | 4 | ||||
-rw-r--r-- | tools/perf/tests/mmap-thread-lookup.c | 233 | ||||
-rw-r--r-- | tools/perf/tests/tests.h | 1 |
5 files changed, 245 insertions, 0 deletions
diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 0807e4c38505..16d4f4ee8a69 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf | |||
@@ -416,6 +416,7 @@ ifeq ($(ARCH),x86) | |||
416 | LIB_OBJS += $(OUTPUT)tests/dwarf-unwind.o | 416 | LIB_OBJS += $(OUTPUT)tests/dwarf-unwind.o |
417 | endif | 417 | endif |
418 | endif | 418 | endif |
419 | LIB_OBJS += $(OUTPUT)tests/mmap-thread-lookup.o | ||
419 | 420 | ||
420 | BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o | 421 | BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o |
421 | BUILTIN_OBJS += $(OUTPUT)builtin-bench.o | 422 | BUILTIN_OBJS += $(OUTPUT)builtin-bench.o |
diff --git a/tools/perf/perf.h b/tools/perf/perf.h index 5c11ecad02a9..ebdad3376c67 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h | |||
@@ -15,6 +15,9 @@ | |||
15 | #ifndef __NR_futex | 15 | #ifndef __NR_futex |
16 | # define __NR_futex 240 | 16 | # define __NR_futex 240 |
17 | #endif | 17 | #endif |
18 | #ifndef __NR_gettid | ||
19 | # define __NR_gettid 224 | ||
20 | #endif | ||
18 | #endif | 21 | #endif |
19 | 22 | ||
20 | #if defined(__x86_64__) | 23 | #if defined(__x86_64__) |
@@ -29,6 +32,9 @@ | |||
29 | #ifndef __NR_futex | 32 | #ifndef __NR_futex |
30 | # define __NR_futex 202 | 33 | # define __NR_futex 202 |
31 | #endif | 34 | #endif |
35 | #ifndef __NR_gettid | ||
36 | # define __NR_gettid 186 | ||
37 | #endif | ||
32 | #endif | 38 | #endif |
33 | 39 | ||
34 | #ifdef __powerpc__ | 40 | #ifdef __powerpc__ |
diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index ceb9daebc389..bb6079233ef8 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c | |||
@@ -128,6 +128,10 @@ static struct test { | |||
128 | .func = test__hists_filter, | 128 | .func = test__hists_filter, |
129 | }, | 129 | }, |
130 | { | 130 | { |
131 | .desc = "Test mmap thread lookup", | ||
132 | .func = test__mmap_thread_lookup, | ||
133 | }, | ||
134 | { | ||
131 | .func = NULL, | 135 | .func = NULL, |
132 | }, | 136 | }, |
133 | }; | 137 | }; |
diff --git a/tools/perf/tests/mmap-thread-lookup.c b/tools/perf/tests/mmap-thread-lookup.c new file mode 100644 index 000000000000..4a456fef66ca --- /dev/null +++ b/tools/perf/tests/mmap-thread-lookup.c | |||
@@ -0,0 +1,233 @@ | |||
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 | } | ||
diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index fe39163e9ea7..82e8061df46e 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h | |||
@@ -42,6 +42,7 @@ int test__keep_tracking(void); | |||
42 | int test__parse_no_sample_id_all(void); | 42 | int test__parse_no_sample_id_all(void); |
43 | int test__dwarf_unwind(void); | 43 | int test__dwarf_unwind(void); |
44 | int test__hists_filter(void); | 44 | int test__hists_filter(void); |
45 | int test__mmap_thread_lookup(void); | ||
45 | 46 | ||
46 | #if defined(__x86_64__) || defined(__i386__) | 47 | #if defined(__x86_64__) || defined(__i386__) |
47 | #ifdef HAVE_DWARF_UNWIND_SUPPORT | 48 | #ifdef HAVE_DWARF_UNWIND_SUPPORT |