Messages in this thread | | | Date | Wed, 1 Apr 2015 13:51:25 +0200 | From | Jiri Olsa <> | Subject | Re: [PATCH V7 04/25] perf tools: Add support for AUX area recording |
| |
On Tue, Mar 31, 2015 at 02:38:33PM +0300, Adrian Hunter wrote: > Add support for reading from the AUX area > tracing mmap and synthesizing AUX area > tracing events. > > This patch introduces an abstraction for recording > AUX area data. Recording is initialized > by auxtrace_record__init() which is a weak function > to be implemented by the architecture to provide > recording callbacks. Recording is mainly handled > by auxtrace_mmap__read() and > perf_event__synthesize_auxtrace() but there are > callbacks for miscellaneous needs including > validating and processing user options, populating > private data in auxtrace_info_event, and freeing > the structure when finished. > > Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Acked-by: Jiri Olsa <jolsa@kernel.org>
> --- > tools/perf/perf.h | 2 + > tools/perf/util/auxtrace.c | 176 +++++++++++++++++++++++++++++++++++++++++++++ > tools/perf/util/auxtrace.h | 56 ++++++++++++++- > tools/perf/util/record.c | 11 ++- > 4 files changed, 243 insertions(+), 2 deletions(-) > > diff --git a/tools/perf/perf.h b/tools/perf/perf.h > index c38a085..95a3bd4 100644 > --- a/tools/perf/perf.h > +++ b/tools/perf/perf.h > @@ -54,8 +54,10 @@ struct record_opts { > bool period; > bool sample_intr_regs; > bool running_time; > + bool full_auxtrace; > unsigned int freq; > unsigned int mmap_pages; > + unsigned int auxtrace_mmap_pages; > unsigned int user_freq; > u64 branch_stack; > u64 default_interval; > diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c > index 75419a5..b3ed200 100644 > --- a/tools/perf/util/auxtrace.c > +++ b/tools/perf/util/auxtrace.c > @@ -23,6 +23,10 @@ > #include <linux/bitops.h> > #include <linux/log2.h> > > +#include <stdlib.h> > +#include <string.h> > +#include <errno.h> > + > #include "../perf.h" > #include "util.h" > #include "evlist.h" > @@ -31,6 +35,9 @@ > #include "asm/bug.h" > #include "auxtrace.h" > > +#include "event.h" > +#include "debug.h" > + > int auxtrace_mmap__mmap(struct auxtrace_mmap *mm, > struct auxtrace_mmap_params *mp, > void *userpg, int fd) > @@ -106,3 +113,172 @@ void auxtrace_mmap_params__set_idx(struct auxtrace_mmap_params *mp, > mp->tid = evlist->threads->map[idx]; > } > } > + > +size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr) > +{ > + if (itr) > + return itr->info_priv_size(itr); > + return 0; > +} > + > +static int auxtrace_not_supported(void) > +{ > + pr_err("AUX area tracing is not supported on this architecture\n"); > + return -EINVAL; > +} > + > +int auxtrace_record__info_fill(struct auxtrace_record *itr, > + struct perf_session *session, > + struct auxtrace_info_event *auxtrace_info, > + size_t priv_size) > +{ > + if (itr) > + return itr->info_fill(itr, session, auxtrace_info, priv_size); > + return auxtrace_not_supported(); > +} > + > +void auxtrace_record__free(struct auxtrace_record *itr) > +{ > + if (itr) > + itr->free(itr); > +} > + > +int auxtrace_record__options(struct auxtrace_record *itr, > + struct perf_evlist *evlist, > + struct record_opts *opts) > +{ > + if (itr) > + return itr->recording_options(itr, evlist, opts); > + return 0; > +} > + > +u64 auxtrace_record__reference(struct auxtrace_record *itr) > +{ > + if (itr) > + return itr->reference(itr); > + return 0; > +} > + > +struct auxtrace_record *__weak > +auxtrace_record__init(struct perf_evlist *evlist __maybe_unused, int *err) > +{ > + *err = 0; > + return NULL; > +} > + > +int perf_event__synthesize_auxtrace_info(struct auxtrace_record *itr, > + struct perf_tool *tool, > + struct perf_session *session, > + perf_event__handler_t process) > +{ > + union perf_event *ev; > + size_t priv_size; > + int err; > + > + pr_debug2("Synthesizing auxtrace information\n"); > + priv_size = auxtrace_record__info_priv_size(itr); > + ev = zalloc(sizeof(struct auxtrace_info_event) + priv_size); > + if (!ev) > + return -ENOMEM; > + > + ev->auxtrace_info.header.type = PERF_RECORD_AUXTRACE_INFO; > + ev->auxtrace_info.header.size = sizeof(struct auxtrace_info_event) + > + priv_size; > + err = auxtrace_record__info_fill(itr, session, &ev->auxtrace_info, > + priv_size); > + if (err) > + goto out_free; > + > + err = process(tool, ev, NULL, NULL); > +out_free: > + free(ev); > + return err; > +} > + > +int auxtrace_mmap__read(struct auxtrace_mmap *mm, struct auxtrace_record *itr, > + struct perf_tool *tool, process_auxtrace_t fn) > +{ > + u64 head = auxtrace_mmap__read_head(mm); > + u64 old = mm->prev, offset, ref; > + unsigned char *data = mm->base; > + size_t size, head_off, old_off, len1, len2, padding; > + union perf_event ev; > + void *data1, *data2; > + > + if (old == head) > + return 0; > + > + pr_debug3("auxtrace idx %d old %#"PRIx64" head %#"PRIx64" diff %#"PRIx64"\n", > + mm->idx, old, head, head - old); > + > + if (mm->mask) { > + head_off = head & mm->mask; > + old_off = old & mm->mask; > + } else { > + head_off = head % mm->len; > + old_off = old % mm->len; > + } > + > + if (head_off > old_off) > + size = head_off - old_off; > + else > + size = mm->len - (old_off - head_off); > + > + ref = auxtrace_record__reference(itr); > + > + if (head > old || size <= head || mm->mask) { > + offset = head - size; > + } else { > + /* > + * When the buffer size is not a power of 2, 'head' wraps at the > + * highest multiple of the buffer size, so we have to subtract > + * the remainder here. > + */ > + u64 rem = (0ULL - mm->len) % mm->len; > + > + offset = head - size - rem; > + } > + > + if (size > head_off) { > + len1 = size - head_off; > + data1 = &data[mm->len - len1]; > + len2 = head_off; > + data2 = &data[0]; > + } else { > + len1 = size; > + data1 = &data[head_off - len1]; > + len2 = 0; > + data2 = NULL; > + } > + > + /* padding must be written by fn() e.g. record__process_auxtrace() */ > + padding = size & 7; > + if (padding) > + padding = 8 - padding; > + > + memset(&ev, 0, sizeof(ev)); > + ev.auxtrace.header.type = PERF_RECORD_AUXTRACE; > + ev.auxtrace.header.size = sizeof(ev.auxtrace); > + ev.auxtrace.size = size + padding; > + ev.auxtrace.offset = offset; > + ev.auxtrace.reference = ref; > + ev.auxtrace.idx = mm->idx; > + ev.auxtrace.tid = mm->tid; > + ev.auxtrace.cpu = mm->cpu; > + > + if (fn(tool, &ev, data1, len1, data2, len2)) > + return -1; > + > + mm->prev = head; > + > + auxtrace_mmap__write_tail(mm, head); > + if (itr->read_finish) { > + int err; > + > + err = itr->read_finish(itr, mm->idx); > + if (err < 0) > + return err; > + } > + > + return 1; > +} > diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h > index 735ca2a..e00f426 100644 > --- a/tools/perf/util/auxtrace.h > +++ b/tools/perf/util/auxtrace.h > @@ -18,13 +18,18 @@ > > #include <sys/types.h> > #include <stdbool.h> > - > +#include <stddef.h> > #include <linux/perf_event.h> > #include <linux/types.h> > > #include "../perf.h" > > +union perf_event; > +struct perf_session; > struct perf_evlist; > +struct perf_tool; > +struct record_opts; > +struct auxtrace_info_event; > > /** > * struct auxtrace_mmap - records an mmap of the auxtrace buffer. > @@ -70,6 +75,29 @@ struct auxtrace_mmap_params { > int cpu; > }; > > +/** > + * struct auxtrace_record - callbacks for recording AUX area data. > + * @recording_options: validate and process recording options > + * @info_priv_size: return the size of the private data in auxtrace_info_event > + * @info_fill: fill-in the private data in auxtrace_info_event > + * @free: free this auxtrace record structure > + * @reference: provide a 64-bit reference number for auxtrace_event > + * @read_finish: called after reading from an auxtrace mmap > + */ > +struct auxtrace_record { > + int (*recording_options)(struct auxtrace_record *itr, > + struct perf_evlist *evlist, > + struct record_opts *opts); > + size_t (*info_priv_size)(struct auxtrace_record *itr); > + int (*info_fill)(struct auxtrace_record *itr, > + struct perf_session *session, > + struct auxtrace_info_event *auxtrace_info, > + size_t priv_size); > + void (*free)(struct auxtrace_record *itr); > + u64 (*reference)(struct auxtrace_record *itr); > + int (*read_finish)(struct auxtrace_record *itr, int idx); > +}; > + > static inline u64 auxtrace_mmap__read_head(struct auxtrace_mmap *mm __maybe_unused) > { > /* Not yet implemented */ > @@ -94,4 +122,30 @@ void auxtrace_mmap_params__set_idx(struct auxtrace_mmap_params *mp, > struct perf_evlist *evlist, int idx, > bool per_cpu); > > +typedef int (*process_auxtrace_t)(struct perf_tool *tool, > + union perf_event *event, void *data1, > + size_t len1, void *data2, size_t len2); > + > +int auxtrace_mmap__read(struct auxtrace_mmap *mm, struct auxtrace_record *itr, > + struct perf_tool *tool, process_auxtrace_t fn); > + > +struct auxtrace_record *auxtrace_record__init(struct perf_evlist *evlist, > + int *err); > + > +int auxtrace_record__options(struct auxtrace_record *itr, > + struct perf_evlist *evlist, > + struct record_opts *opts); > +size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr); > +int auxtrace_record__info_fill(struct auxtrace_record *itr, > + struct perf_session *session, > + struct auxtrace_info_event *auxtrace_info, > + size_t priv_size); > +void auxtrace_record__free(struct auxtrace_record *itr); > +u64 auxtrace_record__reference(struct auxtrace_record *itr); > + > +int perf_event__synthesize_auxtrace_info(struct auxtrace_record *itr, > + struct perf_tool *tool, > + struct perf_session *session, > + perf_event__handler_t process); > + > #endif > diff --git a/tools/perf/util/record.c b/tools/perf/util/record.c > index 8acd0df..0ccfa49 100644 > --- a/tools/perf/util/record.c > +++ b/tools/perf/util/record.c > @@ -119,7 +119,16 @@ void perf_evlist__config(struct perf_evlist *evlist, struct record_opts *opts) > evsel->attr.comm_exec = 1; > } > > - if (evlist->nr_entries > 1) { > + if (opts->full_auxtrace) { > + /* > + * Need to be able to synthesize and parse selected events with > + * arbitrary sample types, which requires always being able to > + * match the id. > + */ > + use_sample_identifier = perf_can_sample_identifier(); > + evlist__for_each(evlist, evsel) > + perf_evsel__set_sample_id(evsel, use_sample_identifier); > + } else if (evlist->nr_entries > 1) { > struct perf_evsel *first = perf_evlist__first(evlist); > > evlist__for_each(evlist, evsel) { > -- > 1.9.1 >
| |