xref: /openbmc/linux/tools/lib/perf/evsel.c (revision ee7da21a)
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 }
416