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