1 // SPDX-License-Identifier: GPL-2.0
2 #include <sys/time.h>
3 #include <sys/prctl.h>
4 #include <errno.h>
5 #include <time.h>
6 #include <stdlib.h>
7 #include <linux/zalloc.h>
8 
9 #include "parse-events.h"
10 #include "evlist.h"
11 #include "evsel.h"
12 #include "thread_map.h"
13 #include "cpumap.h"
14 #include "tests.h"
15 
16 static int spin_sleep(void)
17 {
18 	struct timeval start, now, diff, maxtime;
19 	struct timespec ts;
20 	int err, i;
21 
22 	maxtime.tv_sec = 0;
23 	maxtime.tv_usec = 50000;
24 
25 	err = gettimeofday(&start, NULL);
26 	if (err)
27 		return err;
28 
29 	/* Spin for 50ms */
30 	while (1) {
31 		for (i = 0; i < 1000; i++)
32 			barrier();
33 
34 		err = gettimeofday(&now, NULL);
35 		if (err)
36 			return err;
37 
38 		timersub(&now, &start, &diff);
39 		if (timercmp(&diff, &maxtime, > /* For checkpatch */))
40 			break;
41 	}
42 
43 	ts.tv_nsec = 50 * 1000 * 1000;
44 	ts.tv_sec = 0;
45 
46 	/* Sleep for 50ms */
47 	err = nanosleep(&ts, NULL);
48 	if (err == EINTR)
49 		err = 0;
50 
51 	return err;
52 }
53 
54 struct switch_tracking {
55 	struct perf_evsel *switch_evsel;
56 	struct perf_evsel *cycles_evsel;
57 	pid_t *tids;
58 	int nr_tids;
59 	int comm_seen[4];
60 	int cycles_before_comm_1;
61 	int cycles_between_comm_2_and_comm_3;
62 	int cycles_after_comm_4;
63 };
64 
65 static int check_comm(struct switch_tracking *switch_tracking,
66 		      union perf_event *event, const char *comm, int nr)
67 {
68 	if (event->header.type == PERF_RECORD_COMM &&
69 	    (pid_t)event->comm.pid == getpid() &&
70 	    (pid_t)event->comm.tid == getpid() &&
71 	    strcmp(event->comm.comm, comm) == 0) {
72 		if (switch_tracking->comm_seen[nr]) {
73 			pr_debug("Duplicate comm event\n");
74 			return -1;
75 		}
76 		switch_tracking->comm_seen[nr] = 1;
77 		pr_debug3("comm event: %s nr: %d\n", event->comm.comm, nr);
78 		return 1;
79 	}
80 	return 0;
81 }
82 
83 static int check_cpu(struct switch_tracking *switch_tracking, int cpu)
84 {
85 	int i, nr = cpu + 1;
86 
87 	if (cpu < 0)
88 		return -1;
89 
90 	if (!switch_tracking->tids) {
91 		switch_tracking->tids = calloc(nr, sizeof(pid_t));
92 		if (!switch_tracking->tids)
93 			return -1;
94 		for (i = 0; i < nr; i++)
95 			switch_tracking->tids[i] = -1;
96 		switch_tracking->nr_tids = nr;
97 		return 0;
98 	}
99 
100 	if (cpu >= switch_tracking->nr_tids) {
101 		void *addr;
102 
103 		addr = realloc(switch_tracking->tids, nr * sizeof(pid_t));
104 		if (!addr)
105 			return -1;
106 		switch_tracking->tids = addr;
107 		for (i = switch_tracking->nr_tids; i < nr; i++)
108 			switch_tracking->tids[i] = -1;
109 		switch_tracking->nr_tids = nr;
110 		return 0;
111 	}
112 
113 	return 0;
114 }
115 
116 static int process_sample_event(struct perf_evlist *evlist,
117 				union perf_event *event,
118 				struct switch_tracking *switch_tracking)
119 {
120 	struct perf_sample sample;
121 	struct perf_evsel *evsel;
122 	pid_t next_tid, prev_tid;
123 	int cpu, err;
124 
125 	if (perf_evlist__parse_sample(evlist, event, &sample)) {
126 		pr_debug("perf_evlist__parse_sample failed\n");
127 		return -1;
128 	}
129 
130 	evsel = perf_evlist__id2evsel(evlist, sample.id);
131 	if (evsel == switch_tracking->switch_evsel) {
132 		next_tid = perf_evsel__intval(evsel, &sample, "next_pid");
133 		prev_tid = perf_evsel__intval(evsel, &sample, "prev_pid");
134 		cpu = sample.cpu;
135 		pr_debug3("sched_switch: cpu: %d prev_tid %d next_tid %d\n",
136 			  cpu, prev_tid, next_tid);
137 		err = check_cpu(switch_tracking, cpu);
138 		if (err)
139 			return err;
140 		/*
141 		 * Check for no missing sched_switch events i.e. that the
142 		 * evsel->system_wide flag has worked.
143 		 */
144 		if (switch_tracking->tids[cpu] != -1 &&
145 		    switch_tracking->tids[cpu] != prev_tid) {
146 			pr_debug("Missing sched_switch events\n");
147 			return -1;
148 		}
149 		switch_tracking->tids[cpu] = next_tid;
150 	}
151 
152 	if (evsel == switch_tracking->cycles_evsel) {
153 		pr_debug3("cycles event\n");
154 		if (!switch_tracking->comm_seen[0])
155 			switch_tracking->cycles_before_comm_1 = 1;
156 		if (switch_tracking->comm_seen[1] &&
157 		    !switch_tracking->comm_seen[2])
158 			switch_tracking->cycles_between_comm_2_and_comm_3 = 1;
159 		if (switch_tracking->comm_seen[3])
160 			switch_tracking->cycles_after_comm_4 = 1;
161 	}
162 
163 	return 0;
164 }
165 
166 static int process_event(struct perf_evlist *evlist, union perf_event *event,
167 			 struct switch_tracking *switch_tracking)
168 {
169 	if (event->header.type == PERF_RECORD_SAMPLE)
170 		return process_sample_event(evlist, event, switch_tracking);
171 
172 	if (event->header.type == PERF_RECORD_COMM) {
173 		int err, done = 0;
174 
175 		err = check_comm(switch_tracking, event, "Test COMM 1", 0);
176 		if (err < 0)
177 			return -1;
178 		done += err;
179 		err = check_comm(switch_tracking, event, "Test COMM 2", 1);
180 		if (err < 0)
181 			return -1;
182 		done += err;
183 		err = check_comm(switch_tracking, event, "Test COMM 3", 2);
184 		if (err < 0)
185 			return -1;
186 		done += err;
187 		err = check_comm(switch_tracking, event, "Test COMM 4", 3);
188 		if (err < 0)
189 			return -1;
190 		done += err;
191 		if (done != 1) {
192 			pr_debug("Unexpected comm event\n");
193 			return -1;
194 		}
195 	}
196 
197 	return 0;
198 }
199 
200 struct event_node {
201 	struct list_head list;
202 	union perf_event *event;
203 	u64 event_time;
204 };
205 
206 static int add_event(struct perf_evlist *evlist, struct list_head *events,
207 		     union perf_event *event)
208 {
209 	struct perf_sample sample;
210 	struct event_node *node;
211 
212 	node = malloc(sizeof(struct event_node));
213 	if (!node) {
214 		pr_debug("malloc failed\n");
215 		return -1;
216 	}
217 	node->event = event;
218 	list_add(&node->list, events);
219 
220 	if (perf_evlist__parse_sample(evlist, event, &sample)) {
221 		pr_debug("perf_evlist__parse_sample failed\n");
222 		return -1;
223 	}
224 
225 	if (!sample.time) {
226 		pr_debug("event with no time\n");
227 		return -1;
228 	}
229 
230 	node->event_time = sample.time;
231 
232 	return 0;
233 }
234 
235 static void free_event_nodes(struct list_head *events)
236 {
237 	struct event_node *node;
238 
239 	while (!list_empty(events)) {
240 		node = list_entry(events->next, struct event_node, list);
241 		list_del_init(&node->list);
242 		free(node);
243 	}
244 }
245 
246 static int compar(const void *a, const void *b)
247 {
248 	const struct event_node *nodea = a;
249 	const struct event_node *nodeb = b;
250 	s64 cmp = nodea->event_time - nodeb->event_time;
251 
252 	return cmp;
253 }
254 
255 static int process_events(struct perf_evlist *evlist,
256 			  struct switch_tracking *switch_tracking)
257 {
258 	union perf_event *event;
259 	unsigned pos, cnt = 0;
260 	LIST_HEAD(events);
261 	struct event_node *events_array, *node;
262 	struct perf_mmap *md;
263 	int i, ret;
264 
265 	for (i = 0; i < evlist->nr_mmaps; i++) {
266 		md = &evlist->mmap[i];
267 		if (perf_mmap__read_init(md) < 0)
268 			continue;
269 
270 		while ((event = perf_mmap__read_event(md)) != NULL) {
271 			cnt += 1;
272 			ret = add_event(evlist, &events, event);
273 			 perf_mmap__consume(md);
274 			if (ret < 0)
275 				goto out_free_nodes;
276 		}
277 		perf_mmap__read_done(md);
278 	}
279 
280 	events_array = calloc(cnt, sizeof(struct event_node));
281 	if (!events_array) {
282 		pr_debug("calloc failed\n");
283 		ret = -1;
284 		goto out_free_nodes;
285 	}
286 
287 	pos = 0;
288 	list_for_each_entry(node, &events, list)
289 		events_array[pos++] = *node;
290 
291 	qsort(events_array, cnt, sizeof(struct event_node), compar);
292 
293 	for (pos = 0; pos < cnt; pos++) {
294 		ret = process_event(evlist, events_array[pos].event,
295 				    switch_tracking);
296 		if (ret < 0)
297 			goto out_free;
298 	}
299 
300 	ret = 0;
301 out_free:
302 	pr_debug("%u events recorded\n", cnt);
303 	free(events_array);
304 out_free_nodes:
305 	free_event_nodes(&events);
306 	return ret;
307 }
308 
309 /**
310  * test__switch_tracking - test using sched_switch and tracking events.
311  *
312  * This function implements a test that checks that sched_switch events and
313  * tracking events can be recorded for a workload (current process) using the
314  * evsel->system_wide and evsel->tracking flags (respectively) with other events
315  * sometimes enabled or disabled.
316  */
317 int test__switch_tracking(struct test *test __maybe_unused, int subtest __maybe_unused)
318 {
319 	const char *sched_switch = "sched:sched_switch";
320 	struct switch_tracking switch_tracking = { .tids = NULL, };
321 	struct record_opts opts = {
322 		.mmap_pages	     = UINT_MAX,
323 		.user_freq	     = UINT_MAX,
324 		.user_interval	     = ULLONG_MAX,
325 		.freq		     = 4000,
326 		.target		     = {
327 			.uses_mmap   = true,
328 		},
329 	};
330 	struct thread_map *threads = NULL;
331 	struct cpu_map *cpus = NULL;
332 	struct perf_evlist *evlist = NULL;
333 	struct perf_evsel *evsel, *cpu_clocks_evsel, *cycles_evsel;
334 	struct perf_evsel *switch_evsel, *tracking_evsel;
335 	const char *comm;
336 	int err = -1;
337 
338 	threads = thread_map__new(-1, getpid(), UINT_MAX);
339 	if (!threads) {
340 		pr_debug("thread_map__new failed!\n");
341 		goto out_err;
342 	}
343 
344 	cpus = cpu_map__new(NULL);
345 	if (!cpus) {
346 		pr_debug("cpu_map__new failed!\n");
347 		goto out_err;
348 	}
349 
350 	evlist = perf_evlist__new();
351 	if (!evlist) {
352 		pr_debug("perf_evlist__new failed!\n");
353 		goto out_err;
354 	}
355 
356 	perf_evlist__set_maps(evlist, cpus, threads);
357 
358 	/* First event */
359 	err = parse_events(evlist, "cpu-clock:u", NULL);
360 	if (err) {
361 		pr_debug("Failed to parse event dummy:u\n");
362 		goto out_err;
363 	}
364 
365 	cpu_clocks_evsel = perf_evlist__last(evlist);
366 
367 	/* Second event */
368 	err = parse_events(evlist, "cycles:u", NULL);
369 	if (err) {
370 		pr_debug("Failed to parse event cycles:u\n");
371 		goto out_err;
372 	}
373 
374 	cycles_evsel = perf_evlist__last(evlist);
375 
376 	/* Third event */
377 	if (!perf_evlist__can_select_event(evlist, sched_switch)) {
378 		pr_debug("No sched_switch\n");
379 		err = 0;
380 		goto out;
381 	}
382 
383 	err = parse_events(evlist, sched_switch, NULL);
384 	if (err) {
385 		pr_debug("Failed to parse event %s\n", sched_switch);
386 		goto out_err;
387 	}
388 
389 	switch_evsel = perf_evlist__last(evlist);
390 
391 	perf_evsel__set_sample_bit(switch_evsel, CPU);
392 	perf_evsel__set_sample_bit(switch_evsel, TIME);
393 
394 	switch_evsel->system_wide = true;
395 	switch_evsel->no_aux_samples = true;
396 	switch_evsel->immediate = true;
397 
398 	/* Test moving an event to the front */
399 	if (cycles_evsel == perf_evlist__first(evlist)) {
400 		pr_debug("cycles event already at front");
401 		goto out_err;
402 	}
403 	perf_evlist__to_front(evlist, cycles_evsel);
404 	if (cycles_evsel != perf_evlist__first(evlist)) {
405 		pr_debug("Failed to move cycles event to front");
406 		goto out_err;
407 	}
408 
409 	perf_evsel__set_sample_bit(cycles_evsel, CPU);
410 	perf_evsel__set_sample_bit(cycles_evsel, TIME);
411 
412 	/* Fourth event */
413 	err = parse_events(evlist, "dummy:u", NULL);
414 	if (err) {
415 		pr_debug("Failed to parse event dummy:u\n");
416 		goto out_err;
417 	}
418 
419 	tracking_evsel = perf_evlist__last(evlist);
420 
421 	perf_evlist__set_tracking_event(evlist, tracking_evsel);
422 
423 	tracking_evsel->attr.freq = 0;
424 	tracking_evsel->attr.sample_period = 1;
425 
426 	perf_evsel__set_sample_bit(tracking_evsel, TIME);
427 
428 	/* Config events */
429 	perf_evlist__config(evlist, &opts, NULL);
430 
431 	/* Check moved event is still at the front */
432 	if (cycles_evsel != perf_evlist__first(evlist)) {
433 		pr_debug("Front event no longer at front");
434 		goto out_err;
435 	}
436 
437 	/* Check tracking event is tracking */
438 	if (!tracking_evsel->attr.mmap || !tracking_evsel->attr.comm) {
439 		pr_debug("Tracking event not tracking\n");
440 		goto out_err;
441 	}
442 
443 	/* Check non-tracking events are not tracking */
444 	evlist__for_each_entry(evlist, evsel) {
445 		if (evsel != tracking_evsel) {
446 			if (evsel->attr.mmap || evsel->attr.comm) {
447 				pr_debug("Non-tracking event is tracking\n");
448 				goto out_err;
449 			}
450 		}
451 	}
452 
453 	if (perf_evlist__open(evlist) < 0) {
454 		pr_debug("Not supported\n");
455 		err = 0;
456 		goto out;
457 	}
458 
459 	err = perf_evlist__mmap(evlist, UINT_MAX);
460 	if (err) {
461 		pr_debug("perf_evlist__mmap failed!\n");
462 		goto out_err;
463 	}
464 
465 	perf_evlist__enable(evlist);
466 
467 	err = perf_evsel__disable(cpu_clocks_evsel);
468 	if (err) {
469 		pr_debug("perf_evlist__disable_event failed!\n");
470 		goto out_err;
471 	}
472 
473 	err = spin_sleep();
474 	if (err) {
475 		pr_debug("spin_sleep failed!\n");
476 		goto out_err;
477 	}
478 
479 	comm = "Test COMM 1";
480 	err = prctl(PR_SET_NAME, (unsigned long)comm, 0, 0, 0);
481 	if (err) {
482 		pr_debug("PR_SET_NAME failed!\n");
483 		goto out_err;
484 	}
485 
486 	err = perf_evsel__disable(cycles_evsel);
487 	if (err) {
488 		pr_debug("perf_evlist__disable_event failed!\n");
489 		goto out_err;
490 	}
491 
492 	comm = "Test COMM 2";
493 	err = prctl(PR_SET_NAME, (unsigned long)comm, 0, 0, 0);
494 	if (err) {
495 		pr_debug("PR_SET_NAME failed!\n");
496 		goto out_err;
497 	}
498 
499 	err = spin_sleep();
500 	if (err) {
501 		pr_debug("spin_sleep failed!\n");
502 		goto out_err;
503 	}
504 
505 	comm = "Test COMM 3";
506 	err = prctl(PR_SET_NAME, (unsigned long)comm, 0, 0, 0);
507 	if (err) {
508 		pr_debug("PR_SET_NAME failed!\n");
509 		goto out_err;
510 	}
511 
512 	err = perf_evsel__enable(cycles_evsel);
513 	if (err) {
514 		pr_debug("perf_evlist__disable_event failed!\n");
515 		goto out_err;
516 	}
517 
518 	comm = "Test COMM 4";
519 	err = prctl(PR_SET_NAME, (unsigned long)comm, 0, 0, 0);
520 	if (err) {
521 		pr_debug("PR_SET_NAME failed!\n");
522 		goto out_err;
523 	}
524 
525 	err = spin_sleep();
526 	if (err) {
527 		pr_debug("spin_sleep failed!\n");
528 		goto out_err;
529 	}
530 
531 	perf_evlist__disable(evlist);
532 
533 	switch_tracking.switch_evsel = switch_evsel;
534 	switch_tracking.cycles_evsel = cycles_evsel;
535 
536 	err = process_events(evlist, &switch_tracking);
537 
538 	zfree(&switch_tracking.tids);
539 
540 	if (err)
541 		goto out_err;
542 
543 	/* Check all 4 comm events were seen i.e. that evsel->tracking works */
544 	if (!switch_tracking.comm_seen[0] || !switch_tracking.comm_seen[1] ||
545 	    !switch_tracking.comm_seen[2] || !switch_tracking.comm_seen[3]) {
546 		pr_debug("Missing comm events\n");
547 		goto out_err;
548 	}
549 
550 	/* Check cycles event got enabled */
551 	if (!switch_tracking.cycles_before_comm_1) {
552 		pr_debug("Missing cycles events\n");
553 		goto out_err;
554 	}
555 
556 	/* Check cycles event got disabled */
557 	if (switch_tracking.cycles_between_comm_2_and_comm_3) {
558 		pr_debug("cycles events even though event was disabled\n");
559 		goto out_err;
560 	}
561 
562 	/* Check cycles event got enabled again */
563 	if (!switch_tracking.cycles_after_comm_4) {
564 		pr_debug("Missing cycles events\n");
565 		goto out_err;
566 	}
567 out:
568 	if (evlist) {
569 		perf_evlist__disable(evlist);
570 		perf_evlist__delete(evlist);
571 	} else {
572 		cpu_map__put(cpus);
573 		thread_map__put(threads);
574 	}
575 
576 	return err;
577 
578 out_err:
579 	err = -1;
580 	goto out;
581 }
582