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#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
  16static 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
  54struct 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
  65static 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
  83static 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
 116static 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
 166static 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
 200struct event_node {
 201        struct list_head list;
 202        union perf_event *event;
 203        u64 event_time;
 204};
 205
 206static 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
 235static 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
 246static 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
 255static 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;
 301out_free:
 302        pr_debug("%u events recorded\n", cnt);
 303        free(events_array);
 304out_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 */
 317int 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        }
 567out:
 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
 578out_err:
 579        err = -1;
 580        goto out;
 581}
 582