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