perf tools: Back [vdso] DSO with real data
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / tools / perf / util / session.c
index 8e4f0755d2aa8364d81fa714888d622d61f6a2db..e0fd6c71cc5fbf2e099c995c6105b106f433e6bb 100644 (file)
@@ -15,6 +15,9 @@
 #include "util.h"
 #include "cpumap.h"
 #include "event-parse.h"
+#include "perf_regs.h"
+#include "unwind.h"
+#include "vdso.h"
 
 static int perf_session__open(struct perf_session *self, bool force)
 {
@@ -80,14 +83,12 @@ out_close:
        return -1;
 }
 
-void perf_session__update_sample_type(struct perf_session *self)
+void perf_session__set_id_hdr_size(struct perf_session *session)
 {
-       self->sample_type = perf_evlist__sample_type(self->evlist);
-       self->sample_size = __perf_evsel__sample_size(self->sample_type);
-       self->sample_id_all = perf_evlist__sample_id_all(self->evlist);
-       self->id_hdr_size = perf_evlist__id_hdr_size(self->evlist);
-       self->host_machine.id_hdr_size = self->id_hdr_size;
-       machines__set_id_hdr_size(&self->machines, self->id_hdr_size);
+       u16 id_hdr_size = perf_evlist__id_hdr_size(session->evlist);
+
+       session->host_machine.id_hdr_size = id_hdr_size;
+       machines__set_id_hdr_size(&session->machines, id_hdr_size);
 }
 
 int perf_session__create_kernel_maps(struct perf_session *self)
