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        int i, ret;
 261
 262        for (i = 0; i < evlist->nr_mmaps; i++) {
 263                while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) {
 264                        cnt += 1;
 265                        ret = add_event(evlist, &events, event);
 266                        perf_evlist__mmap_consume(evlist, i);
 267                        if (ret < 0)
 268                                goto out_free_nodes;
 269                }
 270        }
 271
 272        events_array = calloc(cnt, sizeof(struct event_node));
 273        if (!events_array) {
 274                pr_debug("calloc failed\n");
 275                ret = -1;
 276                goto out_free_nodes;
 277        }
 278
 279        pos = 0;
 280        list_for_each_entry(node, &events, list)
 281                events_array[pos++] = *node;
 282
 283        qsort(events_array, cnt, sizeof(struct event_node), compar);
 284
 285        for (pos = 0; pos < cnt; pos++) {
 286                ret = process_event(evlist, events_array[pos].event,
 287                                    switch_tracking);
 288                if (ret < 0)
 289                        goto out_free;
 290        }
 291
 292        ret = 0;
 293out_free:
 294        pr_debug("%u events recorded\n", cnt);
 295        free(events_array);
 296out_free_nodes:
 297        free_event_nodes(&events);
 298        return ret;
 299}
 300
 301/**
 302 * test__switch_tracking - test using sched_switch and tracking events.
 303 *
 304 * This function implements a test that checks that sched_switch events and
 305 * tracking events can be recorded for a workload (current process) using the
 306 * evsel->system_wide and evsel->tracking flags (respectively) with other events
 307 * sometimes enabled or disabled.
 308 */
 309int test__switch_tracking(int subtest __maybe_unused)
 310{
 311        const char *sched_switch = "sched:sched_switch";
 312        struct switch_tracking switch_tracking = { .tids = NULL, };
 313        struct record_opts opts = {
 314                .mmap_pages          = UINT_MAX,
 315                .user_freq           = UINT_MAX,
 316                .user_interval       = ULLONG_MAX,
 317                .freq                = 4000,
 318                .target              = {
 319                        .uses_mmap   = true,
 320                },
 321        };
 322        struct thread_map *threads = NULL;
 323        struct cpu_map *cpus = NULL;
 324        struct perf_evlist *evlist = NULL;
 325        struct perf_evsel *evsel, *cpu_clocks_evsel, *cycles_evsel;
 326        struct perf_evsel *switch_evsel, *tracking_evsel;
 327        const char *comm;
 328        int err = -1;
 329
 330        threads = thread_map__new(-1, getpid(), UINT_MAX);
 331        if (!threads) {
 332                pr_debug("thread_map__new failed!\n");
 333                goto out_err;
 334        }
 335
 336        cpus = cpu_map__new(NULL);
 337        if (!cpus) {
 338                pr_debug("cpu_map__new failed!\n");
 339                goto out_err;
 340        }
 341
 342        evlist = perf_evlist__new();
 343        if (!evlist) {
 344                pr_debug("perf_evlist__new failed!\n");
 345                goto out_err;
 346        }
 347
 348        perf_evlist__set_maps(evlist, cpus, threads);
 349
 350        /* First event */
 351        err = parse_events(evlist, "cpu-clock:u", NULL);
 352        if (err) {
 353                pr_debug("Failed to parse event dummy:u\n");
 354                goto out_err;
 355        }
 356
 357        cpu_clocks_evsel = perf_evlist__last(evlist);
 358
 359        /* Second event */
 360        err = parse_events(evlist, "cycles:u", NULL);
 361        if (err) {
 362                pr_debug("Failed to parse event cycles:u\n");
 363                goto out_err;
 364        }
 365
 366        cycles_evsel = perf_evlist__last(evlist);
 367
 368        /* Third event */
 369        if (!perf_evlist__can_select_event(evlist, sched_switch)) {
 370                pr_debug("No sched_switch\n");
 371                err = 0;
 372                goto out;
 373        }
 374
 375        err = parse_events(evlist, sched_switch, NULL);
 376        if (err) {
 377                pr_debug("Failed to parse event %s\n", sched_switch);
 378                goto out_err;
 379        }
 380
 381        switch_evsel = perf_evlist__last(evlist);
 382
 383        perf_evsel__set_sample_bit(switch_evsel, CPU);
 384        perf_evsel__set_sample_bit(switch_evsel, TIME);
 385
 386        switch_evsel->system_wide = true;
 387        switch_evsel->no_aux_samples = true;
 388        switch_evsel->immediate = true;
 389
 390        /* Test moving an event to the front */
 391        if (cycles_evsel == perf_evlist__first(evlist)) {
 392                pr_debug("cycles event already at front");
 393                goto out_err;
 394        }
 395        perf_evlist__to_front(evlist, cycles_evsel);
 396        if (cycles_evsel != perf_evlist__first(evlist)) {
 397                pr_debug("Failed to move cycles event to front");
 398                goto out_err;
 399        }
 400
 401        perf_evsel__set_sample_bit(cycles_evsel, CPU);
 402        perf_evsel__set_sample_bit(cycles_evsel, TIME);
 403
 404        /* Fourth event */
 405        err = parse_events(evlist, "dummy:u", NULL);
 406        if (err) {
 407                pr_debug("Failed to parse event dummy:u\n");
 408                goto out_err;
 409        }
 410
 411        tracking_evsel = perf_evlist__last(evlist);
 412
 413        perf_evlist__set_tracking_event(evlist, tracking_evsel);
 414
 415        tracking_evsel->attr.freq = 0;
 416        tracking_evsel->attr.sample_period = 1;
 417
 418        perf_evsel__set_sample_bit(tracking_evsel, TIME);
 419
 420        /* Config events */
 421        perf_evlist__config(evlist, &opts, NULL);
 422
 423        /* Check moved event is still at the front */
 424        if (cycles_evsel != perf_evlist__first(evlist)) {
 425                pr_debug("Front event no longer at front");
 426                goto out_err;
 427        }
 428
 429        /* Check tracking event is tracking */
 430        if (!tracking_evsel->attr.mmap || !tracking_evsel->attr.comm) {
 431                pr_debug("Tracking event not tracking\n");
 432                goto out_err;
 433        }
 434
 435        /* Check non-tracking events are not tracking */
 436        evlist__for_each_entry(evlist, evsel) {
 437                if (evsel != tracking_evsel) {
 438                        if (evsel->attr.mmap || evsel->attr.comm) {
 439                                pr_debug("Non-tracking event is tracking\n");
 440                                goto out_err;
 441                        }
 442                }
 443        }
 444
 445        if (perf_evlist__open(evlist) < 0) {
 446                pr_debug("Not supported\n");
 447                err = 0;
 448                goto out;
 449        }
 450
 451        err = perf_evlist__mmap(evlist, UINT_MAX, false);
 452        if (err) {
 453                pr_debug("perf_evlist__mmap failed!\n");
 454                goto out_err;
 455        }
 456
 457        perf_evlist__enable(evlist);
 458
 459        err = perf_evsel__disable(cpu_clocks_evsel);
 460        if (err) {
 461                pr_debug("perf_evlist__disable_event failed!\n");
 462                goto out_err;
 463        }
 464
 465        err = spin_sleep();
 466        if (err) {
 467                pr_debug("spin_sleep failed!\n");
 468                goto out_err;
 469        }
 470
 471        comm = "Test COMM 1";
 472        err = prctl(PR_SET_NAME, (unsigned long)comm, 0, 0, 0);
 473        if (err) {
 474                pr_debug("PR_SET_NAME failed!\n");
 475                goto out_err;
 476        }
 477
 478        err = perf_evsel__disable(cycles_evsel);
 479        if (err) {
 480                pr_debug("perf_evlist__disable_event failed!\n");
 481                goto out_err;
 482        }
 483
 484        comm = "Test COMM 2";
 485        err = prctl(PR_SET_NAME, (unsigned long)comm, 0, 0, 0);
 486        if (err) {
 487                pr_debug("PR_SET_NAME failed!\n");
 488                goto out_err;
 489        }
 490
 491        err = spin_sleep();
 492        if (err) {
 493                pr_debug("spin_sleep failed!\n");
 494                goto out_err;
 495        }
 496
 497        comm = "Test COMM 3";
 498        err = prctl(PR_SET_NAME, (unsigned long)comm, 0, 0, 0);
 499        if (err) {
 500                pr_debug("PR_SET_NAME failed!\n");
 501                goto out_err;
 502        }
 503
 504        err = perf_evsel__enable(cycles_evsel);
 505        if (err) {
 506                pr_debug("perf_evlist__disable_event failed!\n");
 507                goto out_err;
 508        }
 509
 510        comm = "Test COMM 4";
 511        err = prctl(PR_SET_NAME, (unsigned long)comm, 0, 0, 0);
 512        if (err) {
 513                pr_debug("PR_SET_NAME failed!\n");
 514                goto out_err;
 515        }
 516
 517        err = spin_sleep();
 518        if (err) {
 519                pr_debug("spin_sleep failed!\n");
 520                goto out_err;
 521        }
 522
 523        perf_evlist__disable(evlist);
 524
 525        switch_tracking.switch_evsel = switch_evsel;
 526        switch_tracking.cycles_evsel = cycles_evsel;
 527
 528        err = process_events(evlist, &switch_tracking);
 529
 530        zfree(&switch_tracking.tids);
 531
 532        if (err)
 533                goto out_err;
 534
 535        /* Check all 4 comm events were seen i.e. that evsel->tracking works */
 536        if (!switch_tracking.comm_seen[0] || !switch_tracking.comm_seen[1] ||
 537            !switch_tracking.comm_seen[2] || !switch_tracking.comm_seen[3]) {
 538                pr_debug("Missing comm events\n");
 539                goto out_err;
 540        }
 541
 542        /* Check cycles event got enabled */
 543        if (!switch_tracking.cycles_before_comm_1) {
 544                pr_debug("Missing cycles events\n");
 545                goto out_err;
 546        }
 547
 548        /* Check cycles event got disabled */
 549        if (switch_tracking.cycles_between_comm_2_and_comm_3) {
 550                pr_debug("cycles events even though event was disabled\n");
 551                goto out_err;
 552        }
 553
 554        /* Check cycles event got enabled again */
 555        if (!switch_tracking.cycles_after_comm_4) {
 556                pr_debug("Missing cycles events\n");
 557                goto out_err;
 558        }
 559out:
 560        if (evlist) {
 561                perf_evlist__disable(evlist);
 562                perf_evlist__delete(evlist);
 563        } else {
 564                cpu_map__put(cpus);
 565                thread_map__put(threads);
 566        }
 567
 568        return err;
 569
 570out_err:
 571        err = -1;
 572        goto out;
 573}
 574