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