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