diff options
Diffstat (limited to 'tools/perf/util/evsel.c')
-rw-r--r-- | tools/perf/util/evsel.c | 234 |
1 files changed, 215 insertions, 19 deletions
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index d8575d31ee6c..662596afd7f1 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
@@ -1,20 +1,34 @@ | |||
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 | |||
1 | #include "evsel.h" | 10 | #include "evsel.h" |
2 | #include "../perf.h" | 11 | #include "evlist.h" |
3 | #include "util.h" | 12 | #include "util.h" |
4 | #include "cpumap.h" | 13 | #include "cpumap.h" |
5 | #include "thread.h" | 14 | #include "thread_map.h" |
6 | 15 | ||
7 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) | 16 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) |
8 | 17 | ||
18 | void perf_evsel__init(struct perf_evsel *evsel, | ||
19 | struct perf_event_attr *attr, int idx) | ||
20 | { | ||
21 | evsel->idx = idx; | ||
22 | evsel->attr = *attr; | ||
23 | INIT_LIST_HEAD(&evsel->node); | ||
24 | } | ||
25 | |||
9 | struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx) | 26 | struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx) |
10 | { | 27 | { |
11 | struct perf_evsel *evsel = zalloc(sizeof(*evsel)); | 28 | struct perf_evsel *evsel = zalloc(sizeof(*evsel)); |
12 | 29 | ||
13 | if (evsel != NULL) { | 30 | if (evsel != NULL) |
14 | evsel->idx = idx; | 31 | perf_evsel__init(evsel, attr, idx); |
15 | evsel->attr = *attr; | ||
16 | INIT_LIST_HEAD(&evsel->node); | ||
17 | } | ||
18 | 32 | ||
19 | return evsel; | 33 | return evsel; |
20 | } | 34 | } |
@@ -25,6 +39,22 @@ int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads) | |||
25 | return evsel->fd != NULL ? 0 : -ENOMEM; | 39 | return evsel->fd != NULL ? 0 : -ENOMEM; |
26 | } | 40 | } |
27 | 41 | ||
42 | int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads) | ||
43 | { | ||
44 | evsel->sample_id = xyarray__new(ncpus, nthreads, sizeof(struct perf_sample_id)); | ||
45 | if (evsel->sample_id == NULL) | ||
46 | return -ENOMEM; | ||
47 | |||
48 | evsel->id = zalloc(ncpus * nthreads * sizeof(u64)); | ||
49 | if (evsel->id == NULL) { | ||
50 | xyarray__delete(evsel->sample_id); | ||
51 | evsel->sample_id = NULL; | ||
52 | return -ENOMEM; | ||
53 | } | ||
54 | |||
55 | return 0; | ||
56 | } | ||
57 | |||
28 | int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus) | 58 | int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus) |
29 | { | 59 | { |
30 | evsel->counts = zalloc((sizeof(*evsel->counts) + | 60 | evsel->counts = zalloc((sizeof(*evsel->counts) + |
@@ -38,6 +68,14 @@ void perf_evsel__free_fd(struct perf_evsel *evsel) | |||
38 | evsel->fd = NULL; | 68 | evsel->fd = NULL; |
39 | } | 69 | } |
40 | 70 | ||
71 | void perf_evsel__free_id(struct perf_evsel *evsel) | ||
72 | { | ||
73 | xyarray__delete(evsel->sample_id); | ||
74 | evsel->sample_id = NULL; | ||
75 | free(evsel->id); | ||
76 | evsel->id = NULL; | ||
77 | } | ||
78 | |||
41 | void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads) | 79 | void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads) |
42 | { | 80 | { |
43 | int cpu, thread; | 81 | int cpu, thread; |
@@ -49,10 +87,19 @@ void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads) | |||
49 | } | 87 | } |
50 | } | 88 | } |
51 | 89 | ||
52 | void perf_evsel__delete(struct perf_evsel *evsel) | 90 | void perf_evsel__exit(struct perf_evsel *evsel) |
53 | { | 91 | { |
54 | assert(list_empty(&evsel->node)); | 92 | assert(list_empty(&evsel->node)); |
55 | xyarray__delete(evsel->fd); | 93 | xyarray__delete(evsel->fd); |
94 | xyarray__delete(evsel->sample_id); | ||
95 | free(evsel->id); | ||
96 | } | ||
97 | |||
98 | void perf_evsel__delete(struct perf_evsel *evsel) | ||
99 | { | ||
100 | perf_evsel__exit(evsel); | ||
101 | close_cgroup(evsel->cgrp); | ||
102 | free(evsel->name); | ||
56 | free(evsel); | 103 | free(evsel); |
57 | } | 104 | } |
58 | 105 | ||
@@ -128,21 +175,51 @@ int __perf_evsel__read(struct perf_evsel *evsel, | |||
128 | } | 175 | } |
129 | 176 | ||
130 | static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, | 177 | static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, |
131 | struct thread_map *threads) | 178 | struct thread_map *threads, bool group, bool inherit) |
132 | { | 179 | { |
133 | int cpu, thread; | 180 | int cpu, thread; |
181 | unsigned long flags = 0; | ||
182 | int pid = -1; | ||
134 | 183 | ||
135 | if (evsel->fd == NULL && | 184 | if (evsel->fd == NULL && |
136 | perf_evsel__alloc_fd(evsel, cpus->nr, threads->nr) < 0) | 185 | perf_evsel__alloc_fd(evsel, cpus->nr, threads->nr) < 0) |
137 | return -1; | 186 | return -1; |
138 | 187 | ||
188 | if (evsel->cgrp) { | ||
189 | flags = PERF_FLAG_PID_CGROUP; | ||
190 | pid = evsel->cgrp->fd; | ||
191 | } | ||
192 | |||
139 | for (cpu = 0; cpu < cpus->nr; cpu++) { | 193 | for (cpu = 0; cpu < cpus->nr; cpu++) { |
194 | int group_fd = -1; | ||
195 | /* | ||
196 | * Don't allow mmap() of inherited per-task counters. This | ||
197 | * would create a performance issue due to all children writing | ||
198 | * to the same buffer. | ||
199 | * | ||
200 | * FIXME: | ||
201 | * Proper fix is not to pass 'inherit' to perf_evsel__open*, | ||
202 | * but a 'flags' parameter, with 'group' folded there as well, | ||
203 | * then introduce a PERF_O_{MMAP,GROUP,INHERIT} enum, and if | ||
204 | * O_MMAP is set, emit a warning if cpu < 0 and O_INHERIT is | ||
205 | * set. Lets go for the minimal fix first tho. | ||
206 | */ | ||
207 | evsel->attr.inherit = (cpus->map[cpu] >= 0) && inherit; | ||
208 | |||
140 | for (thread = 0; thread < threads->nr; thread++) { | 209 | for (thread = 0; thread < threads->nr; thread++) { |
210 | |||
211 | if (!evsel->cgrp) | ||
212 | pid = threads->map[thread]; | ||
213 | |||
141 | FD(evsel, cpu, thread) = sys_perf_event_open(&evsel->attr, | 214 | FD(evsel, cpu, thread) = sys_perf_event_open(&evsel->attr, |
142 | threads->map[thread], | 215 | pid, |
143 | cpus->map[cpu], -1, 0); | 216 | cpus->map[cpu], |
217 | group_fd, flags); | ||
144 | if (FD(evsel, cpu, thread) < 0) | 218 | if (FD(evsel, cpu, thread) < 0) |
145 | goto out_close; | 219 | goto out_close; |
220 | |||
221 | if (group && group_fd == -1) | ||
222 | group_fd = FD(evsel, cpu, thread); | ||
146 | } | 223 | } |
147 | } | 224 | } |
148 | 225 | ||
@@ -175,10 +252,9 @@ static struct { | |||
175 | .threads = { -1, }, | 252 | .threads = { -1, }, |
176 | }; | 253 | }; |
177 | 254 | ||
178 | int perf_evsel__open(struct perf_evsel *evsel, | 255 | int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, |
179 | struct cpu_map *cpus, struct thread_map *threads) | 256 | struct thread_map *threads, bool group, bool inherit) |
180 | { | 257 | { |
181 | |||
182 | if (cpus == NULL) { | 258 | if (cpus == NULL) { |
183 | /* Work around old compiler warnings about strict aliasing */ | 259 | /* Work around old compiler warnings about strict aliasing */ |
184 | cpus = &empty_cpu_map.map; | 260 | cpus = &empty_cpu_map.map; |
@@ -187,15 +263,135 @@ int perf_evsel__open(struct perf_evsel *evsel, | |||
187 | if (threads == NULL) | 263 | if (threads == NULL) |
188 | threads = &empty_thread_map.map; | 264 | threads = &empty_thread_map.map; |
189 | 265 | ||
190 | return __perf_evsel__open(evsel, cpus, threads); | 266 | return __perf_evsel__open(evsel, cpus, threads, group, inherit); |
191 | } | 267 | } |
192 | 268 | ||
193 | int perf_evsel__open_per_cpu(struct perf_evsel *evsel, struct cpu_map *cpus) | 269 | int perf_evsel__open_per_cpu(struct perf_evsel *evsel, |
270 | struct cpu_map *cpus, bool group, bool inherit) | ||
194 | { | 271 | { |
195 | return __perf_evsel__open(evsel, cpus, &empty_thread_map.map); | 272 | return __perf_evsel__open(evsel, cpus, &empty_thread_map.map, group, inherit); |
273 | } | ||
274 | |||
275 | int perf_evsel__open_per_thread(struct perf_evsel *evsel, | ||
276 | struct thread_map *threads, bool group, bool inherit) | ||
277 | { | ||
278 | return __perf_evsel__open(evsel, &empty_cpu_map.map, threads, group, inherit); | ||
279 | } | ||
280 | |||
281 | static int perf_event__parse_id_sample(const union perf_event *event, u64 type, | ||
282 | struct perf_sample *sample) | ||
283 | { | ||
284 | const u64 *array = event->sample.array; | ||
285 | |||
286 | array += ((event->header.size - | ||
287 | sizeof(event->header)) / sizeof(u64)) - 1; | ||
288 | |||
289 | if (type & PERF_SAMPLE_CPU) { | ||
290 | u32 *p = (u32 *)array; | ||
291 | sample->cpu = *p; | ||
292 | array--; | ||
293 | } | ||
294 | |||
295 | if (type & PERF_SAMPLE_STREAM_ID) { | ||
296 | sample->stream_id = *array; | ||
297 | array--; | ||
298 | } | ||
299 | |||
300 | if (type & PERF_SAMPLE_ID) { | ||
301 | sample->id = *array; | ||
302 | array--; | ||
303 | } | ||
304 | |||
305 | if (type & PERF_SAMPLE_TIME) { | ||
306 | sample->time = *array; | ||
307 | array--; | ||
308 | } | ||
309 | |||
310 | if (type & PERF_SAMPLE_TID) { | ||
311 | u32 *p = (u32 *)array; | ||
312 | sample->pid = p[0]; | ||
313 | sample->tid = p[1]; | ||
314 | } | ||
315 | |||
316 | return 0; | ||
196 | } | 317 | } |
197 | 318 | ||
198 | int perf_evsel__open_per_thread(struct perf_evsel *evsel, struct thread_map *threads) | 319 | int perf_event__parse_sample(const union perf_event *event, u64 type, |
320 | bool sample_id_all, struct perf_sample *data) | ||
199 | { | 321 | { |
200 | return __perf_evsel__open(evsel, &empty_cpu_map.map, threads); | 322 | const u64 *array; |
323 | |||
324 | data->cpu = data->pid = data->tid = -1; | ||
325 | data->stream_id = data->id = data->time = -1ULL; | ||
326 | |||
327 | if (event->header.type != PERF_RECORD_SAMPLE) { | ||
328 | if (!sample_id_all) | ||
329 | return 0; | ||
330 | return perf_event__parse_id_sample(event, type, data); | ||
331 | } | ||
332 | |||
333 | array = event->sample.array; | ||
334 | |||
335 | if (type & PERF_SAMPLE_IP) { | ||
336 | data->ip = event->ip.ip; | ||
337 | array++; | ||
338 | } | ||
339 | |||
340 | if (type & PERF_SAMPLE_TID) { | ||
341 | u32 *p = (u32 *)array; | ||
342 | data->pid = p[0]; | ||
343 | data->tid = p[1]; | ||
344 | array++; | ||
345 | } | ||
346 | |||
347 | if (type & PERF_SAMPLE_TIME) { | ||
348 | data->time = *array; | ||
349 | array++; | ||
350 | } | ||
351 | |||
352 | if (type & PERF_SAMPLE_ADDR) { | ||
353 | data->addr = *array; | ||
354 | array++; | ||
355 | } | ||
356 | |||
357 | data->id = -1ULL; | ||
358 | if (type & PERF_SAMPLE_ID) { | ||
359 | data->id = *array; | ||
360 | array++; | ||
361 | } | ||
362 | |||
363 | if (type & PERF_SAMPLE_STREAM_ID) { | ||
364 | data->stream_id = *array; | ||
365 | array++; | ||
366 | } | ||
367 | |||
368 | if (type & PERF_SAMPLE_CPU) { | ||
369 | u32 *p = (u32 *)array; | ||
370 | data->cpu = *p; | ||
371 | array++; | ||
372 | } | ||
373 | |||
374 | if (type & PERF_SAMPLE_PERIOD) { | ||
375 | data->period = *array; | ||
376 | array++; | ||
377 | } | ||
378 | |||
379 | if (type & PERF_SAMPLE_READ) { | ||
380 | fprintf(stderr, "PERF_SAMPLE_READ is unsuported for now\n"); | ||
381 | return -1; | ||
382 | } | ||
383 | |||
384 | if (type & PERF_SAMPLE_CALLCHAIN) { | ||
385 | data->callchain = (struct ip_callchain *)array; | ||
386 | array += 1 + data->callchain->nr; | ||
387 | } | ||
388 | |||
389 | if (type & PERF_SAMPLE_RAW) { | ||
390 | u32 *p = (u32 *)array; | ||
391 | data->raw_size = *p; | ||
392 | p++; | ||
393 | data->raw_data = p; | ||
394 | } | ||
395 | |||
396 | return 0; | ||
201 | } | 397 | } |