]> Pileus Git - ~andy/linux/blobdiff - tools/perf/util/session.c
perf session: Use evlist/evsel for managing perf.data attributes
[~andy/linux] / tools / perf / util / session.c
index 313dac2d94ce9f7c9e74ef0e8995383866427dbf..f26639fa0fb3a1fd9f2c7376a44a386d5491f49a 100644 (file)
@@ -7,6 +7,8 @@
 #include <sys/types.h>
 #include <sys/mman.h>
 
+#include "evlist.h"
+#include "evsel.h"
 #include "session.h"
 #include "sort.h"
 #include "util.h"
@@ -19,7 +21,7 @@ static int perf_session__open(struct perf_session *self, bool force)
                self->fd_pipe = true;
                self->fd = STDIN_FILENO;
 
-               if (perf_header__read(self, self->fd) < 0)
+               if (perf_session__read_header(self, self->fd) < 0)
                        pr_err("incompatible file format");
 
                return 0;
@@ -51,7 +53,7 @@ static int perf_session__open(struct perf_session *self, bool force)
                goto out_close;
        }
 
-       if (perf_header__read(self, self->fd) < 0) {
+       if (perf_session__read_header(self, self->fd) < 0) {
                pr_err("incompatible file format");
                goto out_close;
        }
@@ -67,7 +69,7 @@ out_close:
 
 static void perf_session__id_header_size(struct perf_session *session)
 {
-       struct sample_data *data;
+       struct perf_sample *data;
        u64 sample_type = session->sample_type;
        u16 size = 0;
 
@@ -92,21 +94,10 @@ out:
        session->id_hdr_size = size;
 }
 
-void perf_session__set_sample_id_all(struct perf_session *session, bool value)
-{
-       session->sample_id_all = value;
-       perf_session__id_header_size(session);
-}
-
-void perf_session__set_sample_type(struct perf_session *session, u64 type)
-{
-       session->sample_type = type;
-}
-
 void perf_session__update_sample_type(struct perf_session *self)
 {
-       self->sample_type = perf_header__sample_type(&self->header);
-       self->sample_id_all = perf_header__sample_id_all(&self->header);
+       self->sample_type = perf_evlist__sample_type(self->evlist);
+       self->sample_id_all = perf_evlist__sample_id_all(self->evlist);
        perf_session__id_header_size(self);
 }
 
@@ -135,13 +126,9 @@ struct perf_session *perf_session__new(const char *filename, int mode,
        if (self == NULL)
                goto out;
 
-       if (perf_header__init(&self->header) < 0)
-               goto out_free;
-
        memcpy(self->filename, filename, len);
        self->threads = RB_ROOT;
        INIT_LIST_HEAD(&self->dead_threads);
-       self->hists_tree = RB_ROOT;
        self->last_match = NULL;
        /*
         * On 64bit we can mmap the data file in one go. No need for tiny mmap
@@ -162,17 +149,16 @@ 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);
        } else if (mode == O_WRONLY) {
                /*
                 * In O_RDONLY mode this will be performed when reading the
-                * kernel MMAP event, in event__process_mmap().
+                * kernel MMAP event, in perf_event__process_mmap().
                 */
                if (perf_session__create_kernel_maps(self) < 0)
                        goto out_delete;
        }
 
-       perf_session__update_sample_type(self);
-
        if (ops && ops->ordering_requires_timestamps &&
            ops->ordered_samples && !self->sample_id_all) {
                dump_printf("WARNING: No sample_id_all support, falling back to unordered processing\n");
@@ -181,9 +167,6 @@ struct perf_session *perf_session__new(const char *filename, int mode,
 
 out:
        return self;
-out_free:
-       free(self);
-       return NULL;
 out_delete:
        perf_session__delete(self);
        return NULL;
@@ -214,7 +197,6 @@ static void perf_session__delete_threads(struct perf_session *self)
 
 void perf_session__delete(struct perf_session *self)
 {
-       perf_header__exit(&self->header);
        perf_session__destroy_kernel_maps(self);
        perf_session__delete_dead_threads(self);
        perf_session__delete_threads(self);
@@ -242,17 +224,16 @@ static bool symbol__match_parent_regex(struct symbol *sym)
        return 0;
 }
 
-struct map_symbol *perf_session__resolve_callchain(struct perf_session *self,
-                                                  struct thread *thread,
-                                                  struct ip_callchain *chain,
-                                                  struct symbol **parent)
+int perf_session__resolve_callchain(struct perf_session *self,
+                                   struct thread *thread,
+                                   struct ip_callchain *chain,
+                                   struct symbol **parent)
 {
        u8 cpumode = PERF_RECORD_MISC_USER;
        unsigned int i;
-       struct map_symbol *syms = calloc(chain->nr, sizeof(*syms));
+       int err;
 
-       if (!syms)
-               return NULL;
+       callchain_cursor_reset(&self->callchain_cursor);
 
        for (i = 0; i < chain->nr; i++) {
                u64 ip = chain->ips[i];
@@ -281,30 +262,33 @@ struct map_symbol *perf_session__resolve_callchain(struct perf_session *self,
                                *parent = al.sym;
                        if (!symbol_conf.use_callchain)
                                break;
-                       syms[i].map = al.map;
-                       syms[i].sym = al.sym;
                }
+
+               err = callchain_cursor_append(&self->callchain_cursor,
+                                             ip, al.map, al.sym);
+               if (err)
+                       return err;
        }
 
-       return syms;
+       return 0;
 }
 
-static int process_event_synth_stub(event_t *event __used,
+static int process_event_synth_stub(union perf_event *event __used,
                                    struct perf_session *session __used)
 {
        dump_printf(": unhandled!\n");
        return 0;
 }
 
-static int process_event_stub(event_t *event __used,
-                             struct sample_data *sample __used,
+static int process_event_stub(union perf_event *event __used,
+                             struct perf_sample *sample __used,
                              struct perf_session *session __used)
 {
        dump_printf(": unhandled!\n");
        return 0;
 }
 
-static int process_finished_round_stub(event_t *event __used,
+static int process_finished_round_stub(union perf_event *event __used,
                                       struct perf_session *session __used,
                                       struct perf_event_ops *ops __used)
 {
@@ -312,7 +296,7 @@ static int process_finished_round_stub(event_t *event __used,
        return 0;
 }
 
-static int process_finished_round(event_t *event,
+static int process_finished_round(union perf_event *event,
                                  struct perf_session *session,
                                  struct perf_event_ops *ops);
 
@@ -329,7 +313,7 @@ static void perf_event_ops__fill_defaults(struct perf_event_ops *handler)
        if (handler->exit == NULL)
                handler->exit = process_event_stub;
        if (handler->lost == NULL)
-               handler->lost = event__process_lost;
+               handler->lost = perf_event__process_lost;
        if (handler->read == NULL)
                handler->read = process_event_stub;
        if (handler->throttle == NULL)
@@ -363,98 +347,98 @@ void mem_bswap_64(void *src, int byte_size)
        }
 }
 
-static void event__all64_swap(event_t *self)
+static void perf_event__all64_swap(union perf_event *event)
 {
-       struct perf_event_header *hdr = &self->header;
-       mem_bswap_64(hdr + 1, self->header.size - sizeof(*hdr));
+       struct perf_event_header *hdr = &event->header;
+       mem_bswap_64(hdr + 1, event->header.size - sizeof(*hdr));
 }
 
-static void event__comm_swap(event_t *self)
+static void perf_event__comm_swap(union perf_event *event)
 {
-       self->comm.pid = bswap_32(self->comm.pid);
-       self->comm.tid = bswap_32(self->comm.tid);
+       event->comm.pid = bswap_32(event->comm.pid);
+       event->comm.tid = bswap_32(event->comm.tid);
 }
 
-static void event__mmap_swap(event_t *self)
+static void perf_event__mmap_swap(union perf_event *event)
 {
-       self->mmap.pid   = bswap_32(self->mmap.pid);
-       self->mmap.tid   = bswap_32(self->mmap.tid);
-       self->mmap.start = bswap_64(self->mmap.start);
-       self->mmap.len   = bswap_64(self->mmap.len);
-       self->mmap.pgoff = bswap_64(self->mmap.pgoff);
+       event->mmap.pid   = bswap_32(event->mmap.pid);
+       event->mmap.tid   = bswap_32(event->mmap.tid);
+       event->mmap.start = bswap_64(event->mmap.start);
+       event->mmap.len   = bswap_64(event->mmap.len);
+       event->mmap.pgoff = bswap_64(event->mmap.pgoff);
 }
 
-static void event__task_swap(event_t *self)
+static void perf_event__task_swap(union perf_event *event)
 {
-       self->fork.pid  = bswap_32(self->fork.pid);
-       self->fork.tid  = bswap_32(self->fork.tid);
-       self->fork.ppid = bswap_32(self->fork.ppid);
-       self->fork.ptid = bswap_32(self->fork.ptid);
-       self->fork.time = bswap_64(self->fork.time);
+       event->fork.pid  = bswap_32(event->fork.pid);
+       event->fork.tid  = bswap_32(event->fork.tid);
+       event->fork.ppid = bswap_32(event->fork.ppid);
+       event->fork.ptid = bswap_32(event->fork.ptid);
+       event->fork.time = bswap_64(event->fork.time);
 }
 
-static void event__read_swap(event_t *self)
+static void perf_event__read_swap(union perf_event *event)
 {
-       self->read.pid          = bswap_32(self->read.pid);
-       self->read.tid          = bswap_32(self->read.tid);
-       self->read.value        = bswap_64(self->read.value);
-       self->read.time_enabled = bswap_64(self->read.time_enabled);
-       self->read.time_running = bswap_64(self->read.time_running);
-       self->read.id           = bswap_64(self->read.id);
+       event->read.pid          = bswap_32(event->read.pid);
+       event->read.tid          = bswap_32(event->read.tid);
+       event->read.value        = bswap_64(event->read.value);
+       event->read.time_enabled = bswap_64(event->read.time_enabled);
+       event->read.time_running = bswap_64(event->read.time_running);
+       event->read.id           = bswap_64(event->read.id);
 }
 
-static void event__attr_swap(event_t *self)
+static void perf_event__attr_swap(union perf_event *event)
 {
        size_t size;
 
-       self->attr.attr.type            = bswap_32(self->attr.attr.type);
-       self->attr.attr.size            = bswap_32(self->attr.attr.size);
-       self->attr.attr.config          = bswap_64(self->attr.attr.config);
-       self->attr.attr.sample_period   = bswap_64(self->attr.attr.sample_period);
-       self->attr.attr.sample_type     = bswap_64(self->attr.attr.sample_type);
-       self->attr.attr.read_format     = bswap_64(self->attr.attr.read_format);
-       self->attr.attr.wakeup_events   = bswap_32(self->attr.attr.wakeup_events);
-       self->attr.attr.bp_type         = bswap_32(self->attr.attr.bp_type);
-       self->attr.attr.bp_addr         = bswap_64(self->attr.attr.bp_addr);
-       self->attr.attr.bp_len          = bswap_64(self->attr.attr.bp_len);
-
-       size = self->header.size;
-       size -= (void *)&self->attr.id - (void *)self;
-       mem_bswap_64(self->attr.id, size);
+       event->attr.attr.type           = bswap_32(event->attr.attr.type);
+       event->attr.attr.size           = bswap_32(event->attr.attr.size);
+       event->attr.attr.config         = bswap_64(event->attr.attr.config);
+       event->attr.attr.sample_period  = bswap_64(event->attr.attr.sample_period);
+       event->attr.attr.sample_type    = bswap_64(event->attr.attr.sample_type);
+       event->attr.attr.read_format    = bswap_64(event->attr.attr.read_format);
+       event->attr.attr.wakeup_events  = bswap_32(event->attr.attr.wakeup_events);
+       event->attr.attr.bp_type        = bswap_32(event->attr.attr.bp_type);
+       event->attr.attr.bp_addr        = bswap_64(event->attr.attr.bp_addr);
+       event->attr.attr.bp_len         = bswap_64(event->attr.attr.bp_len);
+
+       size = event->header.size;
+       size -= (void *)&event->attr.id - (void *)event;
+       mem_bswap_64(event->attr.id, size);
 }
 
-static void event__event_type_swap(event_t *self)
+static void perf_event__event_type_swap(union perf_event *event)
 {
-       self->event_type.event_type.event_id =
-               bswap_64(self->event_type.event_type.event_id);
+       event->event_type.event_type.event_id =
+               bswap_64(event->event_type.event_type.event_id);
 }
 
-static void event__tracing_data_swap(event_t *self)
+static void perf_event__tracing_data_swap(union perf_event *event)
 {
-       self->tracing_data.size = bswap_32(self->tracing_data.size);
+       event->tracing_data.size = bswap_32(event->tracing_data.size);
 }
 
-typedef void (*event__swap_op)(event_t *self);
-
-static event__swap_op event__swap_ops[] = {
-       [PERF_RECORD_MMAP]   event__mmap_swap,
-       [PERF_RECORD_COMM]   event__comm_swap,
-       [PERF_RECORD_FORK]   event__task_swap,
-       [PERF_RECORD_EXIT]   event__task_swap,
-       [PERF_RECORD_LOST]   event__all64_swap,
-       [PERF_RECORD_READ]   event__read_swap,
-       [PERF_RECORD_SAMPLE] event__all64_swap,
-       [PERF_RECORD_HEADER_ATTR]   event__attr_swap,
-       [PERF_RECORD_HEADER_EVENT_TYPE]   = event__event_type_swap,
-       [PERF_RECORD_HEADER_TRACING_DATA]   = event__tracing_data_swap,
-       [PERF_RECORD_HEADER_BUILD_ID]   = NULL,
-       [PERF_RECORD_HEADER_MAX]    = NULL,
+typedef void (*perf_event__swap_op)(union perf_event *event);
+
+static perf_event__swap_op perf_event__swap_ops[] = {
+       [PERF_RECORD_MMAP]                = perf_event__mmap_swap,
+       [PERF_RECORD_COMM]                = perf_event__comm_swap,
+       [PERF_RECORD_FORK]                = perf_event__task_swap,
+       [PERF_RECORD_EXIT]                = perf_event__task_swap,
+       [PERF_RECORD_LOST]                = perf_event__all64_swap,
+       [PERF_RECORD_READ]                = perf_event__read_swap,
+       [PERF_RECORD_SAMPLE]              = perf_event__all64_swap,
+       [PERF_RECORD_HEADER_ATTR]         = perf_event__attr_swap,
+       [PERF_RECORD_HEADER_EVENT_TYPE]   = perf_event__event_type_swap,
+       [PERF_RECORD_HEADER_TRACING_DATA] = perf_event__tracing_data_swap,
+       [PERF_RECORD_HEADER_BUILD_ID]     = NULL,
+       [PERF_RECORD_HEADER_MAX]          = NULL,
 };
 
 struct sample_queue {
        u64                     timestamp;
        u64                     file_offset;
-       event_t                 *event;
+       union perf_event        *event;
        struct list_head        list;
 };
 
@@ -472,8 +456,8 @@ static void perf_session_free_sample_buffers(struct perf_session *session)
 }
 
 static int perf_session_deliver_event(struct perf_session *session,
-                                     event_t *event,
-                                     struct sample_data *sample,
+                                     union perf_event *event,
+                                     struct perf_sample *sample,
                                      struct perf_event_ops *ops,
                                      u64 file_offset);
 
@@ -483,7 +467,7 @@ static void flush_sample_queue(struct perf_session *s,
        struct ordered_samples *os = &s->ordered_samples;
        struct list_head *head = &os->samples;
        struct sample_queue *tmp, *iter;
-       struct sample_data sample;
+       struct perf_sample sample;
        u64 limit = os->next_flush;
        u64 last_ts = os->last_sample ? os->last_sample->timestamp : 0ULL;
 
@@ -494,7 +478,7 @@ static void flush_sample_queue(struct perf_session *s,
                if (iter->timestamp > limit)
                        break;
 
-               event__parse_sample(iter->event, s, &sample);
+               perf_session__parse_sample(s, iter->event, &sample);
                perf_session_deliver_event(s, iter->event, &sample, ops,
                                           iter->file_offset);
 
@@ -550,7 +534,7 @@ static void flush_sample_queue(struct perf_session *s,
  *      Flush every events below timestamp 7
  *      etc...
  */
-static int process_finished_round(event_t *event __used,
+static int process_finished_round(union perf_event *event __used,
                                  struct perf_session *session,
                                  struct perf_event_ops *ops)
 {
@@ -607,12 +591,12 @@ static void __queue_event(struct sample_queue *new, struct perf_session *s)
 
 #define MAX_SAMPLE_BUFFER      (64 * 1024 / sizeof(struct sample_queue))
 
-static int perf_session_queue_event(struct perf_session *s, event_t *event,
-                                   struct sample_data *data, u64 file_offset)
+static int perf_session_queue_event(struct perf_session *s, union perf_event *event,
+                                   struct perf_sample *sample, u64 file_offset)
 {
        struct ordered_samples *os = &s->ordered_samples;
        struct list_head *sc = &os->sample_cache;
-       u64 timestamp = data->time;
+       u64 timestamp = sample->time;
        struct sample_queue *new;
 
        if (!timestamp || timestamp == ~0ULL)
@@ -648,19 +632,20 @@ static int perf_session_queue_event(struct perf_session *s, event_t *event,
        return 0;
 }
 
-static void callchain__printf(struct sample_data *sample)
+static void callchain__printf(struct perf_sample *sample)
 {
        unsigned int i;
 
-       printf("... chain: nr:%Lu\n", sample->callchain->nr);
+       printf("... chain: nr:%" PRIu64 "\n", sample->callchain->nr);
 
        for (i = 0; i < sample->callchain->nr; i++)
-               printf("..... %2d: %016Lx\n", i, sample->callchain->ips[i]);
+               printf("..... %2d: %016" PRIx64 "\n",
+                      i, sample->callchain->ips[i]);
 }
 
 static void perf_session__print_tstamp(struct perf_session *session,
-                                      event_t *event,
-                                      struct sample_data *sample)
+                                      union perf_event *event,
+                                      struct perf_sample *sample)
 {
        if (event->header.type != PERF_RECORD_SAMPLE &&
            !session->sample_id_all) {
@@ -672,43 +657,44 @@ static void perf_session__print_tstamp(struct perf_session *session,
                printf("%u ", sample->cpu);
 
        if (session->sample_type & PERF_SAMPLE_TIME)
-               printf("%Lu ", sample->time);
+               printf("%" PRIu64 " ", sample->time);
 }
 
-static void dump_event(struct perf_session *session, event_t *event,
-                      u64 file_offset, struct sample_data *sample)
+static void dump_event(struct perf_session *session, union perf_event *event,
+                      u64 file_offset, struct perf_sample *sample)
 {
        if (!dump_trace)
                return;
 
-       printf("\n%#Lx [%#x]: event: %d\n", file_offset, event->header.size,
-              event->header.type);
+       printf("\n%#" PRIx64 " [%#x]: event: %d\n",
+              file_offset, event->header.size, event->header.type);
 
        trace_event(event);
 
        if (sample)
                perf_session__print_tstamp(session, event, sample);
 
-       printf("%#Lx [%#x]: PERF_RECORD_%s", file_offset, event->header.size,
-              event__get_event_name(event->header.type));
+       printf("%#" PRIx64 " [%#x]: PERF_RECORD_%s", file_offset,
+              event->header.size, perf_event__name(event->header.type));
 }
 
-static void dump_sample(struct perf_session *session, event_t *event,
-                       struct sample_data *sample)
+static void dump_sample(struct perf_session *session, union perf_event *event,
+                       struct perf_sample *sample)
 {
        if (!dump_trace)
                return;
 
-       printf("(IP, %d): %d/%d: %#Lx period: %Ld\n", event->header.misc,
-              sample->pid, sample->tid, sample->ip, sample->period);
+       printf("(IP, %d): %d/%d: %#" PRIx64 " period: %" PRIu64 "\n",
+              event->header.misc, sample->pid, sample->tid, sample->ip,
+              sample->period);
 
        if (session->sample_type & PERF_SAMPLE_CALLCHAIN)
                callchain__printf(sample);
 }
 
 static int perf_session_deliver_event(struct perf_session *session,
-                                     event_t *event,
-                                     struct sample_data *sample,
+                                     union perf_event *event,
+                                     struct perf_sample *sample,
                                      struct perf_event_ops *ops,
                                      u64 file_offset)
 {
@@ -741,7 +727,7 @@ static int perf_session_deliver_event(struct perf_session *session,
 }
 
 static int perf_session__preprocess_sample(struct perf_session *session,
-                                          event_t *event, struct sample_data *sample)
+                                          union perf_event *event, struct perf_sample *sample)
 {
        if (event->header.type != PERF_RECORD_SAMPLE ||
            !(session->sample_type & PERF_SAMPLE_CALLCHAIN))
@@ -756,7 +742,7 @@ static int perf_session__preprocess_sample(struct perf_session *session,
        return 0;
 }
 
-static int perf_session__process_user_event(struct perf_session *session, event_t *event,
+static int perf_session__process_user_event(struct perf_session *session, union perf_event *event,
                                            struct perf_event_ops *ops, u64 file_offset)
 {
        dump_event(session, event, file_offset, NULL);
@@ -781,15 +767,16 @@ static int perf_session__process_user_event(struct perf_session *session, event_
 }
 
 static int perf_session__process_event(struct perf_session *session,
-                                      event_t *event,
+                                      union perf_event *event,
                                       struct perf_event_ops *ops,
                                       u64 file_offset)
 {
-       struct sample_data sample;
+       struct perf_sample sample;
        int ret;
 
-       if (session->header.needs_swap && event__swap_ops[event->header.type])
-               event__swap_ops[event->header.type](event);
+       if (session->header.needs_swap &&
+           perf_event__swap_ops[event->header.type])
+               perf_event__swap_ops[event->header.type](event);
 
        if (event->header.type >= PERF_RECORD_HEADER_MAX)
                return -EINVAL;
@@ -802,7 +789,7 @@ static int perf_session__process_event(struct perf_session *session,
        /*
         * For all kernel events we get the sample data
         */
-       event__parse_sample(event, session, &sample);
+       perf_session__parse_sample(session, event, &sample);
 
        /* Preprocess sample records - precheck callchains */
        if (perf_session__preprocess_sample(session, event, &sample))
@@ -841,10 +828,10 @@ static struct thread *perf_session__register_idle_thread(struct perf_session *se
 static void perf_session__warn_about_errors(const struct perf_session *session,
                                            const struct perf_event_ops *ops)
 {
-       if (ops->lost == event__process_lost &&
+       if (ops->lost == perf_event__process_lost &&
            session->hists.stats.total_lost != 0) {
-               ui__warning("Processed %Lu events and LOST %Lu!\n\n"
-                           "Check IO/CPU overload!\n\n",
+               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);
        }
@@ -873,7 +860,7 @@ volatile int session_done;
 static int __perf_session__process_pipe_events(struct perf_session *self,
                                               struct perf_event_ops *ops)
 {
-       event_t event;
+       union perf_event event;
        uint32_t size;
        int skip = 0;
        u64 head;
@@ -918,7 +905,7 @@ more:
 
        if (size == 0 ||
            (skip = perf_session__process_event(self, &event, ops, head)) < 0) {
-               dump_printf("%#Lx [%#x]: skipping unknown header type: %d\n",
+               dump_printf("%#" PRIx64 " [%#x]: skipping unknown header type: %d\n",
                            head, event.header.size, event.header.type);
                /*
                 * assume we lost track of the stream, check alignment, and
@@ -954,7 +941,7 @@ int __perf_session__process_events(struct perf_session *session,
        struct ui_progress *progress;
        size_t  page_size, mmap_size;
        char *buf, *mmaps[8];
-       event_t *event;
+       union perf_event *event;
        uint32_t size;
 
        perf_event_ops__fill_defaults(ops);
@@ -999,7 +986,7 @@ remap:
        file_pos = file_offset + head;
 
 more:
-       event = (event_t *)(buf + head);
+       event = (union perf_event *)(buf + head);
 
        if (session->header.needs_swap)
                perf_event_header__bswap(&event->header);
@@ -1023,7 +1010,7 @@ more:
 
        if (size == 0 ||
            perf_session__process_event(session, event, ops, file_pos) < 0) {
-               dump_printf("%#Lx [%#x]: skipping unknown header type: %d\n",
+               dump_printf("%#" PRIx64 " [%#x]: skipping unknown header type: %d\n",
                            file_offset + head, event->header.size,
                            event->header.type);
                /*
@@ -1132,3 +1119,18 @@ size_t perf_session__fprintf_dsos_buildid(struct perf_session *self, FILE *fp,
        size_t ret = machine__fprintf_dsos_buildid(&self->host_machine, fp, with_hits);
        return ret + machines__fprintf_dsos_buildid(&self->machines, fp, with_hits);
 }
+
+size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp)
+{
+       struct perf_evsel *pos;
+       size_t ret = fprintf(fp, "Aggregated stats:\n");
+
+       ret += hists__fprintf_nr_events(&session->hists, fp);
+
+       list_for_each_entry(pos, &session->evlist->entries, node) {
+               ret += fprintf(fp, "%s stats:\n", event_name(pos));
+               ret += hists__fprintf_nr_events(&pos->hists, fp);
+       }
+
+       return ret;
+}