perf report: Fix reporting of hypervisor
[firefly-linux-kernel-4.4.55.git] / tools / perf / builtin-report.c
index 681c2233f8827a2709caf1e578318467247d7c85..88e88c510ae586070c48e6c85b7f819c556d958d 100644 (file)
@@ -15,6 +15,8 @@
 #include "util/rbtree.h"
 #include "util/symbol.h"
 #include "util/string.h"
+#include "util/callchain.h"
+#include "util/strlist.h"
 
 #include "perf.h"
 #include "util/header.h"
@@ -31,6 +33,8 @@ static char           *vmlinux = NULL;
 
 static char            default_sort_order[] = "comm,dso";
 static char            *sort_order = default_sort_order;
+static char            *dso_list_str, *comm_list_str, *sym_list_str;
+static struct strlist  *dso_list, *comm_list, *sym_list;
 
 static int             input;
 static int             show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV;
@@ -52,6 +56,7 @@ static char           *parent_pattern = default_parent_pattern;
 static regex_t         parent_regex;
 
 static int             exclude_other = 1;
+static int             callchain;
 
 static u64             sample_type;
 
@@ -62,11 +67,6 @@ struct ip_event {
        unsigned char __more_data[];
 };
 
-struct ip_callchain {
-       u64 nr;
-       u64 ips[0];
-};
-
 struct mmap_event {
        struct perf_event_header header;
        u32 pid, tid;
@@ -240,7 +240,7 @@ static u64 vdso__map_ip(struct map *map, u64 ip)
 
 static inline int is_anon_memory(const char *filename)
 {
-     return strcmp(filename, "//anon") == 0;
+       return strcmp(filename, "//anon") == 0;
 }
 
 static struct map *map__new(struct mmap_event *event)
@@ -493,17 +493,19 @@ static size_t threads__fprintf(FILE *fp)
 static struct rb_root hist;
 
 struct hist_entry {
-       struct rb_node   rb_node;
-
-       struct thread    *thread;
-       struct map       *map;
-       struct dso       *dso;
-       struct symbol    *sym;
-       struct symbol    *parent;
-       u64              ip;
-       char             level;
-
-       u64              count;
+       struct rb_node          rb_node;
+
+       struct thread           *thread;
+       struct map              *map;
+       struct dso              *dso;
+       struct symbol           *sym;
+       struct symbol           *parent;
+       u64                     ip;
+       char                    level;
+       struct callchain_node   callchain;
+       struct rb_root          sorted_chain;
+
+       u64                     count;
 };
 
 /*
@@ -773,6 +775,48 @@ hist_entry__collapse(struct hist_entry *left, struct hist_entry *right)
        return cmp;
 }
 
+static size_t
+callchain__fprintf(FILE *fp, struct callchain_node *self, u64 total_samples)
+{
+       struct callchain_list *chain;
+       size_t ret = 0;
+
+       if (!self)
+               return 0;
+
+       ret += callchain__fprintf(fp, self->parent, total_samples);
+
+
+       list_for_each_entry(chain, &self->val, list)
+               ret += fprintf(fp, "                %p\n", (void *)chain->ip);
+
+       return ret;
+}
+
+static size_t
+hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self,
+                             u64 total_samples)
+{
+       struct rb_node *rb_node;
+       struct callchain_node *chain;
+       size_t ret = 0;
+
+       rb_node = rb_first(&self->sorted_chain);
+       while (rb_node) {
+               double percent;
+
+               chain = rb_entry(rb_node, struct callchain_node, rb_node);
+               percent = chain->hit * 100.0 / total_samples;
+               ret += fprintf(fp, "           %6.2f%%\n", percent);
+               ret += callchain__fprintf(fp, chain, total_samples);
+               ret += fprintf(fp, "\n");
+               rb_node = rb_next(rb_node);
+       }
+
+       return ret;
+}
+
+
 static size_t
 hist_entry__fprintf(FILE *fp, struct hist_entry *self, u64 total_samples)
 {
@@ -813,6 +857,9 @@ hist_entry__fprintf(FILE *fp, struct hist_entry *self, u64 total_samples)
 
        ret += fprintf(fp, "\n");
 
+       if (callchain)
+               hist_entry_callchain__fprintf(fp, self, total_samples);
+
        return ret;
 }
 
@@ -897,6 +944,7 @@ hist_entry__add(struct thread *thread, struct map *map, struct dso *dso,
                .level  = level,
                .count  = count,
                .parent = NULL,
+               .sorted_chain = RB_ROOT
        };
        int cmp;
 
@@ -939,6 +987,8 @@ hist_entry__add(struct thread *thread, struct map *map, struct dso *dso,
 
                if (!cmp) {
                        he->count += count;
+                       if (callchain)
+                               append_chain(&he->callchain, chain);
                        return 0;
                }
 
@@ -952,6 +1002,10 @@ hist_entry__add(struct thread *thread, struct map *map, struct dso *dso,
        if (!he)
                return -ENOMEM;
        *he = entry;
+       if (callchain) {
+               callchain_init(&he->callchain);
+               append_chain(&he->callchain, chain);
+       }
        rb_link_node(&he->rb_node, parent, p);
        rb_insert_color(&he->rb_node, &hist);
 
@@ -1028,6 +1082,9 @@ static void output__insert_entry(struct hist_entry *he)
        struct rb_node *parent = NULL;
        struct hist_entry *iter;
 
+       if (callchain)
+               sort_chain_to_rbtree(&he->sorted_chain, &he->callchain);
+
        while (*p != NULL) {
                parent = *p;
                iter = rb_entry(parent, struct hist_entry, rb_node);
@@ -1156,6 +1213,7 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head)
        struct map *map = NULL;
        void *more_data = event->ip.__more_data;
        struct ip_callchain *chain = NULL;
+       int cpumode;
 
        if (sample_type & PERF_SAMPLE_PERIOD) {
                period = *(u64 *)more_data;
@@ -1196,7 +1254,12 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head)
                return -1;
        }
 
-       if (event->header.misc & PERF_EVENT_MISC_KERNEL) {
+       if (comm_list && !strlist__has_entry(comm_list, thread->comm))
+               return 0;
+
+       cpumode = event->header.misc & PERF_EVENT_MISC_CPUMODE_MASK;
+
+       if (cpumode == PERF_EVENT_MISC_KERNEL) {
                show = SHOW_KERNEL;
                level = 'k';
 
@@ -1204,7 +1267,7 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head)
 
                dprintf(" ...... dso: %s\n", dso->name);
 
-       } else if (event->header.misc & PERF_EVENT_MISC_USER) {
+       } else if (cpumode == PERF_EVENT_MISC_USER) {
 
                show = SHOW_USER;
                level = '.';
@@ -1218,6 +1281,12 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head)
        if (show & show_mask) {
                struct symbol *sym = resolve_symbol(thread, &map, &dso, &ip);
 
+               if (dso_list && dso && dso->name && !strlist__has_entry(dso_list, dso->name))
+                       return 0;
+
+               if (sym_list && sym && !strlist__has_entry(sym_list, sym->name))
+                       return 0;
+
                if (hist_entry__add(thread, map, dso, sym, ip, chain, level, period)) {
                        eprintf("problem incrementing symbol count, skipping event\n");
                        return -1;
@@ -1604,6 +1673,13 @@ static const struct option options[] = {
                   "regex filter to identify parent, see: '--sort parent'"),
        OPT_BOOLEAN('x', "exclude-other", &exclude_other,
                    "Only display entries with parent-match"),
+       OPT_BOOLEAN('c', "callchain", &callchain, "Display callchains"),
+       OPT_STRING('d', "dsos", &dso_list_str, "dso[,dso...]",
+                  "only consider symbols in these dsos"),
+       OPT_STRING('C', "comms", &comm_list_str, "comm[,comm...]",
+                  "only consider symbols in these comms"),
+       OPT_STRING('S', "symbols", &sym_list_str, "symbol[,symbol...]",
+                  "only consider these symbols"),
        OPT_END()
 };
 
@@ -1622,6 +1698,19 @@ static void setup_sorting(void)
        free(str);
 }
 
+static void setup_list(struct strlist **list, const char *list_str,
+                      const char *list_name)
+{
+       if (list_str) {
+               *list = strlist__new(true, list_str);
+               if (!*list) {
+                       fprintf(stderr, "problems parsing %s list\n",
+                               list_name);
+                       exit(129);
+               }
+       }
+}
+
 int cmd_report(int argc, const char **argv, const char *prefix)
 {
        symbol__init();
@@ -1643,6 +1732,10 @@ int cmd_report(int argc, const char **argv, const char *prefix)
        if (argc)
                usage_with_options(report_usage, options);
 
+       setup_list(&dso_list, dso_list_str, "dso");
+       setup_list(&comm_list, comm_list_str, "comm");
+       setup_list(&sym_list, sym_list_str, "symbol");
+
        setup_pager();
 
        return __cmd_report();