Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 #include <errno.h>
0003 #include <unistd.h>
0004 #include <sys/syscall.h>
0005 #include <perf/evsel.h>
0006 #include <perf/cpumap.h>
0007 #include <perf/threadmap.h>
0008 #include <linux/list.h>
0009 #include <internal/evsel.h>
0010 #include <linux/zalloc.h>
0011 #include <stdlib.h>
0012 #include <internal/xyarray.h>
0013 #include <internal/cpumap.h>
0014 #include <internal/mmap.h>
0015 #include <internal/threadmap.h>
0016 #include <internal/lib.h>
0017 #include <linux/string.h>
0018 #include <sys/ioctl.h>
0019 #include <sys/mman.h>
0020 #include <asm/bug.h>
0021 
0022 void perf_evsel__init(struct perf_evsel *evsel, struct perf_event_attr *attr,
0023               int idx)
0024 {
0025     INIT_LIST_HEAD(&evsel->node);
0026     evsel->attr = *attr;
0027     evsel->idx  = idx;
0028     evsel->leader = evsel;
0029 }
0030 
0031 struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr)
0032 {
0033     struct perf_evsel *evsel = zalloc(sizeof(*evsel));
0034 
0035     if (evsel != NULL)
0036         perf_evsel__init(evsel, attr, 0);
0037 
0038     return evsel;
0039 }
0040 
0041 void perf_evsel__delete(struct perf_evsel *evsel)
0042 {
0043     free(evsel);
0044 }
0045 
0046 #define FD(_evsel, _cpu_map_idx, _thread)               \
0047     ((int *)xyarray__entry(_evsel->fd, _cpu_map_idx, _thread))
0048 #define MMAP(_evsel, _cpu_map_idx, _thread)             \
0049     (_evsel->mmap ? ((struct perf_mmap *) xyarray__entry(_evsel->mmap, _cpu_map_idx, _thread)) \
0050               : NULL)
0051 
0052 int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads)
0053 {
0054     evsel->fd = xyarray__new(ncpus, nthreads, sizeof(int));
0055 
0056     if (evsel->fd) {
0057         int idx, thread;
0058 
0059         for (idx = 0; idx < ncpus; idx++) {
0060             for (thread = 0; thread < nthreads; thread++) {
0061                 int *fd = FD(evsel, idx, thread);
0062 
0063                 if (fd)
0064                     *fd = -1;
0065             }
0066         }
0067     }
0068 
0069     return evsel->fd != NULL ? 0 : -ENOMEM;
0070 }
0071 
0072 static int perf_evsel__alloc_mmap(struct perf_evsel *evsel, int ncpus, int nthreads)
0073 {
0074     evsel->mmap = xyarray__new(ncpus, nthreads, sizeof(struct perf_mmap));
0075 
0076     return evsel->mmap != NULL ? 0 : -ENOMEM;
0077 }
0078 
0079 static int
0080 sys_perf_event_open(struct perf_event_attr *attr,
0081             pid_t pid, struct perf_cpu cpu, int group_fd,
0082             unsigned long flags)
0083 {
0084     return syscall(__NR_perf_event_open, attr, pid, cpu.cpu, group_fd, flags);
0085 }
0086 
0087 static int get_group_fd(struct perf_evsel *evsel, int cpu_map_idx, int thread, int *group_fd)
0088 {
0089     struct perf_evsel *leader = evsel->leader;
0090     int *fd;
0091 
0092     if (evsel == leader) {
0093         *group_fd = -1;
0094         return 0;
0095     }
0096 
0097     /*
0098      * Leader must be already processed/open,
0099      * if not it's a bug.
0100      */
0101     if (!leader->fd)
0102         return -ENOTCONN;
0103 
0104     fd = FD(leader, cpu_map_idx, thread);
0105     if (fd == NULL || *fd == -1)
0106         return -EBADF;
0107 
0108     *group_fd = *fd;
0109 
0110     return 0;
0111 }
0112 
0113 int perf_evsel__open(struct perf_evsel *evsel, struct perf_cpu_map *cpus,
0114              struct perf_thread_map *threads)
0115 {
0116     struct perf_cpu cpu;
0117     int idx, thread, err = 0;
0118 
0119     if (cpus == NULL) {
0120         static struct perf_cpu_map *empty_cpu_map;
0121 
0122         if (empty_cpu_map == NULL) {
0123             empty_cpu_map = perf_cpu_map__dummy_new();
0124             if (empty_cpu_map == NULL)
0125                 return -ENOMEM;
0126         }
0127 
0128         cpus = empty_cpu_map;
0129     }
0130 
0131     if (threads == NULL) {
0132         static struct perf_thread_map *empty_thread_map;
0133 
0134         if (empty_thread_map == NULL) {
0135             empty_thread_map = perf_thread_map__new_dummy();
0136             if (empty_thread_map == NULL)
0137                 return -ENOMEM;
0138         }
0139 
0140         threads = empty_thread_map;
0141     }
0142 
0143     if (evsel->fd == NULL &&
0144         perf_evsel__alloc_fd(evsel, perf_cpu_map__nr(cpus), threads->nr) < 0)
0145         return -ENOMEM;
0146 
0147     perf_cpu_map__for_each_cpu(cpu, idx, cpus) {
0148         for (thread = 0; thread < threads->nr; thread++) {
0149             int fd, group_fd, *evsel_fd;
0150 
0151             evsel_fd = FD(evsel, idx, thread);
0152             if (evsel_fd == NULL) {
0153                 err = -EINVAL;
0154                 goto out;
0155             }
0156 
0157             err = get_group_fd(evsel, idx, thread, &group_fd);
0158             if (err < 0)
0159                 goto out;
0160 
0161             fd = sys_perf_event_open(&evsel->attr,
0162                          threads->map[thread].pid,
0163                          cpu, group_fd, 0);
0164 
0165             if (fd < 0) {
0166                 err = -errno;
0167                 goto out;
0168             }
0169 
0170             *evsel_fd = fd;
0171         }
0172     }
0173 out:
0174     if (err)
0175         perf_evsel__close(evsel);
0176 
0177     return err;
0178 }
0179 
0180 static void perf_evsel__close_fd_cpu(struct perf_evsel *evsel, int cpu_map_idx)
0181 {
0182     int thread;
0183 
0184     for (thread = 0; thread < xyarray__max_y(evsel->fd); ++thread) {
0185         int *fd = FD(evsel, cpu_map_idx, thread);
0186 
0187         if (fd && *fd >= 0) {
0188             close(*fd);
0189             *fd = -1;
0190         }
0191     }
0192 }
0193 
0194 void perf_evsel__close_fd(struct perf_evsel *evsel)
0195 {
0196     for (int idx = 0; idx < xyarray__max_x(evsel->fd); idx++)
0197         perf_evsel__close_fd_cpu(evsel, idx);
0198 }
0199 
0200 void perf_evsel__free_fd(struct perf_evsel *evsel)
0201 {
0202     xyarray__delete(evsel->fd);
0203     evsel->fd = NULL;
0204 }
0205 
0206 void perf_evsel__close(struct perf_evsel *evsel)
0207 {
0208     if (evsel->fd == NULL)
0209         return;
0210 
0211     perf_evsel__close_fd(evsel);
0212     perf_evsel__free_fd(evsel);
0213 }
0214 
0215 void perf_evsel__close_cpu(struct perf_evsel *evsel, int cpu_map_idx)
0216 {
0217     if (evsel->fd == NULL)
0218         return;
0219 
0220     perf_evsel__close_fd_cpu(evsel, cpu_map_idx);
0221 }
0222 
0223 void perf_evsel__munmap(struct perf_evsel *evsel)
0224 {
0225     int idx, thread;
0226 
0227     if (evsel->fd == NULL || evsel->mmap == NULL)
0228         return;
0229 
0230     for (idx = 0; idx < xyarray__max_x(evsel->fd); idx++) {
0231         for (thread = 0; thread < xyarray__max_y(evsel->fd); thread++) {
0232             int *fd = FD(evsel, idx, thread);
0233 
0234             if (fd == NULL || *fd < 0)
0235                 continue;
0236 
0237             perf_mmap__munmap(MMAP(evsel, idx, thread));
0238         }
0239     }
0240 
0241     xyarray__delete(evsel->mmap);
0242     evsel->mmap = NULL;
0243 }
0244 
0245 int perf_evsel__mmap(struct perf_evsel *evsel, int pages)
0246 {
0247     int ret, idx, thread;
0248     struct perf_mmap_param mp = {
0249         .prot = PROT_READ | PROT_WRITE,
0250         .mask = (pages * page_size) - 1,
0251     };
0252 
0253     if (evsel->fd == NULL || evsel->mmap)
0254         return -EINVAL;
0255 
0256     if (perf_evsel__alloc_mmap(evsel, xyarray__max_x(evsel->fd), xyarray__max_y(evsel->fd)) < 0)
0257         return -ENOMEM;
0258 
0259     for (idx = 0; idx < xyarray__max_x(evsel->fd); idx++) {
0260         for (thread = 0; thread < xyarray__max_y(evsel->fd); thread++) {
0261             int *fd = FD(evsel, idx, thread);
0262             struct perf_mmap *map;
0263             struct perf_cpu cpu = perf_cpu_map__cpu(evsel->cpus, idx);
0264 
0265             if (fd == NULL || *fd < 0)
0266                 continue;
0267 
0268             map = MMAP(evsel, idx, thread);
0269             perf_mmap__init(map, NULL, false, NULL);
0270 
0271             ret = perf_mmap__mmap(map, &mp, *fd, cpu);
0272             if (ret) {
0273                 perf_evsel__munmap(evsel);
0274                 return ret;
0275             }
0276         }
0277     }
0278 
0279     return 0;
0280 }
0281 
0282 void *perf_evsel__mmap_base(struct perf_evsel *evsel, int cpu_map_idx, int thread)
0283 {
0284     int *fd = FD(evsel, cpu_map_idx, thread);
0285 
0286     if (fd == NULL || *fd < 0 || MMAP(evsel, cpu_map_idx, thread) == NULL)
0287         return NULL;
0288 
0289     return MMAP(evsel, cpu_map_idx, thread)->base;
0290 }
0291 
0292 int perf_evsel__read_size(struct perf_evsel *evsel)
0293 {
0294     u64 read_format = evsel->attr.read_format;
0295     int entry = sizeof(u64); /* value */
0296     int size = 0;
0297     int nr = 1;
0298 
0299     if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED)
0300         size += sizeof(u64);
0301 
0302     if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING)
0303         size += sizeof(u64);
0304 
0305     if (read_format & PERF_FORMAT_ID)
0306         entry += sizeof(u64);
0307 
0308     if (read_format & PERF_FORMAT_LOST)
0309         entry += sizeof(u64);
0310 
0311     if (read_format & PERF_FORMAT_GROUP) {
0312         nr = evsel->nr_members;
0313         size += sizeof(u64);
0314     }
0315 
0316     size += entry * nr;
0317     return size;
0318 }
0319 
0320 /* This only reads values for the leader */
0321 static int perf_evsel__read_group(struct perf_evsel *evsel, int cpu_map_idx,
0322                   int thread, struct perf_counts_values *count)
0323 {
0324     size_t size = perf_evsel__read_size(evsel);
0325     int *fd = FD(evsel, cpu_map_idx, thread);
0326     u64 read_format = evsel->attr.read_format;
0327     u64 *data;
0328     int idx = 1;
0329 
0330     if (fd == NULL || *fd < 0)
0331         return -EINVAL;
0332 
0333     data = calloc(1, size);
0334     if (data == NULL)
0335         return -ENOMEM;
0336 
0337     if (readn(*fd, data, size) <= 0) {
0338         free(data);
0339         return -errno;
0340     }
0341 
0342     /*
0343      * This reads only the leader event intentionally since we don't have
0344      * perf counts values for sibling events.
0345      */
0346     if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED)
0347         count->ena = data[idx++];
0348     if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING)
0349         count->run = data[idx++];
0350 
0351     /* value is always available */
0352     count->val = data[idx++];
0353     if (read_format & PERF_FORMAT_ID)
0354         count->id = data[idx++];
0355     if (read_format & PERF_FORMAT_LOST)
0356         count->lost = data[idx++];
0357 
0358     free(data);
0359     return 0;
0360 }
0361 
0362 /*
0363  * The perf read format is very flexible.  It needs to set the proper
0364  * values according to the read format.
0365  */
0366 static void perf_evsel__adjust_values(struct perf_evsel *evsel, u64 *buf,
0367                       struct perf_counts_values *count)
0368 {
0369     u64 read_format = evsel->attr.read_format;
0370     int n = 0;
0371 
0372     count->val = buf[n++];
0373 
0374     if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED)
0375         count->ena = buf[n++];
0376 
0377     if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING)
0378         count->run = buf[n++];
0379 
0380     if (read_format & PERF_FORMAT_ID)
0381         count->id = buf[n++];
0382 
0383     if (read_format & PERF_FORMAT_LOST)
0384         count->lost = buf[n++];
0385 }
0386 
0387 int perf_evsel__read(struct perf_evsel *evsel, int cpu_map_idx, int thread,
0388              struct perf_counts_values *count)
0389 {
0390     size_t size = perf_evsel__read_size(evsel);
0391     int *fd = FD(evsel, cpu_map_idx, thread);
0392     u64 read_format = evsel->attr.read_format;
0393     struct perf_counts_values buf;
0394 
0395     memset(count, 0, sizeof(*count));
0396 
0397     if (fd == NULL || *fd < 0)
0398         return -EINVAL;
0399 
0400     if (read_format & PERF_FORMAT_GROUP)
0401         return perf_evsel__read_group(evsel, cpu_map_idx, thread, count);
0402 
0403     if (MMAP(evsel, cpu_map_idx, thread) &&
0404         !(read_format & (PERF_FORMAT_ID | PERF_FORMAT_LOST)) &&
0405         !perf_mmap__read_self(MMAP(evsel, cpu_map_idx, thread), count))
0406         return 0;
0407 
0408     if (readn(*fd, buf.values, size) <= 0)
0409         return -errno;
0410 
0411     perf_evsel__adjust_values(evsel, buf.values, count);
0412     return 0;
0413 }
0414 
0415 static int perf_evsel__ioctl(struct perf_evsel *evsel, int ioc, void *arg,
0416                  int cpu_map_idx, int thread)
0417 {
0418     int *fd = FD(evsel, cpu_map_idx, thread);
0419 
0420     if (fd == NULL || *fd < 0)
0421         return -1;
0422 
0423     return ioctl(*fd, ioc, arg);
0424 }
0425 
0426 static int perf_evsel__run_ioctl(struct perf_evsel *evsel,
0427                  int ioc,  void *arg,
0428                  int cpu_map_idx)
0429 {
0430     int thread;
0431 
0432     for (thread = 0; thread < xyarray__max_y(evsel->fd); thread++) {
0433         int err = perf_evsel__ioctl(evsel, ioc, arg, cpu_map_idx, thread);
0434 
0435         if (err)
0436             return err;
0437     }
0438 
0439     return 0;
0440 }
0441 
0442 int perf_evsel__enable_cpu(struct perf_evsel *evsel, int cpu_map_idx)
0443 {
0444     return perf_evsel__run_ioctl(evsel, PERF_EVENT_IOC_ENABLE, NULL, cpu_map_idx);
0445 }
0446 
0447 int perf_evsel__enable_thread(struct perf_evsel *evsel, int thread)
0448 {
0449     struct perf_cpu cpu __maybe_unused;
0450     int idx;
0451     int err;
0452 
0453     perf_cpu_map__for_each_cpu(cpu, idx, evsel->cpus) {
0454         err = perf_evsel__ioctl(evsel, PERF_EVENT_IOC_ENABLE, NULL, idx, thread);
0455         if (err)
0456             return err;
0457     }
0458 
0459     return 0;
0460 }
0461 
0462 int perf_evsel__enable(struct perf_evsel *evsel)
0463 {
0464     int i;
0465     int err = 0;
0466 
0467     for (i = 0; i < xyarray__max_x(evsel->fd) && !err; i++)
0468         err = perf_evsel__run_ioctl(evsel, PERF_EVENT_IOC_ENABLE, NULL, i);
0469     return err;
0470 }
0471 
0472 int perf_evsel__disable_cpu(struct perf_evsel *evsel, int cpu_map_idx)
0473 {
0474     return perf_evsel__run_ioctl(evsel, PERF_EVENT_IOC_DISABLE, NULL, cpu_map_idx);
0475 }
0476 
0477 int perf_evsel__disable(struct perf_evsel *evsel)
0478 {
0479     int i;
0480     int err = 0;
0481 
0482     for (i = 0; i < xyarray__max_x(evsel->fd) && !err; i++)
0483         err = perf_evsel__run_ioctl(evsel, PERF_EVENT_IOC_DISABLE, NULL, i);
0484     return err;
0485 }
0486 
0487 int perf_evsel__apply_filter(struct perf_evsel *evsel, const char *filter)
0488 {
0489     int err = 0, i;
0490 
0491     for (i = 0; i < perf_cpu_map__nr(evsel->cpus) && !err; i++)
0492         err = perf_evsel__run_ioctl(evsel,
0493                      PERF_EVENT_IOC_SET_FILTER,
0494                      (void *)filter, i);
0495     return err;
0496 }
0497 
0498 struct perf_cpu_map *perf_evsel__cpus(struct perf_evsel *evsel)
0499 {
0500     return evsel->cpus;
0501 }
0502 
0503 struct perf_thread_map *perf_evsel__threads(struct perf_evsel *evsel)
0504 {
0505     return evsel->threads;
0506 }
0507 
0508 struct perf_event_attr *perf_evsel__attr(struct perf_evsel *evsel)
0509 {
0510     return &evsel->attr;
0511 }
0512 
0513 int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads)
0514 {
0515     if (ncpus == 0 || nthreads == 0)
0516         return 0;
0517 
0518     if (evsel->system_wide)
0519         nthreads = 1;
0520 
0521     evsel->sample_id = xyarray__new(ncpus, nthreads, sizeof(struct perf_sample_id));
0522     if (evsel->sample_id == NULL)
0523         return -ENOMEM;
0524 
0525     evsel->id = zalloc(ncpus * nthreads * sizeof(u64));
0526     if (evsel->id == NULL) {
0527         xyarray__delete(evsel->sample_id);
0528         evsel->sample_id = NULL;
0529         return -ENOMEM;
0530     }
0531 
0532     return 0;
0533 }
0534 
0535 void perf_evsel__free_id(struct perf_evsel *evsel)
0536 {
0537     xyarray__delete(evsel->sample_id);
0538     evsel->sample_id = NULL;
0539     zfree(&evsel->id);
0540     evsel->ids = 0;
0541 }
0542 
0543 void perf_counts_values__scale(struct perf_counts_values *count,
0544                    bool scale, __s8 *pscaled)
0545 {
0546     s8 scaled = 0;
0547 
0548     if (scale) {
0549         if (count->run == 0) {
0550             scaled = -1;
0551             count->val = 0;
0552         } else if (count->run < count->ena) {
0553             scaled = 1;
0554             count->val = (u64)((double)count->val * count->ena / count->run);
0555         }
0556     }
0557 
0558     if (pscaled)
0559         *pscaled = scaled;
0560 }