Linux Audio

Check our new training course

Embedded Linux training

Mar 31-Apr 8, 2025
Register
Loading...
Note: File does not exist in v3.15.
  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
 22void 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
 31struct 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
 41void 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
 49int 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
 68static 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
 75static int
 76sys_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
 83static 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
109int 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
168static 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
182void 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
190void perf_evsel__free_fd(struct perf_evsel *evsel)
191{
192	xyarray__delete(evsel->fd);
193	evsel->fd = NULL;
194}
195
196void 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
205void 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
213void 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
235int 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
271void *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
281int 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
306int 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
327static 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
349int 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
354int 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
364int 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
369int 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
379int 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
390struct perf_cpu_map *perf_evsel__cpus(struct perf_evsel *evsel)
391{
392	return evsel->cpus;
393}
394
395struct perf_thread_map *perf_evsel__threads(struct perf_evsel *evsel)
396{
397	return evsel->threads;
398}
399
400struct perf_event_attr *perf_evsel__attr(struct perf_evsel *evsel)
401{
402	return &evsel->attr;
403}
404
405int 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
427void 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}