Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.5.6.
  1// SPDX-License-Identifier: GPL-2.0
  2#include <perf/evlist.h>
  3#include <perf/evsel.h>
  4#include <linux/bitops.h>
  5#include <linux/list.h>
  6#include <linux/hash.h>
  7#include <sys/ioctl.h>
  8#include <internal/evlist.h>
  9#include <internal/evsel.h>
 10#include <internal/xyarray.h>
 11#include <linux/zalloc.h>
 12#include <stdlib.h>
 13#include <errno.h>
 14#include <unistd.h>
 15#include <fcntl.h>
 16#include <signal.h>
 17#include <poll.h>
 18#include <perf/cpumap.h>
 19#include <perf/threadmap.h>
 20#include <api/fd/array.h>
 21
 22void perf_evlist__init(struct perf_evlist *evlist)
 23{
 24	int i;
 25
 26	for (i = 0; i < PERF_EVLIST__HLIST_SIZE; ++i)
 27		INIT_HLIST_HEAD(&evlist->heads[i]);
 28	INIT_LIST_HEAD(&evlist->entries);
 29	evlist->nr_entries = 0;
 30}
 31
 32static void __perf_evlist__propagate_maps(struct perf_evlist *evlist,
 33					  struct perf_evsel *evsel)
 34{
 35	/*
 36	 * We already have cpus for evsel (via PMU sysfs) so
 37	 * keep it, if there's no target cpu list defined.
 38	 */
 39	if (!evsel->own_cpus || evlist->has_user_cpus) {
 40		perf_cpu_map__put(evsel->cpus);
 41		evsel->cpus = perf_cpu_map__get(evlist->cpus);
 42	} else if (evsel->cpus != evsel->own_cpus) {
 43		perf_cpu_map__put(evsel->cpus);
 44		evsel->cpus = perf_cpu_map__get(evsel->own_cpus);
 45	}
 46
 47	perf_thread_map__put(evsel->threads);
 48	evsel->threads = perf_thread_map__get(evlist->threads);
 49}
 50
 51static void perf_evlist__propagate_maps(struct perf_evlist *evlist)
 52{
 53	struct perf_evsel *evsel;
 54
 55	perf_evlist__for_each_evsel(evlist, evsel)
 56		__perf_evlist__propagate_maps(evlist, evsel);
 57}
 58
 59void perf_evlist__add(struct perf_evlist *evlist,
 60		      struct perf_evsel *evsel)
 61{
 62	list_add_tail(&evsel->node, &evlist->entries);
 63	evlist->nr_entries += 1;
 64	__perf_evlist__propagate_maps(evlist, evsel);
 65}
 66
 67void perf_evlist__remove(struct perf_evlist *evlist,
 68			 struct perf_evsel *evsel)
 69{
 70	list_del_init(&evsel->node);
 71	evlist->nr_entries -= 1;
 72}
 73
 74struct perf_evlist *perf_evlist__new(void)
 75{
 76	struct perf_evlist *evlist = zalloc(sizeof(*evlist));
 77
 78	if (evlist != NULL)
 79		perf_evlist__init(evlist);
 80
 81	return evlist;
 82}
 83
 84struct perf_evsel *
 85perf_evlist__next(struct perf_evlist *evlist, struct perf_evsel *prev)
 86{
 87	struct perf_evsel *next;
 88
 89	if (!prev) {
 90		next = list_first_entry(&evlist->entries,
 91					struct perf_evsel,
 92					node);
 93	} else {
 94		next = list_next_entry(prev, node);
 95	}
 96
 97	/* Empty list is noticed here so don't need checking on entry. */
 98	if (&next->node == &evlist->entries)
 99		return NULL;
100
101	return next;
102}
103
104void perf_evlist__delete(struct perf_evlist *evlist)
105{
106	free(evlist);
107}
108
109void perf_evlist__set_maps(struct perf_evlist *evlist,
110			   struct perf_cpu_map *cpus,
111			   struct perf_thread_map *threads)
112{
113	/*
114	 * Allow for the possibility that one or another of the maps isn't being
115	 * changed i.e. don't put it.  Note we are assuming the maps that are
116	 * being applied are brand new and evlist is taking ownership of the
117	 * original reference count of 1.  If that is not the case it is up to
118	 * the caller to increase the reference count.
119	 */
120	if (cpus != evlist->cpus) {
121		perf_cpu_map__put(evlist->cpus);
122		evlist->cpus = perf_cpu_map__get(cpus);
123	}
124
125	if (threads != evlist->threads) {
126		perf_thread_map__put(evlist->threads);
127		evlist->threads = perf_thread_map__get(threads);
128	}
129
130	perf_evlist__propagate_maps(evlist);
131}
132
133int perf_evlist__open(struct perf_evlist *evlist)
134{
135	struct perf_evsel *evsel;
136	int err;
137
138	perf_evlist__for_each_entry(evlist, evsel) {
139		err = perf_evsel__open(evsel, evsel->cpus, evsel->threads);
140		if (err < 0)
141			goto out_err;
142	}
143
144	return 0;
145
146out_err:
147	perf_evlist__close(evlist);
148	return err;
149}
150
151void perf_evlist__close(struct perf_evlist *evlist)
152{
153	struct perf_evsel *evsel;
154
155	perf_evlist__for_each_entry_reverse(evlist, evsel)
156		perf_evsel__close(evsel);
157}
158
159void perf_evlist__enable(struct perf_evlist *evlist)
160{
161	struct perf_evsel *evsel;
162
163	perf_evlist__for_each_entry(evlist, evsel)
164		perf_evsel__enable(evsel);
165}
166
167void perf_evlist__disable(struct perf_evlist *evlist)
168{
169	struct perf_evsel *evsel;
170
171	perf_evlist__for_each_entry(evlist, evsel)
172		perf_evsel__disable(evsel);
173}
174
175u64 perf_evlist__read_format(struct perf_evlist *evlist)
176{
177	struct perf_evsel *first = perf_evlist__first(evlist);
178
179	return first->attr.read_format;
180}
181
182#define SID(e, x, y) xyarray__entry(e->sample_id, x, y)
183
184static void perf_evlist__id_hash(struct perf_evlist *evlist,
185				 struct perf_evsel *evsel,
186				 int cpu, int thread, u64 id)
187{
188	int hash;
189	struct perf_sample_id *sid = SID(evsel, cpu, thread);
190
191	sid->id = id;
192	sid->evsel = evsel;
193	hash = hash_64(sid->id, PERF_EVLIST__HLIST_BITS);
194	hlist_add_head(&sid->node, &evlist->heads[hash]);
195}
196
197void perf_evlist__id_add(struct perf_evlist *evlist,
198			 struct perf_evsel *evsel,
199			 int cpu, int thread, u64 id)
200{
201	perf_evlist__id_hash(evlist, evsel, cpu, thread, id);
202	evsel->id[evsel->ids++] = id;
203}
204
205int perf_evlist__id_add_fd(struct perf_evlist *evlist,
206			   struct perf_evsel *evsel,
207			   int cpu, int thread, int fd)
208{
209	u64 read_data[4] = { 0, };
210	int id_idx = 1; /* The first entry is the counter value */
211	u64 id;
212	int ret;
213
214	ret = ioctl(fd, PERF_EVENT_IOC_ID, &id);
215	if (!ret)
216		goto add;
217
218	if (errno != ENOTTY)
219		return -1;
220
221	/* Legacy way to get event id.. All hail to old kernels! */
222
223	/*
224	 * This way does not work with group format read, so bail
225	 * out in that case.
226	 */
227	if (perf_evlist__read_format(evlist) & PERF_FORMAT_GROUP)
228		return -1;
229
230	if (!(evsel->attr.read_format & PERF_FORMAT_ID) ||
231	    read(fd, &read_data, sizeof(read_data)) == -1)
232		return -1;
233
234	if (evsel->attr.read_format & PERF_FORMAT_TOTAL_TIME_ENABLED)
235		++id_idx;
236	if (evsel->attr.read_format & PERF_FORMAT_TOTAL_TIME_RUNNING)
237		++id_idx;
238
239	id = read_data[id_idx];
240
241add:
242	perf_evlist__id_add(evlist, evsel, cpu, thread, id);
243	return 0;
244}
245
246int perf_evlist__alloc_pollfd(struct perf_evlist *evlist)
247{
248	int nr_cpus = perf_cpu_map__nr(evlist->cpus);
249	int nr_threads = perf_thread_map__nr(evlist->threads);
250	int nfds = 0;
251	struct perf_evsel *evsel;
252
253	perf_evlist__for_each_entry(evlist, evsel) {
254		if (evsel->system_wide)
255			nfds += nr_cpus;
256		else
257			nfds += nr_cpus * nr_threads;
258	}
259
260	if (fdarray__available_entries(&evlist->pollfd) < nfds &&
261	    fdarray__grow(&evlist->pollfd, nfds) < 0)
262		return -ENOMEM;
263
264	return 0;
265}
266
267int perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd,
268			    void *ptr, short revent)
269{
270	int pos = fdarray__add(&evlist->pollfd, fd, revent | POLLERR | POLLHUP);
271
272	if (pos >= 0) {
273		evlist->pollfd.priv[pos].ptr = ptr;
274		fcntl(fd, F_SETFL, O_NONBLOCK);
275	}
276
277	return pos;
278}
279
280int perf_evlist__poll(struct perf_evlist *evlist, int timeout)
281{
282	return fdarray__poll(&evlist->pollfd, timeout);
283}