diff options
Diffstat (limited to 'tools/perf/util/evsel.c')
-rw-r--r-- | tools/perf/util/evsel.c | 440 |
1 files changed, 440 insertions, 0 deletions
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c new file mode 100644 index 000000000000..0239eb87b232 --- /dev/null +++ b/tools/perf/util/evsel.c | |||
@@ -0,0 +1,440 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2011, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> | ||
3 | * | ||
4 | * Parts came from builtin-{top,stat,record}.c, see those files for further | ||
5 | * copyright notes. | ||
6 | * | ||
7 | * Released under the GPL v2. (and only v2, not any later version) | ||
8 | */ | ||
9 | |||
10 | #include "evsel.h" | ||
11 | #include "evlist.h" | ||
12 | #include "util.h" | ||
13 | #include "cpumap.h" | ||
14 | #include "thread_map.h" | ||
15 | |||
16 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) | ||
17 | |||
18 | int __perf_evsel__sample_size(u64 sample_type) | ||
19 | { | ||
20 | u64 mask = sample_type & PERF_SAMPLE_MASK; | ||
21 | int size = 0; | ||
22 | int i; | ||
23 | |||
24 | for (i = 0; i < 64; i++) { | ||
25 | if (mask & (1ULL << i)) | ||
26 | size++; | ||
27 | } | ||
28 | |||
29 | size *= sizeof(u64); | ||
30 | |||
31 | return size; | ||
32 | } | ||
33 | |||
34 | void perf_evsel__init(struct perf_evsel *evsel, | ||
35 | struct perf_event_attr *attr, int idx) | ||
36 | { | ||
37 | evsel->idx = idx; | ||
38 | evsel->attr = *attr; | ||
39 | INIT_LIST_HEAD(&evsel->node); | ||
40 | } | ||
41 | |||
42 | struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx) | ||
43 | { | ||
44 | struct perf_evsel *evsel = zalloc(sizeof(*evsel)); | ||
45 | |||
46 | if (evsel != NULL) | ||
47 | perf_evsel__init(evsel, attr, idx); | ||
48 | |||
49 | return evsel; | ||
50 | } | ||
51 | |||
52 | int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads) | ||
53 | { | ||
54 | int cpu, thread; | ||
55 | evsel->fd = xyarray__new(ncpus, nthreads, sizeof(int)); | ||
56 | |||
57 | if (evsel->fd) { | ||
58 | for (cpu = 0; cpu < ncpus; cpu++) { | ||
59 | for (thread = 0; thread < nthreads; thread++) { | ||
60 | FD(evsel, cpu, thread) = -1; | ||
61 | } | ||
62 | } | ||
63 | } | ||
64 | |||
65 | return evsel->fd != NULL ? 0 : -ENOMEM; | ||
66 | } | ||
67 | |||
68 | int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads) | ||
69 | { | ||
70 | evsel->sample_id = xyarray__new(ncpus, nthreads, sizeof(struct perf_sample_id)); | ||
71 | if (evsel->sample_id == NULL) | ||
72 | return -ENOMEM; | ||
73 | |||
74 | evsel->id = zalloc(ncpus * nthreads * sizeof(u64)); | ||
75 | if (evsel->id == NULL) { | ||
76 | xyarray__delete(evsel->sample_id); | ||
77 | evsel->sample_id = NULL; | ||
78 | return -ENOMEM; | ||
79 | } | ||
80 | |||
81 | return 0; | ||
82 | } | ||
83 | |||
84 | int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus) | ||
85 | { | ||
86 | evsel->counts = zalloc((sizeof(*evsel->counts) + | ||
87 | (ncpus * sizeof(struct perf_counts_values)))); | ||
88 | return evsel->counts != NULL ? 0 : -ENOMEM; | ||
89 | } | ||
90 | |||
91 | void perf_evsel__free_fd(struct perf_evsel *evsel) | ||
92 | { | ||
93 | xyarray__delete(evsel->fd); | ||
94 | evsel->fd = NULL; | ||
95 | } | ||
96 | |||
97 | void perf_evsel__free_id(struct perf_evsel *evsel) | ||
98 | { | ||
99 | xyarray__delete(evsel->sample_id); | ||
100 | evsel->sample_id = NULL; | ||
101 | free(evsel->id); | ||
102 | evsel->id = NULL; | ||
103 | } | ||
104 | |||
105 | void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads) | ||
106 | { | ||
107 | int cpu, thread; | ||
108 | |||
109 | for (cpu = 0; cpu < ncpus; cpu++) | ||
110 | for (thread = 0; thread < nthreads; ++thread) { | ||
111 | close(FD(evsel, cpu, thread)); | ||
112 | FD(evsel, cpu, thread) = -1; | ||
113 | } | ||
114 | } | ||
115 | |||
116 | void perf_evsel__exit(struct perf_evsel *evsel) | ||
117 | { | ||
118 | assert(list_empty(&evsel->node)); | ||
119 | xyarray__delete(evsel->fd); | ||
120 | xyarray__delete(evsel->sample_id); | ||
121 | free(evsel->id); | ||
122 | } | ||
123 | |||
124 | void perf_evsel__delete(struct perf_evsel *evsel) | ||
125 | { | ||
126 | perf_evsel__exit(evsel); | ||
127 | close_cgroup(evsel->cgrp); | ||
128 | free(evsel->name); | ||
129 | free(evsel); | ||
130 | } | ||
131 | |||
132 | int __perf_evsel__read_on_cpu(struct perf_evsel *evsel, | ||
133 | int cpu, int thread, bool scale) | ||
134 | { | ||
135 | struct perf_counts_values count; | ||
136 | size_t nv = scale ? 3 : 1; | ||
137 | |||
138 | if (FD(evsel, cpu, thread) < 0) | ||
139 | return -EINVAL; | ||
140 | |||
141 | if (evsel->counts == NULL && perf_evsel__alloc_counts(evsel, cpu + 1) < 0) | ||
142 | return -ENOMEM; | ||
143 | |||
144 | if (readn(FD(evsel, cpu, thread), &count, nv * sizeof(u64)) < 0) | ||
145 | return -errno; | ||
146 | |||
147 | if (scale) { | ||
148 | if (count.run == 0) | ||
149 | count.val = 0; | ||
150 | else if (count.run < count.ena) | ||
151 | count.val = (u64)((double)count.val * count.ena / count.run + 0.5); | ||
152 | } else | ||
153 | count.ena = count.run = 0; | ||
154 | |||
155 | evsel->counts->cpu[cpu] = count; | ||
156 | return 0; | ||
157 | } | ||
158 | |||
159 | int __perf_evsel__read(struct perf_evsel *evsel, | ||
160 | int ncpus, int nthreads, bool scale) | ||
161 | { | ||
162 | size_t nv = scale ? 3 : 1; | ||
163 | int cpu, thread; | ||
164 | struct perf_counts_values *aggr = &evsel->counts->aggr, count; | ||
165 | |||
166 | aggr->val = aggr->ena = aggr->run = 0; | ||
167 | |||
168 | for (cpu = 0; cpu < ncpus; cpu++) { | ||
169 | for (thread = 0; thread < nthreads; thread++) { | ||
170 | if (FD(evsel, cpu, thread) < 0) | ||
171 | continue; | ||
172 | |||
173 | if (readn(FD(evsel, cpu, thread), | ||
174 | &count, nv * sizeof(u64)) < 0) | ||
175 | return -errno; | ||
176 | |||
177 | aggr->val += count.val; | ||
178 | if (scale) { | ||
179 | aggr->ena += count.ena; | ||
180 | aggr->run += count.run; | ||
181 | } | ||
182 | } | ||
183 | } | ||
184 | |||
185 | evsel->counts->scaled = 0; | ||
186 | if (scale) { | ||
187 | if (aggr->run == 0) { | ||
188 | evsel->counts->scaled = -1; | ||
189 | aggr->val = 0; | ||
190 | return 0; | ||
191 | } | ||
192 | |||
193 | if (aggr->run < aggr->ena) { | ||
194 | evsel->counts->scaled = 1; | ||
195 | aggr->val = (u64)((double)aggr->val * aggr->ena / aggr->run + 0.5); | ||
196 | } | ||
197 | } else | ||
198 | aggr->ena = aggr->run = 0; | ||
199 | |||
200 | return 0; | ||
201 | } | ||
202 | |||
203 | static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, | ||
204 | struct thread_map *threads, bool group) | ||
205 | { | ||
206 | int cpu, thread; | ||
207 | unsigned long flags = 0; | ||
208 | int pid = -1; | ||
209 | |||
210 | if (evsel->fd == NULL && | ||
211 | perf_evsel__alloc_fd(evsel, cpus->nr, threads->nr) < 0) | ||
212 | return -1; | ||
213 | |||
214 | if (evsel->cgrp) { | ||
215 | flags = PERF_FLAG_PID_CGROUP; | ||
216 | pid = evsel->cgrp->fd; | ||
217 | } | ||
218 | |||
219 | for (cpu = 0; cpu < cpus->nr; cpu++) { | ||
220 | int group_fd = -1; | ||
221 | |||
222 | for (thread = 0; thread < threads->nr; thread++) { | ||
223 | |||
224 | if (!evsel->cgrp) | ||
225 | pid = threads->map[thread]; | ||
226 | |||
227 | FD(evsel, cpu, thread) = sys_perf_event_open(&evsel->attr, | ||
228 | pid, | ||
229 | cpus->map[cpu], | ||
230 | group_fd, flags); | ||
231 | if (FD(evsel, cpu, thread) < 0) | ||
232 | goto out_close; | ||
233 | |||
234 | if (group && group_fd == -1) | ||
235 | group_fd = FD(evsel, cpu, thread); | ||
236 | } | ||
237 | } | ||
238 | |||
239 | return 0; | ||
240 | |||
241 | out_close: | ||
242 | do { | ||
243 | while (--thread >= 0) { | ||
244 | close(FD(evsel, cpu, thread)); | ||
245 | FD(evsel, cpu, thread) = -1; | ||
246 | } | ||
247 | thread = threads->nr; | ||
248 | } while (--cpu >= 0); | ||
249 | return -1; | ||
250 | } | ||
251 | |||
252 | static struct { | ||
253 | struct cpu_map map; | ||
254 | int cpus[1]; | ||
255 | } empty_cpu_map = { | ||
256 | .map.nr = 1, | ||
257 | .cpus = { -1, }, | ||
258 | }; | ||
259 | |||
260 | static struct { | ||
261 | struct thread_map map; | ||
262 | int threads[1]; | ||
263 | } empty_thread_map = { | ||
264 | .map.nr = 1, | ||
265 | .threads = { -1, }, | ||
266 | }; | ||
267 | |||
268 | int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, | ||
269 | struct thread_map *threads, bool group) | ||
270 | { | ||
271 | if (cpus == NULL) { | ||
272 | /* Work around old compiler warnings about strict aliasing */ | ||
273 | cpus = &empty_cpu_map.map; | ||
274 | } | ||
275 | |||
276 | if (threads == NULL) | ||
277 | threads = &empty_thread_map.map; | ||
278 | |||
279 | return __perf_evsel__open(evsel, cpus, threads, group); | ||
280 | } | ||
281 | |||
282 | int perf_evsel__open_per_cpu(struct perf_evsel *evsel, | ||
283 | struct cpu_map *cpus, bool group) | ||
284 | { | ||
285 | return __perf_evsel__open(evsel, cpus, &empty_thread_map.map, group); | ||
286 | } | ||
287 | |||
288 | int perf_evsel__open_per_thread(struct perf_evsel *evsel, | ||
289 | struct thread_map *threads, bool group) | ||
290 | { | ||
291 | return __perf_evsel__open(evsel, &empty_cpu_map.map, threads, group); | ||
292 | } | ||
293 | |||
294 | static int perf_event__parse_id_sample(const union perf_event *event, u64 type, | ||
295 | struct perf_sample *sample) | ||
296 | { | ||
297 | const u64 *array = event->sample.array; | ||
298 | |||
299 | array += ((event->header.size - | ||
300 | sizeof(event->header)) / sizeof(u64)) - 1; | ||
301 | |||
302 | if (type & PERF_SAMPLE_CPU) { | ||
303 | u32 *p = (u32 *)array; | ||
304 | sample->cpu = *p; | ||
305 | array--; | ||
306 | } | ||
307 | |||
308 | if (type & PERF_SAMPLE_STREAM_ID) { | ||
309 | sample->stream_id = *array; | ||
310 | array--; | ||
311 | } | ||
312 | |||
313 | if (type & PERF_SAMPLE_ID) { | ||
314 | sample->id = *array; | ||
315 | array--; | ||
316 | } | ||
317 | |||
318 | if (type & PERF_SAMPLE_TIME) { | ||
319 | sample->time = *array; | ||
320 | array--; | ||
321 | } | ||
322 | |||
323 | if (type & PERF_SAMPLE_TID) { | ||
324 | u32 *p = (u32 *)array; | ||
325 | sample->pid = p[0]; | ||
326 | sample->tid = p[1]; | ||
327 | } | ||
328 | |||
329 | return 0; | ||
330 | } | ||
331 | |||
332 | static bool sample_overlap(const union perf_event *event, | ||
333 | const void *offset, u64 size) | ||
334 | { | ||
335 | const void *base = event; | ||
336 | |||
337 | if (offset + size > base + event->header.size) | ||
338 | return true; | ||
339 | |||
340 | return false; | ||
341 | } | ||
342 | |||
343 | int perf_event__parse_sample(const union perf_event *event, u64 type, | ||
344 | int sample_size, bool sample_id_all, | ||
345 | struct perf_sample *data) | ||
346 | { | ||
347 | const u64 *array; | ||
348 | |||
349 | data->cpu = data->pid = data->tid = -1; | ||
350 | data->stream_id = data->id = data->time = -1ULL; | ||
351 | |||
352 | if (event->header.type != PERF_RECORD_SAMPLE) { | ||
353 | if (!sample_id_all) | ||
354 | return 0; | ||
355 | return perf_event__parse_id_sample(event, type, data); | ||
356 | } | ||
357 | |||
358 | array = event->sample.array; | ||
359 | |||
360 | if (sample_size + sizeof(event->header) > event->header.size) | ||
361 | return -EFAULT; | ||
362 | |||
363 | if (type & PERF_SAMPLE_IP) { | ||
364 | data->ip = event->ip.ip; | ||
365 | array++; | ||
366 | } | ||
367 | |||
368 | if (type & PERF_SAMPLE_TID) { | ||
369 | u32 *p = (u32 *)array; | ||
370 | data->pid = p[0]; | ||
371 | data->tid = p[1]; | ||
372 | array++; | ||
373 | } | ||
374 | |||
375 | if (type & PERF_SAMPLE_TIME) { | ||
376 | data->time = *array; | ||
377 | array++; | ||
378 | } | ||
379 | |||
380 | if (type & PERF_SAMPLE_ADDR) { | ||
381 | data->addr = *array; | ||
382 | array++; | ||
383 | } | ||
384 | |||
385 | data->id = -1ULL; | ||
386 | if (type & PERF_SAMPLE_ID) { | ||
387 | data->id = *array; | ||
388 | array++; | ||
389 | } | ||
390 | |||
391 | if (type & PERF_SAMPLE_STREAM_ID) { | ||
392 | data->stream_id = *array; | ||
393 | array++; | ||
394 | } | ||
395 | |||
396 | if (type & PERF_SAMPLE_CPU) { | ||
397 | u32 *p = (u32 *)array; | ||
398 | data->cpu = *p; | ||
399 | array++; | ||
400 | } | ||
401 | |||
402 | if (type & PERF_SAMPLE_PERIOD) { | ||
403 | data->period = *array; | ||
404 | array++; | ||
405 | } | ||
406 | |||
407 | if (type & PERF_SAMPLE_READ) { | ||
408 | fprintf(stderr, "PERF_SAMPLE_READ is unsuported for now\n"); | ||
409 | return -1; | ||
410 | } | ||
411 | |||
412 | if (type & PERF_SAMPLE_CALLCHAIN) { | ||
413 | if (sample_overlap(event, array, sizeof(data->callchain->nr))) | ||
414 | return -EFAULT; | ||
415 | |||
416 | data->callchain = (struct ip_callchain *)array; | ||
417 | |||
418 | if (sample_overlap(event, array, data->callchain->nr)) | ||
419 | return -EFAULT; | ||
420 | |||
421 | array += 1 + data->callchain->nr; | ||
422 | } | ||
423 | |||
424 | if (type & PERF_SAMPLE_RAW) { | ||
425 | u32 *p = (u32 *)array; | ||
426 | |||
427 | if (sample_overlap(event, array, sizeof(u32))) | ||
428 | return -EFAULT; | ||
429 | |||
430 | data->raw_size = *p; | ||
431 | p++; | ||
432 | |||
433 | if (sample_overlap(event, p, data->raw_size)) | ||
434 | return -EFAULT; | ||
435 | |||
436 | data->raw_data = p; | ||
437 | } | ||
438 | |||
439 | return 0; | ||
440 | } | ||