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