xref: /linux/tools/perf/tests/perf-record.c (revision 4f9786035f9e519db41375818e1d0b5f20da2f10)
1 // SPDX-License-Identifier: GPL-2.0
2 #include <errno.h>
3 #include <inttypes.h>
4 #include <linux/string.h>
5 
6 #include <sched.h>
7 #include <perf/mmap.h>
8 #include "event.h"
9 #include "evlist.h"
10 #include "evsel.h"
11 #include "debug.h"
12 #include "record.h"
13 #include "tests.h"
14 #include "util/mmap.h"
15 #include "util/sample.h"
16 
17 static int sched__get_first_possible_cpu(pid_t pid, cpu_set_t *maskp)
18 {
19 	int i, cpu = -1, nrcpus = 1024;
20 realloc:
21 	CPU_ZERO(maskp);
22 
23 	if (sched_getaffinity(pid, sizeof(*maskp), maskp) == -1) {
24 		if (errno == EINVAL && nrcpus < (1024 << 8)) {
25 			nrcpus = nrcpus << 2;
26 			goto realloc;
27 		}
28 		perror("sched_getaffinity");
29 			return -1;
30 	}
31 
32 	for (i = 0; i < nrcpus; i++) {
33 		if (CPU_ISSET(i, maskp)) {
34 			if (cpu == -1)
35 				cpu = i;
36 			else
37 				CPU_CLR(i, maskp);
38 		}
39 	}
40 
41 	return cpu;
42 }
43 
44 static int test__PERF_RECORD(struct test_suite *test __maybe_unused, int subtest __maybe_unused)
45 {
46 	struct record_opts opts = {
47 		.target = {
48 			.uid = UINT_MAX,
49 			.uses_mmap = true,
50 		},
51 		.no_buffering = true,
52 		.mmap_pages   = 256,
53 	};
54 	cpu_set_t cpu_mask;
55 	size_t cpu_mask_size = sizeof(cpu_mask);
56 	struct evlist *evlist = evlist__new_dummy();
57 	struct evsel *evsel;
58 	struct perf_sample sample;
59 	const char *cmd = "sleep";
60 	const char *argv[] = { cmd, "1", NULL, };
61 	char *bname, *mmap_filename;
62 	u64 prev_time = 0;
63 	bool found_cmd_mmap = false,
64 	     found_coreutils_mmap = false,
65 	     found_libc_mmap = false,
66 	     found_vdso_mmap = false,
67 	     found_ld_mmap = false;
68 	int err = -1, errs = 0, i, wakeups = 0;
69 	u32 cpu;
70 	int total_events = 0, nr_events[PERF_RECORD_MAX] = { 0, };
71 	char sbuf[STRERR_BUFSIZE];
72 
73 	perf_sample__init(&sample, /*all=*/false);
74 	if (evlist == NULL) /* Fallback for kernels lacking PERF_COUNT_SW_DUMMY */
75 		evlist = evlist__new_default();
76 
77 	if (evlist == NULL) {
78 		pr_debug("Not enough memory to create evlist\n");
79 		goto out;
80 	}
81 
82 	/*
83 	 * Create maps of threads and cpus to monitor. In this case
84 	 * we start with all threads and cpus (-1, -1) but then in
85 	 * evlist__prepare_workload we'll fill in the only thread
86 	 * we're monitoring, the one forked there.
87 	 */
88 	err = evlist__create_maps(evlist, &opts.target);
89 	if (err < 0) {
90 		pr_debug("Not enough memory to create thread/cpu maps\n");
91 		goto out_delete_evlist;
92 	}
93 
94 	/*
95 	 * Prepare the workload in argv[] to run, it'll fork it, and then wait
96 	 * for evlist__start_workload() to exec it. This is done this way
97 	 * so that we have time to open the evlist (calling sys_perf_event_open
98 	 * on all the fds) and then mmap them.
99 	 */
100 	err = evlist__prepare_workload(evlist, &opts.target, argv, false, NULL);
101 	if (err < 0) {
102 		pr_debug("Couldn't run the workload!\n");
103 		goto out_delete_evlist;
104 	}
105 
106 	/*
107 	 * Config the evsels, setting attr->comm on the first one, etc.
108 	 */
109 	evsel = evlist__first(evlist);
110 	evsel__set_sample_bit(evsel, CPU);
111 	evsel__set_sample_bit(evsel, TID);
112 	evsel__set_sample_bit(evsel, TIME);
113 	evlist__config(evlist, &opts, NULL);
114 
115 	err = sched__get_first_possible_cpu(evlist->workload.pid, &cpu_mask);
116 	if (err < 0) {
117 		pr_debug("sched__get_first_possible_cpu: %s\n",
118 			 str_error_r(errno, sbuf, sizeof(sbuf)));
119 		goto out_delete_evlist;
120 	}
121 
122 	cpu = err;
123 
124 	/*
125 	 * So that we can check perf_sample.cpu on all the samples.
126 	 */
127 	if (sched_setaffinity(evlist->workload.pid, cpu_mask_size, &cpu_mask) < 0) {
128 		pr_debug("sched_setaffinity: %s\n",
129 			 str_error_r(errno, sbuf, sizeof(sbuf)));
130 		goto out_delete_evlist;
131 	}
132 
133 	/*
134 	 * Call sys_perf_event_open on all the fds on all the evsels,
135 	 * grouping them if asked to.
136 	 */
137 	err = evlist__open(evlist);
138 	if (err < 0) {
139 		pr_debug("perf_evlist__open: %s\n",
140 			 str_error_r(errno, sbuf, sizeof(sbuf)));
141 		goto out_delete_evlist;
142 	}
143 
144 	/*
145 	 * mmap the first fd on a given CPU and ask for events for the other
146 	 * fds in the same CPU to be injected in the same mmap ring buffer
147 	 * (using ioctl(PERF_EVENT_IOC_SET_OUTPUT)).
148 	 */
149 	err = evlist__mmap(evlist, opts.mmap_pages);
150 	if (err < 0) {
151 		pr_debug("evlist__mmap: %s\n",
152 			 str_error_r(errno, sbuf, sizeof(sbuf)));
153 		goto out_delete_evlist;
154 	}
155 
156 	/*
157 	 * Now that all is properly set up, enable the events, they will
158 	 * count just on workload.pid, which will start...
159 	 */
160 	evlist__enable(evlist);
161 
162 	/*
163 	 * Now!
164 	 */
165 	evlist__start_workload(evlist);
166 
167 	while (1) {
168 		int before = total_events;
169 
170 		for (i = 0; i < evlist->core.nr_mmaps; i++) {
171 			union perf_event *event;
172 			struct mmap *md;
173 
174 			md = &evlist->mmap[i];
175 			if (perf_mmap__read_init(&md->core) < 0)
176 				continue;
177 
178 			while ((event = perf_mmap__read_event(&md->core)) != NULL) {
179 				const u32 type = event->header.type;
180 				const char *name = perf_event__name(type);
181 
182 				++total_events;
183 				if (type < PERF_RECORD_MAX)
184 					nr_events[type]++;
185 
186 				err = evlist__parse_sample(evlist, event, &sample);
187 				if (err < 0) {
188 					if (verbose > 0)
189 						perf_event__fprintf(event, NULL, stderr);
190 					pr_debug("Couldn't parse sample\n");
191 					goto out_delete_evlist;
192 				}
193 
194 				if (verbose > 0) {
195 					pr_info("%" PRIu64" %d ", sample.time, sample.cpu);
196 					perf_event__fprintf(event, NULL, stderr);
197 				}
198 
199 				if (prev_time > sample.time) {
200 					pr_debug("%s going backwards in time, prev=%" PRIu64 ", curr=%" PRIu64 "\n",
201 						 name, prev_time, sample.time);
202 					++errs;
203 				}
204 
205 				prev_time = sample.time;
206 
207 				if (sample.cpu != cpu) {
208 					pr_debug("%s with unexpected cpu, expected %d, got %d\n",
209 						 name, cpu, sample.cpu);
210 					++errs;
211 				}
212 
213 				if ((pid_t)sample.pid != evlist->workload.pid) {
214 					pr_debug("%s with unexpected pid, expected %d, got %d\n",
215 						 name, evlist->workload.pid, sample.pid);
216 					++errs;
217 				}
218 
219 				if ((pid_t)sample.tid != evlist->workload.pid) {
220 					pr_debug("%s with unexpected tid, expected %d, got %d\n",
221 						 name, evlist->workload.pid, sample.tid);
222 					++errs;
223 				}
224 
225 				if ((type == PERF_RECORD_COMM ||
226 				     type == PERF_RECORD_MMAP ||
227 				     type == PERF_RECORD_MMAP2 ||
228 				     type == PERF_RECORD_FORK ||
229 				     type == PERF_RECORD_EXIT) &&
230 				     (pid_t)event->comm.pid != evlist->workload.pid) {
231 					pr_debug("%s with unexpected pid/tid\n", name);
232 					++errs;
233 				}
234 
235 				if ((type == PERF_RECORD_COMM ||
236 				     type == PERF_RECORD_MMAP ||
237 				     type == PERF_RECORD_MMAP2) &&
238 				     event->comm.pid != event->comm.tid) {
239 					pr_debug("%s with different pid/tid!\n", name);
240 					++errs;
241 				}
242 
243 				switch (type) {
244 				case PERF_RECORD_COMM:
245 					if (strcmp(event->comm.comm, cmd)) {
246 						pr_debug("%s with unexpected comm!\n", name);
247 						++errs;
248 					}
249 					break;
250 				case PERF_RECORD_EXIT:
251 					goto found_exit;
252 				case PERF_RECORD_MMAP:
253 					mmap_filename = event->mmap.filename;
254 					goto check_bname;
255 				case PERF_RECORD_MMAP2:
256 					mmap_filename = event->mmap2.filename;
257 				check_bname:
258 					bname = strrchr(mmap_filename, '/');
259 					if (bname != NULL) {
260 						if (!found_cmd_mmap)
261 							found_cmd_mmap = !strcmp(bname + 1, cmd);
262 						if (!found_coreutils_mmap)
263 							found_coreutils_mmap = !strcmp(bname + 1, "coreutils");
264 						if (!found_libc_mmap)
265 							found_libc_mmap = !strncmp(bname + 1, "libc", 4);
266 						if (!found_ld_mmap)
267 							found_ld_mmap = !strncmp(bname + 1, "ld", 2);
268 					} else if (!found_vdso_mmap)
269 						found_vdso_mmap = !strcmp(mmap_filename, "[vdso]");
270 					break;
271 
272 				case PERF_RECORD_SAMPLE:
273 					/* Just ignore samples for now */
274 					break;
275 				default:
276 					pr_debug("Unexpected perf_event->header.type %d!\n",
277 						 type);
278 					++errs;
279 				}
280 
281 				perf_mmap__consume(&md->core);
282 			}
283 			perf_mmap__read_done(&md->core);
284 		}
285 
286 		/*
287 		 * We don't use poll here because at least at 3.1 times the
288 		 * PERF_RECORD_{!SAMPLE} events don't honour
289 		 * perf_event_attr.wakeup_events, just PERF_EVENT_SAMPLE does.
290 		 */
291 		if (total_events == before && false)
292 			evlist__poll(evlist, -1);
293 
294 		sleep(1);
295 		if (++wakeups > 5) {
296 			pr_debug("No PERF_RECORD_EXIT event!\n");
297 			break;
298 		}
299 	}
300 
301 found_exit:
302 	if (nr_events[PERF_RECORD_COMM] > 1 + !!found_coreutils_mmap) {
303 		pr_debug("Excessive number of PERF_RECORD_COMM events!\n");
304 		++errs;
305 	}
306 
307 	if (nr_events[PERF_RECORD_COMM] == 0) {
308 		pr_debug("Missing PERF_RECORD_COMM for %s!\n", cmd);
309 		++errs;
310 	}
311 
312 	if (!found_cmd_mmap && !found_coreutils_mmap) {
313 		pr_debug("PERF_RECORD_MMAP for %s missing!\n", cmd);
314 		++errs;
315 	}
316 
317 	if (!found_libc_mmap) {
318 		pr_debug("PERF_RECORD_MMAP for %s missing!\n", "libc");
319 		++errs;
320 	}
321 
322 	if (!found_ld_mmap) {
323 		pr_debug("PERF_RECORD_MMAP for %s missing!\n", "ld");
324 		++errs;
325 	}
326 
327 	if (!found_vdso_mmap) {
328 		pr_debug("PERF_RECORD_MMAP for %s missing!\n", "[vdso]");
329 		++errs;
330 	}
331 out_delete_evlist:
332 	evlist__delete(evlist);
333 out:
334 	perf_sample__exit(&sample);
335 	if (err == -EACCES)
336 		return TEST_SKIP;
337 	if (err < 0 || errs != 0)
338 		return TEST_FAIL;
339 	return TEST_OK;
340 }
341 
342 static struct test_case tests__PERF_RECORD[] = {
343 	TEST_CASE_REASON("PERF_RECORD_* events & perf_sample fields",
344 			 PERF_RECORD,
345 			 "permissions"),
346 	{	.name = NULL, }
347 };
348 
349 struct test_suite suite__PERF_RECORD = {
350 	.desc = "PERF_RECORD_* events & perf_sample fields",
351 	.test_cases = tests__PERF_RECORD,
352 };
353