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