Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 #include <errno.h>
0003 #include <inttypes.h>
0004 #include <linux/string.h>
0005 /* For the CLR_() macros */
0006 #include <pthread.h>
0007 
0008 #include <sched.h>
0009 #include <perf/mmap.h>
0010 #include "evlist.h"
0011 #include "evsel.h"
0012 #include "debug.h"
0013 #include "record.h"
0014 #include "tests.h"
0015 #include "util/mmap.h"
0016 
0017 static int sched__get_first_possible_cpu(pid_t pid, cpu_set_t *maskp)
0018 {
0019     int i, cpu = -1, nrcpus = 1024;
0020 realloc:
0021     CPU_ZERO(maskp);
0022 
0023     if (sched_getaffinity(pid, sizeof(*maskp), maskp) == -1) {
0024         if (errno == EINVAL && nrcpus < (1024 << 8)) {
0025             nrcpus = nrcpus << 2;
0026             goto realloc;
0027         }
0028         perror("sched_getaffinity");
0029             return -1;
0030     }
0031 
0032     for (i = 0; i < nrcpus; i++) {
0033         if (CPU_ISSET(i, maskp)) {
0034             if (cpu == -1)
0035                 cpu = i;
0036             else
0037                 CPU_CLR(i, maskp);
0038         }
0039     }
0040 
0041     return cpu;
0042 }
0043 
0044 static int test__PERF_RECORD(struct test_suite *test __maybe_unused, int subtest __maybe_unused)
0045 {
0046     struct record_opts opts = {
0047         .target = {
0048             .uid = UINT_MAX,
0049             .uses_mmap = true,
0050         },
0051         .no_buffering = true,
0052         .mmap_pages   = 256,
0053     };
0054     cpu_set_t cpu_mask;
0055     size_t cpu_mask_size = sizeof(cpu_mask);
0056     struct evlist *evlist = evlist__new_dummy();
0057     struct evsel *evsel;
0058     struct perf_sample sample;
0059     const char *cmd = "sleep";
0060     const char *argv[] = { cmd, "1", NULL, };
0061     char *bname, *mmap_filename;
0062     u64 prev_time = 0;
0063     bool found_cmd_mmap = false,
0064          found_coreutils_mmap = false,
0065          found_libc_mmap = false,
0066          found_vdso_mmap = false,
0067          found_ld_mmap = false;
0068     int err = -1, errs = 0, i, wakeups = 0;
0069     u32 cpu;
0070     int total_events = 0, nr_events[PERF_RECORD_MAX] = { 0, };
0071     char sbuf[STRERR_BUFSIZE];
0072 
0073     if (evlist == NULL) /* Fallback for kernels lacking PERF_COUNT_SW_DUMMY */
0074         evlist = evlist__new_default();
0075 
0076     if (evlist == NULL) {
0077         pr_debug("Not enough memory to create evlist\n");
0078         goto out;
0079     }
0080 
0081     /*
0082      * Create maps of threads and cpus to monitor. In this case
0083      * we start with all threads and cpus (-1, -1) but then in
0084      * evlist__prepare_workload we'll fill in the only thread
0085      * we're monitoring, the one forked there.
0086      */
0087     err = evlist__create_maps(evlist, &opts.target);
0088     if (err < 0) {
0089         pr_debug("Not enough memory to create thread/cpu maps\n");
0090         goto out_delete_evlist;
0091     }
0092 
0093     /*
0094      * Prepare the workload in argv[] to run, it'll fork it, and then wait
0095      * for evlist__start_workload() to exec it. This is done this way
0096      * so that we have time to open the evlist (calling sys_perf_event_open
0097      * on all the fds) and then mmap them.
0098      */
0099     err = evlist__prepare_workload(evlist, &opts.target, argv, false, NULL);
0100     if (err < 0) {
0101         pr_debug("Couldn't run the workload!\n");
0102         goto out_delete_evlist;
0103     }
0104 
0105     /*
0106      * Config the evsels, setting attr->comm on the first one, etc.
0107      */
0108     evsel = evlist__first(evlist);
0109     evsel__set_sample_bit(evsel, CPU);
0110     evsel__set_sample_bit(evsel, TID);
0111     evsel__set_sample_bit(evsel, TIME);
0112     evlist__config(evlist, &opts, NULL);
0113 
0114     err = sched__get_first_possible_cpu(evlist->workload.pid, &cpu_mask);
0115     if (err < 0) {
0116         pr_debug("sched__get_first_possible_cpu: %s\n",
0117              str_error_r(errno, sbuf, sizeof(sbuf)));
0118         goto out_delete_evlist;
0119     }
0120 
0121     cpu = err;
0122 
0123     /*
0124      * So that we can check perf_sample.cpu on all the samples.
0125      */
0126     if (sched_setaffinity(evlist->workload.pid, cpu_mask_size, &cpu_mask) < 0) {
0127         pr_debug("sched_setaffinity: %s\n",
0128              str_error_r(errno, sbuf, sizeof(sbuf)));
0129         goto out_delete_evlist;
0130     }
0131 
0132     /*
0133      * Call sys_perf_event_open on all the fds on all the evsels,
0134      * grouping them if asked to.
0135      */
0136     err = evlist__open(evlist);
0137     if (err < 0) {
0138         pr_debug("perf_evlist__open: %s\n",
0139              str_error_r(errno, sbuf, sizeof(sbuf)));
0140         goto out_delete_evlist;
0141     }
0142 
0143     /*
0144      * mmap the first fd on a given CPU and ask for events for the other
0145      * fds in the same CPU to be injected in the same mmap ring buffer
0146      * (using ioctl(PERF_EVENT_IOC_SET_OUTPUT)).
0147      */
0148     err = evlist__mmap(evlist, opts.mmap_pages);
0149     if (err < 0) {
0150         pr_debug("evlist__mmap: %s\n",
0151              str_error_r(errno, sbuf, sizeof(sbuf)));
0152         goto out_delete_evlist;
0153     }
0154 
0155     /*
0156      * Now that all is properly set up, enable the events, they will
0157      * count just on workload.pid, which will start...
0158      */
0159     evlist__enable(evlist);
0160 
0161     /*
0162      * Now!
0163      */
0164     evlist__start_workload(evlist);
0165 
0166     while (1) {
0167         int before = total_events;
0168 
0169         for (i = 0; i < evlist->core.nr_mmaps; i++) {
0170             union perf_event *event;
0171             struct mmap *md;
0172 
0173             md = &evlist->mmap[i];
0174             if (perf_mmap__read_init(&md->core) < 0)
0175                 continue;
0176 
0177             while ((event = perf_mmap__read_event(&md->core)) != NULL) {
0178                 const u32 type = event->header.type;
0179                 const char *name = perf_event__name(type);
0180 
0181                 ++total_events;
0182                 if (type < PERF_RECORD_MAX)
0183                     nr_events[type]++;
0184 
0185                 err = evlist__parse_sample(evlist, event, &sample);
0186                 if (err < 0) {
0187                     if (verbose > 0)
0188                         perf_event__fprintf(event, NULL, stderr);
0189                     pr_debug("Couldn't parse sample\n");
0190                     goto out_delete_evlist;
0191                 }
0192 
0193                 if (verbose > 0) {
0194                     pr_info("%" PRIu64" %d ", sample.time, sample.cpu);
0195                     perf_event__fprintf(event, NULL, stderr);
0196                 }
0197 
0198                 if (prev_time > sample.time) {
0199                     pr_debug("%s going backwards in time, prev=%" PRIu64 ", curr=%" PRIu64 "\n",
0200                          name, prev_time, sample.time);
0201                     ++errs;
0202                 }
0203 
0204                 prev_time = sample.time;
0205 
0206                 if (sample.cpu != cpu) {
0207                     pr_debug("%s with unexpected cpu, expected %d, got %d\n",
0208                          name, cpu, sample.cpu);
0209                     ++errs;
0210                 }
0211 
0212                 if ((pid_t)sample.pid != evlist->workload.pid) {
0213                     pr_debug("%s with unexpected pid, expected %d, got %d\n",
0214                          name, evlist->workload.pid, sample.pid);
0215                     ++errs;
0216                 }
0217 
0218                 if ((pid_t)sample.tid != evlist->workload.pid) {
0219                     pr_debug("%s with unexpected tid, expected %d, got %d\n",
0220                          name, evlist->workload.pid, sample.tid);
0221                     ++errs;
0222                 }
0223 
0224                 if ((type == PERF_RECORD_COMM ||
0225                      type == PERF_RECORD_MMAP ||
0226                      type == PERF_RECORD_MMAP2 ||
0227                      type == PERF_RECORD_FORK ||
0228                      type == PERF_RECORD_EXIT) &&
0229                      (pid_t)event->comm.pid != evlist->workload.pid) {
0230                     pr_debug("%s with unexpected pid/tid\n", name);
0231                     ++errs;
0232                 }
0233 
0234                 if ((type == PERF_RECORD_COMM ||
0235                      type == PERF_RECORD_MMAP ||
0236                      type == PERF_RECORD_MMAP2) &&
0237                      event->comm.pid != event->comm.tid) {
0238                     pr_debug("%s with different pid/tid!\n", name);
0239                     ++errs;
0240                 }
0241 
0242                 switch (type) {
0243                 case PERF_RECORD_COMM:
0244                     if (strcmp(event->comm.comm, cmd)) {
0245                         pr_debug("%s with unexpected comm!\n", name);
0246                         ++errs;
0247                     }
0248                     break;
0249                 case PERF_RECORD_EXIT:
0250                     goto found_exit;
0251                 case PERF_RECORD_MMAP:
0252                     mmap_filename = event->mmap.filename;
0253                     goto check_bname;
0254                 case PERF_RECORD_MMAP2:
0255                     mmap_filename = event->mmap2.filename;
0256                 check_bname:
0257                     bname = strrchr(mmap_filename, '/');
0258                     if (bname != NULL) {
0259                         if (!found_cmd_mmap)
0260                             found_cmd_mmap = !strcmp(bname + 1, cmd);
0261                         if (!found_coreutils_mmap)
0262                             found_coreutils_mmap = !strcmp(bname + 1, "coreutils");
0263                         if (!found_libc_mmap)
0264                             found_libc_mmap = !strncmp(bname + 1, "libc", 4);
0265                         if (!found_ld_mmap)
0266                             found_ld_mmap = !strncmp(bname + 1, "ld", 2);
0267                     } else if (!found_vdso_mmap)
0268                         found_vdso_mmap = !strcmp(mmap_filename, "[vdso]");
0269                     break;
0270 
0271                 case PERF_RECORD_SAMPLE:
0272                     /* Just ignore samples for now */
0273                     break;
0274                 default:
0275                     pr_debug("Unexpected perf_event->header.type %d!\n",
0276                          type);
0277                     ++errs;
0278                 }
0279 
0280                 perf_mmap__consume(&md->core);
0281             }
0282             perf_mmap__read_done(&md->core);
0283         }
0284 
0285         /*
0286          * We don't use poll here because at least at 3.1 times the
0287          * PERF_RECORD_{!SAMPLE} events don't honour
0288          * perf_event_attr.wakeup_events, just PERF_EVENT_SAMPLE does.
0289          */
0290         if (total_events == before && false)
0291             evlist__poll(evlist, -1);
0292 
0293         sleep(1);
0294         if (++wakeups > 5) {
0295             pr_debug("No PERF_RECORD_EXIT event!\n");
0296             break;
0297         }
0298     }
0299 
0300 found_exit:
0301     if (nr_events[PERF_RECORD_COMM] > 1 + !!found_coreutils_mmap) {
0302         pr_debug("Excessive number of PERF_RECORD_COMM events!\n");
0303         ++errs;
0304     }
0305 
0306     if (nr_events[PERF_RECORD_COMM] == 0) {
0307         pr_debug("Missing PERF_RECORD_COMM for %s!\n", cmd);
0308         ++errs;
0309     }
0310 
0311     if (!found_cmd_mmap && !found_coreutils_mmap) {
0312         pr_debug("PERF_RECORD_MMAP for %s missing!\n", cmd);
0313         ++errs;
0314     }
0315 
0316     if (!found_libc_mmap) {
0317         pr_debug("PERF_RECORD_MMAP for %s missing!\n", "libc");
0318         ++errs;
0319     }
0320 
0321     if (!found_ld_mmap) {
0322         pr_debug("PERF_RECORD_MMAP for %s missing!\n", "ld");
0323         ++errs;
0324     }
0325 
0326     if (!found_vdso_mmap) {
0327         pr_debug("PERF_RECORD_MMAP for %s missing!\n", "[vdso]");
0328         ++errs;
0329     }
0330 out_delete_evlist:
0331     evlist__delete(evlist);
0332 out:
0333     if (err == -EACCES)
0334         return TEST_SKIP;
0335     if (err < 0 || errs != 0)
0336         return TEST_FAIL;
0337     return TEST_OK;
0338 }
0339 
0340 static struct test_case tests__PERF_RECORD[] = {
0341     TEST_CASE_REASON("PERF_RECORD_* events & perf_sample fields",
0342              PERF_RECORD,
0343              "permissions"),
0344     {   .name = NULL, }
0345 };
0346 
0347 struct test_suite suite__PERF_RECORD = {
0348     .desc = "PERF_RECORD_* events & perf_sample fields",
0349     .test_cases = tests__PERF_RECORD,
0350 };