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
perf_evsel__init(struct perf_evsel * evsel,struct perf_event_attr * attr,int idx)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
perf_evsel__new(struct perf_event_attr * attr)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
perf_evsel__delete(struct perf_evsel * evsel)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
perf_evsel__alloc_fd(struct perf_evsel * evsel,int ncpus,int nthreads)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
perf_evsel__alloc_mmap(struct perf_evsel * evsel,int ncpus,int nthreads)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
sys_perf_event_open(struct perf_event_attr * attr,pid_t pid,int cpu,int group_fd,unsigned long flags)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
get_group_fd(struct perf_evsel * evsel,int cpu,int thread,int * group_fd)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
perf_evsel__open(struct perf_evsel * evsel,struct perf_cpu_map * cpus,struct perf_thread_map * threads)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
perf_evsel__close_fd_cpu(struct perf_evsel * evsel,int cpu)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
perf_evsel__close_fd(struct perf_evsel * evsel)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
perf_evsel__free_fd(struct perf_evsel * evsel)190 void perf_evsel__free_fd(struct perf_evsel *evsel)
191 {
192 xyarray__delete(evsel->fd);
193 evsel->fd = NULL;
194 }
195
perf_evsel__close(struct perf_evsel * evsel)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
perf_evsel__close_cpu(struct perf_evsel * evsel,int cpu)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
perf_evsel__munmap(struct perf_evsel * evsel)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
perf_evsel__mmap(struct perf_evsel * evsel,int pages)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
perf_evsel__mmap_base(struct perf_evsel * evsel,int cpu,int thread)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
perf_evsel__read_size(struct perf_evsel * evsel)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
perf_evsel__read(struct perf_evsel * evsel,int cpu,int thread,struct perf_counts_values * count)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
perf_evsel__run_ioctl(struct perf_evsel * evsel,int ioc,void * arg,int cpu)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
perf_evsel__enable_cpu(struct perf_evsel * evsel,int cpu)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
perf_evsel__enable(struct perf_evsel * evsel)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
perf_evsel__disable_cpu(struct perf_evsel * evsel,int cpu)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
perf_evsel__disable(struct perf_evsel * evsel)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
perf_evsel__apply_filter(struct perf_evsel * evsel,const char * filter)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
perf_evsel__cpus(struct perf_evsel * evsel)390 struct perf_cpu_map *perf_evsel__cpus(struct perf_evsel *evsel)
391 {
392 return evsel->cpus;
393 }
394
perf_evsel__threads(struct perf_evsel * evsel)395 struct perf_thread_map *perf_evsel__threads(struct perf_evsel *evsel)
396 {
397 return evsel->threads;
398 }
399
perf_evsel__attr(struct perf_evsel * evsel)400 struct perf_event_attr *perf_evsel__attr(struct perf_evsel *evsel)
401 {
402 return &evsel->attr;
403 }
404
perf_evsel__alloc_id(struct perf_evsel * evsel,int ncpus,int nthreads)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
perf_evsel__free_id(struct perf_evsel * evsel)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