perf_counter tools: report: Implement header output for --sort variants
[deliverable/linux.git] / Documentation / perf_counter / builtin-report.c
index a55f15d7651d5a03423b31a34bc925322ed7ccc3..506cde437b785197e9c2ffe6fd091fbb69ac5e16 100644 (file)
@@ -1,11 +1,12 @@
 #include "util/util.h"
+#include "builtin.h"
 
 #include <libelf.h>
 #include <gelf.h>
 #include <elf.h>
-#include <ctype.h>
 
 #include "util/list.h"
+#include "util/cache.h"
 #include "util/rbtree.h"
 
 #include "perf.h"
 #define SHOW_USER      2
 #define SHOW_HV                4
 
-static char            const *input_name = "output.perf";
+static char            const *input_name = "perf.data";
+static char            *vmlinux = NULL;
+static char            *sort_order = "pid,symbol";
 static int             input;
 static int             show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV;
 
 static int             dump_trace = 0;
+static int             verbose;
 
 static unsigned long   page_size;
 static unsigned long   mmap_window = 32;
@@ -59,10 +63,10 @@ typedef union event_union {
 } event_t;
 
 struct symbol {
-       struct rb_node rb_node;
-       uint64_t       start;
-       uint64_t       end;
-       char           name[0];
+       struct rb_node          rb_node;
+       __u64                   start;
+       __u64                   end;
+       char                    name[0];
 };
 
 static struct symbol *symbol__new(uint64_t start, uint64_t len, const char *name)
