perf_counter tools: report: Implement header output for --sort variants
[deliverable/linux.git] / Documentation / perf_counter / builtin-report.c
index 2d4e4cc655a3867454b687d49c2edb7ba16111e8..506cde437b785197e9c2ffe6fd091fbb69ac5e16 100644 (file)
@@ -1,10 +1,12 @@
 #include "util/util.h"
+#include "builtin.h"
 
 #include <libelf.h>
 #include <gelf.h>
 #include <elf.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;
@@ -58,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)
@@ -84,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);
 }
 
@@ -145,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);
@@ -185,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,
@@ -217,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;
 
@@ -253,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;
 
@@ -280,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);
@@ -324,11 +382,23 @@ 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);
        }
@@ -340,7 +410,7 @@ out_delete_dso:
        return NULL;
 }
 
-void dsos__fprintf(FILE *fp)
+static void dsos__fprintf(FILE *fp)
 {
        struct dso *pos;
 
@@ -348,48 +418,97 @@ void dsos__fprintf(FILE *fp)
                dso__fprintf(pos, fp);
 }
 
+static int hex(char ch)
+{
+       if ((ch >= '0') && (ch <= '9'))
+               return ch - '0';
+       if ((ch >= 'a') && (ch <= 'f'))
+               return ch - 'a' + 10;
+       if ((ch >= 'A') && (ch <= 'F'))
+               return ch - 'A' + 10;
+       return -1;
+}
+
+/*
+ * While we find nice hex chars, build a long_val.
+ * Return number of chars processed.
+ */
+static int hex2long(char *ptr, unsigned long *long_val)
+{
+       const char *p = ptr;
+       *long_val = 0;
+
+       while (*p) {
+               const int hex_val = hex(*p);
+
+               if (hex_val < 0)
+                       break;
+
+               *long_val = (*long_val << 4) | hex_val;
+               p++;
+       }
+
+       return p - ptr;
+}
+
 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 long start;
-               char c, symbf[4096];
+               unsigned long start;
+               struct symbol *sym;
+               int line_len, len;
+               char symbol_type;
 
-               if (getline(&line, &n, file) < 0)
+               line_len = getline(&line, &n, file);
+               if (line_len < 0)
                        break;
 
                if (!line)
                        goto out_delete_dso;
 
-               if (sscanf(line, "%llx %c %s", &start, &c, symbf) == 3) {
-                       /*
-                        * Well fix up the end later, when we have all sorted.
-                        */
-                       struct symbol *sym = symbol__new(start, 0xdead, symbf);
+               line[--line_len] = '\0'; /* \n */
 
-                       if (sym == NULL)
-                               goto out_delete_dso;
+               len = hex2long(line, &start);
 
-                       dso__insert_symbol(kernel_dso, sym);
-               }
+               len++;
+               if (len + 2 >= line_len)
+                       continue;
+
+               symbol_type = toupper(line[len]);
+               /*
+                * We're interested only in code ('T'ext)
+                */
+               if (symbol_type != 'T' && symbol_type != 'W')
+                       continue;
+               /*
+                * Well fix up the end later, when we have all sorted.
+                */
+               sym = symbol__new(start, 0xdead, line + len + 2);
+
+               if (sym == NULL)
+                       goto out_delete_dso;
+
+               dso__insert_symbol(kernel_dso, sym);
        }
 
        /*
         * 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;
@@ -405,6 +524,7 @@ static int load_kallsyms(void)
        dsos__add(kernel_dso);
        free(line);
        fclose(file);
+
        return 0;
 
 out_delete_line:
@@ -414,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;
@@ -441,202 +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;
 
-struct symhist {
+struct thread {
        struct rb_node   rb_node;
-       struct dso       *dso;
-       struct symbol    *sym;
-       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 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->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, FILE *fp)
+static struct map *thread__find_map(struct thread *self, uint64_t ip)
 {
-       size_t ret = fprintf(fp, "%#llx [%c] ", (unsigned long long)self->ip, self->level);
+       struct map *pos;
+
+       if (self == NULL)
+               return NULL;
+
+       list_for_each_entry(pos, &self->maps, node)
+               if (ip >= pos->start && ip <= pos->end)
+                       return pos;
 
-       if (self->level != '.')
-               ret += fprintf(fp, "%s", self->sym ? self->sym->name: "<unknown>");
-       else
-               ret += fprintf(fp, "%s: %s",
-                              self->dso ? self->dso->name : "<unknown>",
-                              self->sym ? self->sym->name : "<unknown>");
-       return ret + fprintf(fp, ": %u\n", self->count);
+       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 struct thread *thread__new(pid_t pid)
-{
-       struct thread *self = malloc(sizeof(*self));
+/*
+ * configurable sorting bits
+ */
 
