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 @@ */ #include <byteswap.h> +#include <linux/bitops.h> #include "asm/bug.h" +#include "debugfs.h" +#include "event-parse.h" #include "evsel.h" #include "evlist.h" #include "util.h" @@ -16,9 +19,10 @@ #include "thread_map.h" #include "target.h" #include "../../../include/linux/hw_breakpoint.h" +#include "../../include/linux/perf_event.h" +#include "perf_regs.h" #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) -#define GROUP_FD(group_fd, cpu) (*(int *)xyarray__entry(group_fd, cpu, 0)) static int __perf_evsel__sample_size(u64 sample_type) { @@ -66,7 +70,80 @@ struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx) return evsel; } -static const char *perf_evsel__hw_names[PERF_COUNT_HW_MAX] = { +struct event_format *event_format__new(const char *sys, const char *name) +{ + int fd, n; + char *filename; + void *bf = NULL, *nbf; + size_t size = 0, alloc_size = 0; + struct event_format *format = NULL; + + if (asprintf(&filename, "%s/%s/%s/format", tracing_events_path, sys, name) < 0) + goto out; + + fd = open(filename, O_RDONLY); + if (fd < 0) + goto out_free_filename; + + do { + if (size == alloc_size) { + alloc_size += BUFSIZ; + nbf = realloc(bf, alloc_size); + if (nbf == NULL) + goto out_free_bf; + bf = nbf; + } + + n = read(fd, bf + size, BUFSIZ); + if (n < 0) + goto out_free_bf; + size += n; + } while (n > 0); + + pevent_parse_format(&format, bf, size, sys); + +out_free_bf: + free(bf); + close(fd); +out_free_filename: + free(filename); +out: + return format; +} + +struct perf_evsel *perf_evsel__newtp(const char *sys, const char *name, int idx) +{ + struct perf_evsel *evsel = zalloc(sizeof(*evsel)); + + if (evsel != NULL) { + struct perf_event_attr attr = { + .type = PERF_TYPE_TRACEPOINT, + .sample_type = (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME | + PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD), + }; + + if (asprintf(&evsel->name, "%s:%s", sys, name) < 0) + goto out_free; + + evsel->tp_format = event_format__new(sys, name); + if (evsel->tp_format == NULL) + goto out_free; + + event_attr_init(&attr); + attr.config = evsel->tp_format->id; + attr.sample_period = 1; + perf_evsel__init(evsel, &attr, idx); + } + + return evsel; + +out_free: + free(evsel->name); + free(evsel); + return NULL; +} + +const char *perf_evsel__hw_names[PERF_COUNT_HW_MAX] = { "cycles", "instructions", "cache-references", @@ -129,12 +206,12 @@ static int perf_evsel__hw_name(struct perf_evsel *evsel, char *bf, size_t size) return r + perf_evsel__add_modifiers(evsel, bf + r, size - r); } -static const char *perf_evsel__sw_names[PERF_COUNT_SW_MAX] = { +const char *perf_evsel__sw_names[PERF_COUNT_SW_MAX] = { "cpu-clock", "task-clock", "page-faults", "context-switches", - "CPU-migrations", + "cpu-migrations", "minor-faults", "major-faults", "alignment-faults", @@ -317,7 +394,8 @@ const char *perf_evsel__name(struct perf_evsel *evsel) break; default: - scnprintf(bf, sizeof(bf), "%s", "unknown attr type"); + scnprintf(bf, sizeof(bf), "unknown attr type: %d", + evsel->attr.type); break; } @@ -367,9 +445,18 @@ void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts, attr->mmap_data = track; } - if (opts->call_graph) + if (opts->call_graph) { attr->sample_type |= PERF_SAMPLE_CALLCHAIN; + if (opts->call_graph == CALLCHAIN_DWARF) { + attr->sample_type |= PERF_SAMPLE_REGS_USER | + PERF_SAMPLE_STACK_USER; + attr->sample_regs_user = PERF_REGS_MASK; + attr->sample_stack_user = opts->stack_dump_size; + attr->exclude_callchain_user = 1; + } + } + if (perf_target__has_cpu(&opts->target)) attr->sample_type |= PERF_SAMPLE_CPU; @@ -421,6 +508,24 @@ int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads) return evsel->fd != NULL ? 0 : -ENOMEM; } +int perf_evsel__set_filter(struct perf_evsel *evsel, int ncpus, int nthreads, + const char *filter) +{ + int cpu, thread; + + for (cpu = 0; cpu < ncpus; cpu++) { + for (thread = 0; thread < nthreads; thread++) { + int fd = FD(evsel, cpu, thread), + err = ioctl(fd, PERF_EVENT_IOC_SET_FILTER, filter); + + if (err) + return err; + } + } + + return 0; +} + int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads) { evsel->sample_id = xyarray__new(ncpus, nthreads, sizeof(struct perf_sample_id)); @@ -481,6 +586,9 @@ void perf_evsel__delete(struct perf_evsel *evsel) { perf_evsel__exit(evsel); close_cgroup(evsel->cgrp); + free(evsel->group_name); + if (evsel->tp_format) + pevent_free_format(evsel->tp_format); free(evsel->name); free(evsel); } @@ -556,9 +664,28 @@ int __perf_evsel__read(struct perf_evsel *evsel, return 0; } +static int get_group_fd(struct perf_evsel *evsel, int cpu, int thread) +{ + struct perf_evsel *leader = evsel->leader; + int fd; + + if (!leader) + return -1; + + /* + * Leader must be already processed/open, + * if not it's a bug. + */ + BUG_ON(!leader->fd); + + fd = FD(leader, cpu, thread); + BUG_ON(fd == -1); + + return fd; +} + static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, - struct thread_map *threads, bool group, - struct xyarray *group_fds) + struct thread_map *threads) { int cpu, thread; unsigned long flags = 0; @@ -574,13 +701,15 @@ static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, } for (cpu = 0; cpu < cpus->nr; cpu++) { - int group_fd = group_fds ? GROUP_FD(group_fds, cpu) : -1; for (thread = 0; thread < threads->nr; thread++) { + int group_fd; if (!evsel->cgrp) pid = threads->map[thread]; + group_fd = get_group_fd(evsel, cpu, thread); + FD(evsel, cpu, thread) = sys_perf_event_open(&evsel->attr, pid, cpus->map[cpu], @@ -589,9 +718,6 @@ static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, err = -errno; goto out_close; } - - if (group && group_fd == -1) - group_fd = FD(evsel, cpu, thread); } } @@ -635,8 +761,7 @@ static struct { }; int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, - struct thread_map *threads, bool group, - struct xyarray *group_fd) + struct thread_map *threads) { if (cpus == NULL) { /* Work around old compiler warnings about strict aliasing */ @@ -646,30 +771,28 @@ int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, if (threads == NULL) threads = &empty_thread_map.map; - return __perf_evsel__open(evsel, cpus, threads, group, group_fd); + return __perf_evsel__open(evsel, cpus, threads); } int perf_evsel__open_per_cpu(struct perf_evsel *evsel, - struct cpu_map *cpus, bool group, - struct xyarray *group_fd) + struct cpu_map *cpus) { - return __perf_evsel__open(evsel, cpus, &empty_thread_map.map, group, - group_fd); + return __perf_evsel__open(evsel, cpus, &empty_thread_map.map); } int perf_evsel__open_per_thread(struct perf_evsel *evsel, - struct thread_map *threads, bool group, - struct xyarray *group_fd) + struct thread_map *threads) { - return __perf_evsel__open(evsel, &empty_cpu_map.map, threads, group, - group_fd); + return __perf_evsel__open(evsel, &empty_cpu_map.map, threads); } -static int perf_event__parse_id_sample(const union perf_event *event, u64 type, - struct perf_sample *sample, - bool swapped) +static int perf_evsel__parse_id_sample(const struct perf_evsel *evsel, + const union perf_event *event, + struct perf_sample *sample) { + u64 type = evsel->attr.sample_type; const u64 *array = event->sample.array; + bool swapped = evsel->needs_swap; union u64_swap u; array += ((event->header.size - @@ -730,9 +853,11 @@ static bool sample_overlap(const union perf_event *event, } int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event, - struct perf_sample *data, bool swapped) + struct perf_sample *data) { u64 type = evsel->attr.sample_type; + u64 regs_user = evsel->attr.sample_regs_user; + bool swapped = evsel->needs_swap; const u64 *array; /* @@ -749,7 +874,7 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event, if (event->header.type != PERF_RECORD_SAMPLE) { if (!evsel->attr.sample_id_all) return 0; - return perf_event__parse_id_sample(event, type, data, swapped); + return perf_evsel__parse_id_sample(evsel, event, data); } array = event->sample.array; @@ -869,6 +994,32 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event, sz /= sizeof(u64); array += sz; } + + if (type & PERF_SAMPLE_REGS_USER) { + /* First u64 tells us if we have any regs in sample. */ + u64 avail = *array++; + + if (avail) { + data->user_regs.regs = (u64 *)array; + array += hweight_long(regs_user); + } + } + + if (type & PERF_SAMPLE_STACK_USER) { + u64 size = *array++; + + data->user_stack.offset = ((char *)(array - 1) + - (char *) event); + + if (!size) { + data->user_stack.size = 0; + } else { + data->user_stack.data = (char *)array; + array += size / sizeof(*array); + data->user_stack.size = *array; + } + } + return 0; } @@ -947,3 +1098,72 @@ int perf_event__synthesize_sample(union perf_event *event, u64 type, return 0; } + +struct format_field *perf_evsel__field(struct perf_evsel *evsel, const char *name) +{ + return pevent_find_field(evsel->tp_format, name); +} + +void *perf_evsel__rawptr(struct perf_evsel *evsel, struct perf_sample *sample, + const char *name) +{ + struct format_field *field = perf_evsel__field(evsel, name); + int offset; + + if (!field) + return NULL; + + offset = field->offset; + + if (field->flags & FIELD_IS_DYNAMIC) { + offset = *(int *)(sample->raw_data + field->offset); + offset &= 0xffff; + } + + return sample->raw_data + offset; +} + +u64 perf_evsel__intval(struct perf_evsel *evsel, struct perf_sample *sample, + const char *name) +{ + struct format_field *field = perf_evsel__field(evsel, name); + void *ptr; + u64 value; + + if (!field) + return 0; + + ptr = sample->raw_data + field->offset; + + switch (field->size) { + case 1: + return *(u8 *)ptr; + case 2: + value = *(u16 *)ptr; + break; + case 4: + value = *(u32 *)ptr; + break; + case 8: + value = *(u64 *)ptr; + break; + default: + return 0; + } + + if (!evsel->needs_swap) + return value; + + switch (field->size) { + case 2: + return bswap_16(value); + case 4: + return bswap_32(value); + case 8: + return bswap_64(value); + default: + return 0; + } + + return 0; +} |