9a6d94299ab8cfb0d534d2ed830caa3b69fb7727
[pandora-kernel.git] / tools / perf / util / evsel.c
1 #include "evsel.h"
2 #include "evlist.h"
3 #include "../perf.h"
4 #include "util.h"
5 #include "cpumap.h"
6 #include "thread_map.h"
7
8 #include <unistd.h>
9 #include <sys/mman.h>
10
11 #include <linux/bitops.h>
12 #include <linux/hash.h>
13
14 #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y))
15 #define SID(e, x, y) xyarray__entry(e->id, x, y)
16
17 struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx)
18 {
19         struct perf_evsel *evsel = zalloc(sizeof(*evsel));
20
21         if (evsel != NULL) {
22                 evsel->idx         = idx;
23                 evsel->attr        = *attr;
24                 INIT_LIST_HEAD(&evsel->node);
25         }
26
27         return evsel;
28 }
29
30 int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads)
31 {
32         evsel->fd = xyarray__new(ncpus, nthreads, sizeof(int));
33         return evsel->fd != NULL ? 0 : -ENOMEM;
34 }
35
36 int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads)
37 {
38         evsel->id = xyarray__new(ncpus, nthreads, sizeof(struct perf_sample_id));
39         return evsel->id != NULL ? 0 : -ENOMEM;
40 }
41
42 int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus)
43 {
44         evsel->counts = zalloc((sizeof(*evsel->counts) +
45                                 (ncpus * sizeof(struct perf_counts_values))));
46         return evsel->counts != NULL ? 0 : -ENOMEM;
47 }
48
49 void perf_evsel__free_fd(struct perf_evsel *evsel)
50 {
51         xyarray__delete(evsel->fd);
52         evsel->fd = NULL;
53 }
54
55 void perf_evsel__free_id(struct perf_evsel *evsel)
56 {
57         xyarray__delete(evsel->id);
58         evsel->id = NULL;
59 }
60
61 void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads)
62 {
63         int cpu, thread;
64
65         for (cpu = 0; cpu < ncpus; cpu++)
66                 for (thread = 0; thread < nthreads; ++thread) {
67                         close(FD(evsel, cpu, thread));
68                         FD(evsel, cpu, thread) = -1;
69                 }
70 }
71
72 void perf_evlist__munmap(struct perf_evlist *evlist, int ncpus)
73 {
74         int cpu;
75
76         for (cpu = 0; cpu < ncpus; cpu++) {
77                 if (evlist->mmap[cpu].base != NULL) {
78                         munmap(evlist->mmap[cpu].base, evlist->mmap_len);
79                         evlist->mmap[cpu].base = NULL;
80                 }
81         }
82 }
83
84 int perf_evlist__alloc_mmap(struct perf_evlist *evlist, int ncpus)
85 {
86         evlist->mmap = zalloc(ncpus * sizeof(struct perf_mmap));
87         return evlist->mmap != NULL ? 0 : -ENOMEM;
88 }
89
90 void perf_evsel__delete(struct perf_evsel *evsel)
91 {
92         assert(list_empty(&evsel->node));
93         xyarray__delete(evsel->fd);
94         xyarray__delete(evsel->id);
95         free(evsel);
96 }
97
98 int __perf_evsel__read_on_cpu(struct perf_evsel *evsel,
99                               int cpu, int thread, bool scale)
100 {
101         struct perf_counts_values count;
102         size_t nv = scale ? 3 : 1;
103
104         if (FD(evsel, cpu, thread) < 0)
105                 return -EINVAL;
106
107         if (evsel->counts == NULL && perf_evsel__alloc_counts(evsel, cpu + 1) < 0)
108                 return -ENOMEM;
109
110         if (readn(FD(evsel, cpu, thread), &count, nv * sizeof(u64)) < 0)
111                 return -errno;
112
113         if (scale) {
114                 if (count.run == 0)
115                         count.val = 0;
116                 else if (count.run < count.ena)
117                         count.val = (u64)((double)count.val * count.ena / count.run + 0.5);
118         } else
119                 count.ena = count.run = 0;
120
121         evsel->counts->cpu[cpu] = count;
122         return 0;
123 }
124
125 int __perf_evsel__read(struct perf_evsel *evsel,
126                        int ncpus, int nthreads, bool scale)
127 {
128         size_t nv = scale ? 3 : 1;
129         int cpu, thread;
130         struct perf_counts_values *aggr = &evsel->counts->aggr, count;
131
132         aggr->val = 0;
133
134         for (cpu = 0; cpu < ncpus; cpu++) {
135                 for (thread = 0; thread < nthreads; thread++) {
136                         if (FD(evsel, cpu, thread) < 0)
137                                 continue;
138
139                         if (readn(FD(evsel, cpu, thread),
140                                   &count, nv * sizeof(u64)) < 0)
141                                 return -errno;
142
143                         aggr->val += count.val;
144                         if (scale) {
145                                 aggr->ena += count.ena;
146                                 aggr->run += count.run;
147                         }
148                 }
149         }
150
151         evsel->counts->scaled = 0;
152         if (scale) {
153                 if (aggr->run == 0) {
154                         evsel->counts->scaled = -1;
155                         aggr->val = 0;
156                         return 0;
157                 }
158
159                 if (aggr->run < aggr->ena) {
160                         evsel->counts->scaled = 1;
161                         aggr->val = (u64)((double)aggr->val * aggr->ena / aggr->run + 0.5);
162                 }
163         } else
164                 aggr->ena = aggr->run = 0;
165
166         return 0;
167 }
168
169 static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus,
170                               struct thread_map *threads, bool group, bool inherit)
171 {
172         int cpu, thread;
173
174         if (evsel->fd == NULL &&
175             perf_evsel__alloc_fd(evsel, cpus->nr, threads->nr) < 0)
176                 return -1;
177
178         for (cpu = 0; cpu < cpus->nr; cpu++) {
179                 int group_fd = -1;
180
181                 evsel->attr.inherit = (cpus->map[cpu] < 0) && inherit;
182
183                 for (thread = 0; thread < threads->nr; thread++) {
184                         FD(evsel, cpu, thread) = sys_perf_event_open(&evsel->attr,
185                                                                      threads->map[thread],
186                                                                      cpus->map[cpu],
187                                                                      group_fd, 0);
188                         if (FD(evsel, cpu, thread) < 0)
189                                 goto out_close;
190
191                         if (group && group_fd == -1)
192                                 group_fd = FD(evsel, cpu, thread);
193                 }
194         }
195
196         return 0;
197
198 out_close:
199         do {
200                 while (--thread >= 0) {
201                         close(FD(evsel, cpu, thread));
202                         FD(evsel, cpu, thread) = -1;
203                 }
204                 thread = threads->nr;
205         } while (--cpu >= 0);
206         return -1;
207 }
208
209 static struct {
210         struct cpu_map map;
211         int cpus[1];
212 } empty_cpu_map = {
213         .map.nr = 1,
214         .cpus   = { -1, },
215 };
216
217 static struct {
218         struct thread_map map;
219         int threads[1];
220 } empty_thread_map = {
221         .map.nr  = 1,
222         .threads = { -1, },
223 };
224
225 int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus,
226                      struct thread_map *threads, bool group, bool inherit)
227 {
228         if (cpus == NULL) {
229                 /* Work around old compiler warnings about strict aliasing */
230                 cpus = &empty_cpu_map.map;
231         }
232
233         if (threads == NULL)
234                 threads = &empty_thread_map.map;
235
236         return __perf_evsel__open(evsel, cpus, threads, group, inherit);
237 }
238
239 int perf_evsel__open_per_cpu(struct perf_evsel *evsel,
240                              struct cpu_map *cpus, bool group, bool inherit)
241 {
242         return __perf_evsel__open(evsel, cpus, &empty_thread_map.map, group, inherit);
243 }
244
245 int perf_evsel__open_per_thread(struct perf_evsel *evsel,
246                                 struct thread_map *threads, bool group, bool inherit)
247 {
248         return __perf_evsel__open(evsel, &empty_cpu_map.map, threads, group, inherit);
249 }
250
251 static int __perf_evlist__mmap(struct perf_evlist *evlist, int cpu, int prot,
252                                int mask, int fd)
253 {
254         evlist->mmap[cpu].prev = 0;
255         evlist->mmap[cpu].mask = mask;
256         evlist->mmap[cpu].base = mmap(NULL, evlist->mmap_len, prot,
257                                       MAP_SHARED, fd, 0);
258         if (evlist->mmap[cpu].base == MAP_FAILED)
259                 return -1;
260
261         perf_evlist__add_pollfd(evlist, fd);
262         return 0;
263 }
264
265 static int perf_evlist__id_hash(struct perf_evlist *evlist, struct perf_evsel *evsel,
266                                int cpu, int thread, int fd)
267 {
268         struct perf_sample_id *sid;
269         u64 read_data[4] = { 0, };
270         int hash, id_idx = 1; /* The first entry is the counter value */
271
272         if (!(evsel->attr.read_format & PERF_FORMAT_ID) ||
273             read(fd, &read_data, sizeof(read_data)) == -1)
274                 return -1;
275
276         if (evsel->attr.read_format & PERF_FORMAT_TOTAL_TIME_ENABLED)
277                 ++id_idx;
278         if (evsel->attr.read_format & PERF_FORMAT_TOTAL_TIME_RUNNING)
279                 ++id_idx;
280
281         sid = SID(evsel, cpu, thread);
282         sid->id = read_data[id_idx];
283         sid->evsel = evsel;
284         hash = hash_64(sid->id, PERF_EVLIST__HLIST_BITS);
285         hlist_add_head(&sid->node, &evlist->heads[hash]);
286         return 0;
287 }
288
289 /** perf_evlist__mmap - Create per cpu maps to receive events
290  *
291  * @evlist - list of events
292  * @cpus - cpu map being monitored
293  * @threads - threads map being monitored
294  * @pages - map length in pages
295  * @overwrite - overwrite older events?
296  *
297  * If overwrite is false the user needs to signal event consuption using:
298  *
299  *      struct perf_mmap *m = &evlist->mmap[cpu];
300  *      unsigned int head = perf_mmap__read_head(m);
301  *
302  *      perf_mmap__write_tail(m, head)
303  */
304 int perf_evlist__mmap(struct perf_evlist *evlist, struct cpu_map *cpus,
305                       struct thread_map *threads, int pages, bool overwrite)
306 {
307         unsigned int page_size = sysconf(_SC_PAGE_SIZE);
308         int mask = pages * page_size - 1, cpu;
309         struct perf_evsel *first_evsel, *evsel;
310         int thread, prot = PROT_READ | (overwrite ? 0 : PROT_WRITE);
311
312         if (evlist->mmap == NULL &&
313             perf_evlist__alloc_mmap(evlist, cpus->nr) < 0)
314                 return -ENOMEM;
315
316         if (evlist->pollfd == NULL &&
317             perf_evlist__alloc_pollfd(evlist, cpus->nr, threads->nr) < 0)
318                 return -ENOMEM;
319
320         evlist->mmap_len = (pages + 1) * page_size;
321         first_evsel = list_entry(evlist->entries.next, struct perf_evsel, node);
322
323         list_for_each_entry(evsel, &evlist->entries, node) {
324                 if ((evsel->attr.read_format & PERF_FORMAT_ID) &&
325                     evsel->id == NULL &&
326                     perf_evsel__alloc_id(evsel, cpus->nr, threads->nr) < 0)
327                         return -ENOMEM;
328
329                 for (cpu = 0; cpu < cpus->nr; cpu++) {
330                         for (thread = 0; thread < threads->nr; thread++) {
331                                 int fd = FD(evsel, cpu, thread);
332
333                                 if (evsel->idx || thread) {
334                                         if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT,
335                                                   FD(first_evsel, cpu, 0)) != 0)
336                                                 goto out_unmap;
337                                 } else if (__perf_evlist__mmap(evlist, cpu, prot, mask, fd) < 0)
338                                         goto out_unmap;
339
340                                 if ((evsel->attr.read_format & PERF_FORMAT_ID) &&
341                                     perf_evlist__id_hash(evlist, evsel, cpu, thread, fd) < 0)
342                                         goto out_unmap;
343                         }
344                 }
345         }
346
347         return 0;
348
349 out_unmap:
350         for (cpu = 0; cpu < cpus->nr; cpu++) {
351                 if (evlist->mmap[cpu].base != NULL) {
352                         munmap(evlist->mmap[cpu].base, evlist->mmap_len);
353                         evlist->mmap[cpu].base = NULL;
354                 }
355         }
356         return -1;
357 }