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