File Index
Files and directories
Sign in
FILE

mmap-thread-lookup.c

File preview with syntax highlighting.

linux-6.19.14/tools/perf/tests/mmap-thread-lookup.c 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 "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
 24static int go_away;
 25
 26struct thread_data {
 27	pthread_t	pt;
 28	pid_t		tid;
 29	void		*map;
 30	int		ready[2];
 31};
 32
 33static struct thread_data threads[THREADS];
 34
 35static 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
 55static 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
 80static 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
100static 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
117static 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
133typedef int (*synth_cb)(struct machine *machine);
134
135static 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
142static 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
157static 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 */
233static 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
246DEFINE_SUITE("Lookup mmap thread", mmap_thread_lookup);
~/ Read-only file preview.