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