-       if (self != NULL) {
-               self->pid = pid;
-               self->comm = NULL;
-               INIT_LIST_HEAD(&self->maps);
-               self->symhists = RB_ROOT;
-       }
+struct sort_entry {
+       struct list_head list;
 
-       return self;
+       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)
+{
+       return right->thread->pid - left->thread->pid;
 }
 
-static int thread__symbol_incnew(struct thread *self, struct symbol *sym,
-                                uint64_t ip, struct dso *dso, char level)
+static size_t
+sort__thread_print(FILE *fp, struct hist_entry *self)
 {
-       struct rb_node **p = &self->symhists.rb_node;
-       struct rb_node *parent = NULL;
-       struct symhist *sh;
+       return fprintf(fp, " %16s:%5d", self->thread->comm ?: "", self->thread->pid);
+}
 
-       while (*p != NULL) {
-               parent = *p;
-               sh = rb_entry(parent, struct symhist, rb_node);
+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)
+{
+       char *comm_l = left->thread->comm;
+       char *comm_r = right->thread->comm;
 
-               if (sh->sym == sym || ip == sh->ip) {
-                       symhist__inc(sh);
+       if (!comm_l || !comm_r) {
+               if (!comm_l && !comm_r)
                        return 0;
-               }
+               else if (!comm_l)
+                       return -1;
+               else
+                       return 1;
+       }
+
+       return strcmp(comm_l, comm_r);
+}
 
-               /* Handle unresolved symbols too */
-               const uint64_t start = !sh->sym ? sh->ip : sh->sym->start;
+static size_t
+sort__comm_print(FILE *fp, struct hist_entry *self)
+{
+       return fprintf(fp, " %16s", self->thread->comm ?: "<unknown>");
+}
 
-               if (ip < start)
-                       p = &(*p)->rb_left;
+static struct sort_entry sort_comm = {
+       .header = "         Command",
+       .cmp    = sort__comm_cmp,
+       .print  = sort__comm_print,
+};
+
+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 (!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, 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)
+{
+       uint64_t ip_l, ip_r;
+
+       if (left->sym == right->sym)
+               return 0;
+
+       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
+sort__sym_print(FILE *fp, struct hist_entry *self)
 {
-       struct map *pos;
        size_t ret = 0;
 
-       list_for_each_entry(pos, &self->maps, node)
-               ret += map__fprintf(pos, 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 size_t thread__fprintf(struct thread *self, FILE *fp)
+static struct sort_entry sort_sym = {
+       .header = "Shared Object: Symbol",
+       .cmp    = sort__sym_cmp,
+       .print  = sort__sym_print,
+};
+
+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)
 {
-       int ret = fprintf(fp, "thread: %d %s\n", self->pid, self->comm);
-       struct rb_node *nd;
+       int i;
 
-       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, fp);
+       for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) {
+               struct sort_dimension *sd = &sort_dimensions[i];
+
+               if (sd->taken)
+                       continue;
+
+               if (strcmp(tok, sd->name))
+                       continue;
+
+               list_add_tail(&sd->entry->list, &hist_entry__sort_list);
+               sd->taken = 1;
+               return 0;
+       }
+
+       return -ESRCH;
+}
+
+static void setup_sorting(void)
+{
+       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 int64_t
+hist_entry__cmp(struct hist_entry *left, struct hist_entry *right)
+{
+       struct sort_entry *se;
+       int64_t cmp = 0;
+
+       list_for_each_entry(se, &hist_entry__sort_list, list) {
+               cmp = se->cmp(left, right);
+               if (cmp)
+                       break;
        }
 
+       return cmp;
+}
+
+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;
 }
 
-static struct rb_root threads = RB_ROOT;
+/*
+ * collect histogram counts
+ */
 
-static struct thread *threads__findnew(pid_t pid)
+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 **p = &threads.rb_node;
+       struct rb_node **p = &hist.rb_node;
        struct rb_node *parent = NULL;
-       struct thread *th;
+       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;
-               th = rb_entry(parent, struct thread, rb_node);
+               he = rb_entry(parent, struct hist_entry, rb_node);
 
-               if (th->pid == pid)
-                       return th;
+               cmp = hist_entry__cmp(&entry, he);
 
-               if (pid < th->pid)
+               if (!cmp) {
+                       he->count++;
+                       return 0;
+               }
+
+               if (cmp < 0)
                        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;
