1 // SPDX-License-Identifier: GPL-2.0 2 #include <errno.h> 3 #include <unistd.h> 4 #include <sys/syscall.h> 5 #include <perf/evsel.h> 6 #include <perf/cpumap.h> 7 #include <perf/threadmap.h> 8 #include <linux/list.h> 9 #include <internal/evsel.h> 10 #include <linux/zalloc.h> 11 #include <stdlib.h> 12 #include <internal/xyarray.h> 13 #include <internal/cpumap.h> 14 #include <internal/mmap.h> 15 #include <internal/threadmap.h> 16 #include <internal/lib.h> 17 #include <linux/string.h> 18 #include <sys/ioctl.h> 19 #include <sys/mman.h> 20 #include <asm/bug.h> 21 22 void perf_evsel__init(struct perf_evsel *evsel, struct perf_event_attr *attr, 23 int idx) 24 { 25 INIT_LIST_HEAD(&evsel->node); 26 evsel->attr = *attr; 27 evsel->idx = idx; 28 evsel->leader = evsel; 29 } 30 31 struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr) 32 { 33 struct perf_evsel *evsel = zalloc(sizeof(*evsel)); 34 35 if (evsel != NULL) 36 perf_evsel__init(evsel, attr, 0); 37 38 return evsel; 39 } 40 41 void perf_evsel__delete(struct perf_evsel *evsel) 42 { 43 free(evsel); 44 } 45 46 #define FD(e, x, y) ((int *) xyarray__entry(e->fd, x, y)) 47 #define MMAP(e, x, y) (e->mmap ? ((struct perf_mmap *) xyarray__entry(e->mmap, x, y)) : NULL) 48 49 int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads) 50 { 51 evsel->fd = xyarray__new(ncpus, nthreads, sizeof(int)); 52 53 if (evsel->fd) { 54 int cpu, thread; 55 for (cpu = 0; cpu < ncpus; cpu++) { 56 for (thread = 0; thread < nthreads; thread++) { 57 int *fd = FD(evsel, cpu, thread); 58 59 if (fd) 60 *fd = -1; 61 } 62 } 63 } 64 65 return evsel->fd != NULL ? 0 : -ENOMEM; 66 } 67 68 static int perf_evsel__alloc_mmap(struct perf_evsel *evsel, int ncpus, int nthreads) 69 { 70 evsel->mmap = xyarray__new(ncpus, nthreads, sizeof(struct perf_mmap)); 71 72 return evsel->mmap != NULL ? 0 : -ENOMEM; 73 } 74 75 static int 76 sys_perf_event_open(struct perf_event_attr *attr, 77 pid_t pid, int cpu, int group_fd, 78 unsigned long flags) 79 { 80 return syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags); 81 } 82 83 static int get_group_fd(struct perf_evsel *evsel, int cpu, int thread, int *group_fd) 84 { 85 struct perf_evsel *leader = evsel->leader; 86 int *fd; 87 88 if (evsel == leader) { 89 *group_fd = -1; 90 return 0; 91 } 92 93 /* 94 * Leader must be already processed/open, 95 * if not it's a bug. 96 */ 97 if (!leader->fd) 98 return -ENOTCONN; 99 100 fd = FD(leader, cpu, thread); 101 if (fd == NULL || *fd == -1) 102 return -EBADF; 103 104 *group_fd = *fd; 105 106 return 0; 107 } 108 109 int perf_evsel__open(struct perf_evsel *evsel, struct perf_cpu_map *cpus, 110 struct perf_thread_map *threads) 111 { 112 int cpu, thread, err = 0; 113 114 if (cpus == NULL) { 115 static struct perf_cpu_map *empty_cpu_map; 116 117 if (empty_cpu_map == NULL) { 118 empty_cpu_map = perf_cpu_map__dummy_new(); 119 if (empty_cpu_map == NULL) 120 return -ENOMEM; 121 } 122 123 cpus = empty_cpu_map; 124 } 125 126 if (threads == NULL) { 127 static struct perf_thread_map *empty_thread_map; 128 129 if (empty_thread_map == NULL) { 130 empty_thread_map = perf_thread_map__new_dummy(); 131 if (empty_thread_map == NULL) 132 return -ENOMEM; 133 } 134 135 threads = empty_thread_map; 136 } 137 138 if (evsel->fd == NULL && 139 perf_evsel__alloc_fd(evsel, cpus->nr, threads->nr) < 0) 140 return -ENOMEM; 141 142 for (cpu = 0; cpu < cpus->nr; cpu++) { 143 for (thread = 0; thread < threads->nr; thread++) { 144 int fd, group_fd, *evsel_fd; 145 146 evsel_fd = FD(evsel, cpu, thread); 147 if (evsel_fd == NULL) 148 return -EINVAL; 149 150 err = get_group_fd(evsel, cpu, thread, &group_fd); 151 if (err < 0) 152 return err; 153 154 fd = sys_perf_event_open(&evsel->attr, 155 threads->map[thread].pid, 156 cpus->map[cpu], group_fd, 0); 157 158 if (fd < 0) 159 return -errno; 160 161 *evsel_fd = fd; 162 } 163 } 164 165 return err; 166 } 167 168 static void perf_evsel__close_fd_cpu(struct perf_evsel *evsel, int cpu) 169 { 170 int thread; 171 172 for (thread = 0; thread < xyarray__max_y(evsel->fd); ++thread) { 173 int *fd = FD(evsel, cpu, thread); 174 175 if (fd && *fd >= 0) { 176 close(*fd); 177 *fd = -1; 178 } 179 } 180 } 181 182 void perf_evsel__close_fd(struct perf_evsel *evsel) 183 { 184 int cpu; 185 186 for (cpu = 0; cpu < xyarray__max_x(evsel->fd); cpu++) 187 perf_evsel__close_fd_cpu(evsel, cpu); 188 } 189 190 void perf_evsel__free_fd(struct perf_evsel *evsel) 191 { 192 xyarray__delete(evsel->fd); 193 evsel->fd = NULL; 194 } 195 196 void perf_evsel__close(struct perf_evsel *evsel) 197 { 198 if (evsel->fd == NULL) 199 return; 200 201 perf_evsel__close_fd(evsel); 202 perf_evsel__free_fd(evsel); 203 } 204 205 void perf_evsel__close_cpu(struct perf_evsel *evsel, int cpu) 206 { 207 if (evsel->fd == NULL) 208 return; 209 210 perf_evsel__close_fd_cpu(evsel, cpu); 211 } 212 213 void perf_evsel__munmap(struct perf_evsel *evsel) 214 { 215 int cpu, thread; 216 217 if (evsel->fd == NULL || evsel->mmap == NULL) 218 return; 219 220 for (cpu = 0; cpu < xyarray__max_x(evsel->fd); cpu++) { 221 for (thread = 0; thread < xyarray__max_y(evsel->fd); thread++) { 222 int *fd = FD(evsel, cpu, thread); 223 224 if (fd == NULL || *fd < 0) 225 continue; 226 227 perf_mmap__munmap(MMAP(evsel, cpu, thread)); 228 } 229 } 230 231 xyarray__delete(evsel->mmap); 232 evsel->mmap = NULL; 233 } 234 235 int perf_evsel__mmap(struct perf_evsel *evsel, int pages) 236 { 237 int ret, cpu, thread; 238 struct perf_mmap_param mp = { 239 .prot = PROT_READ | PROT_WRITE, 240 .mask = (pages * page_size) - 1, 241 }; 242 243 if (evsel->fd == NULL || evsel->mmap) 244 return -EINVAL; 245 246 if (perf_evsel__alloc_mmap(evsel, xyarray__max_x(evsel->fd), xyarray__max_y(evsel->fd)) < 0) 247 return -ENOMEM; 248 249 for (cpu = 0; cpu < xyarray__max_x(evsel->fd); cpu++) { 250 for (thread = 0; thread < xyarray__max_y(evsel->fd); thread++) { 251 int *fd = FD(evsel, cpu, thread); 252 struct perf_mmap *map; 253 254 if (fd == NULL || *fd < 0) 255 continue; 256 257 map = MMAP(evsel, cpu, thread); 258 perf_mmap__init(map, NULL, false, NULL); 259 260 ret = perf_mmap__mmap(map, &mp, *fd, cpu); 261 if (ret) { 262 perf_evsel__munmap(evsel); 263 return ret; 264 } 265 } 266 } 267 268 return 0; 269 } 270 271 void *perf_evsel__mmap_base(struct perf_evsel *evsel, int cpu, int thread) 272 { 273 int *fd = FD(evsel, cpu, thread); 274 275 if (fd == NULL || *fd < 0 || MMAP(evsel, cpu, thread) == NULL) 276 return NULL; 277 278 return MMAP(evsel, cpu, thread)->base; 279 } 280 281 int perf_evsel__read_size(struct perf_evsel *evsel) 282 { 283 u64 read_format = evsel->attr.read_format; 284 int entry = sizeof(u64); /* value */ 285 int size = 0; 286 int nr = 1; 287 288 if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) 289 size += sizeof(u64); 290 291 if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) 292 size += sizeof(u64); 293 294 if (read_format & PERF_FORMAT_ID) 295 entry += sizeof(u64); 296 297 if (read_format & PERF_FORMAT_GROUP) { 298 nr = evsel->nr_members; 299 size += sizeof(u64); 300 } 301 302 size += entry * nr; 303 return size; 304 } 305 306 int perf_evsel__read(struct perf_evsel *evsel, int cpu, int thread, 307 struct perf_counts_values *count) 308 { 309 size_t size = perf_evsel__read_size(evsel); 310 int *fd = FD(evsel, cpu, thread); 311 312 memset(count, 0, sizeof(*count)); 313 314 if (fd == NULL || *fd < 0) 315 return -EINVAL; 316 317 if (MMAP(evsel, cpu, thread) && 318 !perf_mmap__read_self(MMAP(evsel, cpu, thread), count)) 319 return 0; 320 321 if (readn(*fd, count->values, size) <= 0) 322 return -errno; 323 324 return 0; 325 } 326 327 static int perf_evsel__run_ioctl(struct perf_evsel *evsel, 328 int ioc, void *arg, 329 int cpu) 330 { 331 int thread; 332 333 for (thread = 0; thread < xyarray__max_y(evsel->fd); thread++) { 334 int err; 335 int *fd = FD(evsel, cpu, thread); 336 337 if (fd == NULL || *fd < 0) 338 return -1; 339 340 err = ioctl(*fd, ioc, arg); 341 342 if (err) 343 return err; 344 } 345 346 return 0; 347 } 348 349 int perf_evsel__enable_cpu(struct perf_evsel *evsel, int cpu) 350 { 351 return perf_evsel__run_ioctl(evsel, PERF_EVENT_IOC_ENABLE, NULL, cpu); 352 } 353 354 int perf_evsel__enable(struct perf_evsel *evsel) 355 { 356 int i; 357 int err = 0; 358 359 for (i = 0; i < xyarray__max_x(evsel->fd) && !err; i++) 360 err = perf_evsel__run_ioctl(evsel, PERF_EVENT_IOC_ENABLE, NULL, i); 361 return err; 362 } 363 364 int perf_evsel__disable_cpu(struct perf_evsel *evsel, int cpu) 365 { 366 return perf_evsel__run_ioctl(evsel, PERF_EVENT_IOC_DISABLE, NULL, cpu); 367 } 368 369 int perf_evsel__disable(struct perf_evsel *evsel) 370 { 371 int i; 372 int err = 0; 373 374 for (i = 0; i < xyarray__max_x(evsel->fd) && !err; i++) 375 err = perf_evsel__run_ioctl(evsel, PERF_EVENT_IOC_DISABLE, NULL, i); 376 return err; 377 } 378 379 int perf_evsel__apply_filter(struct perf_evsel *evsel, const char *filter) 380 { 381 int err = 0, i; 382 383 for (i = 0; i < evsel->cpus->nr && !err; i++) 384 err = perf_evsel__run_ioctl(evsel, 385 PERF_EVENT_IOC_SET_FILTER, 386 (void *)filter, i); 387 return err; 388 } 389 390 struct perf_cpu_map *perf_evsel__cpus(struct perf_evsel *evsel) 391 { 392 return evsel->cpus; 393 } 394 395 struct perf_thread_map *perf_evsel__threads(struct perf_evsel *evsel) 396 { 397 return evsel->threads; 398 } 399 400 struct perf_event_attr *perf_evsel__attr(struct perf_evsel *evsel) 401 { 402 return &evsel->attr; 403 } 404 405 int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads) 406 { 407 if (ncpus == 0 || nthreads == 0) 408 return 0; 409 410 if (evsel->system_wide) 411 nthreads = 1; 412 413 evsel->sample_id = xyarray__new(ncpus, nthreads, sizeof(struct perf_sample_id)); 414 if (evsel->sample_id == NULL) 415 return -ENOMEM; 416 417 evsel->id = zalloc(ncpus * nthreads * sizeof(u64)); 418 if (evsel->id == NULL) { 419 xyarray__delete(evsel->sample_id); 420 evsel->sample_id = NULL; 421 return -ENOMEM; 422 } 423 424 return 0; 425 } 426 427 void perf_evsel__free_id(struct perf_evsel *evsel) 428 { 429 xyarray__delete(evsel->sample_id); 430 evsel->sample_id = NULL; 431 zfree(&evsel->id); 432 evsel->ids = 0; 433 } 434