From 301406b9c69e4914cf45ae9d5f929e7bcf0d93cd Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Sat, 13 Jun 2009 00:11:21 +0200 Subject: [PATCH] perf annotate: Print the filename:line for annotated colored lines 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 Cc: Peter Zijlstra Cc: Mike Galbraith Cc: Paul Mackerras LKML-Reference: <1244844682-12928-1-git-send-email-fweisbec@gmail.com> Signed-off-by: Ingo Molnar --- tools/perf/builtin-annotate.c | 98 ++++++++++++++++++++++++++++++++++- tools/perf/util/symbol.h | 1 + 2 files changed, 98 insertions(+), 1 deletion(-) diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index b1ed5f766cb3..6a08da41f76b 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -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() }; diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 0d1292bd8270..5ad9b06c3f6f 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -12,6 +12,7 @@ struct symbol { __u64 obj_start; __u64 hist_sum; __u64 *hist; + void *priv; char name[0]; };