@@ -85,7 +89,7 @@ static void symbol__delete(struct symbol *self)
 
 static size_t symbol__fprintf(struct symbol *self, FILE *fp)
 {
-       return fprintf(fp, " %lx-%lx %s\n",
+       return fprintf(fp, " %llx-%llx %s\n",
                       self->start, self->end, self->name);
 }
 
@@ -146,10 +150,12 @@ static void dso__insert_symbol(struct dso *self, struct symbol *sym)
 
 static struct symbol *dso__find_symbol(struct dso *self, uint64_t ip)
 {
+       struct rb_node *n;
+
        if (self == NULL)
                return NULL;
 
-       struct rb_node *n = self->syms.rb_node;
+       n = self->syms.rb_node;
 
        while (n) {
                struct symbol *s = rb_entry(n, struct symbol, rb_node);
@@ -186,7 +192,8 @@ static inline int elf_sym__is_function(const GElf_Sym *sym)
 {
        return elf_sym__type(sym) == STT_FUNC &&
               sym->st_name != 0 &&
-              sym->st_shndx != SHN_UNDEF;
+              sym->st_shndx != SHN_UNDEF &&
+              sym->st_size != 0;
 }
 
 static inline const char *elf_sym__name(const GElf_Sym *sym,
@@ -218,35 +225,40 @@ static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep,
        return sec;
 }
 
-static int dso__load(struct dso *self)
+static int dso__load_sym(struct dso *self, int fd, char *name)
 {
-       int fd = open(self->name, O_RDONLY), err = -1;
-
-       if (fd == -1)
-               return -1;
+       Elf_Data *symstrs;
+       uint32_t nr_syms;
+       int err = -1;
+       uint32_t index;
+       GElf_Ehdr ehdr;
+       GElf_Shdr shdr;
+       Elf_Data *syms;
+       GElf_Sym sym;
+       Elf_Scn *sec;
+       Elf *elf;
+       int nr = 0;
 
-       Elf *elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
+       elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
        if (elf == NULL) {
                fprintf(stderr, "%s: cannot read %s ELF file.\n",
-                       __func__, self->name);
+                       __func__, name);
                goto out_close;
        }
 
-       GElf_Ehdr ehdr;
        if (gelf_getehdr(elf, &ehdr) == NULL) {
                fprintf(stderr, "%s: cannot get elf header.\n", __func__);
                goto out_elf_end;
        }
 
-       GElf_Shdr shdr;
-       Elf_Scn *sec = elf_section_by_name(elf, &ehdr, &shdr, ".symtab", NULL);
+       sec = elf_section_by_name(elf, &ehdr, &shdr, ".symtab", NULL);
        if (sec == NULL)
                sec = elf_section_by_name(elf, &ehdr, &shdr, ".dynsym", NULL);
 
        if (sec == NULL)
                goto out_elf_end;
 
-       Elf_Data *syms = elf_getdata(sec, NULL);
+       syms = elf_getdata(sec, NULL);
        if (syms == NULL)
                goto out_elf_end;
 
@@ -254,14 +266,12 @@ static int dso__load(struct dso *self)
        if (sec == NULL)
                goto out_elf_end;
 
-       Elf_Data *symstrs = elf_getdata(sec, NULL);
+       symstrs = elf_getdata(sec, NULL);
        if (symstrs == NULL)
                goto out_elf_end;
 
-       const uint32_t nr_syms = shdr.sh_size / shdr.sh_entsize;
+       nr_syms = shdr.sh_size / shdr.sh_entsize;
 
-       GElf_Sym sym;
-       uint32_t index;
        elf_symtab__for_each_symbol(syms, nr_syms, index, sym) {
                struct symbol *f;
 
@@ -281,16 +291,63 @@ static int dso__load(struct dso *self)
                        goto out_elf_end;
 
                dso__insert_symbol(self, f);
+
+               nr++;
        }
 
-       err = 0;
+       err = nr;
 out_elf_end:
        elf_end(elf);
 out_close:
-       close(fd);
        return err;
 }
 
+static int dso__load(struct dso *self)
+{
+       int size = strlen(self->name) + sizeof("/usr/lib/debug%s.debug");
+       char *name = malloc(size);
+       int variant = 0;
+       int ret = -1;
+       int fd;
+
+       if (!name)
+               return -1;
+
+more:
+       do {
+               switch (variant) {
+               case 0: /* Fedora */
+                       snprintf(name, size, "/usr/lib/debug%s.debug", self->name);
+                       break;
+               case 1: /* Ubuntu */
+                       snprintf(name, size, "/usr/lib/debug%s", self->name);
+                       break;
+               case 2: /* Sane people */
+                       snprintf(name, size, "%s", self->name);
+                       break;
+
+               default:
+                       goto out;
+               }
+               variant++;
+
+               fd = open(name, O_RDONLY);
+       } while (fd < 0);
+
+       ret = dso__load_sym(self, fd, name);
+       close(fd);
+
+       /*
+        * Some people seem to have debuginfo files _WITHOUT_ debug info!?!?
+        */
+       if (!ret)
+               goto more;
+
+out:
+       free(name);
+       return ret;
+}
+
 static size_t dso__fprintf(struct dso *self, FILE *fp)
 {
        size_t ret = fprintf(fp, "dso: %s\n", self->name);
@@ -325,12 +382,24 @@ static struct dso *dsos__find(const char *name)
 static struct dso *dsos__findnew(const char *name)
 {
        struct dso *dso = dsos__find(name);
+       int nr;
 
        if (dso == NULL) {
                dso = dso__new(name);
-               if (dso != NULL && dso__load(dso) < 0)
+               if (!dso)
                        goto out_delete_dso;
 
+               nr = dso__load(dso);
+               if (nr < 0) {
+                       fprintf(stderr, "Failed to open: %s\n", name);
+                       goto out_delete_dso;
+               }
+               if (!nr) {
+                       fprintf(stderr,
+               "Failed to find debug symbols for: %s, maybe install a debug package?\n",
+                                       name);
+               }
+
                dsos__add(dso);
        }
 
@@ -341,7 +410,7 @@ out_delete_dso:
        return NULL;
 }
 
-void dsos__fprintf(FILE *fp)
+static void dsos__fprintf(FILE *fp)
 {
        struct dso *pos;
 
@@ -364,7 +433,7 @@ static int hex(char ch)
  * While we find nice hex chars, build a long_val.
  * Return number of chars processed.
  */
-int hex2long(char *ptr, unsigned long *long_val)
+static int hex2long(char *ptr, unsigned long *long_val)
 {
        const char *p = ptr;
        *long_val = 0;
@@ -384,21 +453,26 @@ int hex2long(char *ptr, unsigned long *long_val)
 
 static int load_kallsyms(void)
 {
+       struct rb_node *nd, *prevnd;
+       char *line = NULL;
+       FILE *file;
+       size_t n;
+
        kernel_dso = dso__new("[kernel]");
        if (kernel_dso == NULL)
                return -1;
 
-       FILE *file = fopen("/proc/kallsyms", "r");
-
+       file = fopen("/proc/kallsyms", "r");
        if (file == NULL)
                goto out_delete_dso;
 
-       char *line = NULL;
-       size_t n;
-
        while (!feof(file)) {
                unsigned long start;
-               int line_len = getline(&line, &n, file);
+               struct symbol *sym;
+               int line_len, len;
+               char symbol_type;
+
+               line_len = getline(&line, &n, file);
                if (line_len < 0)
                        break;
 
@@ -407,22 +481,22 @@ static int load_kallsyms(void)
 
                line[--line_len] = '\0'; /* \n */
 
-               int len = hex2long(line, &start);
-               
+               len = hex2long(line, &start);
+
                len++;
                if (len + 2 >= line_len)
                        continue;
 
-               char symbol_type = line[len];
+               symbol_type = toupper(line[len]);
                /*
                 * We're interested only in code ('T'ext)
                 */
-               if (toupper(symbol_type) != 'T')
+               if (symbol_type != 'T' && symbol_type != 'W')
                        continue;
                /*
                 * Well fix up the end later, when we have all sorted.
                 */
-               struct symbol *sym = symbol__new(start, 0xdead, line + len + 2);
+               sym = symbol__new(start, 0xdead, line + len + 2);
 
                if (sym == NULL)
                        goto out_delete_dso;
@@ -434,7 +508,7 @@ static int load_kallsyms(void)
         * Now that we have all sorted out, just set the ->end of all
         * symbols
         */
-       struct rb_node *nd, *prevnd = rb_first(&kernel_dso->syms);
+       prevnd = rb_first(&kernel_dso->syms);
 
        if (prevnd == NULL)
                goto out_delete_line;
@@ -450,6 +524,7 @@ static int load_kallsyms(void)
        dsos__add(kernel_dso);
        free(line);
        fclose(file);
+
        return 0;
 
 out_delete_line:
@@ -459,6 +534,39 @@ out_delete_dso:
        return -1;
 }
 
+static int load_kernel(void)
+{
+       int fd, nr;
+
+       if (!vmlinux)
+               goto kallsyms;
+
+       fd = open(vmlinux, O_RDONLY);
+       if (fd < 0)
+               goto kallsyms;
+
+       kernel_dso = dso__new("[kernel]");
+       if (!kernel_dso)
+               goto fail_open;
+
+       nr = dso__load_sym(kernel_dso, fd, vmlinux);
+
+       if (nr <= 0)
+               goto fail_load;
+
+       dsos__add(kernel_dso);
+       close(fd);
+
+       return 0;
+
+fail_load:
+       dso__delete(kernel_dso);
+fail_open:
+       close(fd);
+kallsyms:
+       return load_kallsyms();
+}
+
 struct map {
        struct list_head node;
        uint64_t         start;
@@ -486,285 +594,433 @@ out_delete:
        return NULL;
 }
 
-static size_t map__fprintf(struct map *self, FILE *fp)
-{
-       return fprintf(fp, " %lx-%lx %lx %s\n",
-                      self->start, self->end, self->pgoff, self->dso->name);
-}
-
 struct thread;
 
-static const char *thread__name(struct thread *self, char *bf, size_t size);
-
-struct symhist {
+struct thread {
        struct rb_node   rb_node;
-       struct dso       *dso;
-       struct symbol    *sym;
-       struct thread    *thread;
-       uint64_t         ip;
-       uint32_t         count;
-       char             level;
+       struct list_head maps;
+       pid_t            pid;
+       char             *comm;
 };
 
-static struct symhist *symhist__new(struct symbol *sym, uint64_t ip,
-                                   struct thread *thread, struct dso *dso,
-                                   char level)
+static struct thread *thread__new(pid_t pid)
 {
-       struct symhist *self = malloc(sizeof(*self));
+       struct thread *self = malloc(sizeof(*self));
 
        if (self != NULL) {
-               self->sym    = sym;
-               self->thread = thread;
-               self->ip     = ip;
-               self->dso    = dso;
-               self->level  = level;
-               self->count  = 1;
+               self->pid = pid;
+               self->comm = NULL;
+               INIT_LIST_HEAD(&self->maps);
        }
 
        return self;
 }
 
-void symhist__delete(struct symhist *self)
+static int thread__set_comm(struct thread *self, const char *comm)
 {
-       free(self);
+       self->comm = strdup(comm);
+       return self->comm ? 0 : -ENOMEM;
+}
+
+static struct rb_root threads;
+
+static struct thread *threads__findnew(pid_t pid)
+{
+       struct rb_node **p = &threads.rb_node;
+       struct rb_node *parent = NULL;
+       struct thread *th;
+
+       while (*p != NULL) {
+               parent = *p;
+               th = rb_entry(parent, struct thread, rb_node);
+
+               if (th->pid == pid)
+                       return th;
+
+               if (pid < th->pid)
+                       p = &(*p)->rb_left;
+               else
+                       p = &(*p)->rb_right;
+       }
+
+       th = thread__new(pid);
+       if (th != NULL) {
+               rb_link_node(&th->rb_node, parent, p);
+               rb_insert_color(&th->rb_node, &threads);
+       }
+       return th;
 }
 
-static void symhist__inc(struct symhist *self)
+static void thread__insert_map(struct thread *self, struct map *map)
 {
-       ++self->count;
+       list_add_tail(&map->node, &self->maps);
 }
 
-static size_t
-symhist__fprintf(struct symhist *self, uint64_t total_samples, FILE *fp)
+static struct map *thread__find_map(struct thread *self, uint64_t ip)
 {
-       char bf[32];
-       size_t ret;
+       struct map *pos;
 
-       if (total_samples)
-               ret = fprintf(fp, "%5.2f", (self->count * 100.0) / total_samples);
-       else
-               ret = fprintf(fp, "%12d", self->count);
-
-       ret += fprintf(fp, "%14s [%c] %#018llx ",
-                      thread__name(self->thread, bf, sizeof(bf)),
-                      self->level, (unsigned long long)self->ip);
-
-       if (self->level != '.')
-               ret += fprintf(fp, "%s\n",
-                              self->sym ? self->sym->name : "<unknown>");
-       else
-               ret += fprintf(fp, "%s: %s\n",
-                              self->dso ? self->dso->name : "<unknown>",
-                              self->sym ? self->sym->name : "<unknown>");
-       return ret;
+       if (self == NULL)
+               return NULL;
+
+       list_for_each_entry(pos, &self->maps, node)
+               if (ip >= pos->start && ip <= pos->end)
+                       return pos;
+
+       return NULL;
 }
 
-struct thread {
+/*
+ * histogram, sorted on item, collects counts
+ */
+
+static struct rb_root hist;
+
+struct hist_entry {
        struct rb_node   rb_node;
-       struct list_head maps;
-       struct rb_root   symhists;
-       pid_t            pid;
-       char             *comm;
+
+       struct thread    *thread;
+       struct map       *map;
+       struct dso       *dso;
+       struct symbol    *sym;
+       uint64_t         ip;
+       char             level;
+
+       uint32_t         count;
 };
 
-static const char *thread__name(struct thread *self, char *bf, size_t size)
+/*
+ * configurable sorting bits
+ */
+
+struct sort_entry {
+       struct list_head list;
+
+       char *header;
+
+       int64_t (*cmp)(struct hist_entry *, struct hist_entry *);
+       size_t  (*print)(FILE *fp, struct hist_entry *);
+};
+
+static int64_t
+sort__thread_cmp(struct hist_entry *left, struct hist_entry *right)
 {
-       if (self->comm)
-               return self->comm;
+       return right->thread->pid - left->thread->pid;
+}
 
-       snprintf(bf, sizeof(bf), ":%u", self->pid);
-       return bf;
+static size_t
+sort__thread_print(FILE *fp, struct hist_entry *self)
+{
+       return fprintf(fp, " %16s:%5d", self->thread->comm ?: "", self->thread->pid);
 }
 
-static struct thread *thread__new(pid_t pid)
+static struct sort_entry sort_thread = {
+       .header = "         Command: Pid ",
+       .cmp    = sort__thread_cmp,
+       .print  = sort__thread_print,
+};
+
+static int64_t
+sort__comm_cmp(struct hist_entry *left, struct hist_entry *right)
 {
-       struct thread *self = malloc(sizeof(*self));
+       char *comm_l = left->thread->comm;
+       char *comm_r = right->thread->comm;
 
-       if (self != NULL) {
-               self->pid = pid;
-               self->comm = NULL;
-               INIT_LIST_HEAD(&self->maps);
-               self->symhists = RB_ROOT;
+       if (!comm_l || !comm_r) {
+               if (!comm_l && !comm_r)
+                       return 0;
+               else if (!comm_l)
+                       return -1;
+               else
+                       return 1;
        }
 
-       return self;
+       return strcmp(comm_l, comm_r);
 }
 
-static int thread__symbol_incnew(struct thread *self, struct symbol *sym,
-                                uint64_t ip, struct dso *dso, char level)
+static size_t
+sort__comm_print(FILE *fp, struct hist_entry *self)
 {
-       struct rb_node **p = &self->symhists.rb_node;
-       struct rb_node *parent = NULL;
-       struct symhist *sh;
-
-       while (*p != NULL) {
-               parent = *p;
-               sh = rb_entry(parent, struct symhist, rb_node);
+       return fprintf(fp, " %16s", self->thread->comm ?: "<unknown>");
+}
 
-               if (sh->sym == sym || ip == sh->ip) {
-                       symhist__inc(sh);
-                       return 0;
-               }
+static struct sort_entry sort_comm = {
+       .header = "         Command",
+       .cmp    = sort__comm_cmp,
+       .print  = sort__comm_print,
+};
 
-               /* Handle unresolved symbols too */
-               const uint64_t start = !sh->sym ? sh->ip : sh->sym->start;
+static int64_t
+sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
+{
+       struct dso *dso_l = left->dso;
+       struct dso *dso_r = right->dso;
 
-               if (ip < start)
-                       p = &(*p)->rb_left;
+       if (!dso_l || !dso_r) {
+               if (!dso_l && !dso_r)
+                       return 0;
+               else if (!dso_l)
+                       return -1;
                else
-                       p = &(*p)->rb_right;
+                       return 1;
        }
 
-       sh = symhist__new(sym, ip, self, dso, level);
-       if (sh == NULL)
-               return -ENOMEM;
-       rb_link_node(&sh->rb_node, parent, p);
-       rb_insert_color(&sh->rb_node, &self->symhists);
-       return 0;
+       return strcmp(dso_l->name, dso_r->name);
 }
 
-static int thread__set_comm(struct thread *self, const char *comm)
+static size_t
+sort__dso_print(FILE *fp, struct hist_entry *self)
 {
-       self->comm = strdup(comm);
-       return self->comm ? 0 : -ENOMEM;
+       return fprintf(fp, " %64s", self->dso ? self->dso->name : "<unknown>");
 }
 
-size_t thread__maps_fprintf(struct thread *self, FILE *fp)
+static struct sort_entry sort_dso = {
+       .header = "                                                    Shared Object",
+       .cmp    = sort__dso_cmp,
+       .print  = sort__dso_print,
+};
+
+static int64_t
+sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
 {
-       struct map *pos;
-       size_t ret = 0;
+       uint64_t ip_l, ip_r;
 
-       list_for_each_entry(pos, &self->maps, node)
-               ret += map__fprintf(pos, fp);
+       if (left->sym == right->sym)
+               return 0;
 
-       return ret;
+       ip_l = left->sym ? left->sym->start : left->ip;
+       ip_r = right->sym ? right->sym->start : right->ip;
+
+       return (int64_t)(ip_r - ip_l);
 }
 
-static size_t thread__fprintf(struct thread *self, FILE *fp)
+static size_t
+sort__sym_print(FILE *fp, struct hist_entry *self)
 {
-       int ret = fprintf(fp, "thread: %d %s\n", self->pid, self->comm);
-       struct rb_node *nd;
+       size_t ret = 0;
 
-       for (nd = rb_first(&self->symhists); nd; nd = rb_next(nd)) {
-               struct symhist *pos = rb_entry(nd, struct symhist, rb_node);
-               ret += symhist__fprintf(pos, 0, fp);
-       }
+       if (verbose)
+               ret += fprintf(fp, " %#018llx", (unsigned long long)self->ip);
+
+       ret += fprintf(fp, " %s: %s",
+                       self->dso ? self->dso->name : "<unknown>",
+                       self->sym ? self->sym->name : "<unknown>");
 
        return ret;
 }
 
-static struct rb_root threads = RB_ROOT;
+static struct sort_entry sort_sym = {
+       .header = "Shared Object: Symbol",
+       .cmp    = sort__sym_cmp,
+       .print  = sort__sym_print,
+};
 
-static struct thread *threads__findnew(pid_t pid)
+struct sort_dimension {
+       char *name;
+       struct sort_entry *entry;
+       int taken;
+};
+
+static struct sort_dimension sort_dimensions[] = {
+       { .name = "pid",        .entry = &sort_thread,  },
+       { .name = "comm",       .entry = &sort_comm,    },
+       { .name = "dso",        .entry = &sort_dso,     },
+       { .name = "symbol",     .entry = &sort_sym,     },
+};
+
+static LIST_HEAD(hist_entry__sort_list);
+
+static int sort_dimension__add(char *tok)
 {
-       struct rb_node **p = &threads.rb_node;
-       struct rb_node *parent = NULL;
-       struct thread *th;
+       int i;
 
-       while (*p != NULL) {
-               parent = *p;
-               th = rb_entry(parent, struct thread, rb_node);
+       for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) {
+               struct sort_dimension *sd = &sort_dimensions[i];
 
-               if (th->pid == pid)
-                       return th;
+               if (sd->taken)
+                       continue;
 
-               if (pid < th->pid)
-                       p = &(*p)->rb_left;
-               else
-                       p = &(*p)->rb_right;
-       }
+               if (strcmp(tok, sd->name))
+                       continue;
 
-       th = thread__new(pid);
-       if (th != NULL) {
-               rb_link_node(&th->rb_node, parent, p);
-               rb_insert_color(&th->rb_node, &threads);
+               list_add_tail(&sd->entry->list, &hist_entry__sort_list);
+               sd->taken = 1;
+               return 0;
        }
-       return th;
+
+       return -ESRCH;
 }
 
-static void thread__insert_map(struct thread *self, struct map *map)
+static void setup_sorting(void)
 {
-       list_add_tail(&map->node, &self->maps);
+       char *tmp, *tok, *str = strdup(sort_order);
+
+       for (tok = strtok_r(str, ", ", &tmp);
+                       tok; tok = strtok_r(NULL, ", ", &tmp))
+               sort_dimension__add(tok);
+
+       free(str);
 }
 
-static struct map *thread__find_map(struct thread *self, uint64_t ip)
+static int64_t
+hist_entry__cmp(struct hist_entry *left, struct hist_entry *right)
 {
-       if (self == NULL)
-               return NULL;
+       struct sort_entry *se;
+       int64_t cmp = 0;
 
-       struct map *pos;
+       list_for_each_entry(se, &hist_entry__sort_list, list) {
+               cmp = se->cmp(left, right);
+               if (cmp)
+                       break;
+       }
 
-       list_for_each_entry(pos, &self->maps, node)
-               if (ip >= pos->start && ip <= pos->end)
-                       return pos;
+       return cmp;
+}
 
-       return NULL;
+static size_t
+hist_entry__fprintf(FILE *fp, struct hist_entry *self, uint64_t total_samples)
+{
+       struct sort_entry *se;
+       size_t ret;
+
+       if (total_samples) {
+               ret = fprintf(fp, "    %5.2f%%",
+                               (self->count * 100.0) / total_samples);
+       } else
+               ret = fprintf(fp, "%12d ", self->count);
+
+       list_for_each_entry(se, &hist_entry__sort_list, list)
+               ret += se->print(fp, self);
+
+       ret += fprintf(fp, "\n");
+
+       return ret;
 }
 
-void threads__fprintf(FILE *fp)
+/*
+ * collect histogram counts
+ */
+
+static int
+hist_entry__add(struct thread *thread, struct map *map, struct dso *dso,
+               struct symbol *sym, uint64_t ip, char level)
 {
-       struct rb_node *nd;
-       for (nd = rb_first(&threads); nd; nd = rb_next(nd)) {
-               struct thread *pos = rb_entry(nd, struct thread, rb_node);
-               thread__fprintf(pos, fp);
+       struct rb_node **p = &hist.rb_node;
+       struct rb_node *parent = NULL;
+       struct hist_entry *he;
+       struct hist_entry entry = {
+               .thread = thread,
+               .map    = map,
+               .dso    = dso,
+               .sym    = sym,
+               .ip     = ip,
+               .level  = level,
+               .count  = 1,
+       };
+       int cmp;
+
+       while (*p != NULL) {
+               parent = *p;
+               he = rb_entry(parent, struct hist_entry, rb_node);
+
+               cmp = hist_entry__cmp(&entry, he);
+
+               if (!cmp) {
+                       he->count++;
+                       return 0;
+               }
+
+               if (cmp < 0)
+                       p = &(*p)->rb_left;
+               else
+                       p = &(*p)->rb_right;
        }
+
+       he = malloc(sizeof(*he));
+       if (!he)
+               return -ENOMEM;
+       *he = entry;
+       rb_link_node(&he->rb_node, parent, p);
+       rb_insert_color(&he->rb_node, &hist);
+
+       return 0;
 }
 
-static struct rb_root global_symhists = RB_ROOT;
+/*
+ * reverse the map, sort on count.
+ */
+
+static struct rb_root output_hists;
 
-static void threads__insert_symhist(struct symhist *sh)
+static void output__insert_entry(struct hist_entry *he)
 {
-       struct rb_node **p = &global_symhists.rb_node;
+       struct rb_node **p = &output_hists.rb_node;
        struct rb_node *parent = NULL;
-       struct symhist *iter;
+       struct hist_entry *iter;
 
        while (*p != NULL) {
                parent = *p;
-               iter = rb_entry(parent, struct symhist, rb_node);
+               iter = rb_entry(parent, struct hist_entry, rb_node);
 
-               /* Reverse order */
-               if (sh->count > iter->count)
+               if (he->count > iter->count)
                        p = &(*p)->rb_left;
                else
                        p = &(*p)->rb_right;
        }
 
-       rb_link_node(&sh->rb_node, parent, p);
-       rb_insert_color(&sh->rb_node, &global_symhists);
+       rb_link_node(&he->rb_node, parent, p);
+       rb_insert_color(&he->rb_node, &output_hists);
 }
 
-static void threads__sort_symhists(void)
+static void output__resort(void)
 {
-       struct rb_node *nd;
-
-       for (nd = rb_first(&threads); nd; nd = rb_next(nd)) {
-               struct thread *thread = rb_entry(nd, struct thread, rb_node);
-               struct rb_node *next = rb_first(&thread->symhists);
+       struct rb_node *next = rb_first(&hist);
+       struct hist_entry *n;
 
-               while (next) {
-                       struct symhist *n = rb_entry(next, struct symhist,
-                                                    rb_node);
-                       next = rb_next(&n->rb_node);
-                       rb_erase(&n->rb_node, &thread->symhists);
-                       threads__insert_symhist(n);
-               }
+       while (next) {
+               n = rb_entry(next, struct hist_entry, rb_node);
+               next = rb_next(&n->rb_node);
 
+               rb_erase(&n->rb_node, &hist);
+               output__insert_entry(n);
        }
 }
 
-static size_t threads__symhists_fprintf(uint64_t total_samples, FILE *fp)
+static size_t output__fprintf(FILE *fp, uint64_t total_samples)
 {
+       struct hist_entry *pos;
+       struct sort_entry *se;
        struct rb_node *nd;
        size_t ret = 0;
 
-       for (nd = rb_first(&global_symhists); nd; nd = rb_next(nd)) {
-               struct symhist *pos = rb_entry(nd, struct symhist, rb_node);
-               ret += symhist__fprintf(pos, total_samples, fp);
+       fprintf(fp, "#\n");
+
+       fprintf(fp, "# Overhead");
+       list_for_each_entry(se, &hist_entry__sort_list, list)
+               fprintf(fp, " %s", se->header);
+       fprintf(fp, "\n");
+
+       fprintf(fp, "# ........");
+       list_for_each_entry(se, &hist_entry__sort_list, list) {
+               int i;
+
+               fprintf(fp, " ");
+               for (i = 0; i < strlen(se->header); i++)
+                       fprintf(fp, ".");
+       }
+       fprintf(fp, "\n");
+
+       fprintf(fp, "#\n");
+
+       for (nd = rb_first(&output_hists); nd; nd = rb_next(nd)) {
+               pos = rb_entry(nd, struct hist_entry, rb_node);
+               ret += hist_entry__fprintf(fp, pos, total_samples);
        }
 
        return ret;
 }
 
+
 static int __cmd_report(void)
 {
        unsigned long offset = 0;
@@ -793,7 +1049,7 @@ static int __cmd_report(void)
                exit(0);
        }
 
-       if (load_kallsyms() < 0) {
+       if (load_kernel() < 0) {
                perror("failed to open kallsyms");
                return EXIT_FAILURE;
        }
@@ -835,6 +1091,7 @@ more:
                struct dso *dso = NULL;
                struct thread *thread = threads__findnew(event->ip.pid);
                uint64_t ip = event->ip.ip;
+               struct map *map = NULL;
 
                if (dump_trace) {
                        fprintf(stderr, "%p [%p]: PERF_EVENT (IP, %d): %d: %p\n",
@@ -842,27 +1099,32 @@ more:
                                (void *)(long)(event->header.size),
                                event->header.misc,
                                event->ip.pid,
-                               (void *)event->ip.ip);
+                               (void *)(long)ip);
                }
 
                if (thread == NULL) {
-                       fprintf(stderr, "problem processing %d event, bailing out\n",
+                       fprintf(stderr, "problem processing %d event, skipping it.\n",
                                event->header.type);
-                       goto done;
+                       goto broken_event;
                }
 
                if (event->header.misc & PERF_EVENT_MISC_KERNEL) {
                        show = SHOW_KERNEL;
                        level = 'k';
+
                        dso = kernel_dso;
+
                } else if (event->header.misc & PERF_EVENT_MISC_USER) {
+
                        show = SHOW_USER;
                        level = '.';
-                       struct map *map = thread__find_map(thread, ip);
+
+                       map = thread__find_map(thread, ip);
                        if (map != NULL) {
                                dso = map->dso;
                                ip -= map->start + map->pgoff;
                        }
+
                } else {
                        show = SHOW_HV;
                        level = 'H';
@@ -871,9 +1133,10 @@ more:
                if (show & show_mask) {
                        struct symbol *sym = dso__find_symbol(dso, ip);
 
-                       if (thread__symbol_incnew(thread, sym, ip, dso, level)) {
-                               fprintf(stderr, "problem incrementing symbol count, bailing out\n");
-                               goto done;
+                       if (hist_entry__add(thread, map, dso, sym, ip, level)) {
+                               fprintf(stderr,
+               "problem incrementing symbol count, skipping event\n");
+                               goto broken_event;
                        }
                }
                total++;
@@ -886,14 +1149,14 @@ more:
                        fprintf(stderr, "%p [%p]: PERF_EVENT_MMAP: [%p(%p) @ %p]: %s\n",
                                (void *)(offset + head),
                                (void *)(long)(event->header.size),
-                               (void *)event->mmap.start,
-                               (void *)event->mmap.len,
-                               (void *)event->mmap.pgoff,
+                               (void *)(long)event->mmap.start,
+                               (void *)(long)event->mmap.len,
+                               (void *)(long)event->mmap.pgoff,
                                event->mmap.filename);
                }
                if (thread == NULL || map == NULL) {
-                       fprintf(stderr, "problem processing PERF_EVENT_MMAP, bailing out\n");
-                       goto done;
+                       fprintf(stderr, "problem processing PERF_EVENT_MMAP, skipping event.\n");
+                       goto broken_event;
                }
                thread__insert_map(thread, map);
                total_mmap++;
@@ -910,18 +1173,20 @@ more:
                }
                if (thread == NULL ||
                    thread__set_comm(thread, event->comm.comm)) {
-                       fprintf(stderr, "problem processing PERF_EVENT_COMM, bailing out\n");
-                       goto done;
+                       fprintf(stderr, "problem processing PERF_EVENT_COMM, skipping event.\n");
+                       goto broken_event;
                }
                total_comm++;
                break;
        }
        default: {
 broken_event:
-               fprintf(stderr, "%p [%p]: skipping unknown header type: %d\n",
-                       (void *)(offset + head),
-                       (void *)(long)(event->header.size),
-                       event->header.type);
+               if (dump_trace)
+                       fprintf(stderr, "%p [%p]: skipping unknown header type: %d\n",
+                                       (void *)(offset + head),
+                                       (void *)(long)(event->header.size),
+                                       event->header.type);
+
                total_unknown++;
 
                /*
@@ -942,7 +1207,6 @@ broken_event:
                goto more;
 
        rc = EXIT_SUCCESS;
-done:
        close(input);
 
        if (dump_trace) {
@@ -954,8 +1218,11 @@ done:
                return 0;
        }
 
-       threads__sort_symhists();
-       threads__symhists_fprintf(total, stdout);
+       if (verbose >= 2)
+               dsos__fprintf(stdout);
+
+       output__resort();
+       output__fprintf(stdout, total);
 
        return rc;
 }
@@ -968,8 +1235,12 @@ static const char * const report_usage[] = {
 static const struct option options[] = {
        OPT_STRING('i', "input", &input_name, "file",
                    "input file name"),
+       OPT_BOOLEAN('v', "verbose", &verbose,
+                   "be more verbose (show symbol address, etc)"),
        OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
                    "dump raw trace in ASCII"),
+       OPT_STRING('k', "vmlinux", &vmlinux, "file", "vmlinux pathname"),
+       OPT_STRING('s', "sort", &sort_order, "foo", "bar"),
        OPT_END()
 };
 
@@ -981,5 +1252,9 @@ int cmd_report(int argc, const char **argv, const char *prefix)
 
        parse_options(argc, argv, options, report_usage, 0);
 
+       setup_sorting();
+
+       setup_pager();
+
        return __cmd_report();
 }
This page took 0.039121 seconds and 5 git commands to generate.