diff options
Diffstat (limited to 'tools/perf/util/evsel.c')
-rw-r--r-- | tools/perf/util/evsel.c | 318 |
1 files changed, 301 insertions, 17 deletions
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index f5cfed60af98..76ab553637d6 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
@@ -1,20 +1,33 @@ | |||
1 | #include "evsel.h" | 1 | #include "evsel.h" |
2 | #include "evlist.h" | ||
2 | #include "../perf.h" | 3 | #include "../perf.h" |
3 | #include "util.h" | 4 | #include "util.h" |
4 | #include "cpumap.h" | 5 | #include "cpumap.h" |
5 | #include "thread.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> | ||
6 | 13 | ||
7 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) | 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 | void perf_evsel__init(struct perf_evsel *evsel, | ||
18 | struct perf_event_attr *attr, int idx) | ||
19 | { | ||
20 | evsel->idx = idx; | ||
21 | evsel->attr = *attr; | ||
22 | INIT_LIST_HEAD(&evsel->node); | ||
23 | } | ||
8 | 24 | ||
9 | struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx) | 25 | struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx) |
10 | { | 26 | { |
11 | struct perf_evsel *evsel = zalloc(sizeof(*evsel)); | 27 | struct perf_evsel *evsel = zalloc(sizeof(*evsel)); |
12 | 28 | ||
13 | if (evsel != NULL) { | 29 | if (evsel != NULL) |
14 | evsel->idx = idx; | 30 | perf_evsel__init(evsel, attr, idx); |
15 | evsel->attr = *attr; | ||
16 | INIT_LIST_HEAD(&evsel->node); | ||
17 | } | ||
18 | 31 | ||
19 | return evsel; | 32 | return evsel; |
20 | } | 33 | } |
@@ -25,6 +38,12 @@ int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads) | |||
25 | return evsel->fd != NULL ? 0 : -ENOMEM; | 38 | return evsel->fd != NULL ? 0 : -ENOMEM; |
26 | } | 39 | } |
27 | 40 | ||
41 | int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads) | ||
42 | { | ||
43 | evsel->id = xyarray__new(ncpus, nthreads, sizeof(struct perf_sample_id)); | ||
44 | return evsel->id != NULL ? 0 : -ENOMEM; | ||
45 | } | ||
46 | |||
28 | int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus) | 47 | int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus) |
29 | { | 48 | { |
30 | evsel->counts = zalloc((sizeof(*evsel->counts) + | 49 | evsel->counts = zalloc((sizeof(*evsel->counts) + |
@@ -38,6 +57,12 @@ void perf_evsel__free_fd(struct perf_evsel *evsel) | |||
38 | evsel->fd = NULL; | 57 | evsel->fd = NULL; |
39 | } | 58 | } |
40 | 59 | ||
60 | void perf_evsel__free_id(struct perf_evsel *evsel) | ||
61 | { | ||
62 | xyarray__delete(evsel->id); | ||
63 | evsel->id = NULL; | ||
64 | } | ||
65 | |||
41 | void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads) | 66 | void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads) |
42 | { | 67 | { |
43 | int cpu, thread; | 68 | int cpu, thread; |
@@ -49,10 +74,34 @@ void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads) | |||
49 | } | 74 | } |
50 | } | 75 | } |
51 | 76 | ||
52 | void perf_evsel__delete(struct perf_evsel *evsel) | 77 | void perf_evlist__munmap(struct perf_evlist *evlist, int ncpus) |
78 | { | ||
79 | int cpu; | ||
80 | |||
81 | for (cpu = 0; cpu < ncpus; cpu++) { | ||
82 | if (evlist->mmap[cpu].base != NULL) { | ||
83 | munmap(evlist->mmap[cpu].base, evlist->mmap_len); | ||
84 | evlist->mmap[cpu].base = NULL; | ||
85 | } | ||
86 | } | ||
87 | } | ||
88 | |||
89 | int perf_evlist__alloc_mmap(struct perf_evlist *evlist, int ncpus) | ||
90 | { | ||
91 | evlist->mmap = zalloc(ncpus * sizeof(struct perf_mmap)); | ||
92 | return evlist->mmap != NULL ? 0 : -ENOMEM; | ||
93 | } | ||
94 | |||
95 | void perf_evsel__exit(struct perf_evsel *evsel) | ||
53 | { | 96 | { |
54 | assert(list_empty(&evsel->node)); | 97 | assert(list_empty(&evsel->node)); |
55 | xyarray__delete(evsel->fd); | 98 | xyarray__delete(evsel->fd); |
99 | xyarray__delete(evsel->id); | ||
100 | } | ||
101 | |||
102 | void perf_evsel__delete(struct perf_evsel *evsel) | ||
103 | { | ||
104 | perf_evsel__exit(evsel); | ||
56 | free(evsel); | 105 | free(evsel); |
57 | } | 106 | } |
58 | 107 | ||
@@ -128,7 +177,7 @@ int __perf_evsel__read(struct perf_evsel *evsel, | |||
128 | } | 177 | } |
129 | 178 | ||
130 | static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, | 179 | static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, |
131 | struct thread_map *threads) | 180 | struct thread_map *threads, bool group, bool inherit) |
132 | { | 181 | { |
133 | int cpu, thread; | 182 | int cpu, thread; |
134 | 183 | ||
@@ -137,12 +186,20 @@ static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, | |||
137 | return -1; | 186 | return -1; |
138 | 187 | ||
139 | for (cpu = 0; cpu < cpus->nr; cpu++) { | 188 | for (cpu = 0; cpu < cpus->nr; cpu++) { |
189 | int group_fd = -1; | ||
190 | |||
191 | evsel->attr.inherit = (cpus->map[cpu] < 0) && inherit; | ||
192 | |||
140 | for (thread = 0; thread < threads->nr; thread++) { | 193 | for (thread = 0; thread < threads->nr; thread++) { |
141 | FD(evsel, cpu, thread) = sys_perf_event_open(&evsel->attr, | 194 | FD(evsel, cpu, thread) = sys_perf_event_open(&evsel->attr, |
142 | threads->map[thread], | 195 | threads->map[thread], |
143 | cpus->map[cpu], -1, 0); | 196 | cpus->map[cpu], |
197 | group_fd, 0); | ||
144 | if (FD(evsel, cpu, thread) < 0) | 198 | if (FD(evsel, cpu, thread) < 0) |
145 | goto out_close; | 199 | goto out_close; |
200 | |||
201 | if (group && group_fd == -1) | ||
202 | group_fd = FD(evsel, cpu, thread); | ||
146 | } | 203 | } |
147 | } | 204 | } |
148 | 205 | ||
@@ -175,10 +232,9 @@ static struct { | |||
175 | .threads = { -1, }, | 232 | .threads = { -1, }, |
176 | }; | 233 | }; |
177 | 234 | ||
178 | int perf_evsel__open(struct perf_evsel *evsel, | 235 | int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, |
179 | struct cpu_map *cpus, struct thread_map *threads) | 236 | struct thread_map *threads, bool group, bool inherit) |
180 | { | 237 | { |
181 | |||
182 | if (cpus == NULL) { | 238 | if (cpus == NULL) { |
183 | /* Work around old compiler warnings about strict aliasing */ | 239 | /* Work around old compiler warnings about strict aliasing */ |
184 | cpus = &empty_cpu_map.map; | 240 | cpus = &empty_cpu_map.map; |
@@ -187,15 +243,243 @@ int perf_evsel__open(struct perf_evsel *evsel, | |||
187 | if (threads == NULL) | 243 | if (threads == NULL) |
188 | threads = &empty_thread_map.map; | 244 | threads = &empty_thread_map.map; |
189 | 245 | ||
190 | return __perf_evsel__open(evsel, cpus, threads); | 246 | return __perf_evsel__open(evsel, cpus, threads, group, inherit); |
247 | } | ||
248 | |||
249 | int perf_evsel__open_per_cpu(struct perf_evsel *evsel, | ||
250 | struct cpu_map *cpus, bool group, bool inherit) | ||
251 | { | ||
252 | return __perf_evsel__open(evsel, cpus, &empty_thread_map.map, group, inherit); | ||
253 | } | ||
254 | |||
255 | int perf_evsel__open_per_thread(struct perf_evsel *evsel, | ||
256 | struct thread_map *threads, bool group, bool inherit) | ||
257 | { | ||
258 | return __perf_evsel__open(evsel, &empty_cpu_map.map, threads, group, inherit); | ||
259 | } | ||
260 | |||
261 | static int __perf_evlist__mmap(struct perf_evlist *evlist, int cpu, int prot, | ||
262 | int mask, int fd) | ||
263 | { | ||
264 | evlist->mmap[cpu].prev = 0; | ||
265 | evlist->mmap[cpu].mask = mask; | ||
266 | evlist->mmap[cpu].base = mmap(NULL, evlist->mmap_len, prot, | ||
267 | MAP_SHARED, fd, 0); | ||
268 | if (evlist->mmap[cpu].base == MAP_FAILED) | ||
269 | return -1; | ||
270 | |||
271 | perf_evlist__add_pollfd(evlist, fd); | ||
272 | return 0; | ||
273 | } | ||
274 | |||
275 | static int perf_evlist__id_hash(struct perf_evlist *evlist, struct perf_evsel *evsel, | ||
276 | int cpu, int thread, int fd) | ||
277 | { | ||
278 | struct perf_sample_id *sid; | ||
279 | u64 read_data[4] = { 0, }; | ||
280 | int hash, id_idx = 1; /* The first entry is the counter value */ | ||
281 | |||
282 | if (!(evsel->attr.read_format & PERF_FORMAT_ID) || | ||
283 | read(fd, &read_data, sizeof(read_data)) == -1) | ||
284 | return -1; | ||
285 | |||
286 | if (evsel->attr.read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) | ||
287 | ++id_idx; | ||
288 | if (evsel->attr.read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) | ||
289 | ++id_idx; | ||
290 | |||
291 | sid = SID(evsel, cpu, thread); | ||
292 | sid->id = read_data[id_idx]; | ||
293 | sid->evsel = evsel; | ||
294 | hash = hash_64(sid->id, PERF_EVLIST__HLIST_BITS); | ||
295 | hlist_add_head(&sid->node, &evlist->heads[hash]); | ||
296 | return 0; | ||
297 | } | ||
298 | |||
299 | /** perf_evlist__mmap - Create per cpu maps to receive events | ||
300 | * | ||
301 | * @evlist - list of events | ||
302 | * @cpus - cpu map being monitored | ||
303 | * @threads - threads map being monitored | ||
304 | * @pages - map length in pages | ||
305 | * @overwrite - overwrite older events? | ||
306 | * | ||
307 | * If overwrite is false the user needs to signal event consuption using: | ||
308 | * | ||
309 | * struct perf_mmap *m = &evlist->mmap[cpu]; | ||
310 | * unsigned int head = perf_mmap__read_head(m); | ||
311 | * | ||
312 | * perf_mmap__write_tail(m, head) | ||
313 | */ | ||
314 | int perf_evlist__mmap(struct perf_evlist *evlist, struct cpu_map *cpus, | ||
315 | struct thread_map *threads, int pages, bool overwrite) | ||
316 | { | ||
317 | unsigned int page_size = sysconf(_SC_PAGE_SIZE); | ||
318 | int mask = pages * page_size - 1, cpu; | ||
319 | struct perf_evsel *first_evsel, *evsel; | ||
320 | int thread, prot = PROT_READ | (overwrite ? 0 : PROT_WRITE); | ||
321 | |||
322 | if (evlist->mmap == NULL && | ||
323 | perf_evlist__alloc_mmap(evlist, cpus->nr) < 0) | ||
324 | return -ENOMEM; | ||
325 | |||
326 | if (evlist->pollfd == NULL && | ||
327 | perf_evlist__alloc_pollfd(evlist, cpus->nr, threads->nr) < 0) | ||
328 | return -ENOMEM; | ||
329 | |||
330 | evlist->mmap_len = (pages + 1) * page_size; | ||
331 | first_evsel = list_entry(evlist->entries.next, struct perf_evsel, node); | ||
332 | |||
333 | list_for_each_entry(evsel, &evlist->entries, node) { | ||
334 | if ((evsel->attr.read_format & PERF_FORMAT_ID) && | ||
335 | evsel->id == NULL && | ||
336 | perf_evsel__alloc_id(evsel, cpus->nr, threads->nr) < 0) | ||
337 | return -ENOMEM; | ||
338 | |||
339 | for (cpu = 0; cpu < cpus->nr; cpu++) { | ||
340 | for (thread = 0; thread < threads->nr; thread++) { | ||
341 | int fd = FD(evsel, cpu, thread); | ||
342 | |||
343 | if (evsel->idx || thread) { | ||
344 | if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, | ||
345 | FD(first_evsel, cpu, 0)) != 0) | ||
346 | goto out_unmap; | ||
347 | } else if (__perf_evlist__mmap(evlist, cpu, prot, mask, fd) < 0) | ||
348 | goto out_unmap; | ||
349 | |||
350 | if ((evsel->attr.read_format & PERF_FORMAT_ID) && | ||
351 | perf_evlist__id_hash(evlist, evsel, cpu, thread, fd) < 0) | ||
352 | goto out_unmap; | ||
353 | } | ||
354 | } | ||
355 | } | ||
356 | |||
357 | return 0; | ||
358 | |||
359 | out_unmap: | ||
360 | for (cpu = 0; cpu < cpus->nr; cpu++) { | ||
361 | if (evlist->mmap[cpu].base != NULL) { | ||
362 | munmap(evlist->mmap[cpu].base, evlist->mmap_len); | ||
363 | evlist->mmap[cpu].base = NULL; | ||
364 | } | ||
365 | } | ||
366 | return -1; | ||
191 | } | 367 | } |
192 | 368 | ||
193 | int perf_evsel__open_per_cpu(struct perf_evsel *evsel, struct cpu_map *cpus) | 369 | static int event__parse_id_sample(const event_t *event, u64 type, |
370 | struct sample_data *sample) | ||
194 | { | 371 | { |
195 | return __perf_evsel__open(evsel, cpus, &empty_thread_map.map); | 372 | const u64 *array = event->sample.array; |
373 | |||
374 | array += ((event->header.size - | ||
375 | sizeof(event->header)) / sizeof(u64)) - 1; | ||
376 | |||
377 | if (type & PERF_SAMPLE_CPU) { | ||
378 | u32 *p = (u32 *)array; | ||
379 | sample->cpu = *p; | ||
380 | array--; | ||
381 | } | ||
382 | |||
383 | if (type & PERF_SAMPLE_STREAM_ID) { | ||
384 | sample->stream_id = *array; | ||
385 | array--; | ||
386 | } | ||
387 | |||
388 | if (type & PERF_SAMPLE_ID) { | ||
389 | sample->id = *array; | ||
390 | array--; | ||
391 | } | ||
392 | |||
393 | if (type & PERF_SAMPLE_TIME) { | ||
394 | sample->time = *array; | ||
395 | array--; | ||
396 | } | ||
397 | |||
398 | if (type & PERF_SAMPLE_TID) { | ||
399 | u32 *p = (u32 *)array; | ||
400 | sample->pid = p[0]; | ||
401 | sample->tid = p[1]; | ||
402 | } | ||
403 | |||
404 | return 0; | ||
196 | } | 405 | } |
197 | 406 | ||
198 | int perf_evsel__open_per_thread(struct perf_evsel *evsel, struct thread_map *threads) | 407 | int event__parse_sample(const event_t *event, u64 type, bool sample_id_all, |
408 | struct sample_data *data) | ||
199 | { | 409 | { |
200 | return __perf_evsel__open(evsel, &empty_cpu_map.map, threads); | 410 | const u64 *array; |
411 | |||
412 | data->cpu = data->pid = data->tid = -1; | ||
413 | data->stream_id = data->id = data->time = -1ULL; | ||
414 | |||
415 | if (event->header.type != PERF_RECORD_SAMPLE) { | ||
416 | if (!sample_id_all) | ||
417 | return 0; | ||
418 | return event__parse_id_sample(event, type, data); | ||
419 | } | ||
420 | |||
421 | array = event->sample.array; | ||
422 | |||
423 | if (type & PERF_SAMPLE_IP) { | ||
424 | data->ip = event->ip.ip; | ||
425 | array++; | ||
426 | } | ||
427 | |||
428 | if (type & PERF_SAMPLE_TID) { | ||
429 | u32 *p = (u32 *)array; | ||
430 | data->pid = p[0]; | ||
431 | data->tid = p[1]; | ||
432 | array++; | ||
433 | } | ||
434 | |||
435 | if (type & PERF_SAMPLE_TIME) { | ||
436 | data->time = *array; | ||
437 | array++; | ||
438 | } | ||
439 | |||
440 | if (type & PERF_SAMPLE_ADDR) { | ||
441 | data->addr = *array; | ||
442 | array++; | ||
443 | } | ||
444 | |||
445 | data->id = -1ULL; | ||
446 | if (type & PERF_SAMPLE_ID) { | ||
447 | data->id = *array; | ||
448 | array++; | ||
449 | } | ||
450 | |||
451 | if (type & PERF_SAMPLE_STREAM_ID) { | ||
452 | data->stream_id = *array; | ||
453 | array++; | ||
454 | } | ||
455 | |||
456 | if (type & PERF_SAMPLE_CPU) { | ||
457 | u32 *p = (u32 *)array; | ||
458 | data->cpu = *p; | ||
459 | array++; | ||
460 | } | ||
461 | |||
462 | if (type & PERF_SAMPLE_PERIOD) { | ||
463 | data->period = *array; | ||
464 | array++; | ||
465 | } | ||
466 | |||
467 | if (type & PERF_SAMPLE_READ) { | ||
468 | fprintf(stderr, "PERF_SAMPLE_READ is unsuported for now\n"); | ||
469 | return -1; | ||
470 | } | ||
471 | |||
472 | if (type & PERF_SAMPLE_CALLCHAIN) { | ||
473 | data->callchain = (struct ip_callchain *)array; | ||
474 | array += 1 + data->callchain->nr; | ||
475 | } | ||
476 | |||
477 | if (type & PERF_SAMPLE_RAW) { | ||
478 | u32 *p = (u32 *)array; | ||
479 | data->raw_size = *p; | ||
480 | p++; | ||
481 | data->raw_data = p; | ||
482 | } | ||
483 | |||
484 | return 0; | ||
201 | } | 485 | } |