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