1// SPDX-License-Identifier: GPL-2.0
2#define _GNU_SOURCE // needed for sched.h to get sched_[gs]etaffinity and CPU_(ZERO,SET)
3#include <sched.h>
4#include <stdio.h>
5#include <stdarg.h>
6#include <unistd.h>
7#include <stdlib.h>
8#include <linux/perf_event.h>
9#include <linux/limits.h>
10#include <sys/types.h>
11#include <sys/wait.h>
12#include <sys/prctl.h>
13#include <perf/cpumap.h>
14#include <perf/threadmap.h>
15#include <perf/evlist.h>
16#include <perf/evsel.h>
17#include <perf/mmap.h>
18#include <perf/event.h>
19#include <internal/tests.h>
20#include <api/fs/fs.h>
21
22static int libperf_print(enum libperf_print_level level,
23			 const char *fmt, va_list ap)
24{
25	return vfprintf(stderr, fmt, ap);
26}
27
28static int test_stat_cpu(void)
29{
30	struct perf_cpu_map *cpus;
31	struct perf_evlist *evlist;
32	struct perf_evsel *evsel;
33	struct perf_event_attr attr1 = {
34		.type	= PERF_TYPE_SOFTWARE,
35		.config	= PERF_COUNT_SW_CPU_CLOCK,
36	};
37	struct perf_event_attr attr2 = {
38		.type	= PERF_TYPE_SOFTWARE,
39		.config	= PERF_COUNT_SW_TASK_CLOCK,
40	};
41	int err, idx;
42
43	cpus = perf_cpu_map__new(NULL);
44	__T("failed to create cpus", cpus);
45
46	evlist = perf_evlist__new();
47	__T("failed to create evlist", evlist);
48
49	evsel = perf_evsel__new(&attr1);
50	__T("failed to create evsel1", evsel);
51
52	perf_evlist__add(evlist, evsel);
53
54	evsel = perf_evsel__new(&attr2);
55	__T("failed to create evsel2", evsel);
56
57	perf_evlist__add(evlist, evsel);
58
59	perf_evlist__set_maps(evlist, cpus, NULL);
60
61	err = perf_evlist__open(evlist);
62	__T("failed to open evsel", err == 0);
63
64	perf_evlist__for_each_evsel(evlist, evsel) {
65		cpus = perf_evsel__cpus(evsel);
66
67		for (idx = 0; idx < perf_cpu_map__nr(cpus); idx++) {
68			struct perf_counts_values counts = { .val = 0 };
69
70			perf_evsel__read(evsel, idx, 0, &counts);
71			__T("failed to read value for evsel", counts.val != 0);
72		}
73	}
74
75	perf_evlist__close(evlist);
76	perf_evlist__delete(evlist);
77
78	perf_cpu_map__put(cpus);
79	return 0;
80}
81
82static int test_stat_thread(void)
83{
84	struct perf_counts_values counts = { .val = 0 };
85	struct perf_thread_map *threads;
86	struct perf_evlist *evlist;
87	struct perf_evsel *evsel;
88	struct perf_event_attr attr1 = {
89		.type	= PERF_TYPE_SOFTWARE,
90		.config	= PERF_COUNT_SW_CPU_CLOCK,
91	};
92	struct perf_event_attr attr2 = {
93		.type	= PERF_TYPE_SOFTWARE,
94		.config	= PERF_COUNT_SW_TASK_CLOCK,
95	};
96	int err;
97
98	threads = perf_thread_map__new_dummy();
99	__T("failed to create threads", threads);
100
101	perf_thread_map__set_pid(threads, 0, 0);
102
103	evlist = perf_evlist__new();
104	__T("failed to create evlist", evlist);
105
106	evsel = perf_evsel__new(&attr1);
107	__T("failed to create evsel1", evsel);
108
109	perf_evlist__add(evlist, evsel);
110
111	evsel = perf_evsel__new(&attr2);
112	__T("failed to create evsel2", evsel);
113
114	perf_evlist__add(evlist, evsel);
115
116	perf_evlist__set_maps(evlist, NULL, threads);
117
118	err = perf_evlist__open(evlist);
119	__T("failed to open evsel", err == 0);
120
121	perf_evlist__for_each_evsel(evlist, evsel) {
122		perf_evsel__read(evsel, 0, 0, &counts);
123		__T("failed to read value for evsel", counts.val != 0);
124	}
125
126	perf_evlist__close(evlist);
127	perf_evlist__delete(evlist);
128
129	perf_thread_map__put(threads);
130	return 0;
131}
132
133static int test_stat_thread_enable(void)
134{
135	struct perf_counts_values counts = { .val = 0 };
136	struct perf_thread_map *threads;
137	struct perf_evlist *evlist;
138	struct perf_evsel *evsel;
139	struct perf_event_attr attr1 = {
140		.type	  = PERF_TYPE_SOFTWARE,
141		.config	  = PERF_COUNT_SW_CPU_CLOCK,
142		.disabled = 1,
143	};
144	struct perf_event_attr attr2 = {
145		.type	  = PERF_TYPE_SOFTWARE,
146		.config	  = PERF_COUNT_SW_TASK_CLOCK,
147		.disabled = 1,
148	};
149	int err;
150
151	threads = perf_thread_map__new_dummy();
152	__T("failed to create threads", threads);
153
154	perf_thread_map__set_pid(threads, 0, 0);
155
156	evlist = perf_evlist__new();
157	__T("failed to create evlist", evlist);
158
159	evsel = perf_evsel__new(&attr1);
160	__T("failed to create evsel1", evsel);
161
162	perf_evlist__add(evlist, evsel);
163
164	evsel = perf_evsel__new(&attr2);
165	__T("failed to create evsel2", evsel);
166
167	perf_evlist__add(evlist, evsel);
168
169	perf_evlist__set_maps(evlist, NULL, threads);
170
171	err = perf_evlist__open(evlist);
172	__T("failed to open evsel", err == 0);
173
174	perf_evlist__for_each_evsel(evlist, evsel) {
175		perf_evsel__read(evsel, 0, 0, &counts);
176		__T("failed to read value for evsel", counts.val == 0);
177	}
178
179	perf_evlist__enable(evlist);
180
181	perf_evlist__for_each_evsel(evlist, evsel) {
182		perf_evsel__read(evsel, 0, 0, &counts);
183		__T("failed to read value for evsel", counts.val != 0);
184	}
185
186	perf_evlist__disable(evlist);
187
188	perf_evlist__close(evlist);
189	perf_evlist__delete(evlist);
190
191	perf_thread_map__put(threads);
192	return 0;
193}
194
195static int test_mmap_thread(void)
196{
197	struct perf_evlist *evlist;
198	struct perf_evsel *evsel;
199	struct perf_mmap *map;
200	struct perf_cpu_map *cpus;
201	struct perf_thread_map *threads;
202	struct perf_event_attr attr = {
203		.type             = PERF_TYPE_TRACEPOINT,
204		.sample_period    = 1,
205		.wakeup_watermark = 1,
206		.disabled         = 1,
207	};
208	char path[PATH_MAX];
209	int id, err, pid, go_pipe[2];
210	union perf_event *event;
211	char bf;
212	int count = 0;
213
214	snprintf(path, PATH_MAX, "%s/kernel/debug/tracing/events/syscalls/sys_enter_prctl/id",
215		 sysfs__mountpoint());
216
217	if (filename__read_int(path, &id)) {
218		tests_failed++;
219		fprintf(stderr, "error: failed to get tracepoint id: %s\n", path);
220		return -1;
221	}
222
223	attr.config = id;
224
225	err = pipe(go_pipe);
226	__T("failed to create pipe", err == 0);
227
228	fflush(NULL);
229
230	pid = fork();
231	if (!pid) {
232		int i;
233
234		read(go_pipe[0], &bf, 1);
235
236		/* Generate 100 prctl calls. */
237		for (i = 0; i < 100; i++)
238			prctl(0, 0, 0, 0, 0);
239
240		exit(0);
241	}
242
243	threads = perf_thread_map__new_dummy();
244	__T("failed to create threads", threads);
245
246	cpus = perf_cpu_map__dummy_new();
247	__T("failed to create cpus", cpus);
248
249	perf_thread_map__set_pid(threads, 0, pid);
250
251	evlist = perf_evlist__new();
252	__T("failed to create evlist", evlist);
253
254	evsel = perf_evsel__new(&attr);
255	__T("failed to create evsel1", evsel);
256
257	perf_evlist__add(evlist, evsel);
258
259	perf_evlist__set_maps(evlist, cpus, threads);
260
261	err = perf_evlist__open(evlist);
262	__T("failed to open evlist", err == 0);
263
264	err = perf_evlist__mmap(evlist, 4);
265	__T("failed to mmap evlist", err == 0);
266
267	perf_evlist__enable(evlist);
268
269	/* kick the child and wait for it to finish */
270	write(go_pipe[1], &bf, 1);
271	waitpid(pid, NULL, 0);
272
273	/*
274	 * There's no need to call perf_evlist__disable,
275	 * monitored process is dead now.
276	 */
277
278	perf_evlist__for_each_mmap(evlist, map, false) {
279		if (perf_mmap__read_init(map) < 0)
280			continue;
281
282		while ((event = perf_mmap__read_event(map)) != NULL) {
283			count++;
284			perf_mmap__consume(map);
285		}
286
287		perf_mmap__read_done(map);
288	}
289
290	/* calls perf_evlist__munmap/perf_evlist__close */
291	perf_evlist__delete(evlist);
292
293	perf_thread_map__put(threads);
294	perf_cpu_map__put(cpus);
295
296	/*
297	 * The generated prctl calls should match the
298	 * number of events in the buffer.
299	 */
300	__T("failed count", count == 100);
301
302	return 0;
303}
304
305static int test_mmap_cpus(void)
306{
307	struct perf_evlist *evlist;
308	struct perf_evsel *evsel;
309	struct perf_mmap *map;
310	struct perf_cpu_map *cpus;
311	struct perf_event_attr attr = {
312		.type             = PERF_TYPE_TRACEPOINT,
313		.sample_period    = 1,
314		.wakeup_watermark = 1,
315		.disabled         = 1,
316	};
317	cpu_set_t saved_mask;
318	char path[PATH_MAX];
319	int id, err, cpu, tmp;
320	union perf_event *event;
321	int count = 0;
322
323	snprintf(path, PATH_MAX, "%s/kernel/debug/tracing/events/syscalls/sys_enter_prctl/id",
324		 sysfs__mountpoint());
325
326	if (filename__read_int(path, &id)) {
327		fprintf(stderr, "error: failed to get tracepoint id: %s\n", path);
328		return -1;
329	}
330
331	attr.config = id;
332
333	cpus = perf_cpu_map__new(NULL);
334	__T("failed to create cpus", cpus);
335
336	evlist = perf_evlist__new();
337	__T("failed to create evlist", evlist);
338
339	evsel = perf_evsel__new(&attr);
340	__T("failed to create evsel1", evsel);
341
342	perf_evlist__add(evlist, evsel);
343
344	perf_evlist__set_maps(evlist, cpus, NULL);
345
346	err = perf_evlist__open(evlist);
347	__T("failed to open evlist", err == 0);
348
349	err = perf_evlist__mmap(evlist, 4);
350	__T("failed to mmap evlist", err == 0);
351
352	perf_evlist__enable(evlist);
353
354	err = sched_getaffinity(0, sizeof(saved_mask), &saved_mask);
355	__T("sched_getaffinity failed", err == 0);
356
357	perf_cpu_map__for_each_cpu(cpu, tmp, cpus) {
358		cpu_set_t mask;
359
360		CPU_ZERO(&mask);
361		CPU_SET(cpu, &mask);
362
363		err = sched_setaffinity(0, sizeof(mask), &mask);
364		__T("sched_setaffinity failed", err == 0);
365
366		prctl(0, 0, 0, 0, 0);
367	}
368
369	err = sched_setaffinity(0, sizeof(saved_mask), &saved_mask);
370	__T("sched_setaffinity failed", err == 0);
371
372	perf_evlist__disable(evlist);
373
374	perf_evlist__for_each_mmap(evlist, map, false) {
375		if (perf_mmap__read_init(map) < 0)
376			continue;
377
378		while ((event = perf_mmap__read_event(map)) != NULL) {
379			count++;
380			perf_mmap__consume(map);
381		}
382
383		perf_mmap__read_done(map);
384	}
385
386	/* calls perf_evlist__munmap/perf_evlist__close */
387	perf_evlist__delete(evlist);
388
389	/*
390	 * The generated prctl events should match the
391	 * number of cpus or be bigger (we are system-wide).
392	 */
393	__T("failed count", count >= perf_cpu_map__nr(cpus));
394
395	perf_cpu_map__put(cpus);
396
397	return 0;
398}
399
400int main(int argc, char **argv)
401{
402	__T_START;
403
404	libperf_init(libperf_print);
405
406	test_stat_cpu();
407	test_stat_thread();
408	test_stat_thread_enable();
409	test_mmap_thread();
410	test_mmap_cpus();
411
412	__T_END;
413	return tests_failed == 0 ? 0 : -1;
414}
415