libperf: Add perf_evlist__add_pollfd() function
[linux-2.6-block.git] / tools / perf / lib / evlist.c
CommitLineData
285a30c3
JO
1// SPDX-License-Identifier: GPL-2.0
2#include <perf/evlist.h>
80dc2b3e 3#include <perf/evsel.h>
b0031c22 4#include <linux/bitops.h>
285a30c3 5#include <linux/list.h>
b0031c22 6#include <linux/hash.h>
d5a99483 7#include <sys/ioctl.h>
285a30c3 8#include <internal/evlist.h>
9a5edde6 9#include <internal/evsel.h>
b0031c22 10#include <internal/xyarray.h>
634912d6 11#include <linux/zalloc.h>
57f0c3b6 12#include <stdlib.h>
d5a99483
JO
13#include <errno.h>
14#include <unistd.h>
f4009e7b
JO
15#include <fcntl.h>
16#include <signal.h>
17#include <poll.h>
453fa030
JO
18#include <perf/cpumap.h>
19#include <perf/threadmap.h>
31f67fc4 20#include <api/fd/array.h>
4562a739
JO
21
22void perf_evlist__init(struct perf_evlist *evlist)
23{
1d5af02d
JO
24 int i;
25
26 for (i = 0; i < PERF_EVLIST__HLIST_SIZE; ++i)
27 INIT_HLIST_HEAD(&evlist->heads[i]);
4562a739 28 INIT_LIST_HEAD(&evlist->entries);
6484d2f9 29 evlist->nr_entries = 0;
4562a739 30}
9a5edde6 31
453fa030
JO
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
9a5edde6
JO
59void perf_evlist__add(struct perf_evlist *evlist,
60 struct perf_evsel *evsel)
61{
62 list_add_tail(&evsel->node, &evlist->entries);
6484d2f9 63 evlist->nr_entries += 1;
453fa030 64 __perf_evlist__propagate_maps(evlist, evsel);
9a5edde6 65}
52e22fb8
JO
66
67void perf_evlist__remove(struct perf_evlist *evlist,
68 struct perf_evsel *evsel)
69{
70 list_del_init(&evsel->node);
6484d2f9 71 evlist->nr_entries -= 1;
52e22fb8 72}
634912d6
JO
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}
651bf38c
JO
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}
57f0c3b6
JO
103
104void perf_evlist__delete(struct perf_evlist *evlist)
105{
106 free(evlist);
107}
453fa030
JO
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}
80dc2b3e
JO
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}
fcc97c3e
JO
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}
ff47d86a
JO
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}
b0031c22
JO
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}
d5a99483
JO
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}
31f67fc4
JO
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}
f4009e7b
JO
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}