perf tools: Pass tool context in the the perf_event_ops functions
[firefly-linux-kernel-4.4.55.git] / tools / perf / util / session.c
index 20e011c99a9459351f8741a418a4d630e334ac22..a36023a667798532161075b1d354d567b7968163 100644 (file)
@@ -78,39 +78,12 @@ out_close:
        return -1;
 }
 
-static void perf_session__id_header_size(struct perf_session *session)
-{
-       struct perf_sample *data;
-       u64 sample_type = session->sample_type;
-       u16 size = 0;
-
-       if (!session->sample_id_all)
-               goto out;
-
-       if (sample_type & PERF_SAMPLE_TID)
-               size += sizeof(data->tid) * 2;
-
-       if (sample_type & PERF_SAMPLE_TIME)
-               size += sizeof(data->time);
-
-       if (sample_type & PERF_SAMPLE_ID)
-               size += sizeof(data->id);
-
-       if (sample_type & PERF_SAMPLE_STREAM_ID)
-               size += sizeof(data->stream_id);
-
-       if (sample_type & PERF_SAMPLE_CPU)
-               size += sizeof(data->cpu) * 2;
-out:
-       session->id_hdr_size = size;
-}
-
 void perf_session__update_sample_type(struct perf_session *self)
 {
        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);
-       perf_session__id_header_size(self);
+       self->id_hdr_size = perf_evlist__id_hdr_size(self->evlist);
 }
 
 int perf_session__create_kernel_maps(struct perf_session *self)
