From b36f19d572151abb987ce308a3e066b977a2146f Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 20 May 2010 12:15:33 -0300 Subject: perf annotate: Use build-ids to find the right DSO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We were still using the pathname found on the MMAP event, that could not be the one we used when recording, so use the build-id cache for that, only falling back to use the pathname in the MMAP event if no build-ids are available. With this we now also are able to do secure, seamless offline annotation. Example: [root@doppio linux-2.6-tip]# perf report -g none -v 2> /dev/null | head -10 8.12% Xorg /usr/lib64/libpixman-1.so.0.14.0 0x0000000000026d02 B [.] pixman_rasterize_edges 4.68% firefox /usr/lib64/xulrunner-1.9.1/libxul.so 0x00000000005dbdba B [.] 0x000000005dbdba 3.70% swapper /lib/modules/2.6.34-rc6/build/vmlinux 0xffffffff81022cea ! [k] read_hpet 2.96% init /lib/modules/2.6.34-rc6/build/vmlinux 0xffffffff81022cea ! [k] read_hpet 2.73% swapper /lib/modules/2.6.34-rc6/build/vmlinux 0xffffffff8100a738 ! [k] mwait_idle_with_hints [root@doppio linux-2.6-tip]# perf annotate -v pixman_rasterize_edges 2>&1 | grep Executing Executing: objdump --start-address=0x000000371ce26670 --stop-address=0x000000371ce2709f -dS /root/.debug/.build-id/bd/6ac5199137aaeb279f864717d8d061477466c1|grep -v /root/.debug/.build-id/bd/6ac5199137aaeb279f864717d8d061477466c1|expand [root@doppio linux-2.6-tip]# perf buildid-list | grep libpixman-1.so.0.14.0 bd6ac5199137aaeb279f864717d8d061477466c1 /usr/lib64/libpixman-1.so.0.14.0 [root@doppio linux-2.6-tip]# Reported-by: Stephane Eranian Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/hist.c | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) (limited to 'tools/perf/util/hist.c') diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 9a71c94f057a..739c39fd0ade 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -1,3 +1,4 @@ +#include "build-id.h" #include "util.h" #include "hist.h" #include "session.h" @@ -988,22 +989,35 @@ int hist_entry__annotate(struct hist_entry *self, struct list_head *head) struct symbol *sym = self->ms.sym; struct map *map = self->ms.map; struct dso *dso = map->dso; - const char *filename = dso->long_name; + char *filename = dso__build_id_filename(dso, NULL, 0); char command[PATH_MAX * 2]; FILE *file; + int err = -1; u64 len; - if (!filename) - return -1; + if (filename == NULL) { + if (dso->has_build_id) { + pr_err("Can't annotate %s: not enough memory\n", + sym->name); + return -1; + } + /* + * If we don't have build-ids, well, lets hope that this + * DSO is the same as when 'perf record' ran. + */ + filename = dso->long_name; + } if (dso->origin == DSO__ORIG_KERNEL) { - if (dso->annotate_warned) - return 0; + if (dso->annotate_warned) { + err = 0; + goto out_free_filename; + } dso->annotate_warned = 1; pr_err("Can't annotate %s: No vmlinux file was found in the " "path:\n", sym->name); vmlinux_path__fprintf(stderr); - return -1; + goto out_free_filename; } pr_debug("%s: filename=%s, sym=%s, start=%#Lx, end=%#Lx\n", __func__, @@ -1025,14 +1039,18 @@ int hist_entry__annotate(struct hist_entry *self, struct list_head *head) file = popen(command, "r"); if (!file) - return -1; + goto out_free_filename; while (!feof(file)) if (hist_entry__parse_objdump_line(self, file, head) < 0) break; pclose(file); - return 0; + err = 0; +out_free_filename: + if (dso->has_build_id) + free(filename); + return err; } void hists__inc_nr_events(struct hists *self, u32 type) -- cgit v1.2.3 From 598357eba6a55d27ddc7ead80ebb83fe1aad9b83 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Fri, 21 May 2010 12:48:39 +0200 Subject: perf: Fix getline undeclared MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We need to have stdio.h included with _GNU_SOURCEfopr getline, which is broken with the inclusion of build-id.h. Keep util.h included first in hist.c Fixes: util/hist.c: Dans la fonction «hist_entry__parse_objdump_line» : util/hist.c:938: attention : déclaration implicite de la fonction « «getline» » util/hist.c:938: attention : nested extern declaration of «getline» make: *** [util/hist.o] Erreur 1 Signed-off-by: Frederic Weisbecker Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras LKML-Reference: <1274438919-5104-1-git-send-regression-fweisbec@gmail.com> Signed-off-by: Ingo Molnar --- tools/perf/util/hist.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools/perf/util/hist.c') diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 739c39fd0ade..009ad76b0879 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -1,5 +1,5 @@ -#include "build-id.h" #include "util.h" +#include "build-id.h" #include "hist.h" #include "session.h" #include "sort.h" -- cgit v1.2.3 From 46e3e055ce69a00d735e458445ab1d24718ff751 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 22 May 2010 11:25:40 -0300 Subject: perf annotate: Add TUI interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When annotating multiple entries, for instance, when running simply as: $ perf annotate the right and left keys, as well as TAB can be used to cycle thru the multiple symbols being annotated. If one doesn't like TUI annotate, disable it by editing ~/.perfconfig and adding: [tui] annotate = off Just like it is possible for report. Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-annotate.c | 61 +++++++++++++++++++++++++++++++------------ tools/perf/builtin-report.c | 4 +-- tools/perf/util/hist.c | 13 ++++----- tools/perf/util/hist.h | 10 +++++++ tools/perf/util/newt.c | 56 +++++++++++++++++++++++++-------------- tools/perf/util/util.h | 15 ++++++++++- 6 files changed, 112 insertions(+), 47 deletions(-) (limited to 'tools/perf/util/hist.c') diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 77bcc9b130f5..08278eda31a5 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -277,7 +277,7 @@ static void hist_entry__print_hits(struct hist_entry *self) printf("%*s: %Lu\n", BITS_PER_LONG / 2, "h->sum", h->sum); } -static void annotate_sym(struct hist_entry *he) +static int hist_entry__tty_annotate(struct hist_entry *he) { struct map *map = he->ms.map; struct dso *dso = map->dso; @@ -288,7 +288,7 @@ static void annotate_sym(struct hist_entry *he) struct objdump_line *pos, *n; if (hist_entry__annotate(he, &head) < 0) - return; + return -1; if (full_paths) d_filename = filename; @@ -317,30 +317,59 @@ static void annotate_sym(struct hist_entry *he) if (print_line) free_source_line(he, len); + + return 0; } static void hists__find_annotations(struct hists *self) { - struct rb_node *nd; + struct rb_node *first = rb_first(&self->entries), *nd = first; + int key = KEY_RIGHT; - for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { + while (nd) { struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node); struct sym_priv *priv; - if (he->ms.sym == NULL) - continue; + if (he->ms.sym == NULL || he->ms.map->dso->annotate_warned) + goto find_next; priv = symbol__priv(he->ms.sym); - if (priv->hist == NULL) + if (priv->hist == NULL) { +find_next: + if (key == KEY_LEFT) + nd = rb_prev(nd); + else + nd = rb_next(nd); continue; + } - annotate_sym(he); - /* - * Since we have a hist_entry per IP for the same symbol, free - * he->ms.sym->hist to signal we already processed this symbol. - */ - free(priv->hist); - priv->hist = NULL; + if (use_browser) { + key = hist_entry__tui_annotate(he); + if (is_exit_key(key)) + break; + switch (key) { + case KEY_RIGHT: + case '\t': + nd = rb_next(nd); + break; + case KEY_LEFT: + if (nd == first) + continue; + nd = rb_prev(nd); + default: + break; + } + } else { + hist_entry__tty_annotate(he); + nd = rb_next(nd); + /* + * Since we have a hist_entry per IP for the same + * symbol, free he->ms.sym->hist to signal we already + * processed this symbol. + */ + free(priv->hist); + priv->hist = NULL; + } } } @@ -416,6 +445,8 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __used) { argc = parse_options(argc, argv, options, annotate_usage, 0); + setup_browser(); + symbol_conf.priv_size = sizeof(struct sym_priv); symbol_conf.try_vmlinux_path = true; @@ -435,8 +466,6 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __used) sym_hist_filter = argv[0]; } - setup_pager(); - if (field_sep && *field_sep == '.') { pr_err("'.' is the only non valid --field-separator argument\n"); return -1; diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 2c39bd358975..a7b8760e401c 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -484,9 +484,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __used) { argc = parse_options(argc, argv, options, report_usage, 0); - if (dump_trace) - setup_pager(); - else if (strcmp(input_name, "-") != 0) + if (strcmp(input_name, "-") != 0) setup_browser(); /* * Only in the newt browser we are doing integrated annotation, diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 009ad76b0879..682a6d88862c 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -992,14 +992,14 @@ int hist_entry__annotate(struct hist_entry *self, struct list_head *head) char *filename = dso__build_id_filename(dso, NULL, 0); char command[PATH_MAX * 2]; FILE *file; - int err = -1; + int err = 0; u64 len; if (filename == NULL) { if (dso->has_build_id) { pr_err("Can't annotate %s: not enough memory\n", sym->name); - return -1; + return -ENOMEM; } /* * If we don't have build-ids, well, lets hope that this @@ -1009,14 +1009,12 @@ int hist_entry__annotate(struct hist_entry *self, struct list_head *head) } if (dso->origin == DSO__ORIG_KERNEL) { - if (dso->annotate_warned) { - err = 0; + if (dso->annotate_warned) goto out_free_filename; - } + err = -ENOENT; dso->annotate_warned = 1; pr_err("Can't annotate %s: No vmlinux file was found in the " - "path:\n", sym->name); - vmlinux_path__fprintf(stderr); + "path\n", sym->name); goto out_free_filename; } @@ -1046,7 +1044,6 @@ int hist_entry__annotate(struct hist_entry *self, struct list_head *head) break; pclose(file); - err = 0; out_free_filename: if (dso->has_build_id) free(filename); diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 6f17dcd8412c..2d5203fedb20 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -102,8 +102,18 @@ static inline int hists__browse(struct hists *self __used, { return 0; } +static inline int hist_entry__tui_annotate(struct hist_entry *self __used) +{ + return 0; +} +#define KEY_LEFT -1 +#define KEY_RIGHT -2 #else +#include int hists__browse(struct hists *self, const char *helpline, const char *input_name); +int hist_entry__tui_annotate(struct hist_entry *self); +#define KEY_LEFT NEWT_KEY_LEFT +#define KEY_RIGHT NEWT_KEY_RIGHT #endif #endif /* __PERF_HIST_H */ diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index c65838c99354..ffd04720b754 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -235,6 +235,15 @@ static bool dialog_yesno(const char *msg) return newtWinChoice(NULL, yes, no, (char *)msg) == 1; } +static void ui__error_window(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + newtWinMessagev((char *)"Error", (char *)"Ok", (char *)fmt, ap); + va_end(ap); +} + #define HE_COLORSET_TOP 50 #define HE_COLORSET_MEDIUM 51 #define HE_COLORSET_NORMAL 52 @@ -386,6 +395,8 @@ static int ui_browser__run(struct ui_browser *self, const char *title, newtFormAddHotKey(self->form, ' '); newtFormAddHotKey(self->form, NEWT_KEY_HOME); newtFormAddHotKey(self->form, NEWT_KEY_END); + newtFormAddHotKey(self->form, NEWT_KEY_TAB); + newtFormAddHotKey(self->form, NEWT_KEY_RIGHT); if (ui_browser__refresh_entries(self) < 0) return -1; @@ -398,6 +409,8 @@ static int ui_browser__run(struct ui_browser *self, const char *title, if (es->reason != NEWT_EXIT_HOTKEY) break; + if (is_exit_key(es->u.key)) + return es->u.key; switch (es->u.key) { case NEWT_KEY_DOWN: if (self->index == self->nr_entries - 1) @@ -471,12 +484,10 @@ static int ui_browser__run(struct ui_browser *self, const char *title, } } break; - case NEWT_KEY_ESCAPE: + case NEWT_KEY_RIGHT: case NEWT_KEY_LEFT: - case CTRL('c'): - case 'Q': - case 'q': - return 0; + case NEWT_KEY_TAB: + return es->u.key; default: continue; } @@ -668,18 +679,24 @@ static size_t hist_entry__append_browser(struct hist_entry *self, return ret; } -static void hist_entry__annotate_browser(struct hist_entry *self) +int hist_entry__tui_annotate(struct hist_entry *self) { struct ui_browser browser; struct newtExitStruct es; struct objdump_line *pos, *n; LIST_HEAD(head); + int ret; if (self->ms.sym == NULL) - return; + return -1; - if (hist_entry__annotate(self, &head) < 0) - return; + if (self->ms.map->dso->annotate_warned) + return -1; + + if (hist_entry__annotate(self, &head) < 0) { + ui__error_window(browser__last_msg); + return -1; + } ui_helpline__push("Press <- or ESC to exit"); @@ -694,7 +711,7 @@ static void hist_entry__annotate_browser(struct hist_entry *self) } browser.width += 18; /* Percentage */ - ui_browser__run(&browser, self->ms.sym->name, &es); + ret = ui_browser__run(&browser, self->ms.sym->name, &es); newtFormDestroy(browser.form); newtPopWindow(); list_for_each_entry_safe(pos, n, &head, node) { @@ -702,6 +719,7 @@ static void hist_entry__annotate_browser(struct hist_entry *self) objdump_line__free(pos); } ui_helpline__pop(); + return ret; } static const void *newt__symbol_tree_get_current(newtComponent self) @@ -935,14 +953,14 @@ do_help: continue; default:; } - if (toupper(es.u.key) == 'Q' || - es.u.key == CTRL('c')) - break; - if (es.u.key == NEWT_KEY_ESCAPE) { - if (dialog_yesno("Do you really want to exit?")) + if (is_exit_key(es.u.key)) { + if (es.u.key == NEWT_KEY_ESCAPE) { + if (dialog_yesno("Do you really want to exit?")) + break; + else + continue; + } else break; - else - continue; } if (es.u.key == NEWT_KEY_LEFT) { @@ -1006,7 +1024,7 @@ do_annotate: if (he == NULL) continue; - hist_entry__annotate_browser(he); + hist_entry__tui_annotate(he); } else if (choice == zoom_dso) { zoom_dso: if (dso_filter) { @@ -1074,7 +1092,7 @@ void setup_browser(void) { struct newtPercentTreeColors *c = &defaultPercentTreeColors; - if (!isatty(1) || !use_browser) { + if (!isatty(1) || !use_browser || dump_trace) { setup_pager(); return; } diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 45b9655f3a5c..4e8b6b0c551c 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -81,7 +81,7 @@ #include #include "../../../include/linux/magic.h" #include "types.h" - +#include #ifndef NO_ICONV #include @@ -263,6 +263,19 @@ bool strglobmatch(const char *str, const char *pat); bool strlazymatch(const char *str, const char *pat); unsigned long convert_unit(unsigned long value, char *unit); +#ifndef ESC +#define ESC 27 +#endif + +static inline bool is_exit_key(int key) +{ + char up; + if (key == CTRL('c') || key == ESC) + return true; + up = toupper(key); + return up == 'Q'; +} + #define _STR(x) #x #define STR(x) _STR(x) -- cgit v1.2.3 From 44bf460649a9b91f291176097e9d7e846e8c001e Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 23 May 2010 19:12:25 -0300 Subject: perf annotate: Fix up usage of the build id cache MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It was assuming that the cache was always available and also wasn't checking if the file found in the build id cache was just a kallsyms file, that is not supported by objdump for disassembly. Reported-by: Ingo Molnar Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/hist.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'tools/perf/util/hist.c') diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 682a6d88862c..cbf7eae2ce09 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -990,6 +990,7 @@ int hist_entry__annotate(struct hist_entry *self, struct list_head *head) struct map *map = self->ms.map; struct dso *dso = map->dso; char *filename = dso__build_id_filename(dso, NULL, 0); + bool free_filename = true; char command[PATH_MAX * 2]; FILE *file; int err = 0; @@ -1001,11 +1002,19 @@ int hist_entry__annotate(struct hist_entry *self, struct list_head *head) sym->name); return -ENOMEM; } + goto fallback; + } else if (readlink(filename, command, sizeof(command)) < 0 || + strstr(command, "[kernel.kallsyms]") || + access(filename, R_OK)) { + free(filename); +fallback: /* - * If we don't have build-ids, well, lets hope that this + * If we don't have build-ids or the build-id file isn't in the + * cache, or is just a kallsyms file, well, lets hope that this * DSO is the same as when 'perf record' ran. */ filename = dso->long_name; + free_filename = false; } if (dso->origin == DSO__ORIG_KERNEL) { @@ -1045,7 +1054,7 @@ int hist_entry__annotate(struct hist_entry *self, struct list_head *head) pclose(file); out_free_filename: - if (dso->has_build_id) + if (free_filename) free(filename); return err; } -- cgit v1.2.3