• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
perf_evlist__init(struct perf_evlist * evlist)22 void 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 
__perf_evlist__propagate_maps(struct perf_evlist * evlist,struct perf_evsel * evsel)32 static 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 
perf_evlist__propagate_maps(struct perf_evlist * evlist)51 static 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 
perf_evlist__add(struct perf_evlist * evlist,struct perf_evsel * evsel)59 void 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 
perf_evlist__remove(struct perf_evlist * evlist,struct perf_evsel * evsel)67 void 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 
perf_evlist__new(void)74 struct 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 
84 struct perf_evsel *
perf_evlist__next(struct perf_evlist * evlist,struct perf_evsel * prev)85 perf_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 
perf_evlist__delete(struct perf_evlist * evlist)104 void perf_evlist__delete(struct perf_evlist *evlist)
105 {
106 	free(evlist);
107 }
108 
perf_evlist__set_maps(struct perf_evlist * evlist,struct perf_cpu_map * cpus,struct perf_thread_map * threads)109 void 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 
perf_evlist__open(struct perf_evlist * evlist)133 int 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 
146 out_err:
147 	perf_evlist__close(evlist);
148 	return err;
149 }
150 
perf_evlist__close(struct perf_evlist * evlist)151 void 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 
perf_evlist__enable(struct perf_evlist * evlist)159 void 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 
perf_evlist__disable(struct perf_evlist * evlist)167 void 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 
perf_evlist__read_format(struct perf_evlist * evlist)175 u64 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 
perf_evlist__id_hash(struct perf_evlist * evlist,struct perf_evsel * evsel,int cpu,int thread,u64 id)184 static 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 
perf_evlist__id_add(struct perf_evlist * evlist,struct perf_evsel * evsel,int cpu,int thread,u64 id)197 void 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 
perf_evlist__id_add_fd(struct perf_evlist * evlist,struct perf_evsel * evsel,int cpu,int thread,int fd)205 int 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 
241 add:
242 	perf_evlist__id_add(evlist, evsel, cpu, thread, id);
243 	return 0;
244 }
245 
perf_evlist__alloc_pollfd(struct perf_evlist * evlist)246 int 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 
perf_evlist__add_pollfd(struct perf_evlist * evlist,int fd,void * ptr,short revent)267 int 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 
perf_evlist__poll(struct perf_evlist * evlist,int timeout)280 int perf_evlist__poll(struct perf_evlist *evlist, int timeout)
281 {
282 	return fdarray__poll(&evlist->pollfd, timeout);
283 }
284