@@ -139,9 +112,6 @@ struct perf_session *perf_session__new(const char *filename, int mode,
                goto out;
 
        memcpy(self->filename, filename, len);
-       self->threads = RB_ROOT;
-       INIT_LIST_HEAD(&self->dead_threads);
-       self->last_match = NULL;
        /*
         * On 64bit we can mmap the data file in one go. No need for tiny mmap
         * slices. On 32bit we use 32MB.
@@ -184,17 +154,22 @@ out_delete:
        return NULL;
 }
 
-static void perf_session__delete_dead_threads(struct perf_session *self)
+static void machine__delete_dead_threads(struct machine *machine)
 {
        struct thread *n, *t;
 
-       list_for_each_entry_safe(t, n, &self->dead_threads, node) {
+       list_for_each_entry_safe(t, n, &machine->dead_threads, node) {
                list_del(&t->node);
                thread__delete(t);
        }
 }
 
-static void perf_session__delete_threads(struct perf_session *self)
+static void perf_session__delete_dead_threads(struct perf_session *session)
+{
+       machine__delete_dead_threads(&session->host_machine);
+}
+
+static void machine__delete_threads(struct machine *self)
 {
        struct rb_node *nd = rb_first(&self->threads);
 
@@ -207,6 +182,11 @@ static void perf_session__delete_threads(struct perf_session *self)
        }
 }
 
+static void perf_session__delete_threads(struct perf_session *session)
+{
+       machine__delete_threads(&session->host_machine);
+}
+
 void perf_session__delete(struct perf_session *self)
 {
        perf_session__destroy_kernel_maps(self);
@@ -217,7 +197,7 @@ void perf_session__delete(struct perf_session *self)
        free(self);
 }
 
-void perf_session__remove_thread(struct perf_session *self, struct thread *th)
+void machine__remove_thread(struct machine *self, struct thread *th)
 {
        self->last_match = NULL;
        rb_erase(&th->rb_node, &self->threads);
@@ -236,7 +216,7 @@ static bool symbol__match_parent_regex(struct symbol *sym)
        return 0;
 }
 
-int perf_session__resolve_callchain(struct perf_session *self,
+int perf_session__resolve_callchain(struct perf_session *self, struct perf_evsel *evsel,
                                    struct thread *thread,
                                    struct ip_callchain *chain,
                                    struct symbol **parent)
@@ -245,7 +225,7 @@ int perf_session__resolve_callchain(struct perf_session *self,
        unsigned int i;
        int err;
 
-       callchain_cursor_reset(&self->callchain_cursor);
+       callchain_cursor_reset(&evsel->hists.callchain_cursor);
 
        for (i = 0; i < chain->nr; i++) {
                u64 ip;
@@ -281,7 +261,7 @@ int perf_session__resolve_callchain(struct perf_session *self,
                                break;
                }
 
-               err = callchain_cursor_append(&self->callchain_cursor,
+               err = callchain_cursor_append(&evsel->hists.callchain_cursor,
                                              ip, al.map, al.sym);
                if (err)
                        return err;
@@ -290,14 +270,30 @@ int perf_session__resolve_callchain(struct perf_session *self,
        return 0;
 }
 
-static int process_event_synth_stub(union perf_event *event __used,
+static int process_event_synth_stub(struct perf_event_ops *ops __used,
+                                   union perf_event *event __used,
                                    struct perf_session *session __used)
 {
        dump_printf(": unhandled!\n");
        return 0;
 }
 
-static int process_event_sample_stub(union perf_event *event __used,
+static int process_event_synth_tracing_data_stub(union perf_event *event __used,
+                                                struct perf_session *session __used)
+{
+       dump_printf(": unhandled!\n");
+       return 0;
+}
+
+static int process_event_synth_attr_stub(union perf_event *event __used,
+                                        struct perf_evlist **pevlist __used)
+{
+       dump_printf(": unhandled!\n");
+       return 0;
+}
+
+static int process_event_sample_stub(struct perf_event_ops *ops __used,
+                                    union perf_event *event __used,
                                     struct perf_sample *sample __used,
                                     struct perf_evsel *evsel __used,
                                     struct perf_session *session __used)
@@ -306,7 +302,8 @@ static int process_event_sample_stub(union perf_event *event __used,
        return 0;
 }
 
-static int process_event_stub(union perf_event *event __used,
+static int process_event_stub(struct perf_event_ops *ops __used,
+                             union perf_event *event __used,
                              struct perf_sample *sample __used,
                              struct perf_session *session __used)
 {
@@ -314,17 +311,17 @@ static int process_event_stub(union perf_event *event __used,
        return 0;
 }
 
-static int process_finished_round_stub(union perf_event *event __used,
-                                      struct perf_session *session __used,
-                                      struct perf_event_ops *ops __used)
+static int process_finished_round_stub(struct perf_event_ops *ops __used,
+                                      union perf_event *event __used,
+                                      struct perf_session *session __used)
 {
        dump_printf(": unhandled!\n");
        return 0;
 }
 
-static int process_finished_round(union perf_event *event,
-                                 struct perf_session *session,
-                                 struct perf_event_ops *ops);
+static int process_finished_round(struct perf_event_ops *ops,
+                                 union perf_event *event,
+                                 struct perf_session *session);
 
 static void perf_event_ops__fill_defaults(struct perf_event_ops *handler)
 {
@@ -347,11 +344,11 @@ static void perf_event_ops__fill_defaults(struct perf_event_ops *handler)
        if (handler->unthrottle == NULL)
                handler->unthrottle = process_event_stub;
        if (handler->attr == NULL)
-               handler->attr = process_event_synth_stub;
+               handler->attr = process_event_synth_attr_stub;
        if (handler->event_type == NULL)
                handler->event_type = process_event_synth_stub;
        if (handler->tracing_data == NULL)
-               handler->tracing_data = process_event_synth_stub;
+               handler->tracing_data = process_event_synth_tracing_data_stub;
        if (handler->build_id == NULL)
                handler->build_id = process_event_synth_stub;
        if (handler->finished_round == NULL) {
@@ -502,6 +499,7 @@ static void flush_sample_queue(struct perf_session *s,
        struct perf_sample sample;
        u64 limit = os->next_flush;
        u64 last_ts = os->last_sample ? os->last_sample->timestamp : 0ULL;
+       unsigned idx = 0, progress_next = os->nr_samples / 16;
        int ret;
 
        if (!ops->ordered_samples || !limit)
@@ -521,6 +519,11 @@ static void flush_sample_queue(struct perf_session *s,
                os->last_flush = iter->timestamp;
                list_del(&iter->list);
                list_add(&iter->list, &os->sample_cache);
+               if (++idx >= progress_next) {
+                       progress_next += os->nr_samples / 16;
+                       ui_progress__update(idx, os->nr_samples,
+                                           "Processing time ordered events...");
+               }
        }
 
        if (list_empty(head)) {
@@ -529,6 +532,8 @@ static void flush_sample_queue(struct perf_session *s,
                os->last_sample =
                        list_entry(head->prev, struct sample_queue, list);
        }
+
+       os->nr_samples = 0;
 }
 
 /*
@@ -570,9 +575,9 @@ static void flush_sample_queue(struct perf_session *s,
  *      Flush every events below timestamp 7
  *      etc...
  */
-static int process_finished_round(union perf_event *event __used,
-                                 struct perf_session *session,
-                                 struct perf_event_ops *ops)
+static int process_finished_round(struct perf_event_ops *ops,
+                                 union perf_event *event __used,
+                                 struct perf_session *session)
 {
        flush_sample_queue(session, ops);
        session->ordered_samples.next_flush = session->ordered_samples.max_timestamp;
@@ -588,6 +593,7 @@ static void __queue_event(struct sample_queue *new, struct perf_session *s)
        u64 timestamp = new->timestamp;
        struct list_head *p;
 
+       ++os->nr_samples;
        os->last_sample = new;
 
        if (!sample) {
@@ -738,31 +744,48 @@ static int perf_session_deliver_event(struct perf_session *session,
 
        dump_event(session, event, file_offset, sample);
 
+       evsel = perf_evlist__id2evsel(session->evlist, sample->id);
+       if (evsel != NULL && event->header.type != PERF_RECORD_SAMPLE) {
+               /*
+                * XXX We're leaving PERF_RECORD_SAMPLE unnacounted here
+                * because the tools right now may apply filters, discarding
+                * some of the samples. For consistency, in the future we
+                * should have something like nr_filtered_samples and remove
+                * the sample->period from total_sample_period, etc, KISS for
+                * now tho.
+                *
+                * Also testing against NULL allows us to handle files without
+                * attr.sample_id_all and/or without PERF_SAMPLE_ID. In the
+                * future probably it'll be a good idea to restrict event
+                * processing via perf_session to files with both set.
+                */
+               hists__inc_nr_events(&evsel->hists, event->header.type);
+       }
+
        switch (event->header.type) {
        case PERF_RECORD_SAMPLE:
                dump_sample(session, event, sample);
-               evsel = perf_evlist__id2evsel(session->evlist, sample->id);
                if (evsel == NULL) {
                        ++session->hists.stats.nr_unknown_id;
                        return -1;
                }
-               return ops->sample(event, sample, evsel, session);
+               return ops->sample(ops, event, sample, evsel, session);
        case PERF_RECORD_MMAP:
-               return ops->mmap(event, sample, session);
+               return ops->mmap(ops, event, sample, session);
        case PERF_RECORD_COMM:
-               return ops->comm(event, sample, session);
+               return ops->comm(ops, event, sample, session);
        case PERF_RECORD_FORK:
-               return ops->fork(event, sample, session);
+               return ops->fork(ops, event, sample, session);
        case PERF_RECORD_EXIT:
-               return ops->exit(event, sample, session);
+               return ops->exit(ops, event, sample, session);
        case PERF_RECORD_LOST:
-               return ops->lost(event, sample, session);
+               return ops->lost(ops, event, sample, session);
        case PERF_RECORD_READ:
-               return ops->read(event, sample, session);
+               return ops->read(ops, event, sample, session);
        case PERF_RECORD_THROTTLE:
-               return ops->throttle(event, sample, session);
+               return ops->throttle(ops, event, sample, session);
        case PERF_RECORD_UNTHROTTLE:
-               return ops->unthrottle(event, sample, session);
+               return ops->unthrottle(ops, event, sample, session);
        default:
                ++session->hists.stats.nr_unknown_events;
                return -1;
@@ -788,22 +811,27 @@ static int perf_session__preprocess_sample(struct perf_session *session,
 static int perf_session__process_user_event(struct perf_session *session, union perf_event *event,
                                            struct perf_event_ops *ops, u64 file_offset)
 {
+       int err;
+
        dump_event(session, event, file_offset, NULL);
 
        /* These events are processed right away */
        switch (event->header.type) {
        case PERF_RECORD_HEADER_ATTR:
-               return ops->attr(event, session);
+               err = ops->attr(event, &session->evlist);
+               if (err == 0)
+                       perf_session__update_sample_type(session);
+               return err;
        case PERF_RECORD_HEADER_EVENT_TYPE:
-               return ops->event_type(event, session);
+               return ops->event_type(ops, event, session);
        case PERF_RECORD_HEADER_TRACING_DATA:
                /* setup for reading amidst mmap */
                lseek(session->fd, file_offset, SEEK_SET);
                return ops->tracing_data(event, session);
        case PERF_RECORD_HEADER_BUILD_ID:
-               return ops->build_id(event, session);
+               return ops->build_id(ops, event, session);
        case PERF_RECORD_FINISHED_ROUND:
-               return ops->finished_round(event, session, ops);
+               return ops->finished_round(ops, event, session);
        default:
                return -EINVAL;
        }
@@ -858,6 +886,11 @@ void perf_event_header__bswap(struct perf_event_header *self)
        self->size = bswap_16(self->size);
 }
 
+struct thread *perf_session__findnew(struct perf_session *session, pid_t pid)
+{
+       return machine__findnew_thread(&session->host_machine, pid);
+}
+
 static struct thread *perf_session__register_idle_thread(struct perf_session *self)
 {
        struct thread *thread = perf_session__findnew(self, 0);
@@ -874,11 +907,11 @@ static void perf_session__warn_about_errors(const struct perf_session *session,
                                            const struct perf_event_ops *ops)
 {
        if (ops->lost == perf_event__process_lost &&
-           session->hists.stats.total_lost != 0) {
-               ui__warning("Processed %" PRIu64 " events and LOST %" PRIu64
-                           "!\n\nCheck IO/CPU overload!\n\n",
-                           session->hists.stats.total_period,
-                           session->hists.stats.total_lost);
+           session->hists.stats.nr_events[PERF_RECORD_LOST] != 0) {
+               ui__warning("Processed %d events and lost %d chunks!\n\n"
+                           "Check IO/CPU overload!\n\n",
+                           session->hists.stats.nr_events[0],
+                           session->hists.stats.nr_events[PERF_RECORD_LOST]);
        }
 
        if (session->hists.stats.nr_unknown_events != 0) {
@@ -1012,7 +1045,6 @@ int __perf_session__process_events(struct perf_session *session,
 {
        u64 head, page_offset, file_offset, file_pos, progress_next;
        int err, mmap_prot, mmap_flags, map_idx = 0;
-       struct ui_progress *progress;
        size_t  page_size, mmap_size;
        char *buf, *mmaps[8];
        union perf_event *event;
@@ -1030,9 +1062,6 @@ int __perf_session__process_events(struct perf_session *session,
                file_size = data_offset + data_size;
 
        progress_next = file_size / 16;
-       progress = ui_progress__new("Processing events...", file_size);
-       if (progress == NULL)
-               return -1;
 
        mmap_size = session->mmap_window;
        if (mmap_size > file_size)
@@ -1095,7 +1124,8 @@ more:
 
        if (file_pos >= progress_next) {
                progress_next += file_size / 16;
-               ui_progress__update(progress, file_pos);
+               ui_progress__update(file_pos, file_size,
+                                   "Processing events...");
        }
 
        if (file_pos < file_size)
@@ -1106,7 +1136,6 @@ more:
        session->ordered_samples.next_flush = ULLONG_MAX;
        flush_sample_queue(session, ops);
 out_err:
-       ui_progress__delete(progress);
        perf_session__warn_about_errors(session, ops);
        perf_session_free_sample_buffers(session);
        return err;
@@ -1202,6 +1231,27 @@ size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp)
        return ret;
 }
 
+size_t perf_session__fprintf(struct perf_session *session, FILE *fp)
+{
+       /*
+        * FIXME: Here we have to actually print all the machines in this
+        * session, not just the host...
+        */
+       return machine__fprintf(&session->host_machine, fp);
+}
+
+void perf_session__remove_thread(struct perf_session *session,
+                                struct thread *th)
+{
+       /*
+        * FIXME: This one makes no sense, we need to remove the thread from
+        * the machine it belongs to, perf_session can have many machines, so
+        * doing it always on ->host_machine is wrong.  Fix when auditing all
+        * the 'perf kvm' code.
+        */
+       machine__remove_thread(&session->host_machine, th);
+}
+
 struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session,
                                              unsigned int type)
 {
@@ -1214,14 +1264,14 @@ struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session,
        return NULL;
 }
 
-void perf_session__print_ip(union perf_event *event,
+void perf_session__print_ip(union perf_event *event, struct perf_evsel *evsel,
                            struct perf_sample *sample,
                            struct perf_session *session,
                            int print_sym, int print_dso)
 {
        struct addr_location al;
        const char *symname, *dsoname;
-       struct callchain_cursor *cursor = &session->callchain_cursor;
+       struct callchain_cursor *cursor = &evsel->hists.callchain_cursor;
        struct callchain_cursor_node *node;
 
        if (perf_event__preprocess_sample(event, session, &al, sample,
@@ -1233,7 +1283,7 @@ void perf_session__print_ip(union perf_event *event,
 
        if (symbol_conf.use_callchain && sample->callchain) {
 
-               if (perf_session__resolve_callchain(session, al.thread,
+               if (perf_session__resolve_callchain(session, evsel, al.thread,
                                                sample->callchain, NULL) != 0) {
                        if (verbose)
                                error("Failed to resolve callchain. Skipping\n");