perf annotate: Print the filename:line for annotated colored lines
Frederic Weisbecker [Fri, 12 Jun 2009 22:11:21 +0000 (00:11 +0200)]
When we have a colored line in perf annotate, ie a middle/high
overhead one, it's sometimes useful to get the matching line
and filename from the source file, especially this path prepares
to another subsequent one which will print a sorted summary of
midle/high overhead lines in the beginning of the output.

Filename:Lines have the same color than the concerned ip lines.

It can be slow because it relies on addr2line. We could also
use objdump with -l but that implies we would have to bufferize
objdump output and parse it to filter the relevant lines since
we want to print a sorted summary in the beginning.

Signed-off-by: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Paul Mackerras <paulus@samba.org>
LKML-Reference: <1244844682-12928-1-git-send-email-fweisbec@gmail.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>

tools/perf/builtin-annotate.c
tools/perf/util/symbol.h

index b1ed5f7..6a08da4 100644 (file)
@@ -39,6 +39,8 @@ static int            dump_trace = 0;
 
 static int             verbose;
 
+static int             print_line;
+
 static unsigned long   page_size;
 static unsigned long   mmap_window = 32;
 
@@ -84,6 +86,12 @@ typedef union event_union {
        struct period_event             period;
 } event_t;
 
+
+struct sym_ext {
+       double          percent;
+       char            *path;
+};
+
 static LIST_HEAD(dsos);
 static struct dso *kernel_dso;
 static struct dso *vdso;
@@ -1034,6 +1042,8 @@ static int
 parse_line(FILE *file, struct symbol *sym, __u64 start, __u64 len)
 {
        char *line = NULL, *tmp, *tmp2;
+       static const char *prev_line;
+       static const char *prev_color;
        unsigned int offset;
        size_t line_len;
        __u64 line_ip;
@@ -1073,15 +1083,20 @@ parse_line(FILE *file, struct symbol *sym, __u64 start, __u64 len)
        }
 
        if (line_ip != -1) {
+               const char *path = NULL;
                unsigned int hits = 0;
                double percent = 0.0;
                char *color = PERF_COLOR_NORMAL;
+               struct sym_ext *sym_ext = sym->priv;
 
                offset = line_ip - start;
                if (offset < len)
                        hits = sym->hist[offset];
 
-               if (sym->hist_sum)
+               if (sym_ext) {
+                       path = sym_ext[offset].path;
+                       percent = sym_ext[offset].percent;
+               } else if (sym->hist_sum)
                        percent = 100.0 * hits / sym->hist_sum;
 
                /*
@@ -1096,6 +1111,20 @@ parse_line(FILE *file, struct symbol *sym, __u64 start, __u64 len)
                                color = PERF_COLOR_GREEN;
                }
 
+               /*
+                * Also color the filename and line if needed, with
+                * the same color than the percentage. Don't print it
+                * twice for close colored ip with the same filename:line
+                */
+               if (path) {
+                       if (!prev_line || strcmp(prev_line, path)
+                                      || color != prev_color) {
+                               color_fprintf(stdout, color, " %s", path);
+                               prev_line = path;
+                               prev_color = color;
+                       }
+               }
+
                color_fprintf(stdout, color, " %7.2f", percent);
                printf(" :      ");
                color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", line);
@@ -1109,6 +1138,67 @@ parse_line(FILE *file, struct symbol *sym, __u64 start, __u64 len)
        return 0;
 }
 
+static void free_source_line(struct symbol *sym, int len)
+{
+       struct sym_ext *sym_ext = sym->priv;
+       int i;
+
+       if (!sym_ext)
+               return;
+
+       for (i = 0; i < len; i++)
+               free(sym_ext[i].path);
+       free(sym_ext);
+
+       sym->priv = NULL;
+}
+
+/* Get the filename:line for the colored entries */
+static void get_source_line(struct symbol *sym, __u64 start, int len)
+{
+       int i;
+       char cmd[PATH_MAX * 2];
+       struct sym_ext *sym_ext;
+
+       if (!sym->hist_sum)
+               return;
+
+       sym->priv = calloc(len, sizeof(struct sym_ext));
+       if (!sym->priv)
+               return;
+
+       sym_ext = sym->priv;
+
+       for (i = 0; i < len; i++) {
+               char *path = NULL;
+               size_t line_len;
+               __u64 offset;
+               FILE *fp;
+
+               sym_ext[i].percent = 100.0 * sym->hist[i] / sym->hist_sum;
+               if (sym_ext[i].percent <= 0.5)
+                       continue;
+
+               offset = start + i;
+               sprintf(cmd, "addr2line -e %s %016llx", vmlinux, offset);
+               fp = popen(cmd, "r");
+               if (!fp)
+                       continue;
+
+               if (getline(&path, &line_len, fp) < 0 || !line_len)
+                       goto next;
+
+               sym_ext[i].path = malloc(sizeof(char) * line_len);
+               if (!sym_ext[i].path)
+                       goto next;
+
+               strcpy(sym_ext[i].path, path);
+
+       next:
+               pclose(fp);
+       }
+}
+
 static void annotate_sym(struct dso *dso, struct symbol *sym)
 {
        char *filename = dso->name;
@@ -1135,6 +1225,9 @@ static void annotate_sym(struct dso *dso, struct symbol *sym)
        end = start + sym->end - sym->start + 1;
        len = sym->end - sym->start;
 
+       if (print_line)
+               get_source_line(sym, start, len);
+
        sprintf(command, "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s", (__u64)start, (__u64)end, filename);
 
        if (verbose >= 3)
@@ -1150,6 +1243,7 @@ static void annotate_sym(struct dso *dso, struct symbol *sym)
        }
 
        pclose(file);
+       free_source_line(sym, len);
 }
 
 static void find_annotations(void)
@@ -1308,6 +1402,8 @@ static const struct option options[] = {
        OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
                    "dump raw trace in ASCII"),
        OPT_STRING('k', "vmlinux", &vmlinux, "file", "vmlinux pathname"),
+       OPT_BOOLEAN('l', "print-line", &print_line,
+                   "print matching source lines (may be slow)"),
        OPT_END()
 };
 
index 0d1292b..5ad9b06 100644 (file)
@@ -12,6 +12,7 @@ struct symbol {
        __u64           obj_start;
        __u64           hist_sum;
        __u64           *hist;
+       void            *priv;
        char            name[0];
 };