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