linux/tools/perf/tests/perf-record.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2#include <errno.h>
   3#include <inttypes.h>
   4/* For the CLR_() macros */
   5#include <pthread.h>
   6
   7#include <sched.h>
   8#include "evlist.h"
   9#include "evsel.h"
  10#include "perf.h"
  11#include "debug.h"
  12#include "tests.h"
  13
  14static int sched__get_first_possible_cpu(pid_t pid, cpu_set_t *maskp)
  15{
  16        int i, cpu = -1, nrcpus = 1024;
  17realloc:
  18        CPU_ZERO(maskp);
  19
  20        if (sched_getaffinity(pid, sizeof(*maskp), maskp) == -1) {
  21                if (errno == EINVAL && nrcpus < (1024 << 8)) {
  22                        nrcpus = nrcpus << 2;
  23                        goto realloc;
  24                }
  25                perror("sched_getaffinity");
  26                        return -1;
  27        }
  28
  29        for (i = 0; i < nrcpus; i++) {
  30                if (CPU_ISSET(i, maskp)) {
  31                        if (cpu == -1)
  32                                cpu = i;
  33                        else
  34                                CPU_CLR(i, maskp);
  35                }
  36        }
  37
  38        return cpu;
  39}
  40
  41int test__PERF_RECORD(struct test *test __maybe_unused, int subtest __maybe_unused)
  42{
  43        struct record_opts opts = {
  44                .target = {
  45                        .uid = UINT_MAX,
  46                        .uses_mmap = true,
  47                },
  48                .no_buffering = true,
  49                .mmap_pages   = 256,
  50        };
  51        cpu_set_t cpu_mask;
  52        size_t cpu_mask_size = sizeof(cpu_mask);
  53        struct perf_evlist *evlist = perf_evlist__new_dummy();
  54        struct perf_evsel *evsel;
  55        struct perf_sample sample;
  56        const char *cmd = "sleep";
  57        const char *argv[] = { cmd, "1", NULL, };
  58        char *bname, *mmap_filename;
  59        u64 prev_time = 0;
  60        bool found_cmd_mmap = false,
  61             found_libc_mmap = false,
  62             found_vdso_mmap = false,
  63             found_ld_mmap = false;
  64        int err = -1, errs = 0, i, wakeups = 0;
  65        u32 cpu;
  66        int total_events = 0, nr_events[PERF_RECORD_MAX] = { 0, };
  67        char sbuf[STRERR_BUFSIZE];
  68
  69        if (evlist == NULL) /* Fallback for kernels lacking PERF_COUNT_SW_DUMMY */
  70                evlist = perf_evlist__new_default();
  71
  72        if (evlist == NULL) {
  73                pr_debug("Not enough memory to create evlist\n");
  74                goto out;
  75        }
  76
  77        /*
  78         * Create maps of threads and cpus to monitor. In this case
  79         * we start with all threads and cpus (-1, -1) but then in
  80         * perf_evlist__prepare_workload we'll fill in the only thread
  81         * we're monitoring, the one forked there.
  82         */
  83        err = perf_evlist__create_maps(evlist, &opts.target);
  84        if (err < 0) {
  85                pr_debug("Not enough memory to create thread/cpu maps\n");
  86                goto out_delete_evlist;
  87        }
  88
  89        /*
  90         * Prepare the workload in argv[] to run, it'll fork it, and then wait
  91         * for perf_evlist__start_workload() to exec it. This is done this way
  92         * so that we have time to open the evlist (calling sys_perf_event_open
  93         * on all the fds) and then mmap them.
  94         */
  95        err = perf_evlist__prepare_workload(evlist, &opts.target, argv, false, NULL);
  96        if (err < 0) {
  97                pr_debug("Couldn't run the workload!\n");
  98                goto out_delete_evlist;
  99        }
 100
 101        /*
 102         * Config the evsels, setting attr->comm on the first one, etc.
 103         */
 104        evsel = perf_evlist__first(evlist);
 105        perf_evsel__set_sample_bit(evsel, CPU);
 106        perf_evsel__set_sample_bit(evsel, TID);
 107        perf_evsel__set_sample_bit(evsel, TIME);
 108        perf_evlist__config(evlist, &opts, NULL);
 109
 110        err = sched__get_first_possible_cpu(evlist->workload.pid, &cpu_mask);
 111        if (err < 0) {
 112                pr_debug("sched__get_first_possible_cpu: %s\n",
 113                         str_error_r(errno, sbuf, sizeof(sbuf)));
 114                goto out_delete_evlist;
 115        }
 116
 117        cpu = err;
 118
 119        /*
 120         * So that we can check perf_sample.cpu on all the samples.
 121         */
 122        if (sched_setaffinity(evlist->workload.pid, cpu_mask_size, &cpu_mask) < 0) {
 123                pr_debug("sched_setaffinity: %s\n",
 124                         str_error_r(errno, sbuf, sizeof(sbuf)));
 125                goto out_delete_evlist;
 126        }
 127
 128        /*
 129         * Call sys_perf_event_open on all the fds on all the evsels,
 130         * grouping them if asked to.
 131         */
 132        err = perf_evlist__open(evlist);
 133        if (err < 0) {
 134                pr_debug("perf_evlist__open: %s\n",
 135                         str_error_r(errno, sbuf, sizeof(sbuf)));
 136                goto out_delete_evlist;
 137        }
 138
 139        /*
 140         * mmap the first fd on a given CPU and ask for events for the other
 141         * fds in the same CPU to be injected in the same mmap ring buffer
 142         * (using ioctl(PERF_EVENT_IOC_SET_OUTPUT)).
 143         */
 144        err = perf_evlist__mmap(evlist, opts.mmap_pages);
 145        if (err < 0) {
 146                pr_debug("perf_evlist__mmap: %s\n",
 147                         str_error_r(errno, sbuf, sizeof(sbuf)));
 148                goto out_delete_evlist;
 149        }
 150
 151        /*
 152         * Now that all is properly set up, enable the events, they will
 153         * count just on workload.pid, which will start...
 154         */
 155        perf_evlist__enable(evlist);
 156
 157        /*
 158         * Now!
 159         */
 160        perf_evlist__start_workload(evlist);
 161
 162        while (1) {
 163                int before = total_events;
 164
 165                for (i = 0; i < evlist->nr_mmaps; i++) {
 166                        union perf_event *event;
 167                        struct perf_mmap *md;
 168
 169                        md = &evlist->mmap[i];
 170                        if (perf_mmap__read_init(md) < 0)
 171                                continue;
 172
 173                        while ((event = perf_mmap__read_event(md)) != NULL) {
 174                                const u32 type = event->header.type;
 175                                const char *name = perf_event__name(type);
 176
 177                                ++total_events;
 178                                if (type < PERF_RECORD_MAX)
 179                                        nr_events[type]++;
 180
 181                                err = perf_evlist__parse_sample(evlist, event, &sample);
 182                                if (err < 0) {
 183                                        if (verbose > 0)
 184                                                perf_event__fprintf(event, stderr);
 185                                        pr_debug("Couldn't parse sample\n");
 186                                        goto out_delete_evlist;
 187                                }
 188
 189                                if (verbose > 0) {
 190                                        pr_info("%" PRIu64" %d ", sample.time, sample.cpu);
 191                                        perf_event__fprintf(event, stderr);
 192                                }
 193
 194                                if (prev_time > sample.time) {
 195                                        pr_debug("%s going backwards in time, prev=%" PRIu64 ", curr=%" PRIu64 "\n",
 196                                                 name, prev_time, sample.time);
 197                                        ++errs;
 198                                }
 199
 200                                prev_time = sample.time;
 201
 202                                if (sample.cpu != cpu) {
 203                                        pr_debug("%s with unexpected cpu, expected %d, got %d\n",
 204                                                 name, cpu, sample.cpu);
 205                                        ++errs;
 206                                }
 207
 208                                if ((pid_t)sample.pid != evlist->workload.pid) {
 209                                        pr_debug("%s with unexpected pid, expected %d, got %d\n",
 210                                                 name, evlist->workload.pid, sample.pid);
 211                                        ++errs;
 212                                }
 213
 214                                if ((pid_t)sample.tid != evlist->workload.pid) {
 215                                        pr_debug("%s with unexpected tid, expected %d, got %d\n",
 216                                                 name, evlist->workload.pid, sample.tid);
 217                                        ++errs;
 218                                }
 219
 220                                if ((type == PERF_RECORD_COMM ||
 221                                     type == PERF_RECORD_MMAP ||
 222                                     type == PERF_RECORD_MMAP2 ||
 223                                     type == PERF_RECORD_FORK ||
 224                                     type == PERF_RECORD_EXIT) &&
 225                                     (pid_t)event->comm.pid != evlist->workload.pid) {
 226                                        pr_debug("%s with unexpected pid/tid\n", name);
 227                                        ++errs;
 228                                }
 229
 230                                if ((type == PERF_RECORD_COMM ||
 231                                     type == PERF_RECORD_MMAP ||
 232                                     type == PERF_RECORD_MMAP2) &&
 233                                     event->comm.pid != event->comm.tid) {
 234                                        pr_debug("%s with different pid/tid!\n", name);
 235                                        ++errs;
 236                                }
 237
 238                                switch (type) {
 239                                case PERF_RECORD_COMM:
 240                                        if (strcmp(event->comm.comm, cmd)) {
 241                                                pr_debug("%s with unexpected comm!\n", name);
 242                                                ++errs;
 243                                        }
 244                                        break;
 245                                case PERF_RECORD_EXIT:
 246                                        goto found_exit;
 247                                case PERF_RECORD_MMAP:
 248                                        mmap_filename = event->mmap.filename;
 249                                        goto check_bname;
 250                                case PERF_RECORD_MMAP2:
 251                                        mmap_filename = event->mmap2.filename;
 252                                check_bname:
 253                                        bname = strrchr(mmap_filename, '/');
 254                                        if (bname != NULL) {
 255                                                if (!found_cmd_mmap)
 256                                                        found_cmd_mmap = !strcmp(bname + 1, cmd);
 257                                                if (!found_libc_mmap)
 258                                                        found_libc_mmap = !strncmp(bname + 1, "libc", 4);
 259                                                if (!found_ld_mmap)
 260                                                        found_ld_mmap = !strncmp(bname + 1, "ld", 2);
 261                                        } else if (!found_vdso_mmap)
 262                                                found_vdso_mmap = !strcmp(mmap_filename, "[vdso]");
 263                                        break;
 264
 265                                case PERF_RECORD_SAMPLE:
 266                                        /* Just ignore samples for now */
 267                                        break;
 268                                default:
 269                                        pr_debug("Unexpected perf_event->header.type %d!\n",
 270                                                 type);
 271                                        ++errs;
 272                                }
 273
 274                                perf_mmap__consume(md);
 275                        }
 276                        perf_mmap__read_done(md);
 277                }
 278
 279                /*
 280                 * We don't use poll here because at least at 3.1 times the
 281                 * PERF_RECORD_{!SAMPLE} events don't honour
 282                 * perf_event_attr.wakeup_events, just PERF_EVENT_SAMPLE does.
 283                 */
 284                if (total_events == before && false)
 285                        perf_evlist__poll(evlist, -1);
 286
 287                sleep(1);
 288                if (++wakeups > 5) {
 289                        pr_debug("No PERF_RECORD_EXIT event!\n");
 290                        break;
 291                }
 292        }
 293
 294found_exit:
 295        if (nr_events[PERF_RECORD_COMM] > 1) {
 296                pr_debug("Excessive number of PERF_RECORD_COMM events!\n");
 297                ++errs;
 298        }
 299
 300        if (nr_events[PERF_RECORD_COMM] == 0) {
 301                pr_debug("Missing PERF_RECORD_COMM for %s!\n", cmd);
 302                ++errs;
 303        }
 304
 305        if (!found_cmd_mmap) {
 306                pr_debug("PERF_RECORD_MMAP for %s missing!\n", cmd);
 307                ++errs;
 308        }
 309
 310        if (!found_libc_mmap) {
 311                pr_debug("PERF_RECORD_MMAP for %s missing!\n", "libc");
 312                ++errs;
 313        }
 314
 315        if (!found_ld_mmap) {
 316                pr_debug("PERF_RECORD_MMAP for %s missing!\n", "ld");
 317                ++errs;
 318        }
 319
 320        if (!found_vdso_mmap) {
 321                pr_debug("PERF_RECORD_MMAP for %s missing!\n", "[vdso]");
 322                ++errs;
 323        }
 324out_delete_evlist:
 325        perf_evlist__delete(evlist);
 326out:
 327        return (err < 0 || errs > 0) ? -1 : 0;
 328}
 329