0001
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
0099
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);
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
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
0344
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
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
0364
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 }