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