diff options
Diffstat (limited to 'tools/perf/util/evsel.c')
| -rw-r--r-- | tools/perf/util/evsel.c | 276 |
1 files changed, 248 insertions, 28 deletions
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 2eaae140def2..ffdd94e9c9c3 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
| @@ -8,7 +8,10 @@ | |||
| 8 | */ | 8 | */ |
| 9 | 9 | ||
| 10 | #include <byteswap.h> | 10 | #include <byteswap.h> |
| 11 | #include <linux/bitops.h> | ||
| 11 | #include "asm/bug.h" | 12 | #include "asm/bug.h" |
| 13 | #include "debugfs.h" | ||
| 14 | #include "event-parse.h" | ||
| 12 | #include "evsel.h" | 15 | #include "evsel.h" |
| 13 | #include "evlist.h" | 16 | #include "evlist.h" |
| 14 | #include "util.h" | 17 | #include "util.h" |
| @@ -16,9 +19,10 @@ | |||
| 16 | #include "thread_map.h" | 19 | #include "thread_map.h" |
| 17 | #include "target.h" | 20 | #include "target.h" |
| 18 | #include "../../../include/linux/hw_breakpoint.h" | 21 | #include "../../../include/linux/hw_breakpoint.h" |
| 22 | #include "../../include/linux/perf_event.h" | ||
| 23 | #include "perf_regs.h" | ||
| 19 | 24 | ||
| 20 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) | 25 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) |
| 21 | #define GROUP_FD(group_fd, cpu) (*(int *)xyarray__entry(group_fd, cpu, 0)) | ||
| 22 | 26 | ||
| 23 | static int __perf_evsel__sample_size(u64 sample_type) | 27 | static int __perf_evsel__sample_size(u64 sample_type) |
| 24 | { | 28 | { |
| @@ -66,7 +70,80 @@ struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx) | |||
| 66 | return evsel; | 70 | return evsel; |
| 67 | } | 71 | } |
| 68 | 72 | ||
| 69 | static const char *perf_evsel__hw_names[PERF_COUNT_HW_MAX] = { | 73 | struct event_format *event_format__new(const char *sys, const char *name) |
| 74 | { | ||
| 75 | int fd, n; | ||
| 76 | char *filename; | ||
| 77 | void *bf = NULL, *nbf; | ||
| 78 | size_t size = 0, alloc_size = 0; | ||
| 79 | struct event_format *format = NULL; | ||
| 80 | |||
| 81 | if (asprintf(&filename, "%s/%s/%s/format", tracing_events_path, sys, name) < 0) | ||
| 82 | goto out; | ||
| 83 | |||
| 84 | fd = open(filename, O_RDONLY); | ||
| 85 | if (fd < 0) | ||
| 86 | goto out_free_filename; | ||
| 87 | |||
| 88 | do { | ||
| 89 | if (size == alloc_size) { | ||
| 90 | alloc_size += BUFSIZ; | ||
| 91 | nbf = realloc(bf, alloc_size); | ||
| 92 | if (nbf == NULL) | ||
| 93 | goto out_free_bf; | ||
| 94 | bf = nbf; | ||
| 95 | } | ||
| 96 | |||
| 97 | n = read(fd, bf + size, BUFSIZ); | ||
| 98 | if (n < 0) | ||
| 99 | goto out_free_bf; | ||
| 100 | size += n; | ||
| 101 | } while (n > 0); | ||
| 102 | |||
| 103 | pevent_parse_format(&format, bf, size, sys); | ||
| 104 | |||
| 105 | out_free_bf: | ||
| 106 | free(bf); | ||
| 107 | close(fd); | ||
| 108 | out_free_filename: | ||
| 109 | free(filename); | ||
| 110 | out: | ||
| 111 | return format; | ||
| 112 | } | ||
| 113 | |||
| 114 | struct perf_evsel *perf_evsel__newtp(const char *sys, const char *name, int idx) | ||
| 115 | { | ||
| 116 | struct perf_evsel *evsel = zalloc(sizeof(*evsel)); | ||
| 117 | |||
| 118 | if (evsel != NULL) { | ||
| 119 | struct perf_event_attr attr = { | ||
| 120 | .type = PERF_TYPE_TRACEPOINT, | ||
| 121 | .sample_type = (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME | | ||
| 122 | PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD), | ||
| 123 | }; | ||
| 124 | |||
| 125 | if (asprintf(&evsel->name, "%s:%s", sys, name) < 0) | ||
| 126 | goto out_free; | ||
| 127 | |||
| 128 | evsel->tp_format = event_format__new(sys, name); | ||
| 129 | if (evsel->tp_format == NULL) | ||
| 130 | goto out_free; | ||
| 131 | |||
| 132 | event_attr_init(&attr); | ||
| 133 | attr.config = evsel->tp_format->id; | ||
| 134 | attr.sample_period = 1; | ||
| 135 | perf_evsel__init(evsel, &attr, idx); | ||
| 136 | } | ||
| 137 | |||
| 138 | return evsel; | ||
| 139 | |||
| 140 | out_free: | ||
| 141 | free(evsel->name); | ||
| 142 | free(evsel); | ||
| 143 | return NULL; | ||
| 144 | } | ||
| 145 | |||
| 146 | const char *perf_evsel__hw_names[PERF_COUNT_HW_MAX] = { | ||
| 70 | "cycles", | 147 | "cycles", |
| 71 | "instructions", | 148 | "instructions", |
| 72 | "cache-references", | 149 | "cache-references", |
| @@ -129,12 +206,12 @@ static int perf_evsel__hw_name(struct perf_evsel *evsel, char *bf, size_t size) | |||
| 129 | return r + perf_evsel__add_modifiers(evsel, bf + r, size - r); | 206 | return r + perf_evsel__add_modifiers(evsel, bf + r, size - r); |
| 130 | } | 207 | } |
| 131 | 208 | ||
| 132 | static const char *perf_evsel__sw_names[PERF_COUNT_SW_MAX] = { | 209 | const char *perf_evsel__sw_names[PERF_COUNT_SW_MAX] = { |
| 133 | "cpu-clock", | 210 | "cpu-clock", |
| 134 | "task-clock", | 211 | "task-clock", |
| 135 | "page-faults", | 212 | "page-faults", |
| 136 | "context-switches", | 213 | "context-switches", |
| 137 | "CPU-migrations", | 214 | "cpu-migrations", |
| 138 | "minor-faults", | 215 | "minor-faults", |
| 139 | "major-faults", | 216 | "major-faults", |
| 140 | "alignment-faults", | 217 | "alignment-faults", |
| @@ -317,7 +394,8 @@ const char *perf_evsel__name(struct perf_evsel *evsel) | |||
| 317 | break; | 394 | break; |
| 318 | 395 | ||
| 319 | default: | 396 | default: |
| 320 | scnprintf(bf, sizeof(bf), "%s", "unknown attr type"); | 397 | scnprintf(bf, sizeof(bf), "unknown attr type: %d", |
| 398 | evsel->attr.type); | ||
| 321 | break; | 399 | break; |
| 322 | } | 400 | } |
| 323 | 401 | ||
| @@ -367,9 +445,18 @@ void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts, | |||
| 367 | attr->mmap_data = track; | 445 | attr->mmap_data = track; |
| 368 | } | 446 | } |
| 369 | 447 | ||
| 370 | if (opts->call_graph) | 448 | if (opts->call_graph) { |
| 371 | attr->sample_type |= PERF_SAMPLE_CALLCHAIN; | 449 | attr->sample_type |= PERF_SAMPLE_CALLCHAIN; |
| 372 | 450 | ||
| 451 | if (opts->call_graph == CALLCHAIN_DWARF) { | ||
| 452 | attr->sample_type |= PERF_SAMPLE_REGS_USER | | ||
| 453 | PERF_SAMPLE_STACK_USER; | ||
| 454 | attr->sample_regs_user = PERF_REGS_MASK; | ||
| 455 | attr->sample_stack_user = opts->stack_dump_size; | ||
| 456 | attr->exclude_callchain_user = 1; | ||
| 457 | } | ||
| 458 | } | ||
| 459 | |||
| 373 | if (perf_target__has_cpu(&opts->target)) | 460 | if (perf_target__has_cpu(&opts->target)) |
| 374 | attr->sample_type |= PERF_SAMPLE_CPU; | 461 | attr->sample_type |= PERF_SAMPLE_CPU; |
| 375 | 462 | ||
| @@ -421,6 +508,24 @@ int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads) | |||
| 421 | return evsel->fd != NULL ? 0 : -ENOMEM; | 508 | return evsel->fd != NULL ? 0 : -ENOMEM; |
| 422 | } | 509 | } |
| 423 | 510 | ||
| 511 | int perf_evsel__set_filter(struct perf_evsel *evsel, int ncpus, int nthreads, | ||
| 512 | const char *filter) | ||
| 513 | { | ||
| 514 | int cpu, thread; | ||
| 515 | |||
| 516 | for (cpu = 0; cpu < ncpus; cpu++) { | ||
| 517 | for (thread = 0; thread < nthreads; thread++) { | ||
| 518 | int fd = FD(evsel, cpu, thread), | ||
| 519 | err = ioctl(fd, PERF_EVENT_IOC_SET_FILTER, filter); | ||
| 520 | |||
| 521 | if (err) | ||
| 522 | return err; | ||
| 523 | } | ||
| 524 | } | ||
| 525 | |||
| 526 | return 0; | ||
| 527 | } | ||
| 528 | |||
| 424 | int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads) | 529 | int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads) |
| 425 | { | 530 | { |
| 426 | evsel->sample_id = xyarray__new(ncpus, nthreads, sizeof(struct perf_sample_id)); | 531 | evsel->sample_id = xyarray__new(ncpus, nthreads, sizeof(struct perf_sample_id)); |
| @@ -481,6 +586,9 @@ void perf_evsel__delete(struct perf_evsel *evsel) | |||
| 481 | { | 586 | { |
| 482 | perf_evsel__exit(evsel); | 587 | perf_evsel__exit(evsel); |
| 483 | close_cgroup(evsel->cgrp); | 588 | close_cgroup(evsel->cgrp); |
| 589 | free(evsel->group_name); | ||
| 590 | if (evsel->tp_format) | ||
| 591 | pevent_free_format(evsel->tp_format); | ||
| 484 | free(evsel->name); | 592 | free(evsel->name); |
| 485 | free(evsel); | 593 | free(evsel); |
| 486 | } | 594 | } |
| @@ -556,9 +664,28 @@ int __perf_evsel__read(struct perf_evsel *evsel, | |||
| 556 | return 0; | 664 | return 0; |
| 557 | } | 665 | } |
| 558 | 666 | ||
| 667 | static int get_group_fd(struct perf_evsel *evsel, int cpu, int thread) | ||
| 668 | { | ||
| 669 | struct perf_evsel *leader = evsel->leader; | ||
| 670 | int fd; | ||
| 671 | |||
| 672 | if (!leader) | ||
| 673 | return -1; | ||
| 674 | |||
| 675 | /* | ||
| 676 | * Leader must be already processed/open, | ||
| 677 | * if not it's a bug. | ||
| 678 | */ | ||
| 679 | BUG_ON(!leader->fd); | ||
| 680 | |||
| 681 | fd = FD(leader, cpu, thread); | ||
| 682 | BUG_ON(fd == -1); | ||
| 683 | |||
| 684 | return fd; | ||
| 685 | } | ||
| 686 | |||
| 559 | static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, | 687 | static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, |
| 560 | struct thread_map *threads, bool group, | 688 | struct thread_map *threads) |
| 561 | struct xyarray *group_fds) | ||
| 562 | { | 689 | { |
| 563 | int cpu, thread; | 690 | int cpu, thread; |
| 564 | unsigned long flags = 0; | 691 | unsigned long flags = 0; |
| @@ -574,13 +701,15 @@ static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, | |||
| 574 | } | 701 | } |
| 575 | 702 | ||
| 576 | for (cpu = 0; cpu < cpus->nr; cpu++) { | 703 | for (cpu = 0; cpu < cpus->nr; cpu++) { |
| 577 | int group_fd = group_fds ? GROUP_FD(group_fds, cpu) : -1; | ||
| 578 | 704 | ||
| 579 | for (thread = 0; thread < threads->nr; thread++) { | 705 | for (thread = 0; thread < threads->nr; thread++) { |
| 706 | int group_fd; | ||
| 580 | 707 | ||
| 581 | if (!evsel->cgrp) | 708 | if (!evsel->cgrp) |
| 582 | pid = threads->map[thread]; | 709 | pid = threads->map[thread]; |
| 583 | 710 | ||
| 711 | group_fd = get_group_fd(evsel, cpu, thread); | ||
| 712 | |||
| 584 | FD(evsel, cpu, thread) = sys_perf_event_open(&evsel->attr, | 713 | FD(evsel, cpu, thread) = sys_perf_event_open(&evsel->attr, |
| 585 | pid, | 714 | pid, |
| 586 | cpus->map[cpu], | 715 | cpus->map[cpu], |
| @@ -589,9 +718,6 @@ static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, | |||
| 589 | err = -errno; | 718 | err = -errno; |
| 590 | goto out_close; | 719 | goto out_close; |
| 591 | } | 720 | } |
| 592 | |||
| 593 | if (group && group_fd == -1) | ||
| 594 | group_fd = FD(evsel, cpu, thread); | ||
| 595 | } | 721 | } |
| 596 | } | 722 | } |
| 597 | 723 | ||
| @@ -635,8 +761,7 @@ static struct { | |||
| 635 | }; | 761 | }; |
| 636 | 762 | ||
| 637 | int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, | 763 | int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, |
| 638 | struct thread_map *threads, bool group, | 764 | struct thread_map *threads) |
| 639 | struct xyarray *group_fd) | ||
| 640 | { | 765 | { |
| 641 | if (cpus == NULL) { | 766 | if (cpus == NULL) { |
| 642 | /* Work around old compiler warnings about strict aliasing */ | 767 | /* Work around old compiler warnings about strict aliasing */ |
| @@ -646,30 +771,28 @@ int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, | |||
| 646 | if (threads == NULL) | 771 | if (threads == NULL) |
| 647 | threads = &empty_thread_map.map; | 772 | threads = &empty_thread_map.map; |
| 648 | 773 | ||
| 649 | return __perf_evsel__open(evsel, cpus, threads, group, group_fd); | 774 | return __perf_evsel__open(evsel, cpus, threads); |
| 650 | } | 775 | } |
| 651 | 776 | ||
| 652 | int perf_evsel__open_per_cpu(struct perf_evsel *evsel, | 777 | int perf_evsel__open_per_cpu(struct perf_evsel *evsel, |
| 653 | struct cpu_map *cpus, bool group, | 778 | struct cpu_map *cpus) |
| 654 | struct xyarray *group_fd) | ||
| 655 | { | 779 | { |
| 656 | return __perf_evsel__open(evsel, cpus, &empty_thread_map.map, group, | 780 | return __perf_evsel__open(evsel, cpus, &empty_thread_map.map); |
| 657 | group_fd); | ||
| 658 | } | 781 | } |
| 659 | 782 | ||
| 660 | int perf_evsel__open_per_thread(struct perf_evsel *evsel, | 783 | int perf_evsel__open_per_thread(struct perf_evsel *evsel, |
| 661 | struct thread_map *threads, bool group, | 784 | struct thread_map *threads) |
| 662 | struct xyarray *group_fd) | ||
| 663 | { | 785 | { |
| 664 | return __perf_evsel__open(evsel, &empty_cpu_map.map, threads, group, | 786 | return __perf_evsel__open(evsel, &empty_cpu_map.map, threads); |
| 665 | group_fd); | ||
| 666 | } | 787 | } |
| 667 | 788 | ||
| 668 | static int perf_event__parse_id_sample(const union perf_event *event, u64 type, | 789 | static int perf_evsel__parse_id_sample(const struct perf_evsel *evsel, |
| 669 | struct perf_sample *sample, | 790 | const union perf_event *event, |
| 670 | bool swapped) | 791 | struct perf_sample *sample) |
| 671 | { | 792 | { |
| 793 | u64 type = evsel->attr.sample_type; | ||
| 672 | const u64 *array = event->sample.array; | 794 | const u64 *array = event->sample.array; |
| 795 | bool swapped = evsel->needs_swap; | ||
| 673 | union u64_swap u; | 796 | union u64_swap u; |
| 674 | 797 | ||
| 675 | array += ((event->header.size - | 798 | array += ((event->header.size - |
| @@ -730,9 +853,11 @@ static bool sample_overlap(const union perf_event *event, | |||
| 730 | } | 853 | } |
| 731 | 854 | ||
| 732 | int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event, | 855 | int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event, |
| 733 | struct perf_sample *data, bool swapped) | 856 | struct perf_sample *data) |
| 734 | { | 857 | { |
| 735 | u64 type = evsel->attr.sample_type; | 858 | u64 type = evsel->attr.sample_type; |
| 859 | u64 regs_user = evsel->attr.sample_regs_user; | ||
| 860 | bool swapped = evsel->needs_swap; | ||
| 736 | const u64 *array; | 861 | const u64 *array; |
| 737 | 862 | ||
| 738 | /* | 863 | /* |
| @@ -749,7 +874,7 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event, | |||
| 749 | if (event->header.type != PERF_RECORD_SAMPLE) { | 874 | if (event->header.type != PERF_RECORD_SAMPLE) { |
| 750 | if (!evsel->attr.sample_id_all) | 875 | if (!evsel->attr.sample_id_all) |
| 751 | return 0; | 876 | return 0; |
| 752 | return perf_event__parse_id_sample(event, type, data, swapped); | 877 | return perf_evsel__parse_id_sample(evsel, event, data); |
| 753 | } | 878 | } |
| 754 | 879 | ||
| 755 | array = event->sample.array; | 880 | array = event->sample.array; |
| @@ -869,6 +994,32 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event, | |||
| 869 | sz /= sizeof(u64); | 994 | sz /= sizeof(u64); |
| 870 | array += sz; | 995 | array += sz; |
| 871 | } | 996 | } |
| 997 | |||
| 998 | if (type & PERF_SAMPLE_REGS_USER) { | ||
| 999 | /* First u64 tells us if we have any regs in sample. */ | ||
| 1000 | u64 avail = *array++; | ||
| 1001 | |||
| 1002 | if (avail) { | ||
| 1003 | data->user_regs.regs = (u64 *)array; | ||
| 1004 | array += hweight_long(regs_user); | ||
| 1005 | } | ||
| 1006 | } | ||
| 1007 | |||
| 1008 | if (type & PERF_SAMPLE_STACK_USER) { | ||
| 1009 | u64 size = *array++; | ||
| 1010 | |||
| 1011 | data->user_stack.offset = ((char *)(array - 1) | ||
| 1012 | - (char *) event); | ||
| 1013 | |||
| 1014 | if (!size) { | ||
| 1015 | data->user_stack.size = 0; | ||
| 1016 | } else { | ||
| 1017 | data->user_stack.data = (char *)array; | ||
| 1018 | array += size / sizeof(*array); | ||
| 1019 | data->user_stack.size = *array; | ||
| 1020 | } | ||
| 1021 | } | ||
| 1022 | |||
| 872 | return 0; | 1023 | return 0; |
| 873 | } | 1024 | } |
| 874 | 1025 | ||
| @@ -947,3 +1098,72 @@ int perf_event__synthesize_sample(union perf_event *event, u64 type, | |||
| 947 | 1098 | ||
| 948 | return 0; | 1099 | return 0; |
| 949 | } | 1100 | } |
| 1101 | |||
| 1102 | struct format_field *perf_evsel__field(struct perf_evsel *evsel, const char *name) | ||
| 1103 | { | ||
| 1104 | return pevent_find_field(evsel->tp_format, name); | ||
| 1105 | } | ||
| 1106 | |||
| 1107 | void *perf_evsel__rawptr(struct perf_evsel *evsel, struct perf_sample *sample, | ||
| 1108 | const char *name) | ||
| 1109 | { | ||
| 1110 | struct format_field *field = perf_evsel__field(evsel, name); | ||
| 1111 | int offset; | ||
| 1112 | |||
| 1113 | if (!field) | ||
| 1114 | return NULL; | ||
| 1115 | |||
| 1116 | offset = field->offset; | ||
| 1117 | |||
| 1118 | if (field->flags & FIELD_IS_DYNAMIC) { | ||
| 1119 | offset = *(int *)(sample->raw_data + field->offset); | ||
| 1120 | offset &= 0xffff; | ||
| 1121 | } | ||
| 1122 | |||
| 1123 | return sample->raw_data + offset; | ||
| 1124 | } | ||
| 1125 | |||
| 1126 | u64 perf_evsel__intval(struct perf_evsel *evsel, struct perf_sample *sample, | ||
| 1127 | const char *name) | ||
| 1128 | { | ||
| 1129 | struct format_field *field = perf_evsel__field(evsel, name); | ||
| 1130 | void *ptr; | ||
| 1131 | u64 value; | ||
| 1132 | |||
| 1133 | if (!field) | ||
| 1134 | return 0; | ||
| 1135 | |||
| 1136 | ptr = sample->raw_data + field->offset; | ||
| 1137 | |||
| 1138 | switch (field->size) { | ||
| 1139 | case 1: | ||
| 1140 | return *(u8 *)ptr; | ||
| 1141 | case 2: | ||
| 1142 | value = *(u16 *)ptr; | ||
| 1143 | break; | ||
| 1144 | case 4: | ||
| 1145 | value = *(u32 *)ptr; | ||
| 1146 | break; | ||
| 1147 | case 8: | ||
| 1148 | value = *(u64 *)ptr; | ||
| 1149 | break; | ||
| 1150 | default: | ||
| 1151 | return 0; | ||
| 1152 | } | ||
| 1153 | |||
| 1154 | if (!evsel->needs_swap) | ||
| 1155 | return value; | ||
| 1156 | |||
| 1157 | switch (field->size) { | ||
| 1158 | case 2: | ||
| 1159 | return bswap_16(value); | ||
| 1160 | case 4: | ||
| 1161 | return bswap_32(value); | ||
| 1162 | case 8: | ||
| 1163 | return bswap_64(value); | ||
| 1164 | default: | ||
| 1165 | return 0; | ||
| 1166 | } | ||
| 1167 | |||
| 1168 | return 0; | ||
| 1169 | } | ||
