Commit 99fa2984 authored by Adrian Hunter's avatar Adrian Hunter Committed by Arnaldo Carvalho de Melo

perf tools: Add AUX area tracing index

Add an index of AUX area tracing events within a perf.data file.

perf record uses a special user event PERF_RECORD_FINISHED_ROUND to
enable sorting of events in chunks instead of having to sort all events
altogether.

AUX area tracing events contain data that can span back to the very
beginning of the recording period. i.e. they do not obey the rules of
PERF_RECORD_FINISHED_ROUND.

By adding an index, AUX area tracing events can be found in advance and
the PERF_RECORD_FINISHED_ROUND approach works as usual.

The index is recorded with the auxtrace feature in the perf.data file.
A session reads the index but does not process it.  An AUX area decoder
can queue all the AUX area data in advance using
auxtrace_queues__process_index() or otherwise process the index in some
custom manner.
Signed-off-by: default avatarAdrian Hunter <adrian.hunter@intel.com>
Acked-by: default avatarJiri Olsa <jolsa@kernel.org>
Cc: David Ahern <dsahern@gmail.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Namhyung Kim <namhyung@gmail.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Link: http://lkml.kernel.org/r/1430404667-10593-3-git-send-email-adrian.hunter@intel.comSigned-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent 64a7e61f
...@@ -122,6 +122,18 @@ static s64 perf_event__repipe_auxtrace(struct perf_tool *tool, ...@@ -122,6 +122,18 @@ static s64 perf_event__repipe_auxtrace(struct perf_tool *tool,
tool); tool);
int ret; int ret;
if (!inject->output.is_pipe) {
off_t offset;
offset = lseek(inject->output.fd, 0, SEEK_CUR);
if (offset == -1)
return -errno;
ret = auxtrace_index__auxtrace_event(&session->auxtrace_index,
event, offset);
if (ret < 0)
return ret;
}
if (perf_data_file__is_pipe(session->file) || !session->one_mmap) { if (perf_data_file__is_pipe(session->file) || !session->one_mmap) {
ret = output_bytes(inject, event, event->header.size); ret = output_bytes(inject, event, event->header.size);
if (ret < 0) if (ret < 0)
...@@ -487,6 +499,9 @@ static int __cmd_inject(struct perf_inject *inject) ...@@ -487,6 +499,9 @@ static int __cmd_inject(struct perf_inject *inject)
output_data_offset = 4096; output_data_offset = 4096;
} }
if (!inject->itrace_synth_opts.set)
auxtrace_index__free(&session->auxtrace_index);
if (!file_out->is_pipe) if (!file_out->is_pipe)
lseek(fd, output_data_offset, SEEK_SET); lseek(fd, output_data_offset, SEEK_SET);
......
...@@ -117,9 +117,24 @@ static int record__process_auxtrace(struct perf_tool *tool, ...@@ -117,9 +117,24 @@ static int record__process_auxtrace(struct perf_tool *tool,
size_t len1, void *data2, size_t len2) size_t len1, void *data2, size_t len2)
{ {
struct record *rec = container_of(tool, struct record, tool); struct record *rec = container_of(tool, struct record, tool);
struct perf_data_file *file = &rec->file;
size_t padding; size_t padding;
u8 pad[8] = {0}; u8 pad[8] = {0};
if (!perf_data_file__is_pipe(file)) {
off_t file_offset;
int fd = perf_data_file__fd(file);
int err;
file_offset = lseek(fd, 0, SEEK_CUR);
if (file_offset == -1)
return -1;
err = auxtrace_index__auxtrace_event(&rec->session->auxtrace_index,
event, file_offset);
if (err)
return err;
}
/* event.auxtrace.size includes padding, see __auxtrace_mmap__read() */ /* event.auxtrace.size includes padding, see __auxtrace_mmap__read() */
padding = (len1 + len2) & 7; padding = (len1 + len2) & 7;
if (padding) if (padding)
......
...@@ -344,6 +344,33 @@ int auxtrace_queues__add_event(struct auxtrace_queues *queues, ...@@ -344,6 +344,33 @@ int auxtrace_queues__add_event(struct auxtrace_queues *queues,
return err; return err;
} }
static int auxtrace_queues__add_indexed_event(struct auxtrace_queues *queues,
struct perf_session *session,
off_t file_offset, size_t sz)
{
union perf_event *event;
int err;
char buf[PERF_SAMPLE_MAX_SIZE];
err = perf_session__peek_event(session, file_offset, buf,
PERF_SAMPLE_MAX_SIZE, &event, NULL);
if (err)
return err;
if (event->header.type == PERF_RECORD_AUXTRACE) {
if (event->header.size < sizeof(struct auxtrace_event) ||
event->header.size != sz) {
err = -EINVAL;
goto out;
}
file_offset += event->header.size;
err = auxtrace_queues__add_event(queues, session, event,
file_offset, NULL);
}
out:
return err;
}
void auxtrace_queues__free(struct auxtrace_queues *queues) void auxtrace_queues__free(struct auxtrace_queues *queues)
{ {
unsigned int i; unsigned int i;
...@@ -500,6 +527,194 @@ auxtrace_record__init(struct perf_evlist *evlist __maybe_unused, int *err) ...@@ -500,6 +527,194 @@ auxtrace_record__init(struct perf_evlist *evlist __maybe_unused, int *err)
return NULL; return NULL;
} }
static int auxtrace_index__alloc(struct list_head *head)
{
struct auxtrace_index *auxtrace_index;
auxtrace_index = malloc(sizeof(struct auxtrace_index));
if (!auxtrace_index)
return -ENOMEM;
auxtrace_index->nr = 0;
INIT_LIST_HEAD(&auxtrace_index->list);
list_add_tail(&auxtrace_index->list, head);
return 0;
}
void auxtrace_index__free(struct list_head *head)
{
struct auxtrace_index *auxtrace_index, *n;
list_for_each_entry_safe(auxtrace_index, n, head, list) {
list_del(&auxtrace_index->list);
free(auxtrace_index);
}
}
static struct auxtrace_index *auxtrace_index__last(struct list_head *head)
{
struct auxtrace_index *auxtrace_index;
int err;
if (list_empty(head)) {
err = auxtrace_index__alloc(head);
if (err)
return NULL;
}
auxtrace_index = list_entry(head->prev, struct auxtrace_index, list);
if (auxtrace_index->nr >= PERF_AUXTRACE_INDEX_ENTRY_COUNT) {
err = auxtrace_index__alloc(head);
if (err)
return NULL;
auxtrace_index = list_entry(head->prev, struct auxtrace_index,
list);
}
return auxtrace_index;
}
int auxtrace_index__auxtrace_event(struct list_head *head,
union perf_event *event, off_t file_offset)
{
struct auxtrace_index *auxtrace_index;
size_t nr;
auxtrace_index = auxtrace_index__last(head);
if (!auxtrace_index)
return -ENOMEM;
nr = auxtrace_index->nr;
auxtrace_index->entries[nr].file_offset = file_offset;
auxtrace_index->entries[nr].sz = event->header.size;
auxtrace_index->nr += 1;
return 0;
}
static int auxtrace_index__do_write(int fd,
struct auxtrace_index *auxtrace_index)
{
struct auxtrace_index_entry ent;
size_t i;
for (i = 0; i < auxtrace_index->nr; i++) {
ent.file_offset = auxtrace_index->entries[i].file_offset;
ent.sz = auxtrace_index->entries[i].sz;
if (writen(fd, &ent, sizeof(ent)) != sizeof(ent))
return -errno;
}
return 0;
}
int auxtrace_index__write(int fd, struct list_head *head)
{
struct auxtrace_index *auxtrace_index;
u64 total = 0;
int err;
list_for_each_entry(auxtrace_index, head, list)
total += auxtrace_index->nr;
if (writen(fd, &total, sizeof(total)) != sizeof(total))
return -errno;
list_for_each_entry(auxtrace_index, head, list) {
err = auxtrace_index__do_write(fd, auxtrace_index);
if (err)
return err;
}
return 0;
}
static int auxtrace_index__process_entry(int fd, struct list_head *head,
bool needs_swap)
{
struct auxtrace_index *auxtrace_index;
struct auxtrace_index_entry ent;
size_t nr;
if (readn(fd, &ent, sizeof(ent)) != sizeof(ent))
return -1;
auxtrace_index = auxtrace_index__last(head);
if (!auxtrace_index)
return -1;
nr = auxtrace_index->nr;
if (needs_swap) {
auxtrace_index->entries[nr].file_offset =
bswap_64(ent.file_offset);
auxtrace_index->entries[nr].sz = bswap_64(ent.sz);
} else {
auxtrace_index->entries[nr].file_offset = ent.file_offset;
auxtrace_index->entries[nr].sz = ent.sz;
}
auxtrace_index->nr = nr + 1;
return 0;
}
int auxtrace_index__process(int fd, u64 size, struct perf_session *session,
bool needs_swap)
{
struct list_head *head = &session->auxtrace_index;
u64 nr;
if (readn(fd, &nr, sizeof(u64)) != sizeof(u64))
return -1;
if (needs_swap)
nr = bswap_64(nr);
if (sizeof(u64) + nr * sizeof(struct auxtrace_index_entry) > size)
return -1;
while (nr--) {
int err;
err = auxtrace_index__process_entry(fd, head, needs_swap);
if (err)
return -1;
}
return 0;
}
static int auxtrace_queues__process_index_entry(struct auxtrace_queues *queues,
struct perf_session *session,
struct auxtrace_index_entry *ent)
{
return auxtrace_queues__add_indexed_event(queues, session,
ent->file_offset, ent->sz);
}
int auxtrace_queues__process_index(struct auxtrace_queues *queues,
struct perf_session *session)
{
struct auxtrace_index *auxtrace_index;
struct auxtrace_index_entry *ent;
size_t i;
int err;
list_for_each_entry(auxtrace_index, &session->auxtrace_index, list) {
for (i = 0; i < auxtrace_index->nr; i++) {
ent = &auxtrace_index->entries[i];
err = auxtrace_queues__process_index_entry(queues,
session,
ent);
if (err)
return err;
}
}
return 0;
}
struct auxtrace_buffer *auxtrace_buffer__next(struct auxtrace_queue *queue, struct auxtrace_buffer *auxtrace_buffer__next(struct auxtrace_queue *queue,
struct auxtrace_buffer *buffer) struct auxtrace_buffer *buffer)
{ {
......
...@@ -79,6 +79,32 @@ struct itrace_synth_opts { ...@@ -79,6 +79,32 @@ struct itrace_synth_opts {
enum itrace_period_type period_type; enum itrace_period_type period_type;
}; };
/**
* struct auxtrace_index_entry - indexes a AUX area tracing event within a
* perf.data file.
* @file_offset: offset within the perf.data file
* @sz: size of the event
*/
struct auxtrace_index_entry {
u64 file_offset;
u64 sz;
};
#define PERF_AUXTRACE_INDEX_ENTRY_COUNT 256
/**
* struct auxtrace_index - index of AUX area tracing events within a perf.data
* file.
* @list: linking a number of arrays of entries
* @nr: number of entries
* @entries: array of entries
*/
struct auxtrace_index {
struct list_head list;
size_t nr;
struct auxtrace_index_entry entries[PERF_AUXTRACE_INDEX_ENTRY_COUNT];
};
/** /**
* struct auxtrace - session callbacks to allow AUX area data decoding. * struct auxtrace - session callbacks to allow AUX area data decoding.
* @process_event: lets the decoder see all session events * @process_event: lets the decoder see all session events
...@@ -321,6 +347,8 @@ int auxtrace_queues__add_event(struct auxtrace_queues *queues, ...@@ -321,6 +347,8 @@ int auxtrace_queues__add_event(struct auxtrace_queues *queues,
union perf_event *event, off_t data_offset, union perf_event *event, off_t data_offset,
struct auxtrace_buffer **buffer_ptr); struct auxtrace_buffer **buffer_ptr);
void auxtrace_queues__free(struct auxtrace_queues *queues); void auxtrace_queues__free(struct auxtrace_queues *queues);
int auxtrace_queues__process_index(struct auxtrace_queues *queues,
struct perf_session *session);
struct auxtrace_buffer *auxtrace_buffer__next(struct auxtrace_queue *queue, struct auxtrace_buffer *auxtrace_buffer__next(struct auxtrace_queue *queue,
struct auxtrace_buffer *buffer); struct auxtrace_buffer *buffer);
void *auxtrace_buffer__get_data(struct auxtrace_buffer *buffer, int fd); void *auxtrace_buffer__get_data(struct auxtrace_buffer *buffer, int fd);
...@@ -361,6 +389,13 @@ int auxtrace_record__info_fill(struct auxtrace_record *itr, ...@@ -361,6 +389,13 @@ int auxtrace_record__info_fill(struct auxtrace_record *itr,
void auxtrace_record__free(struct auxtrace_record *itr); void auxtrace_record__free(struct auxtrace_record *itr);
u64 auxtrace_record__reference(struct auxtrace_record *itr); u64 auxtrace_record__reference(struct auxtrace_record *itr);
int auxtrace_index__auxtrace_event(struct list_head *head, union perf_event *event,
off_t file_offset);
int auxtrace_index__write(int fd, struct list_head *head);
int auxtrace_index__process(int fd, u64 size, struct perf_session *session,
bool needs_swap);
void auxtrace_index__free(struct list_head *head);
void auxtrace_synth_error(struct auxtrace_error_event *auxtrace_error, int type, void auxtrace_synth_error(struct auxtrace_error_event *auxtrace_error, int type,
int code, int cpu, pid_t pid, pid_t tid, u64 ip, int code, int cpu, pid_t pid, pid_t tid, u64 ip,
const char *msg); const char *msg);
......
...@@ -869,11 +869,18 @@ static int write_branch_stack(int fd __maybe_unused, ...@@ -869,11 +869,18 @@ static int write_branch_stack(int fd __maybe_unused,
return 0; return 0;
} }
static int write_auxtrace(int fd __maybe_unused, static int write_auxtrace(int fd, struct perf_header *h,
struct perf_header *h __maybe_unused,
struct perf_evlist *evlist __maybe_unused) struct perf_evlist *evlist __maybe_unused)
{ {
return 0; struct perf_session *session;
int err;
session = container_of(h, struct perf_session, header);
err = auxtrace_index__write(fd, &session->auxtrace_index);
if (err < 0)
pr_err("Failed to write auxtrace index\n");
return err;
} }
static void print_hostname(struct perf_header *ph, int fd __maybe_unused, static void print_hostname(struct perf_header *ph, int fd __maybe_unused,
...@@ -1834,6 +1841,22 @@ static int process_group_desc(struct perf_file_section *section __maybe_unused, ...@@ -1834,6 +1841,22 @@ static int process_group_desc(struct perf_file_section *section __maybe_unused,
return ret; return ret;
} }
static int process_auxtrace(struct perf_file_section *section,
struct perf_header *ph, int fd,
void *data __maybe_unused)
{
struct perf_session *session;
int err;
session = container_of(ph, struct perf_session, header);
err = auxtrace_index__process(fd, section->size, session,
ph->needs_swap);
if (err < 0)
pr_err("Failed to process auxtrace index\n");
return err;
}
struct feature_ops { struct feature_ops {
int (*write)(int fd, struct perf_header *h, struct perf_evlist *evlist); int (*write)(int fd, struct perf_header *h, struct perf_evlist *evlist);
void (*print)(struct perf_header *h, int fd, FILE *fp); void (*print)(struct perf_header *h, int fd, FILE *fp);
...@@ -1874,7 +1897,7 @@ static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = { ...@@ -1874,7 +1897,7 @@ static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = {
FEAT_OPA(HEADER_BRANCH_STACK, branch_stack), FEAT_OPA(HEADER_BRANCH_STACK, branch_stack),
FEAT_OPP(HEADER_PMU_MAPPINGS, pmu_mappings), FEAT_OPP(HEADER_PMU_MAPPINGS, pmu_mappings),
FEAT_OPP(HEADER_GROUP_DESC, group_desc), FEAT_OPP(HEADER_GROUP_DESC, group_desc),
FEAT_OPA(HEADER_AUXTRACE, auxtrace), FEAT_OPP(HEADER_AUXTRACE, auxtrace),
}; };
struct header_print_data { struct header_print_data {
......
...@@ -120,6 +120,7 @@ struct perf_session *perf_session__new(struct perf_data_file *file, ...@@ -120,6 +120,7 @@ struct perf_session *perf_session__new(struct perf_data_file *file,
session->repipe = repipe; session->repipe = repipe;
session->tool = tool; session->tool = tool;
INIT_LIST_HEAD(&session->auxtrace_index);
machines__init(&session->machines); machines__init(&session->machines);
ordered_events__init(&session->ordered_events, ordered_events__deliver_event); ordered_events__init(&session->ordered_events, ordered_events__deliver_event);
...@@ -187,6 +188,7 @@ static void perf_session_env__delete(struct perf_session_env *env) ...@@ -187,6 +188,7 @@ static void perf_session_env__delete(struct perf_session_env *env)
void perf_session__delete(struct perf_session *session) void perf_session__delete(struct perf_session *session)
{ {
auxtrace__free(session); auxtrace__free(session);
auxtrace_index__free(&session->auxtrace_index);
perf_session__destroy_kernel_maps(session); perf_session__destroy_kernel_maps(session);
perf_session__delete_threads(session); perf_session__delete_threads(session);
perf_session_env__delete(&session->header.env); perf_session_env__delete(&session->header.env);
......
...@@ -24,6 +24,7 @@ struct perf_session { ...@@ -24,6 +24,7 @@ struct perf_session {
struct perf_evlist *evlist; struct perf_evlist *evlist;
struct auxtrace *auxtrace; struct auxtrace *auxtrace;
struct itrace_synth_opts *itrace_synth_opts; struct itrace_synth_opts *itrace_synth_opts;
struct list_head auxtrace_index;
struct trace_event tevent; struct trace_event tevent;
bool repipe; bool repipe;
bool one_mmap; bool one_mmap;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment