]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - tools/perf/builtin-annotate.c
perf symbols: Rename find_symbol routines to find_function
[karo-tx-linux.git] / tools / perf / builtin-annotate.c
index df516dce954032ac112c46b31f220c5647e53852..18ac5eaefc363ec69c71fd835c0b598e09fa1182 100644 (file)
 #include "util/thread.h"
 #include "util/sort.h"
 #include "util/hist.h"
+#include "util/process_events.h"
 
 static char            const *input_name = "perf.data";
 
 static int             force;
 static int             input;
-static int             show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV;
 
 static int             full_paths;
 
@@ -38,9 +38,10 @@ static int           print_line;
 static unsigned long   page_size;
 static unsigned long   mmap_window = 32;
 
-static struct rb_root  threads;
-static struct thread   *last_match;
-
+struct sym_hist {
+       u64             sum;
+       u64             ip[0];
+};
 
 struct sym_ext {
        struct rb_node  node;
@@ -48,6 +49,39 @@ struct sym_ext {
        char            *path;
 };
 
+struct sym_priv {
+       struct sym_hist *hist;
+       struct sym_ext  *ext;
+};
+
+static struct symbol_conf symbol_conf = {
+       .priv_size        = sizeof(struct sym_priv),
+       .try_vmlinux_path = true,
+};
+
+static const char *sym_hist_filter;
+
+static int symbol_filter(struct map *map __used, struct symbol *sym)
+{
+       if (sym_hist_filter == NULL ||
+           strcmp(sym->name, sym_hist_filter) == 0) {
+               struct sym_priv *priv = symbol__priv(sym);
+               const int size = (sizeof(*priv->hist) +
+                                 (sym->end - sym->start) * sizeof(u64));
+
+               priv->hist = malloc(size);
+               if (priv->hist)
+                       memset(priv->hist, 0, size);
+               return 0;
+       }
+       /*
+        * FIXME: We should really filter it out, as we don't want to go thru symbols
+        * we're not interested, and if a DSO ends up with no symbols, delete it too,
+        * but right now the kernel loading routines in symbol.c bail out if no symbols
+        * are found, fix it later.
+        */
+       return 0;
+}
 
 /*
  * collect histogram counts
@@ -56,72 +90,49 @@ static void hist_hit(struct hist_entry *he, u64 ip)
 {
        unsigned int sym_size, offset;
        struct symbol *sym = he->sym;
+       struct sym_priv *priv;
+       struct sym_hist *h;
 
        he->count++;
 
-       if (!sym || !sym->hist)
+       if (!sym || !he->map)
+               return;
+
+       priv = symbol__priv(sym);
+       if (!priv->hist)
                return;
 
        sym_size = sym->end - sym->start;
        offset = ip - sym->start;
 
+       if (verbose)
+               fprintf(stderr, "%s: ip=%Lx\n", __func__,
+                       he->map->unmap_ip(he->map, ip));
+
        if (offset >= sym_size)
                return;
 
-       sym->hist_sum++;
-       sym->hist[offset]++;
+       h = priv->hist;
+       h->sum++;
+       h->ip[offset]++;
 
        if (verbose >= 3)
                printf("%p %s: count++ [ip: %p, %08Lx] => %Ld\n",
                        (void *)(unsigned long)he->sym->start,
                        he->sym->name,
                        (void *)(unsigned long)ip, ip - he->sym->start,
-                       sym->hist[offset]);
+                       h->ip[offset]);
 }
 
-static int
-hist_entry__add(struct thread *thread, struct map *map, struct dso *dso,
-               struct symbol *sym, u64 ip, char level)
+static int hist_entry__add(struct thread *thread, struct map *map,
+                          struct symbol *sym, u64 ip, u64 count, char level)
 {
-       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) {
-                       hist_hit(he, ip);
-
-                       return 0;
-               }
-
-               if (cmp < 0)
-                       p = &(*p)->rb_left;
-               else
-                       p = &(*p)->rb_right;
-       }
-
-       he = malloc(sizeof(*he));
-       if (!he)
+       bool hit;
+       struct hist_entry *he = __hist_entry__add(thread, map, sym, NULL, ip,
+                                                 count, level, &hit);
+       if (he == NULL)
                return -ENOMEM;
-       *he = entry;
-       rb_link_node(&he->rb_node, parent, p);
-       rb_insert_color(&he->rb_node, &hist);
-
+       hist_hit(he, ip);
        return 0;
 }
 
@@ -129,13 +140,10 @@ static int
 process_sample_event(event_t *event, unsigned long offset, unsigned long head)
 {
        char level;
-       int show = 0;
-       struct dso *dso = NULL;
-       struct thread *thread;
        u64 ip = event->ip.ip;
        struct map *map = NULL;
-
-       thread = threads__findnew(event->ip.pid, &threads, &last_match);
+       struct symbol *sym = NULL;
+       struct thread *thread = threads__findnew(event->ip.pid);
 
        dump_printf("%p [%p]: PERF_EVENT (IP, %d): %d: %p\n",
                (void *)(offset + head),
@@ -144,100 +152,64 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head)
                event->ip.pid,
                (void *)(long)ip);
 
-       dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid);
-
        if (thread == NULL) {
                fprintf(stderr, "problem processing %d event, skipping it.\n",
                        event->header.type);
                return -1;
        }
 
+       dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid);
+
        if (event->header.misc & PERF_RECORD_MISC_KERNEL) {
-               show = SHOW_KERNEL;
                level = 'k';
-
-               dso = kernel_dso;
-
-               dump_printf(" ...... dso: %s\n", dso->name);
-
+               sym = kernel_maps__find_function(ip, &map, symbol_filter);
+               dump_printf(" ...... dso: %s\n",
+                           map ? map->dso->long_name : "<not found>");
        } else if (event->header.misc & PERF_RECORD_MISC_USER) {
-
-               show = SHOW_USER;
                level = '.';
-
                map = thread__find_map(thread, ip);
                if (map != NULL) {
+got_map:
                        ip = map->map_ip(map, ip);
-                       dso = map->dso;
+                       sym = map__find_function(map, ip, symbol_filter);
                } else {
                        /*
                         * If this is outside of all known maps,
                         * and is a negative address, try to look it
                         * up in the kernel dso, as it might be a
-                        * vsyscall (which executes in user-mode):
+                        * vsyscall or vdso (which executes in user-mode).
+                        *
+                        * XXX This is nasty, we should have a symbol list in
+                        * the "[vdso]" dso, but for now lets use the old
+                        * trick of looking in the whole kernel symbol list.
                         */
-                       if ((long long)ip < 0)
-                               dso = kernel_dso;
+                       if ((long long)ip < 0) {
+                               map = kernel_map;
+                               goto got_map;
+                       }
                }
-               dump_printf(" ...... dso: %s\n", dso ? dso->name : "<not found>");
-
+               dump_printf(" ...... dso: %s\n",
+                           map ? map->dso->long_name : "<not found>");
        } else {
-               show = SHOW_HV;
                level = 'H';
                dump_printf(" ...... dso: [hypervisor]\n");
        }
 
-       if (show & show_mask) {
-               struct symbol *sym = NULL;
-
-               if (dso)
-                       sym = dso->find_symbol(dso, ip);
-
-               if (hist_entry__add(thread, map, dso, sym, ip, level)) {
-                       fprintf(stderr,
-               "problem incrementing symbol count, skipping event\n");
-                       return -1;
-               }
+       if (hist_entry__add(thread, map, sym, ip, 1, level)) {
+               fprintf(stderr, "problem incrementing symbol count, "
+                               "skipping event\n");
+               return -1;
        }
        total++;
 
        return 0;
 }
 
-static int
-process_mmap_event(event_t *event, unsigned long offset, unsigned long head)
-{
-       struct thread *thread;
-       struct map *map = map__new(&event->mmap, NULL, 0);
-
-       thread = threads__findnew(event->mmap.pid, &threads, &last_match);
-
-       dump_printf("%p [%p]: PERF_RECORD_MMAP %d: [%p(%p) @ %p]: %s\n",
-               (void *)(offset + head),
-               (void *)(long)(event->header.size),
-               event->mmap.pid,
-               (void *)(long)event->mmap.start,
-               (void *)(long)event->mmap.len,
-               (void *)(long)event->mmap.pgoff,
-               event->mmap.filename);
-
-       if (thread == NULL || map == NULL) {
-               dump_printf("problem processing PERF_RECORD_MMAP, skipping event.\n");
-               return 0;
-       }
-
-       thread__insert_map(thread, map);
-       total_mmap++;
-
-       return 0;
-}
-
 static int
 process_comm_event(event_t *event, unsigned long offset, unsigned long head)
 {
-       struct thread *thread;
+       struct thread *thread = threads__findnew(event->comm.pid);
 
-       thread = threads__findnew(event->comm.pid, &threads, &last_match);
        dump_printf("%p [%p]: PERF_RECORD_COMM: %s:%d\n",
                (void *)(offset + head),
                (void *)(long)(event->header.size),
@@ -253,35 +225,6 @@ process_comm_event(event_t *event, unsigned long offset, unsigned long head)
        return 0;
 }
 
-static int
-process_fork_event(event_t *event, unsigned long offset, unsigned long head)
-{
-       struct thread *thread;
-       struct thread *parent;
-
-       thread = threads__findnew(event->fork.pid, &threads, &last_match);
-       parent = threads__findnew(event->fork.ppid, &threads, &last_match);
-       dump_printf("%p [%p]: PERF_RECORD_FORK: %d:%d\n",
-               (void *)(offset + head),
-               (void *)(long)(event->header.size),
-               event->fork.pid, event->fork.ppid);
-
-       /*
-        * A thread clone will have the same PID for both
-        * parent and child.
-        */
-       if (thread == parent)
-               return 0;
-
-       if (!thread || !parent || thread__fork(thread, parent)) {
-               dump_printf("problem processing PERF_RECORD_FORK, skipping event.\n");
-               return -1;
-       }
-       total_fork++;
-
-       return 0;
-}
-
 static int
 process_event(event_t *event, unsigned long offset, unsigned long head)
 {
@@ -296,7 +239,7 @@ process_event(event_t *event, unsigned long offset, unsigned long head)
                return process_comm_event(event, offset, head);
 
        case PERF_RECORD_FORK:
-               return process_fork_event(event, offset, head);
+               return process_task_event(event, offset, head);
        /*
         * We dont process them right now but they are fine:
         */
@@ -312,14 +255,15 @@ process_event(event_t *event, unsigned long offset, unsigned long head)
        return 0;
 }
 
-static int
-parse_line(FILE *file, struct symbol *sym, u64 start, u64 len)
+static int parse_line(FILE *file, struct hist_entry *he, u64 len)
 {
+       struct symbol *sym = he->sym;
        char *line = NULL, *tmp, *tmp2;
        static const char *prev_line;
        static const char *prev_color;
        unsigned int offset;
        size_t line_len;
+       u64 start;
        s64 line_ip;
        int ret;
        char *c;
@@ -356,22 +300,26 @@ parse_line(FILE *file, struct symbol *sym, u64 start, u64 len)
                        line_ip = -1;
        }
 
+       start = he->map->unmap_ip(he->map, sym->start);
+
        if (line_ip != -1) {
                const char *path = NULL;
                unsigned int hits = 0;
                double percent = 0.0;
                const char *color;
-               struct sym_ext *sym_ext = sym->priv;
+               struct sym_priv *priv = symbol__priv(sym);
+               struct sym_ext *sym_ext = priv->ext;
+               struct sym_hist *h = priv->hist;
 
                offset = line_ip - start;
                if (offset < len)
-                       hits = sym->hist[offset];
+                       hits = h->ip[offset];
 
                if (offset < len && sym_ext) {
                        path = sym_ext[offset].path;
                        percent = sym_ext[offset].percent;
-               } else if (sym->hist_sum)
-                       percent = 100.0 * hits / sym->hist_sum;
+               } else if (h->sum)
+                       percent = 100.0 * hits / h->sum;
 
                color = get_percent_color(percent);
 
@@ -424,9 +372,10 @@ static void insert_source_line(struct sym_ext *sym_ext)
        rb_insert_color(&sym_ext->node, &root_sym_ext);
 }
 
-static void free_source_line(struct symbol *sym, int len)
+static void free_source_line(struct hist_entry *he, int len)
 {
-       struct sym_ext *sym_ext = sym->priv;
+       struct sym_priv *priv = symbol__priv(he->sym);
+       struct sym_ext *sym_ext = priv->ext;
        int i;
 
        if (!sym_ext)
@@ -436,26 +385,30 @@ static void free_source_line(struct symbol *sym, int len)
                free(sym_ext[i].path);
        free(sym_ext);
 
-       sym->priv = NULL;
+       priv->ext = NULL;
        root_sym_ext = RB_ROOT;
 }
 
 /* Get the filename:line for the colored entries */
 static void
-get_source_line(struct symbol *sym, u64 start, int len, const char *filename)
+get_source_line(struct hist_entry *he, int len, const char *filename)
 {
+       struct symbol *sym = he->sym;
+       u64 start;
        int i;
        char cmd[PATH_MAX * 2];
        struct sym_ext *sym_ext;
+       struct sym_priv *priv = symbol__priv(sym);
+       struct sym_hist *h = priv->hist;
 
-       if (!sym->hist_sum)
+       if (!h->sum)
                return;
 
-       sym->priv = calloc(len, sizeof(struct sym_ext));
-       if (!sym->priv)
+       sym_ext = priv->ext = calloc(len, sizeof(struct sym_ext));
+       if (!priv->ext)
                return;
 
-       sym_ext = sym->priv;
+       start = he->map->unmap_ip(he->map, sym->start);
 
        for (i = 0; i < len; i++) {
                char *path = NULL;
@@ -463,7 +416,7 @@ get_source_line(struct symbol *sym, u64 start, int len, const char *filename)
                u64 offset;
                FILE *fp;
 
-               sym_ext[i].percent = 100.0 * sym->hist[i] / sym->hist_sum;
+               sym_ext[i].percent = 100.0 * h->ip[i] / h->sum;
                if (sym_ext[i].percent <= 0.5)
                        continue;
 
@@ -517,33 +470,34 @@ static void print_summary(const char *filename)
        }
 }
 
-static void annotate_sym(struct dso *dso, struct symbol *sym)
+static void annotate_sym(struct hist_entry *he)
 {
-       const char *filename = dso->name, *d_filename;
-       u64 start, end, len;
+       struct map *map = he->map;
+       struct dso *dso = map->dso;
+       struct symbol *sym = he->sym;
+       const char *filename = dso->long_name, *d_filename;
+       u64 len;
        char command[PATH_MAX*2];
        FILE *file;
 
        if (!filename)
                return;
-       if (sym->module)
-               filename = sym->module->path;
-       else if (dso == kernel_dso)
-               filename = vmlinux_name;
-
-       start = sym->obj_start;
-       if (!start)
-               start = sym->start;
+
+       if (verbose)
+               fprintf(stderr, "%s: filename=%s, sym=%s, start=%Lx, end=%Lx\n",
+                       __func__, filename, sym->name,
+                       map->unmap_ip(map, sym->start),
+                       map->unmap_ip(map, sym->end));
+
        if (full_paths)
                d_filename = filename;
        else
                d_filename = basename(filename);
 
-       end = start + sym->end - sym->start + 1;
        len = sym->end - sym->start;
 
        if (print_line) {
-               get_source_line(sym, start, len, filename);
+               get_source_line(he, len, filename);
                print_summary(filename);
        }
 
@@ -552,10 +506,12 @@ static void annotate_sym(struct dso *dso, struct symbol *sym)
        printf("------------------------------------------------\n");
 
        if (verbose >= 2)
-               printf("annotating [%p] %30s : [%p] %30s\n", dso, dso->name, sym, sym->name);
+               printf("annotating [%p] %30s : [%p] %30s\n",
+                      dso, dso->long_name, sym, sym->name);
 
        sprintf(command, "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s|grep -v %s",
-                       (u64)start, (u64)end, filename, filename);
+               map->unmap_ip(map, sym->start), map->unmap_ip(map, sym->end),
+               filename, filename);
 
        if (verbose >= 3)
                printf("doing: %s\n", command);
@@ -565,35 +521,38 @@ static void annotate_sym(struct dso *dso, struct symbol *sym)
                return;
 
        while (!feof(file)) {
-               if (parse_line(file, sym, start, len) < 0)
+               if (parse_line(file, he, len) < 0)
                        break;
        }
 
        pclose(file);
        if (print_line)
-               free_source_line(sym, len);
+               free_source_line(he, len);
 }
 
 static void find_annotations(void)
 {
        struct rb_node *nd;
-       struct dso *dso;
-       int count = 0;
 
-       list_for_each_entry(dso, &dsos, node) {
+       for (nd = rb_first(&output_hists); nd; nd = rb_next(nd)) {
+               struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
+               struct sym_priv *priv;
 
-               for (nd = rb_first(&dso->syms); nd; nd = rb_next(nd)) {
-                       struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
+               if (he->sym == NULL)
+                       continue;
 
-                       if (sym->hist) {
-                               annotate_sym(dso, sym);
-                               count++;
-                       }
-               }
-       }
+               priv = symbol__priv(he->sym);
+               if (priv->hist == NULL)
+                       continue;
 
-       if (!count)
-               printf(" Error: symbol '%s' not present amongst the samples.\n", sym_hist_filter);
+               annotate_sym(he);
+               /*
+                * Since we have a hist_entry per IP for the same symbol, free
+                * he->sym->hist to signal we already processed this symbol.
+                */
+               free(priv->hist);
+               priv->hist = NULL;
+       }
 }
 
 static int __cmd_annotate(void)
@@ -606,7 +565,7 @@ static int __cmd_annotate(void)
        uint32_t size;
        char *buf;
 
-       register_idle_thread(&threads, &last_match);
+       register_idle_thread();
 
        input = open(input_name, O_RDONLY);
        if (input < 0) {
@@ -630,11 +589,6 @@ static int __cmd_annotate(void)
                exit(0);
        }
 
-       if (load_kernel() < 0) {
-               perror("failed to load kernel symbols");
-               return EXIT_FAILURE;
-       }
-
 remap:
        buf = (char *)mmap(NULL, page_size * mmap_window, PROT_READ,
                           MAP_SHARED, input, offset);
@@ -706,10 +660,10 @@ more:
        if (dump_trace)
                return 0;
 
-       if (verbose >= 3)
-               threads__fprintf(stdout, &threads);
+       if (verbose > 3)
+               threads__fprintf(stdout);
 
-       if (verbose >= 2)
+       if (verbose > 2)
                dsos__fprintf(stdout);
 
        collapse__resort();
@@ -735,8 +689,9 @@ static const struct option options[] = {
                    "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_name, "file", "vmlinux pathname"),
-       OPT_BOOLEAN('m', "modules", &modules,
+       OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
+                  "file", "vmlinux pathname"),
+       OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules,
                    "load module symbols - WARNING: use only with -k and LIVE kernel"),
        OPT_BOOLEAN('l', "print-line", &print_line,
                    "print matching source lines (may be slow)"),
@@ -762,7 +717,8 @@ static void setup_sorting(void)
 
 int cmd_annotate(int argc, const char **argv, const char *prefix __used)
 {
-       symbol__init();
+       if (symbol__init(&symbol_conf) < 0)
+               return -1;
 
        page_size = getpagesize();
 
@@ -781,9 +737,6 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __used)
                sym_hist_filter = argv[0];
        }
 
-       if (!sym_hist_filter)
-               usage_with_options(annotate_usage, options);
-
        setup_pager();
 
        if (field_sep && *field_sep == '.') {