@@ -147,7 +148,7 @@ struct perf_session *perf_session__new(const char *filename, int mode,
        if (mode == O_RDONLY) {
                if (perf_session__open(self, force) < 0)
                        goto out_delete;
-               perf_session__update_sample_type(self);
+               perf_session__set_id_hdr_size(self);
        } else if (mode == O_WRONLY) {
                /*
                 * In O_RDONLY mode this will be performed when reading the
@@ -158,7 +159,7 @@ struct perf_session *perf_session__new(const char *filename, int mode,
        }
 
        if (tool && tool->ordering_requires_timestamps &&
-           tool->ordered_samples && !self->sample_id_all) {
+           tool->ordered_samples && !perf_evlist__sample_id_all(self->evlist)) {
                dump_printf("WARNING: No sample_id_all support, falling back to unordered processing\n");
                tool->ordered_samples = false;
        }
@@ -211,6 +212,7 @@ void perf_session__delete(struct perf_session *self)
        machine__exit(&self->host_machine);
        close(self->fd);
        free(self);
+       vdso__exit();
 }
 
 void machine__remove_thread(struct machine *self, struct thread *th)
@@ -290,10 +292,11 @@ struct branch_info *machine__resolve_bstack(struct machine *self,
        return bi;
 }
 
-int machine__resolve_callchain(struct machine *self,
-                              struct thread *thread,
-                              struct ip_callchain *chain,
-                              struct symbol **parent)
+static int machine__resolve_callchain_sample(struct machine *machine,
+                                            struct thread *thread,
+                                            struct ip_callchain *chain,
+                                            struct symbol **parent)
+
 {
        u8 cpumode = PERF_RECORD_MISC_USER;
        unsigned int i;
@@ -318,11 +321,14 @@ int machine__resolve_callchain(struct machine *self,
                if (ip >= PERF_CONTEXT_MAX) {
                        switch (ip) {
                        case PERF_CONTEXT_HV:
-                               cpumode = PERF_RECORD_MISC_HYPERVISOR;  break;
+                               cpumode = PERF_RECORD_MISC_HYPERVISOR;
+                               break;
                        case PERF_CONTEXT_KERNEL:
-                               cpumode = PERF_RECORD_MISC_KERNEL;      break;
+                               cpumode = PERF_RECORD_MISC_KERNEL;
+                               break;
                        case PERF_CONTEXT_USER:
-                               cpumode = PERF_RECORD_MISC_USER;        break;
+                               cpumode = PERF_RECORD_MISC_USER;
+                               break;
                        default:
                                pr_debug("invalid callchain context: "
                                         "%"PRId64"\n", (s64) ip);
@@ -337,7 +343,7 @@ int machine__resolve_callchain(struct machine *self,
                }
 
                al.filtered = false;
-               thread__find_addr_location(thread, self, cpumode,
+               thread__find_addr_location(thread, machine, cpumode,
                                           MAP__FUNCTION, ip, &al, NULL);
                if (al.sym != NULL) {
                        if (sort__has_parent && !*parent &&
@@ -356,6 +362,45 @@ int machine__resolve_callchain(struct machine *self,
        return 0;
 }
 
+static int unwind_entry(struct unwind_entry *entry, void *arg)
+{
+       struct callchain_cursor *cursor = arg;
+       return callchain_cursor_append(cursor, entry->ip,
+                                      entry->map, entry->sym);
+}
+
+int machine__resolve_callchain(struct machine *machine,
+                              struct perf_evsel *evsel,
+                              struct thread *thread,
+                              struct perf_sample *sample,
+                              struct symbol **parent)
+
+{
+       int ret;
+
+       callchain_cursor_reset(&callchain_cursor);
+
+       ret = machine__resolve_callchain_sample(machine, thread,
+                                               sample->callchain, parent);
+       if (ret)
+               return ret;
+
+       /* Can we do dwarf post unwind? */
+       if (!((evsel->attr.sample_type & PERF_SAMPLE_REGS_USER) &&
+             (evsel->attr.sample_type & PERF_SAMPLE_STACK_USER)))
+               return 0;
+
+       /* Bail out if nothing was captured. */
+       if ((!sample->user_regs.regs) ||
+           (!sample->user_stack.size))
+               return 0;
+
+       return unwind__get_entries(unwind_entry, &callchain_cursor, machine,
+                                  thread, evsel->attr.sample_regs_user,
+                                  sample);
+
+}
+
 static int process_event_synth_tracing_data_stub(union perf_event *event __used,
                                                 struct perf_session *session __used)
 {
@@ -489,7 +534,7 @@ static void perf_event__comm_swap(union perf_event *event, bool sample_id_all)
        if (sample_id_all) {
                void *data = &event->comm.comm;
 
-               data += ALIGN(strlen(data) + 1, sizeof(u64));
+               data += PERF_ALIGN(strlen(data) + 1, sizeof(u64));
                swap_sample_id_all(event, data);
        }
 }
@@ -506,7 +551,7 @@ static void perf_event__mmap_swap(union perf_event *event,
        if (sample_id_all) {
                void *data = &event->mmap.filename;
 
-               data += ALIGN(strlen(data) + 1, sizeof(u64));
+               data += PERF_ALIGN(strlen(data) + 1, sizeof(u64));
                swap_sample_id_all(event, data);
        }
 }
@@ -654,7 +699,7 @@ static int perf_session_deliver_event(struct perf_session *session,
                                      struct perf_tool *tool,
                                      u64 file_offset);
 
-static void flush_sample_queue(struct perf_session *s,
+static int flush_sample_queue(struct perf_session *s,
                               struct perf_tool *tool)
 {
        struct ordered_samples *os = &s->ordered_samples;
@@ -667,18 +712,22 @@ static void flush_sample_queue(struct perf_session *s,
        int ret;
 
        if (!tool->ordered_samples || !limit)
-               return;
+               return 0;
 
        list_for_each_entry_safe(iter, tmp, head, list) {
                if (iter->timestamp > limit)
                        break;
 
-               ret = perf_session__parse_sample(s, iter->event, &sample);
+               ret = perf_evlist__parse_sample(s->evlist, iter->event, &sample,
+                                               s->header.needs_swap);
                if (ret)
                        pr_err("Can't parse sample, err = %d\n", ret);
-               else
-                       perf_session_deliver_event(s, iter->event, &sample, tool,
-                                                  iter->file_offset);
+               else {
+                       ret = perf_session_deliver_event(s, iter->event, &sample, tool,
+                                                        iter->file_offset);
+                       if (ret)
+                               return ret;
+               }
 
                os->last_flush = iter->timestamp;
                list_del(&iter->list);
@@ -698,6 +747,8 @@ static void flush_sample_queue(struct perf_session *s,
        }
 
        os->nr_samples = 0;
+
+       return 0;
 }
 
 /*
@@ -743,10 +794,11 @@ static int process_finished_round(struct perf_tool *tool,
                                  union perf_event *event __used,
                                  struct perf_session *session)
 {
-       flush_sample_queue(session, tool);
-       session->ordered_samples.next_flush = session->ordered_samples.max_timestamp;
+       int ret = flush_sample_queue(session, tool);
+       if (!ret)
+               session->ordered_samples.next_flush = session->ordered_samples.max_timestamp;
 
-       return 0;
+       return ret;
 }
 
 /* The queue is ordered by time */
@@ -861,20 +913,50 @@ static void branch_stack__printf(struct perf_sample *sample)
                        sample->branch_stack->entries[i].to);
 }
 
+static void regs_dump__printf(u64 mask, u64 *regs)
+{
+       unsigned rid, i = 0;
+
+       for_each_set_bit(rid, (unsigned long *) &mask, sizeof(mask) * 8) {
+               u64 val = regs[i++];
+
+               printf(".... %-5s 0x%" PRIx64 "\n",
+                      perf_reg_name(rid), val);
+       }
+}
+
+static void regs_user__printf(struct perf_sample *sample, u64 mask)
+{
+       struct regs_dump *user_regs = &sample->user_regs;
+
+       if (user_regs->regs) {
+               printf("... user regs: mask 0x%" PRIx64 "\n", mask);
+               regs_dump__printf(mask, user_regs->regs);
+       }
+}
+
+static void stack_user__printf(struct stack_dump *dump)
+{
+       printf("... ustack: size %" PRIu64 ", offset 0x%x\n",
+              dump->size, dump->offset);
+}
+
 static void perf_session__print_tstamp(struct perf_session *session,
                                       union perf_event *event,
                                       struct perf_sample *sample)
 {
+       u64 sample_type = perf_evlist__sample_type(session->evlist);
+
        if (event->header.type != PERF_RECORD_SAMPLE &&
-           !session->sample_id_all) {
+           !perf_evlist__sample_id_all(session->evlist)) {
                fputs("-1 -1 ", stdout);
                return;
        }
 
-       if ((session->sample_type & PERF_SAMPLE_CPU))
+       if ((sample_type & PERF_SAMPLE_CPU))
                printf("%u ", sample->cpu);
 
-       if (session->sample_type & PERF_SAMPLE_TIME)
+       if (sample_type & PERF_SAMPLE_TIME)
                printf("%" PRIu64 " ", sample->time);
 }
 
@@ -896,9 +978,11 @@ static void dump_event(struct perf_session *session, union perf_event *event,
               event->header.size, perf_event__name(event->header.type));
 }
 
-static void dump_sample(struct perf_session *session, union perf_event *event,
+static void dump_sample(struct perf_evsel *evsel, union perf_event *event,
                        struct perf_sample *sample)
 {
+       u64 sample_type;
+
        if (!dump_trace)
                return;
 
@@ -906,11 +990,19 @@ static void dump_sample(struct perf_session *session, union perf_event *event,
               event->header.misc, sample->pid, sample->tid, sample->ip,
               sample->period, sample->addr);
 
-       if (session->sample_type & PERF_SAMPLE_CALLCHAIN)
+       sample_type = evsel->attr.sample_type;
+
+       if (sample_type & PERF_SAMPLE_CALLCHAIN)
                callchain__printf(sample);
 
-       if (session->sample_type & PERF_SAMPLE_BRANCH_STACK)
+       if (sample_type & PERF_SAMPLE_BRANCH_STACK)
                branch_stack__printf(sample);
+
+       if (sample_type & PERF_SAMPLE_REGS_USER)
+               regs_user__printf(sample, evsel->attr.sample_regs_user);
+
+       if (sample_type & PERF_SAMPLE_STACK_USER)
+               stack_user__printf(&sample->user_stack);
 }
 
 static struct machine *
@@ -968,7 +1060,7 @@ static int perf_session_deliver_event(struct perf_session *session,
 
        switch (event->header.type) {
        case PERF_RECORD_SAMPLE:
-               dump_sample(session, event, sample);
+               dump_sample(evsel, event, sample);
                if (evsel == NULL) {
                        ++session->hists.stats.nr_unknown_id;
                        return 0;
@@ -1006,7 +1098,7 @@ static int perf_session__preprocess_sample(struct perf_session *session,
                                           union perf_event *event, struct perf_sample *sample)
 {
        if (event->header.type != PERF_RECORD_SAMPLE ||
-           !(session->sample_type & PERF_SAMPLE_CALLCHAIN))
+           !(perf_evlist__sample_type(session->evlist) & PERF_SAMPLE_CALLCHAIN))
                return 0;
 
        if (!ip_callchain__valid(sample->callchain, event)) {
@@ -1030,7 +1122,7 @@ static int perf_session__process_user_event(struct perf_session *session, union
        case PERF_RECORD_HEADER_ATTR:
                err = tool->attr(event, &session->evlist);
                if (err == 0)
-                       perf_session__update_sample_type(session);
+                       perf_session__set_id_hdr_size(session);
                return err;
        case PERF_RECORD_HEADER_EVENT_TYPE:
                return tool->event_type(tool, event);
@@ -1065,7 +1157,7 @@ static int perf_session__process_event(struct perf_session *session,
        int ret;
 
        if (session->header.needs_swap)
-               event_swap(event, session->sample_id_all);
+               event_swap(event, perf_evlist__sample_id_all(session->evlist));
 
        if (event->header.type >= PERF_RECORD_HEADER_MAX)
                return -EINVAL;
@@ -1078,7 +1170,8 @@ static int perf_session__process_event(struct perf_session *session,
        /*
         * For all kernel events we get the sample data
         */
-       ret = perf_session__parse_sample(session, event, &sample);
+       ret = perf_evlist__parse_sample(session->evlist, event, &sample,
+                                       session->header.needs_swap);
        if (ret)
                return ret;
 
@@ -1363,7 +1456,7 @@ more:
        err = 0;
        /* do the final flush for ordered samples */
        session->ordered_samples.next_flush = ULLONG_MAX;
-       flush_sample_queue(session, tool);
+       err = flush_sample_queue(session, tool);
 out_err:
        perf_session__warn_about_errors(session, tool);
        perf_session_free_sample_buffers(session);
@@ -1389,9 +1482,9 @@ int perf_session__process_events(struct perf_session *self,
        return err;
 }
 
-bool perf_session__has_traces(struct perf_session *self, const char *msg)
+bool perf_session__has_traces(struct perf_session *session, const char *msg)
 {
-       if (!(self->sample_type & PERF_SAMPLE_RAW)) {
+       if (!(perf_evlist__sample_type(session->evlist) & PERF_SAMPLE_RAW)) {
                pr_err("No trace sample to read. Did you call 'perf %s'?\n", msg);
                return false;
        }
@@ -1492,9 +1585,9 @@ struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session,
        return NULL;
 }
 
-void perf_event__print_ip(union perf_event *event, struct perf_sample *sample,
-                         struct machine *machine, int print_sym,
-                         int print_dso, int print_symoffset)
+void perf_evsel__print_ip(struct perf_evsel *evsel, union perf_event *event,
+                         struct perf_sample *sample, struct machine *machine,
+                         int print_sym, int print_dso, int print_symoffset)
 {
        struct addr_location al;
        struct callchain_cursor_node *node;
@@ -1508,8 +1601,9 @@ void perf_event__print_ip(union perf_event *event, struct perf_sample *sample,
 
        if (symbol_conf.use_callchain && sample->callchain) {
 
-               if (machine__resolve_callchain(machine, al.thread,
-                                               sample->callchain, NULL) != 0) {
+
+               if (machine__resolve_callchain(machine, evsel, al.thread,
+                                              sample, NULL) != 0) {
                        if (verbose)
                                error("Failed to resolve callchain. Skipping\n");
                        return;