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