+       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 void thread__insert_map(struct thread *self, struct map *map)
+/*
+ * reverse the map, sort on count.
+ */
+
+static struct rb_root output_hists;
+
+static void output__insert_entry(struct hist_entry *he)
 {
-       list_add_tail(&map->node, &self->maps);
+       struct rb_node **p = &output_hists.rb_node;
+       struct rb_node *parent = NULL;
+       struct hist_entry *iter;
+
+       while (*p != NULL) {
+               parent = *p;
+               iter = rb_entry(parent, struct hist_entry, rb_node);
+
+               if (he->count > iter->count)
+                       p = &(*p)->rb_left;
+               else
+                       p = &(*p)->rb_right;
+       }
+
+       rb_link_node(&he->rb_node, parent, p);
+       rb_insert_color(&he->rb_node, &output_hists);
 }
 
-static struct map *thread__find_map(struct thread *self, uint64_t ip)
+static void output__resort(void)
 {
-       if (self == NULL)
-               return NULL;
-
-       struct map *pos;
+       struct rb_node *next = rb_first(&hist);
+       struct hist_entry *n;
 
-       list_for_each_entry(pos, &self->maps, node)
-               if (ip >= pos->start && ip <= pos->end)
-                       return pos;
+       while (next) {
+               n = rb_entry(next, struct hist_entry, rb_node);
+               next = rb_next(&n->rb_node);
 
-       return NULL;
+               rb_erase(&n->rb_node, &hist);
+               output__insert_entry(n);
+       }
 }
 
-static void threads__fprintf(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;
-       for (nd = rb_first(&threads); nd; nd = rb_next(nd)) {
-               struct thread *pos = rb_entry(nd, struct thread, rb_node);
-               thread__fprintf(pos, fp);
+       size_t ret = 0;
+
+       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;
@@ -645,6 +1029,7 @@ static int __cmd_report(void)
        char *buf;
        event_t *event;
        int ret, rc = EXIT_FAILURE;
+       uint32_t size;
        unsigned long total = 0, total_mmap = 0, total_comm = 0, total_unknown = 0;
 
        input = open(input_name, O_RDONLY);
@@ -664,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;
        }
@@ -680,6 +1065,10 @@ remap:
 more:
        event = (event_t *)(buf + head);
 
+       size = event->header.size;
+       if (!size)
+               size = 8;
+
        if (head + event->header.size >= page_size * mmap_window) {
                unsigned long shift = page_size * (head / page_size);
                int ret;
@@ -692,12 +1081,9 @@ more:
                goto remap;
        }
 
-
-       if (!event->header.size) {
-               fprintf(stderr, "zero-sized event at file offset %ld\n", offset + head);
-               fprintf(stderr, "skipping %ld bytes of events.\n", stat.st_size - offset - head);
-               goto done;
-       }
+       size = event->header.size;
+       if (!size)
+               goto broken_event;
 
        if (event->header.misc & PERF_EVENT_MISC_OVERFLOW) {
                char level;
@@ -705,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",
@@ -712,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';
@@ -741,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++;
@@ -756,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++;
@@ -780,28 +1173,40 @@ 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: {
-               fprintf(stderr, "%p [%p]: skipping unknown header type: %d\n",
-                       (void *)(offset + head),
-                       (void *)(long)(event->header.size),
-                       event->header.type);
+broken_event:
+               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++;
+
+               /*
+                * assume we lost track of the stream, check alignment, and
+                * increment a single u64 in the hope to catch on again 'soon'.
+                */
+
+               if (unlikely(head & 7))
+                       head &= ~7ULL;
+
+               size = 8;
        }
        }
 
-       head += event->header.size;
+       head += size;
 
        if (offset + head < stat.st_size)
                goto more;
 
        rc = EXIT_SUCCESS;
-done:
        close(input);
 
        if (dump_trace) {
@@ -813,23 +1218,12 @@ done:
                return 0;
        }
 
-       //dsos__fprintf(stdout);
-       threads__fprintf(stdout);
-#if 0
-       std::map<std::string, int>::iterator hi = hist.begin();
+       if (verbose >= 2)
+               dsos__fprintf(stdout);
 
-       while (hi != hist.end()) {
-               rev_hist.insert(std::pair<int, std::string>(hi->second, hi->first));
-               hist.erase(hi++);
-       }
+       output__resort();
+       output__fprintf(stdout, total);
 
-       std::multimap<int, std::string>::const_iterator ri = rev_hist.begin();
-
-       while (ri != rev_hist.end()) {
-               printf(" %5.2f %s\n", (100.0 * ri->first)/total, ri->second.c_str());
-               ri++;
-       }
-#endif
        return rc;
 }
 
@@ -841,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()
 };
 
@@ -854,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.039818 seconds and 5 git commands to generate.