From f9224c5c944b60cf709db4adf1f5195264b8d194 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 11 Mar 2010 20:12:44 -0300 Subject: perf report: Implement initial UI using newt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Newt has widespread availability and provides a rather simple API as can be seen by the size of this patch. The work needed to support it will benefit other frontends too. In this initial patch it just checks if the output is a tty, if not it falls back to the previous behaviour, also if newt-devel/libnewt-dev is not installed the previous behaviour is maintaned. Pressing enter on a symbol will annotate it, ESC in the annotation window will return to the report symbol list. More work will be done to remove the special casing in color_fprintf, stop using fmemopen/FILE in the printing of hist_entries, etc. Also the annotation doesn't need to be done via spawning "perf annotate" and then browsing its output, we can do better by calling directly the builtin-annotate.c functions, that would then be moved to tools/perf/util/annotate.c and shared with perf top, etc But lets go by baby steps, this patch already improves perf usability by allowing to quickly do annotations on symbols from the report screen and provides a first experimentation with libnewt/TUI integration of tools. Tested on RHEL5 and Fedora12 X86_64 and on Debian PARISC64 to browse a perf.data file collected on a Fedora12 x86_64 box. Signed-off-by: Arnaldo Carvalho de Melo Cc: Avi Kivity Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: <1268349164-5822-5-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar --- tools/perf/util/newt.c | 194 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 194 insertions(+) create mode 100644 tools/perf/util/newt.c (limited to 'tools/perf/util/newt.c') diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c new file mode 100644 index 00000000000..3d3a936acb6 --- /dev/null +++ b/tools/perf/util/newt.c @@ -0,0 +1,194 @@ +#define _GNU_SOURCE +#include +#undef _GNU_SOURCE + +#include +#include + +#include "cache.h" +#include "hist.h" +#include "session.h" +#include "sort.h" +#include "symbol.h" + +static size_t hist_entry__append_browser(struct hist_entry *self, + newtComponent listbox, u64 total) +{ + char bf[1024]; + size_t len; + FILE *fp; + + if (symbol_conf.exclude_other && !self->parent) + return 0; + + fp = fmemopen(bf, sizeof(bf), "w"); + if (fp == NULL) + return 0; + + len = hist_entry__fprintf(self, NULL, false, 0, fp, total); + + fclose(fp); + newtListboxAppendEntry(listbox, bf, self); + return len; +} + +static void hist_entry__annotate_browser(struct hist_entry *self) +{ + FILE *fp; + struct winsize ws; + newtComponent form, listbox; + struct newtExitStruct es; + char *str; + size_t line_len, max_line_len = 0; + size_t max_usable_width; + char *line = NULL; + + if (self->sym == NULL) + return; + + if (asprintf(&str, "perf annotate %s | expand", self->sym->name) < 0) + return; + + fp = popen(str, "r"); + if (fp == NULL) + goto out_free_str; + + newtPushHelpLine("Press ESC to exit"); + get_term_dimensions(&ws); + listbox = newtListbox(0, 0, ws.ws_row - 5, NEWT_FLAG_SCROLL); + + while (!feof(fp)) { + if (getline(&line, &line_len, fp) < 0 || !line_len) + break; + while (line_len != 0 && isspace(line[line_len - 1])) + line[--line_len] = '\0'; + + if (line_len > max_line_len) + max_line_len = line_len; + newtListboxAppendEntry(listbox, line, NULL); + } + fclose(fp); + free(line); + + max_usable_width = ws.ws_col - 22; + if (max_line_len > max_usable_width) + max_line_len = max_usable_width; + + newtListboxSetWidth(listbox, max_line_len); + + newtCenteredWindow(max_line_len + 2, ws.ws_row - 5, self->sym->name); + form = newtForm(NULL, NULL, 0); + newtFormAddHotKey(form, NEWT_KEY_ESCAPE); + newtFormAddComponents(form, listbox, NULL); + + newtFormRun(form, &es); + newtFormDestroy(form); + newtPopWindow(); + newtPopHelpLine(); +out_free_str: + free(str); +} + +void perf_session__browse_hists(struct rb_root *hists, u64 session_total, + const char *helpline) +{ + struct sort_entry *se; + struct rb_node *nd; + unsigned int width; + char *col_width = symbol_conf.col_width_list_str; + struct winsize ws; + size_t max_len = 0; + char str[1024]; + newtComponent form, listbox; + struct newtExitStruct es; + + snprintf(str, sizeof(str), "Samples: %Ld", session_total); + newtDrawRootText(0, 0, str); + newtPushHelpLine(helpline); + + get_term_dimensions(&ws); + + form = newtForm(NULL, NULL, 0); + newtFormAddHotKey(form, NEWT_KEY_ESCAPE); + + listbox = newtListbox(1, 1, ws.ws_row - 2, (NEWT_FLAG_SCROLL | + NEWT_FLAG_BORDER | + NEWT_FLAG_RETURNEXIT)); + + list_for_each_entry(se, &hist_entry__sort_list, list) { + if (se->elide) + continue; + width = strlen(se->header); + if (se->width) { + if (symbol_conf.col_width_list_str) { + if (col_width) { + *se->width = atoi(col_width); + col_width = strchr(col_width, ','); + if (col_width) + ++col_width; + } + } + *se->width = max(*se->width, width); + } + } + + for (nd = rb_first(hists); nd; nd = rb_next(nd)) { + struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); + size_t len = hist_entry__append_browser(h, listbox, session_total); + if (len > max_len) + max_len = len; + } + + newtListboxSetWidth(listbox, max_len); + newtFormAddComponents(form, listbox, NULL); + + while (1) { + struct hist_entry *selection; + + newtFormRun(form, &es); + if (es.reason == NEWT_EXIT_HOTKEY) + break; + selection = newtListboxGetCurrent(listbox); + hist_entry__annotate_browser(selection); + } + + newtFormDestroy(form); +} + +int browser__show_help(const char *format, va_list ap) +{ + int ret; + static int backlog; + static char msg[1024]; + + ret = vsnprintf(msg + backlog, sizeof(msg) - backlog, format, ap); + backlog += ret; + + if (msg[backlog - 1] == '\n') { + newtPopHelpLine(); + newtPushHelpLine(msg); + newtRefresh(); + backlog = 0; + } + + return ret; +} + +bool use_browser; + +void setup_browser(void) +{ + if (!isatty(1)) + return; + + use_browser = true; + newtInit(); + newtCls(); + newtPushHelpLine(" "); +} + +void exit_browser(void) +{ + if (use_browser) + newtFinished(); +} -- cgit v1.2.3 From 7081e087b90d4eb4348f7970bd6b266d837321ef Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 12 Mar 2010 10:48:12 -0300 Subject: perf newt: Add 'Q', 'q' and Ctrl+C as ways to exit from forms MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These are keys people expect when pressed to exit the current widget, so have associate all of them to this semantic. Suggested-by: Ingo Molnar Signed-off-by: Arnaldo Carvalho de Melo Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: <1268401692-9361-1-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar --- tools/perf/util/newt.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) (limited to 'tools/perf/util/newt.c') diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 3d3a936acb6..2a4308a29ba 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -4,6 +4,7 @@ #include #include +#include #include "cache.h" #include "hist.h" @@ -11,6 +12,22 @@ #include "sort.h" #include "symbol.h" +static void newt_form__set_exit_keys(newtComponent self) +{ + newtFormAddHotKey(self, NEWT_KEY_ESCAPE); + newtFormAddHotKey(self, 'Q'); + newtFormAddHotKey(self, 'q'); + newtFormAddHotKey(self, CTRL('c')); +} + +static newtComponent newt_form__new(void) +{ + newtComponent self = newtForm(NULL, NULL, 0); + if (self) + newt_form__set_exit_keys(self); + return self; +} + static size_t hist_entry__append_browser(struct hist_entry *self, newtComponent listbox, u64 total) { @@ -77,8 +94,7 @@ static void hist_entry__annotate_browser(struct hist_entry *self) newtListboxSetWidth(listbox, max_line_len); newtCenteredWindow(max_line_len + 2, ws.ws_row - 5, self->sym->name); - form = newtForm(NULL, NULL, 0); - newtFormAddHotKey(form, NEWT_KEY_ESCAPE); + form = newt_form__new(); newtFormAddComponents(form, listbox, NULL); newtFormRun(form, &es); @@ -108,8 +124,7 @@ void perf_session__browse_hists(struct rb_root *hists, u64 session_total, get_term_dimensions(&ws); - form = newtForm(NULL, NULL, 0); - newtFormAddHotKey(form, NEWT_KEY_ESCAPE); + form = newt_form__new(); listbox = newtListbox(1, 1, ws.ws_row - 2, (NEWT_FLAG_SCROLL | NEWT_FLAG_BORDER | -- cgit v1.2.3 From cb7afb7092bc502b890f0a897ffd67c2b078d347 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 12 Mar 2010 12:46:47 -0300 Subject: perf newt: Use newtGetScreenSize MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For consistency, use the newt API more fully. Signed-off-by: Arnaldo Carvalho de Melo Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: <1268408808-13595-1-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar --- tools/perf/util/newt.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'tools/perf/util/newt.c') diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 2a4308a29ba..f6ec6f5c0fd 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -52,7 +52,7 @@ static size_t hist_entry__append_browser(struct hist_entry *self, static void hist_entry__annotate_browser(struct hist_entry *self) { FILE *fp; - struct winsize ws; + int cols, rows; newtComponent form, listbox; struct newtExitStruct es; char *str; @@ -71,8 +71,8 @@ static void hist_entry__annotate_browser(struct hist_entry *self) goto out_free_str; newtPushHelpLine("Press ESC to exit"); - get_term_dimensions(&ws); - listbox = newtListbox(0, 0, ws.ws_row - 5, NEWT_FLAG_SCROLL); + newtGetScreenSize(&cols, &rows); + listbox = newtListbox(0, 0, rows - 5, NEWT_FLAG_SCROLL); while (!feof(fp)) { if (getline(&line, &line_len, fp) < 0 || !line_len) @@ -87,13 +87,13 @@ static void hist_entry__annotate_browser(struct hist_entry *self) fclose(fp); free(line); - max_usable_width = ws.ws_col - 22; + max_usable_width = cols - 22; if (max_line_len > max_usable_width) max_line_len = max_usable_width; newtListboxSetWidth(listbox, max_line_len); - newtCenteredWindow(max_line_len + 2, ws.ws_row - 5, self->sym->name); + newtCenteredWindow(max_line_len + 2, rows - 5, self->sym->name); form = newt_form__new(); newtFormAddComponents(form, listbox, NULL); @@ -112,7 +112,7 @@ void perf_session__browse_hists(struct rb_root *hists, u64 session_total, struct rb_node *nd; unsigned int width; char *col_width = symbol_conf.col_width_list_str; - struct winsize ws; + int rows; size_t max_len = 0; char str[1024]; newtComponent form, listbox; @@ -122,13 +122,13 @@ void perf_session__browse_hists(struct rb_root *hists, u64 session_total, newtDrawRootText(0, 0, str); newtPushHelpLine(helpline); - get_term_dimensions(&ws); + newtGetScreenSize(NULL, &rows); form = newt_form__new(); - listbox = newtListbox(1, 1, ws.ws_row - 2, (NEWT_FLAG_SCROLL | - NEWT_FLAG_BORDER | - NEWT_FLAG_RETURNEXIT)); + listbox = newtListbox(1, 1, rows - 2, (NEWT_FLAG_SCROLL | + NEWT_FLAG_BORDER | + NEWT_FLAG_RETURNEXIT)); list_for_each_entry(se, &hist_entry__sort_list, list) { if (se->elide) -- cgit v1.2.3 From 567e54790e5c07152a93b6de4d0210af8b77da87 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 12 Mar 2010 21:05:10 -0300 Subject: perf tools: Fix non-newt build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The use_browser needs to be in a file that is always built and also we need a browser__show_help stub in that case. Reported-by: Anton Blanchard Signed-off-by: Arnaldo Carvalho de Melo Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: <1268438710-32697-1-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar --- tools/perf/perf.c | 2 ++ tools/perf/util/debug.h | 8 ++++++++ tools/perf/util/newt.c | 2 -- 3 files changed, 10 insertions(+), 2 deletions(-) (limited to 'tools/perf/util/newt.c') diff --git a/tools/perf/perf.c b/tools/perf/perf.c index 9ff186b57cb..0d4b9edfab1 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c @@ -16,6 +16,8 @@ #include "util/string.h" #include "util/debugfs.h" +bool use_browser; + const char perf_usage_string[] = "perf [--version] [--help] COMMAND [ARGS]"; diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h index 03accb86799..0172edf3f15 100644 --- a/tools/perf/util/debug.h +++ b/tools/perf/util/debug.h @@ -9,6 +9,14 @@ extern int dump_trace; int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); void trace_event(event_t *event); + +#ifdef NO_NEWT_SUPPORT +static inline int browser__show_help(const char *format __used, va_list ap __used) +{ + return 0; +} +#else int browser__show_help(const char *format, va_list ap); +#endif #endif /* __PERF_DEBUG_H */ diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index f6ec6f5c0fd..2836394459d 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -189,8 +189,6 @@ int browser__show_help(const char *format, va_list ap) return ret; } -bool use_browser; - void setup_browser(void) { if (!isatty(1)) -- cgit v1.2.3 From d06d92b7c9b99ea52bdaeb13f544675529891b8a Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 15 Mar 2010 13:04:33 -0300 Subject: perf annotate: Properly notify the user that vmlinux is missing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before this patch we would not find a vmlinux, then try to pass objdump "[kernel.kallsyms]" as the filename, it would get confused and produce no output: [root@doppio ~]# perf annotate n_tty_write ------------------------------------------------ Percent | Source code & Disassembly of [kernel.kallsyms] ------------------------------------------------ Now we check that and emit meaningful warning: [root@doppio ~]# perf annotate n_tty_write Can't annotate n_tty_write: No vmlinux file was found in the path: [0] vmlinux [1] /boot/vmlinux [2] /boot/vmlinux-2.6.34-rc1-tip+ [3] /lib/modules/2.6.34-rc1-tip+/build/vmlinux [4] /usr/lib/debug/lib/modules/2.6.34-rc1-tip+/vmlinux [root@doppio ~]# This bug was introduced when we added automatic search for vmlinux, before that time the user had to specify a vmlinux file. v2: Print the warning just for the first symbol found when no symbol name is specified, otherwise it will spam the screen repeating the warning for each symbol. Reported-by: Ingo Molnar Signed-off-by: Arnaldo Carvalho de Melo Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras Cc: LKML-Reference: <1268669073-6856-1-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar --- tools/perf/builtin-annotate.c | 10 ++++++++++ tools/perf/util/newt.c | 2 +- tools/perf/util/symbol.h | 1 + 3 files changed, 12 insertions(+), 1 deletion(-) (limited to 'tools/perf/util/newt.c') diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 6ad7148451c..45d14660d53 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -452,6 +452,16 @@ static void annotate_sym(struct hist_entry *he) if (!filename) return; + if (dso->origin == DSO__ORIG_KERNEL) { + if (dso->annotate_warned) + return; + 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; + } + pr_debug("%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)); diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 2836394459d..2d19e7a3e6e 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -63,7 +63,7 @@ static void hist_entry__annotate_browser(struct hist_entry *self) if (self->sym == NULL) return; - if (asprintf(&str, "perf annotate %s | expand", self->sym->name) < 0) + if (asprintf(&str, "perf annotate %s 2>&1 | expand", self->sym->name) < 0) return; fp = popen(str, "r"); diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index d983bbaf99c..5bd91d14270 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -106,6 +106,7 @@ struct dso { u8 has_build_id:1; u8 kernel:1; u8 hit:1; + u8 annotate_warned:1; unsigned char origin; u8 sorted_by_name; u8 loaded; -- cgit v1.2.3 From f3a1f0ea9432ec395cd112f42201e8e523c07bc5 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 22 Mar 2010 13:10:25 -0300 Subject: perf newt: Properly restore the screen when error exiting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Show an OK message box with the last message sent via pr_err, etc. Reported-by: Ingo Molnar Signed-off-by: Arnaldo Carvalho de Melo Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: <1269274229-20442-1-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar --- tools/perf/perf.c | 4 ++-- tools/perf/util/cache.h | 4 ++-- tools/perf/util/newt.c | 19 +++++++++++++------ 3 files changed, 17 insertions(+), 10 deletions(-) (limited to 'tools/perf/util/newt.c') diff --git a/tools/perf/perf.c b/tools/perf/perf.c index d2de8393a33..2826e702986 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c @@ -264,11 +264,11 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv) set_debugfs_path(); status = p->fn(argc, argv, prefix); + exit_browser(status); + if (status) return status & 0xff; - exit_browser(); - /* Somebody closed stdout? */ if (fstat(fileno(stdout), &st)) return 0; diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h index 47b12a3d11b..4b9aab7f040 100644 --- a/tools/perf/util/cache.h +++ b/tools/perf/util/cache.h @@ -77,10 +77,10 @@ static inline void setup_browser(void) { setup_pager(); } -static inline void exit_browser(void) {} +static inline void exit_browser(bool wait_for_ok __used) {} #else void setup_browser(void); -void exit_browser(void); +void exit_browser(bool wait_for_ok); #endif extern const char *editor_program; diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 2d19e7a3e6e..3c2ef95d940 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -170,18 +170,20 @@ void perf_session__browse_hists(struct rb_root *hists, u64 session_total, newtFormDestroy(form); } +static char browser__last_msg[1024]; + int browser__show_help(const char *format, va_list ap) { int ret; static int backlog; - static char msg[1024]; - ret = vsnprintf(msg + backlog, sizeof(msg) - backlog, format, ap); + ret = vsnprintf(browser__last_msg + backlog, + sizeof(browser__last_msg) - backlog, format, ap); backlog += ret; - if (msg[backlog - 1] == '\n') { + if (browser__last_msg[backlog - 1] == '\n') { newtPopHelpLine(); - newtPushHelpLine(msg); + newtPushHelpLine(browser__last_msg); newtRefresh(); backlog = 0; } @@ -200,8 +202,13 @@ void setup_browser(void) newtPushHelpLine(" "); } -void exit_browser(void) +void exit_browser(bool wait_for_ok) { - if (use_browser) + if (use_browser) { + if (wait_for_ok) { + char title[] = "Fatal Error", ok[] = "Ok"; + newtWinMessage(title, ok, browser__last_msg); + } newtFinished(); + } } -- cgit v1.2.3 From 4ded2b250f1fbba4e414d17dc55ee513485c0aa1 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 22 Mar 2010 17:52:49 -0300 Subject: perf report: Implement Newt callgraphs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Starts collapsed, allows annotating by pressing 'A' or 'a' on the symbol, be it the top level one or any of the symbols in the chains. It (ab)uses the only tree widget in newt, that is actually a checkbox tree that we use with just one option ('.'), end result is usable but we really need to create a custom widget tree so that we can use the data structures we have (hist_entry rb_tree + callchain rb_tree + lists), so that we reduce the memory footprint by not creating a mirror set of data structures in the newtCheckboxTree widget. Thanks to Frédéric Weisbacker for fixing the orphanage problem in 301fde2, without that we were tripping a newt bug (fix already sent to newt's maintainer). Signed-off-by: Arnaldo Carvalho de Melo Cc: Avi Kivity Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: <1269291169-29820-1-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar --- tools/perf/util/newt.c | 291 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 259 insertions(+), 32 deletions(-) (limited to 'tools/perf/util/newt.c') diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 3c2ef95d940..12b229bb9dc 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -28,11 +28,197 @@ static newtComponent newt_form__new(void) return self; } +/* + * When debugging newt problems it was useful to be able to "unroll" + * the calls to newtCheckBoxTreeAdd{Array,Item}, so that we can generate + * a source file with the sequence of calls to these methods, to then + * tweak the arrays to get the intended results, so I'm keeping this code + * here, may be useful again in the future. + */ +#undef NEWT_DEBUG + +static void newt_checkbox_tree__add(newtComponent tree, const char *str, + void *priv, int *indexes) +{ +#ifdef NEWT_DEBUG + /* Print the newtCheckboxTreeAddArray to tinker with its index arrays */ + int i = 0, len = 40 - strlen(str); + + fprintf(stderr, + "\tnewtCheckboxTreeAddItem(tree, %*.*s\"%s\", (void *)%p, 0, ", + len, len, " ", str, priv); + while (indexes[i] != NEWT_ARG_LAST) { + if (indexes[i] != NEWT_ARG_APPEND) + fprintf(stderr, " %d,", indexes[i]); + else + fprintf(stderr, " %s,", "NEWT_ARG_APPEND"); + ++i; + } + fprintf(stderr, " %s", " NEWT_ARG_LAST);\n"); + fflush(stderr); +#endif + newtCheckboxTreeAddArray(tree, str, priv, 0, indexes); +} + +static char *callchain_list__sym_name(struct callchain_list *self, + char *bf, size_t bfsize) +{ + if (self->sym) + return self->sym->name; + + snprintf(bf, bfsize, "%#Lx", self->ip); + return bf; +} + +static void __callchain__append_graph_browser(struct callchain_node *self, + newtComponent tree, u64 total, + int *indexes, int depth) +{ + struct rb_node *node; + u64 new_total, remaining; + int idx = 0; + + if (callchain_param.mode == CHAIN_GRAPH_REL) + new_total = self->children_hit; + else + new_total = total; + + remaining = new_total; + node = rb_first(&self->rb_root); + while (node) { + struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); + struct rb_node *next = rb_next(node); + u64 cumul = cumul_hits(child); + struct callchain_list *chain; + int first = true, printed = 0; + int chain_idx = -1; + remaining -= cumul; + + indexes[depth] = NEWT_ARG_APPEND; + indexes[depth + 1] = NEWT_ARG_LAST; + + list_for_each_entry(chain, &child->val, list) { + char ipstr[BITS_PER_LONG / 4 + 1], + *alloc_str = NULL; + const char *str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); + + if (first) { + double percent = cumul * 100.0 / new_total; + + first = false; + if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0) + str = "Not enough memory!"; + else + str = alloc_str; + } else { + indexes[depth] = idx; + indexes[depth + 1] = NEWT_ARG_APPEND; + indexes[depth + 2] = NEWT_ARG_LAST; + ++chain_idx; + } + newt_checkbox_tree__add(tree, str, chain->sym, indexes); + free(alloc_str); + ++printed; + } + + indexes[depth] = idx; + if (chain_idx != -1) + indexes[depth + 1] = chain_idx; + if (printed != 0) + ++idx; + __callchain__append_graph_browser(child, tree, new_total, indexes, + depth + (chain_idx != -1 ? 2 : 1)); + node = next; + } +} + +static void callchain__append_graph_browser(struct callchain_node *self, + newtComponent tree, u64 total, + int *indexes, int parent_idx) +{ + struct callchain_list *chain; + int i = 0; + + indexes[1] = NEWT_ARG_APPEND; + indexes[2] = NEWT_ARG_LAST; + + list_for_each_entry(chain, &self->val, list) { + char ipstr[BITS_PER_LONG / 4 + 1], *str; + + if (chain->ip >= PERF_CONTEXT_MAX) + continue; + + if (!i++ && sort__first_dimension == SORT_SYM) + continue; + + str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); + newt_checkbox_tree__add(tree, str, chain->sym, indexes); + } + + indexes[1] = parent_idx; + indexes[2] = NEWT_ARG_APPEND; + indexes[3] = NEWT_ARG_LAST; + __callchain__append_graph_browser(self, tree, total, indexes, 2); +} + +static void hist_entry__append_callchain_browser(struct hist_entry *self, + newtComponent tree, u64 total, int parent_idx) +{ + struct rb_node *rb_node; + int indexes[1024] = { [0] = parent_idx, }; + int idx = 0; + struct callchain_node *chain; + + rb_node = rb_first(&self->sorted_chain); + while (rb_node) { + chain = rb_entry(rb_node, struct callchain_node, rb_node); + switch (callchain_param.mode) { + case CHAIN_FLAT: + break; + case CHAIN_GRAPH_ABS: /* falldown */ + case CHAIN_GRAPH_REL: + callchain__append_graph_browser(chain, tree, total, indexes, idx++); + break; + case CHAIN_NONE: + default: + break; + } + rb_node = rb_next(rb_node); + } +} + +/* + * FIXME: get lib/string.c linked with perf somehow + */ +static char *skip_spaces(const char *str) +{ + while (isspace(*str)) + ++str; + return (char *)str; +} + +static char *strim(char *s) +{ + size_t size; + char *end; + + s = skip_spaces(s); + size = strlen(s); + if (!size) + return s; + + end = s + size - 1; + while (end >= s && isspace(*end)) + end--; + *(end + 1) = '\0'; + + return s; +} + static size_t hist_entry__append_browser(struct hist_entry *self, - newtComponent listbox, u64 total) + newtComponent tree, u64 total) { - char bf[1024]; - size_t len; + char bf[1024], *s; FILE *fp; if (symbol_conf.exclude_other && !self->parent) @@ -42,28 +228,46 @@ static size_t hist_entry__append_browser(struct hist_entry *self, if (fp == NULL) return 0; - len = hist_entry__fprintf(self, NULL, false, 0, fp, total); - + hist_entry__fprintf(self, NULL, false, 0, fp, total); fclose(fp); - newtListboxAppendEntry(listbox, bf, self); - return len; + + /* + * FIXME: We shouldn't need to trim, as the printing routines shouldn't + * add spaces it in the first place, the stdio output routines should + * call a __snprintf method instead of the current __print (that + * actually is a __fprintf) one, but get the raw string and _then_ add + * the newline, as this is a detail of stdio printing, not needed in + * other UIs, e.g. newt. + */ + s = strim(bf); + + if (symbol_conf.use_callchain) { + int indexes[2]; + + indexes[0] = NEWT_ARG_APPEND; + indexes[1] = NEWT_ARG_LAST; + newt_checkbox_tree__add(tree, s, self->sym, indexes); + } else + newtListboxAppendEntry(tree, s, self->sym); + + return strlen(s); } -static void hist_entry__annotate_browser(struct hist_entry *self) +static void symbol__annotate_browser(const struct symbol *self) { FILE *fp; int cols, rows; - newtComponent form, listbox; + newtComponent form, tree; struct newtExitStruct es; char *str; size_t line_len, max_line_len = 0; size_t max_usable_width; char *line = NULL; - if (self->sym == NULL) + if (self == NULL) return; - if (asprintf(&str, "perf annotate %s 2>&1 | expand", self->sym->name) < 0) + if (asprintf(&str, "perf annotate %s 2>&1 | expand", self->name) < 0) return; fp = popen(str, "r"); @@ -72,7 +276,7 @@ static void hist_entry__annotate_browser(struct hist_entry *self) newtPushHelpLine("Press ESC to exit"); newtGetScreenSize(&cols, &rows); - listbox = newtListbox(0, 0, rows - 5, NEWT_FLAG_SCROLL); + tree = newtListbox(0, 0, rows - 5, NEWT_FLAG_SCROLL); while (!feof(fp)) { if (getline(&line, &line_len, fp) < 0 || !line_len) @@ -82,7 +286,7 @@ static void hist_entry__annotate_browser(struct hist_entry *self) if (line_len > max_line_len) max_line_len = line_len; - newtListboxAppendEntry(listbox, line, NULL); + newtListboxAppendEntry(tree, line, NULL); } fclose(fp); free(line); @@ -91,11 +295,11 @@ static void hist_entry__annotate_browser(struct hist_entry *self) if (max_line_len > max_usable_width) max_line_len = max_usable_width; - newtListboxSetWidth(listbox, max_line_len); + newtListboxSetWidth(tree, max_line_len); - newtCenteredWindow(max_line_len + 2, rows - 5, self->sym->name); + newtCenteredWindow(max_line_len + 2, rows - 5, self->name); form = newt_form__new(); - newtFormAddComponents(form, listbox, NULL); + newtFormAddComponents(form, tree, NULL); newtFormRun(form, &es); newtFormDestroy(form); @@ -110,25 +314,27 @@ void perf_session__browse_hists(struct rb_root *hists, u64 session_total, { struct sort_entry *se; struct rb_node *nd; + char seq[] = "."; unsigned int width; char *col_width = symbol_conf.col_width_list_str; - int rows; - size_t max_len = 0; + int rows, cols, idx; + int max_len = 0; char str[1024]; - newtComponent form, listbox; + newtComponent form, tree; struct newtExitStruct es; snprintf(str, sizeof(str), "Samples: %Ld", session_total); newtDrawRootText(0, 0, str); newtPushHelpLine(helpline); - newtGetScreenSize(NULL, &rows); - - form = newt_form__new(); + newtGetScreenSize(&cols, &rows); - listbox = newtListbox(1, 1, rows - 2, (NEWT_FLAG_SCROLL | - NEWT_FLAG_BORDER | - NEWT_FLAG_RETURNEXIT)); + if (symbol_conf.use_callchain) + tree = newtCheckboxTreeMulti(0, 0, rows - 5, seq, + NEWT_FLAG_SCROLL); + else + tree = newtListbox(0, 0, rows - 5, (NEWT_FLAG_SCROLL | + NEWT_FLAG_RETURNEXIT)); list_for_each_entry(se, &hist_entry__sort_list, list) { if (se->elide) @@ -147,27 +353,48 @@ void perf_session__browse_hists(struct rb_root *hists, u64 session_total, } } + idx = 0; for (nd = rb_first(hists); nd; nd = rb_next(nd)) { struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); - size_t len = hist_entry__append_browser(h, listbox, session_total); + int len = hist_entry__append_browser(h, tree, session_total); if (len > max_len) max_len = len; + if (symbol_conf.use_callchain) { + hist_entry__append_callchain_browser(h, tree, session_total, idx++); + if (idx > 3300) + break; + } } - newtListboxSetWidth(listbox, max_len); - newtFormAddComponents(form, listbox, NULL); + if (max_len > cols) + max_len = cols - 3; + + if (!symbol_conf.use_callchain) + newtListboxSetWidth(tree, max_len); + + newtCenteredWindow(max_len + (symbol_conf.use_callchain ? 5 : 0), + rows - 5, "Report"); + form = newt_form__new(); + newtFormAddHotKey(form, 'A'); + newtFormAddHotKey(form, 'a'); + newtFormAddComponents(form, tree, NULL); while (1) { - struct hist_entry *selection; + const struct symbol *selection; newtFormRun(form, &es); - if (es.reason == NEWT_EXIT_HOTKEY) + if (es.reason == NEWT_EXIT_HOTKEY && + toupper(es.u.key) != 'A') break; - selection = newtListboxGetCurrent(listbox); - hist_entry__annotate_browser(selection); + if (!symbol_conf.use_callchain) + selection = newtListboxGetCurrent(tree); + else + selection = newtCheckboxTreeGetCurrent(tree); + symbol__annotate_browser(selection); } newtFormDestroy(form); + newtPopWindow(); } static char browser__last_msg[1024]; -- cgit v1.2.3 From 53c540195724b52422da067a31ef6916d2c70202 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 24 Mar 2010 16:40:14 -0300 Subject: perf report: Add a popup menu to ask what operation is to be performed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Right now it presents a menu with these options: +------------------------------+ | Annotate CURRENT_SYMBOL_NAME | | Exit | +------------------------------+ If the highlighted (current) symbol is not annotatable only the "Exit" option will appear. Also add a confirmation dialog when ESC is pressed on the top level to avoid exiting the application by pressing one too many ESC key. To get to the menu just press the -> (Right navigation key), to exit just press ESC. Signed-off-by: Arnaldo Carvalho de Melo Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: <1269459619-982-1-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar --- tools/perf/util/newt.c | 96 +++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 88 insertions(+), 8 deletions(-) (limited to 'tools/perf/util/newt.c') diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 12b229bb9dc..a3465a0ad70 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -28,6 +28,47 @@ static newtComponent newt_form__new(void) return self; } +static int popup_menu(int argc, const char *argv[]) +{ + struct newtExitStruct es; + int i, rc = -1, max_len = 5; + newtComponent listbox, form = newt_form__new(); + + if (form == NULL) + return -1; + + listbox = newtListbox(0, 0, argc, NEWT_FLAG_RETURNEXIT); + if (listbox == NULL) + goto out_destroy_form; + + newtFormAddComponents(form, listbox, NULL); + + for (i = 0; i < argc; ++i) { + int len = strlen(argv[i]); + if (len > max_len) + max_len = len; + if (newtListboxAddEntry(listbox, argv[i], (void *)(long)i)) + goto out_destroy_form; + } + + newtCenteredWindow(max_len, argc, NULL); + newtFormRun(form, &es); + rc = newtListboxGetCurrent(listbox) - NULL; + if (es.reason == NEWT_EXIT_HOTKEY) + rc = -1; + newtPopWindow(); +out_destroy_form: + newtFormDestroy(form); + return rc; +} + +static bool dialog_yesno(const char *msg) +{ + /* newtWinChoice should really be accepting const char pointers... */ + char yes[] = "Yes", no[] = "No"; + return newtWinChoice(NULL, no, yes, (char *)msg) == 2; +} + /* * When debugging newt problems it was useful to be able to "unroll" * the calls to newtCheckBoxTreeAdd{Array,Item}, so that we can generate @@ -309,6 +350,19 @@ out_free_str: free(str); } +static const void *newt__symbol_tree_get_current(newtComponent self) +{ + if (symbol_conf.use_callchain) + return newtCheckboxTreeGetCurrent(self); + return newtListboxGetCurrent(self); +} + +static void perf_session__selection(newtComponent self, void *data) +{ + const struct symbol **symbol_ptr = data; + *symbol_ptr = newt__symbol_tree_get_current(self); +} + void perf_session__browse_hists(struct rb_root *hists, u64 session_total, const char *helpline) { @@ -322,6 +376,7 @@ void perf_session__browse_hists(struct rb_root *hists, u64 session_total, char str[1024]; newtComponent form, tree; struct newtExitStruct es; + const struct symbol *selection; snprintf(str, sizeof(str), "Samples: %Ld", session_total); newtDrawRootText(0, 0, str); @@ -336,6 +391,8 @@ void perf_session__browse_hists(struct rb_root *hists, u64 session_total, tree = newtListbox(0, 0, rows - 5, (NEWT_FLAG_SCROLL | NEWT_FLAG_RETURNEXIT)); + newtComponentAddCallback(tree, perf_session__selection, &selection); + list_for_each_entry(se, &hist_entry__sort_list, list) { if (se->elide) continue; @@ -377,20 +434,43 @@ void perf_session__browse_hists(struct rb_root *hists, u64 session_total, form = newt_form__new(); newtFormAddHotKey(form, 'A'); newtFormAddHotKey(form, 'a'); + newtFormAddHotKey(form, NEWT_KEY_RIGHT); newtFormAddComponents(form, tree, NULL); + selection = newt__symbol_tree_get_current(tree); while (1) { - const struct symbol *selection; + char annotate[512]; + const char *options[2]; + int nr_options = 0, choice; newtFormRun(form, &es); - if (es.reason == NEWT_EXIT_HOTKEY && - toupper(es.u.key) != 'A') + if (es.reason == NEWT_EXIT_HOTKEY) { + if (toupper(es.u.key) == 'A') { + symbol__annotate_browser(selection); + continue; + } + if (es.u.key == NEWT_KEY_ESCAPE || + toupper(es.u.key) == 'Q' || + es.u.key == CTRL('c')) { + if (dialog_yesno("Do you really want to exit?")) + break; + else + continue; + } + } + + if (selection != NULL) { + snprintf(annotate, sizeof(annotate), + "Annotate %s", selection->name); + options[nr_options++] = annotate; + } + + options[nr_options++] = "Exit"; + choice = popup_menu(nr_options, options); + if (choice == nr_options - 1) break; - if (!symbol_conf.use_callchain) - selection = newtListboxGetCurrent(tree); - else - selection = newtCheckboxTreeGetCurrent(tree); - symbol__annotate_browser(selection); + else if (selection != NULL && choice >= 0) + symbol__annotate_browser(selection); } newtFormDestroy(form); -- cgit v1.2.3 From 59fd53062f71011a68d03f4cd0ba93d822ac3249 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 24 Mar 2010 16:40:17 -0300 Subject: perf tools: Introduce struct map_symbol MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit That will be in both struct hist_entry and struct callchain_list, so that the TUI can store a pointer to the pair (map, symbol) in the trees where hist_entries and callchain_lists are present, to allow precise annotation instead of looking for the first symbol with the selected name. Signed-off-by: Arnaldo Carvalho de Melo Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: <1269459619-982-4-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar --- tools/perf/builtin-annotate.c | 34 +++++++++++++++++----------------- tools/perf/util/hist.c | 8 +++++--- tools/perf/util/newt.c | 4 ++-- tools/perf/util/sort.c | 22 +++++++++++----------- tools/perf/util/sort.h | 3 +-- tools/perf/util/symbol.h | 5 +++++ 6 files changed, 41 insertions(+), 35 deletions(-) (limited to 'tools/perf/util/newt.c') diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index ce9b1ef784b..887e8e04a6f 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -69,13 +69,13 @@ static int sym__alloc_hist(struct symbol *self) static int annotate__hist_hit(struct hist_entry *he, u64 ip) { unsigned int sym_size, offset; - struct symbol *sym = he->sym; + struct symbol *sym = he->ms.sym; struct sym_priv *priv; struct sym_hist *h; he->count++; - if (!sym || !he->map) + if (!sym || !he->ms.map) return 0; priv = symbol__priv(sym); @@ -85,7 +85,7 @@ static int annotate__hist_hit(struct hist_entry *he, u64 ip) sym_size = sym->end - sym->start; offset = ip - sym->start; - pr_debug3("%s: ip=%#Lx\n", __func__, he->map->unmap_ip(he->map, ip)); + pr_debug3("%s: ip=%#Lx\n", __func__, he->ms.map->unmap_ip(he->ms.map, ip)); if (offset >= sym_size) return 0; @@ -94,8 +94,8 @@ static int annotate__hist_hit(struct hist_entry *he, u64 ip) h->sum++; h->ip[offset]++; - pr_debug3("%#Lx %s: count++ [ip: %#Lx, %#Lx] => %Ld\n", he->sym->start, - he->sym->name, ip, ip - he->sym->start, h->ip[offset]); + pr_debug3("%#Lx %s: count++ [ip: %#Lx, %#Lx] => %Ld\n", he->ms.sym->start, + he->ms.sym->name, ip, ip - he->ms.sym->start, h->ip[offset]); return 0; } @@ -187,7 +187,7 @@ static struct objdump_line *objdump__get_next_ip_line(struct list_head *head, static int parse_line(FILE *file, struct hist_entry *he, struct list_head *head) { - struct symbol *sym = he->sym; + struct symbol *sym = he->ms.sym; struct objdump_line *objdump_line; char *line = NULL, *tmp, *tmp2; size_t line_len; @@ -226,7 +226,7 @@ static int parse_line(FILE *file, struct hist_entry *he, } if (line_ip != -1) { - u64 start = map__rip_2objdump(he->map, sym->start); + u64 start = map__rip_2objdump(he->ms.map, sym->start); offset = line_ip - start; } @@ -244,7 +244,7 @@ static int objdump_line__print(struct objdump_line *self, struct list_head *head, struct hist_entry *he, u64 len) { - struct symbol *sym = he->sym; + struct symbol *sym = he->ms.sym; static const char *prev_line; static const char *prev_color; @@ -327,7 +327,7 @@ static void insert_source_line(struct sym_ext *sym_ext) static void free_source_line(struct hist_entry *he, int len) { - struct sym_priv *priv = symbol__priv(he->sym); + struct sym_priv *priv = symbol__priv(he->ms.sym); struct sym_ext *sym_ext = priv->ext; int i; @@ -346,7 +346,7 @@ static void free_source_line(struct hist_entry *he, int len) static void get_source_line(struct hist_entry *he, int len, const char *filename) { - struct symbol *sym = he->sym; + struct symbol *sym = he->ms.sym; u64 start; int i; char cmd[PATH_MAX * 2]; @@ -361,7 +361,7 @@ get_source_line(struct hist_entry *he, int len, const char *filename) if (!priv->ext) return; - start = he->map->unmap_ip(he->map, sym->start); + start = he->ms.map->unmap_ip(he->ms.map, sym->start); for (i = 0; i < len; i++) { char *path = NULL; @@ -425,7 +425,7 @@ static void print_summary(const char *filename) static void hist_entry__print_hits(struct hist_entry *self) { - struct symbol *sym = self->sym; + struct symbol *sym = self->ms.sym; struct sym_priv *priv = symbol__priv(sym); struct sym_hist *h = priv->hist; u64 len = sym->end - sym->start, offset; @@ -439,9 +439,9 @@ static void hist_entry__print_hits(struct hist_entry *self) static void annotate_sym(struct hist_entry *he) { - struct map *map = he->map; + struct map *map = he->ms.map; struct dso *dso = map->dso; - struct symbol *sym = he->sym; + struct symbol *sym = he->ms.sym; const char *filename = dso->long_name, *d_filename; u64 len; char command[PATH_MAX*2]; @@ -526,17 +526,17 @@ static void perf_session__find_annotations(struct perf_session *self) struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node); struct sym_priv *priv; - if (he->sym == NULL) + if (he->ms.sym == NULL) continue; - priv = symbol__priv(he->sym); + priv = symbol__priv(he->ms.sym); if (priv->hist == NULL) continue; 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. + * he->ms.sym->hist to signal we already processed this symbol. */ free(priv->hist); priv->hist = NULL; diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 5843a9c572a..4eefb52a866 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -22,8 +22,10 @@ struct hist_entry *__perf_session__add_hist_entry(struct rb_root *hists, struct hist_entry *he; struct hist_entry entry = { .thread = al->thread, - .map = al->map, - .sym = al->sym, + .ms = { + .map = al->map, + .sym = al->sym, + }, .ip = al->addr, .level = al->level, .count = count, @@ -654,7 +656,7 @@ print_entries: if (symbol_conf.use_callchain) ret += hist_entry__fprintf_callchain(h, fp, session_total); - if (h->map == NULL && verbose > 1) { + if (h->ms.map == NULL && verbose > 1) { __map_groups__fprintf_maps(&h->thread->mg, MAP__FUNCTION, fp); fprintf(fp, "%.10s end\n", graph_dotted_line); diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index a3465a0ad70..25cd2e1f425 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -287,9 +287,9 @@ static size_t hist_entry__append_browser(struct hist_entry *self, indexes[0] = NEWT_ARG_APPEND; indexes[1] = NEWT_ARG_LAST; - newt_checkbox_tree__add(tree, s, self->sym, indexes); + newt_checkbox_tree__add(tree, s, self->ms.sym, indexes); } else - newtListboxAppendEntry(tree, s, self->sym); + newtListboxAppendEntry(tree, s, self->ms.sym); return strlen(s); } diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index cb0f327de9e..9b80c13cae4 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -131,8 +131,8 @@ sort__comm_print(FILE *fp, struct hist_entry *self, unsigned int width) int64_t sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) { - struct dso *dso_l = left->map ? left->map->dso : NULL; - struct dso *dso_r = right->map ? right->map->dso : NULL; + struct dso *dso_l = left->ms.map ? left->ms.map->dso : NULL; + struct dso *dso_r = right->ms.map ? right->ms.map->dso : NULL; const char *dso_name_l, *dso_name_r; if (!dso_l || !dso_r) @@ -152,9 +152,9 @@ sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) size_t sort__dso_print(FILE *fp, struct hist_entry *self, unsigned int width) { - if (self->map && self->map->dso) { - const char *dso_name = !verbose ? self->map->dso->short_name : - self->map->dso->long_name; + if (self->ms.map && self->ms.map->dso) { + const char *dso_name = !verbose ? self->ms.map->dso->short_name : + self->ms.map->dso->long_name; return repsep_fprintf(fp, "%-*s", width, dso_name); } @@ -168,11 +168,11 @@ sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) { u64 ip_l, ip_r; - if (left->sym == right->sym) + if (left->ms.sym == right->ms.sym) return 0; - ip_l = left->sym ? left->sym->start : left->ip; - ip_r = right->sym ? right->sym->start : right->ip; + ip_l = left->ms.sym ? left->ms.sym->start : left->ip; + ip_r = right->ms.sym ? right->ms.sym->start : right->ip; return (int64_t)(ip_r - ip_l); } @@ -184,13 +184,13 @@ sort__sym_print(FILE *fp, struct hist_entry *self, unsigned int width __used) size_t ret = 0; if (verbose) { - char o = self->map ? dso__symtab_origin(self->map->dso) : '!'; + char o = self->ms.map ? dso__symtab_origin(self->ms.map->dso) : '!'; ret += repsep_fprintf(fp, "%#018llx %c ", (u64)self->ip, o); } ret += repsep_fprintf(fp, "[%c] ", self->level); - if (self->sym) - ret += repsep_fprintf(fp, "%s", self->sym->name); + if (self->ms.sym) + ret += repsep_fprintf(fp, "%s", self->ms.sym->name); else ret += repsep_fprintf(fp, "%#016llx", (u64)self->ip); diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 753f9ea99fb..598568696f9 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -45,8 +45,7 @@ struct hist_entry { struct rb_node rb_node; u64 count; struct thread *thread; - struct map *map; - struct symbol *sym; + struct map_symbol ms; u64 ip; char level; struct symbol *parent; diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 0da2455d5b9..a4a894b8ea0 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -88,6 +88,11 @@ struct ref_reloc_sym { u64 unrelocated_addr; }; +struct map_symbol { + struct map *map; + struct symbol *sym; +}; + struct addr_location { struct thread *thread; struct map *map; -- cgit v1.2.3 From b3c9ac0846c654dea4df095999ee202e8b4cb253 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 24 Mar 2010 16:40:18 -0300 Subject: perf callchains: Store the map together with the symbol MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We need this to know where a symbol in a callchain came from, for various reasons, among them precise annotation from a TUI/GUI tool. Signed-off-by: Arnaldo Carvalho de Melo Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: <1269459619-982-5-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar --- tools/perf/builtin-report.c | 3 ++- tools/perf/util/callchain.c | 21 ++++++++++----------- tools/perf/util/callchain.h | 4 ++-- tools/perf/util/hist.c | 14 +++++++------- tools/perf/util/newt.c | 8 ++++---- tools/perf/util/session.c | 13 +++++++------ tools/perf/util/session.h | 8 ++++---- 7 files changed, 36 insertions(+), 35 deletions(-) (limited to 'tools/perf/util/newt.c') diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index d609afbd1a3..6ab16980dd6 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -81,7 +81,8 @@ static int perf_session__add_hist_entry(struct perf_session *self, struct addr_location *al, struct sample_data *data) { - struct symbol **syms = NULL, *parent = NULL; + struct map_symbol *syms = NULL; + struct symbol *parent = NULL; bool hit; int err; struct hist_entry *he; diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index 883844eb4b0..db628af6d20 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c @@ -185,8 +185,8 @@ create_child(struct callchain_node *parent, bool inherit_children) struct resolved_ip { - u64 ip; - struct symbol *sym; + u64 ip; + struct map_symbol ms; }; struct resolved_chain { @@ -212,7 +212,7 @@ fill_node(struct callchain_node *node, struct resolved_chain *chain, int start) return; } call->ip = chain->ips[i].ip; - call->sym = chain->ips[i].sym; + call->ms = chain->ips[i].ms; list_add_tail(&call->list, &node->val); } node->val_nr = chain->nr - start; @@ -318,10 +318,10 @@ __append_chain(struct callchain_node *root, struct resolved_chain *chain, if (i == chain->nr) break; - sym = chain->ips[i].sym; + sym = chain->ips[i].ms.sym; - if (cnode->sym && sym) { - if (cnode->sym->start != sym->start) + if (cnode->ms.sym && sym) { + if (cnode->ms.sym->start != sym->start) break; } else if (cnode->ip != chain->ips[i].ip) break; @@ -353,9 +353,8 @@ __append_chain(struct callchain_node *root, struct resolved_chain *chain, return 0; } -static void -filter_context(struct ip_callchain *old, struct resolved_chain *new, - struct symbol **syms) +static void filter_context(struct ip_callchain *old, struct resolved_chain *new, + struct map_symbol *syms) { int i, j = 0; @@ -364,7 +363,7 @@ filter_context(struct ip_callchain *old, struct resolved_chain *new, continue; new->ips[j].ip = old->ips[i]; - new->ips[j].sym = syms[i]; + new->ips[j].ms = syms[i]; j++; } @@ -373,7 +372,7 @@ filter_context(struct ip_callchain *old, struct resolved_chain *new, int append_chain(struct callchain_node *root, struct ip_callchain *chain, - struct symbol **syms) + struct map_symbol *syms) { struct resolved_chain *filtered; diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index bbd76da27f2..8a7e8bbd0fd 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h @@ -39,7 +39,7 @@ struct callchain_param { struct callchain_list { u64 ip; - struct symbol *sym; + struct map_symbol ms; struct list_head list; }; @@ -57,5 +57,5 @@ static inline u64 cumul_hits(struct callchain_node *node) int register_callchain_param(struct callchain_param *param); int append_chain(struct callchain_node *root, struct ip_callchain *chain, - struct symbol **syms); + struct map_symbol *syms); #endif /* __PERF_CALLCHAIN_H */ diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 4eefb52a866..09e09e78cb6 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -260,8 +260,8 @@ static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, } else ret += fprintf(fp, "%s", " "); } - if (chain->sym) - ret += fprintf(fp, "%s\n", chain->sym->name); + if (chain->ms.sym) + ret += fprintf(fp, "%s\n", chain->ms.sym->name); else ret += fprintf(fp, "%p\n", (void *)(long)chain->ip); @@ -280,7 +280,7 @@ static void init_rem_hits(void) } strcpy(rem_sq_bracket->name, "[...]"); - rem_hits.sym = rem_sq_bracket; + rem_hits.ms.sym = rem_sq_bracket; } static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, @@ -382,8 +382,8 @@ static size_t callchain__fprintf_graph(FILE *fp, struct callchain_node *self, } else ret += callchain__fprintf_left_margin(fp, left_margin); - if (chain->sym) - ret += fprintf(fp, " %s\n", chain->sym->name); + if (chain->ms.sym) + ret += fprintf(fp, " %s\n", chain->ms.sym->name); else ret += fprintf(fp, " %p\n", (void *)(long)chain->ip); } @@ -408,8 +408,8 @@ static size_t callchain__fprintf_flat(FILE *fp, struct callchain_node *self, list_for_each_entry(chain, &self->val, list) { if (chain->ip >= PERF_CONTEXT_MAX) continue; - if (chain->sym) - ret += fprintf(fp, " %s\n", chain->sym->name); + if (chain->ms.sym) + ret += fprintf(fp, " %s\n", chain->ms.sym->name); else ret += fprintf(fp, " %p\n", (void *)(long)chain->ip); diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 25cd2e1f425..f6953ca89bb 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -104,8 +104,8 @@ static void newt_checkbox_tree__add(newtComponent tree, const char *str, static char *callchain_list__sym_name(struct callchain_list *self, char *bf, size_t bfsize) { - if (self->sym) - return self->sym->name; + if (self->ms.sym) + return self->ms.sym->name; snprintf(bf, bfsize, "%#Lx", self->ip); return bf; @@ -157,7 +157,7 @@ static void __callchain__append_graph_browser(struct callchain_node *self, indexes[depth + 2] = NEWT_ARG_LAST; ++chain_idx; } - newt_checkbox_tree__add(tree, str, chain->sym, indexes); + newt_checkbox_tree__add(tree, str, chain->ms.sym, indexes); free(alloc_str); ++printed; } @@ -193,7 +193,7 @@ static void callchain__append_graph_browser(struct callchain_node *self, continue; str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); - newt_checkbox_tree__add(tree, str, chain->sym, indexes); + newt_checkbox_tree__add(tree, str, chain->ms.sym, indexes); } indexes[1] = parent_idx; diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index eed1cb88900..2cef3730cd9 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -117,13 +117,13 @@ static bool symbol__match_parent_regex(struct symbol *sym) return 0; } -struct symbol **perf_session__resolve_callchain(struct perf_session *self, - struct thread *thread, - struct ip_callchain *chain, - struct symbol **parent) +struct map_symbol *perf_session__resolve_callchain(struct perf_session *self, + struct thread *thread, + struct ip_callchain *chain, + struct symbol **parent) { u8 cpumode = PERF_RECORD_MISC_USER; - struct symbol **syms = NULL; + struct map_symbol *syms = NULL; unsigned int i; if (symbol_conf.use_callchain) { @@ -160,7 +160,8 @@ struct symbol **perf_session__resolve_callchain(struct perf_session *self, *parent = al.sym; if (!symbol_conf.use_callchain) break; - syms[i] = al.sym; + syms[i].map = al.map; + syms[i].sym = al.sym; } } diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 34d73395baa..631f8157fc1 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -57,10 +57,10 @@ int __perf_session__process_events(struct perf_session *self, int perf_session__process_events(struct perf_session *self, struct perf_event_ops *event_ops); -struct symbol **perf_session__resolve_callchain(struct perf_session *self, - struct thread *thread, - struct ip_callchain *chain, - struct symbol **parent); +struct map_symbol *perf_session__resolve_callchain(struct perf_session *self, + struct thread *thread, + struct ip_callchain *chain, + struct symbol **parent); bool perf_session__has_traces(struct perf_session *self, const char *msg); -- cgit v1.2.3 From d5679ae4d2d4369477dc3b60027cca222aef33e9 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 24 Mar 2010 16:40:19 -0300 Subject: perf report: Pass the DSO to 'perf annotate' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit So that we ensure that the symbol asked for annotation really is in the DSO we are interested in. Signed-off-by: Arnaldo Carvalho de Melo Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: <1269459619-982-6-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar --- tools/perf/util/newt.c | 51 +++++++++++++++++++++++++++----------------------- 1 file changed, 28 insertions(+), 23 deletions(-) (limited to 'tools/perf/util/newt.c') diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index f6953ca89bb..e99bcc8d193 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -157,7 +157,7 @@ static void __callchain__append_graph_browser(struct callchain_node *self, indexes[depth + 2] = NEWT_ARG_LAST; ++chain_idx; } - newt_checkbox_tree__add(tree, str, chain->ms.sym, indexes); + newt_checkbox_tree__add(tree, str, &chain->ms, indexes); free(alloc_str); ++printed; } @@ -193,7 +193,7 @@ static void callchain__append_graph_browser(struct callchain_node *self, continue; str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); - newt_checkbox_tree__add(tree, str, chain->ms.sym, indexes); + newt_checkbox_tree__add(tree, str, &chain->ms, indexes); } indexes[1] = parent_idx; @@ -287,14 +287,14 @@ static size_t hist_entry__append_browser(struct hist_entry *self, indexes[0] = NEWT_ARG_APPEND; indexes[1] = NEWT_ARG_LAST; - newt_checkbox_tree__add(tree, s, self->ms.sym, indexes); + newt_checkbox_tree__add(tree, s, &self->ms, indexes); } else - newtListboxAppendEntry(tree, s, self->ms.sym); + newtListboxAppendEntry(tree, s, &self->ms); return strlen(s); } -static void symbol__annotate_browser(const struct symbol *self) +static void map_symbol__annotate_browser(const struct map_symbol *self) { FILE *fp; int cols, rows; @@ -305,10 +305,11 @@ static void symbol__annotate_browser(const struct symbol *self) size_t max_usable_width; char *line = NULL; - if (self == NULL) + if (self->sym == NULL) return; - if (asprintf(&str, "perf annotate %s 2>&1 | expand", self->name) < 0) + if (asprintf(&str, "perf annotate -d \"%s\" %s 2>&1 | expand", + self->map->dso->name, self->sym->name) < 0) return; fp = popen(str, "r"); @@ -338,7 +339,7 @@ static void symbol__annotate_browser(const struct symbol *self) newtListboxSetWidth(tree, max_line_len); - newtCenteredWindow(max_line_len + 2, rows - 5, self->name); + newtCenteredWindow(max_line_len + 2, rows - 5, self->sym->name); form = newt_form__new(); newtFormAddComponents(form, tree, NULL); @@ -359,7 +360,7 @@ static const void *newt__symbol_tree_get_current(newtComponent self) static void perf_session__selection(newtComponent self, void *data) { - const struct symbol **symbol_ptr = data; + const struct map_symbol **symbol_ptr = data; *symbol_ptr = newt__symbol_tree_get_current(self); } @@ -376,7 +377,7 @@ void perf_session__browse_hists(struct rb_root *hists, u64 session_total, char str[1024]; newtComponent form, tree; struct newtExitStruct es; - const struct symbol *selection; + const struct map_symbol *selection; snprintf(str, sizeof(str), "Samples: %Ld", session_total); newtDrawRootText(0, 0, str); @@ -416,11 +417,8 @@ void perf_session__browse_hists(struct rb_root *hists, u64 session_total, int len = hist_entry__append_browser(h, tree, session_total); if (len > max_len) max_len = len; - if (symbol_conf.use_callchain) { + if (symbol_conf.use_callchain) hist_entry__append_callchain_browser(h, tree, session_total, idx++); - if (idx > 3300) - break; - } } if (max_len > cols) @@ -441,14 +439,12 @@ void perf_session__browse_hists(struct rb_root *hists, u64 session_total, while (1) { char annotate[512]; const char *options[2]; - int nr_options = 0, choice; + int nr_options = 0, choice = 0; newtFormRun(form, &es); if (es.reason == NEWT_EXIT_HOTKEY) { - if (toupper(es.u.key) == 'A') { - symbol__annotate_browser(selection); - continue; - } + if (toupper(es.u.key) == 'A') + goto do_annotate; if (es.u.key == NEWT_KEY_ESCAPE || toupper(es.u.key) == 'Q' || es.u.key == CTRL('c')) { @@ -459,9 +455,9 @@ void perf_session__browse_hists(struct rb_root *hists, u64 session_total, } } - if (selection != NULL) { + if (selection->sym != NULL) { snprintf(annotate, sizeof(annotate), - "Annotate %s", selection->name); + "Annotate %s", selection->sym->name); options[nr_options++] = annotate; } @@ -469,8 +465,17 @@ void perf_session__browse_hists(struct rb_root *hists, u64 session_total, choice = popup_menu(nr_options, options); if (choice == nr_options - 1) break; - else if (selection != NULL && choice >= 0) - symbol__annotate_browser(selection); +do_annotate: + if (selection->sym != NULL && choice >= 0) { + if (selection->map->dso->origin == DSO__ORIG_KERNEL) { + newtPopHelpLine(); + newtPushHelpLine("No vmlinux file found, can't " + "annotate with just a " + "kallsyms file"); + continue; + } + map_symbol__annotate_browser(selection); + } } newtFormDestroy(form); -- cgit v1.2.3 From 5f4d3f8816461300ce54505c9117bf85b3044aa0 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 26 Mar 2010 21:16:22 -0300 Subject: perf report: Add progress bars MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For when we are processing the events and inserting the entries in the browser. Experimentation here: naming "ui_something" we may be treading into creating a TUI/GUI set of routines that can then be implemented in terms of multiple backends. Also the time it takes for adding things to the "browser" takes, visually (I guess I should do some profiling here ;-) ), more time than for processing the events... That means we probably need to create a custom hist_entry browser, so that we reuse the structures we have in place instead of duplicating them in newt. But progress was made and at least we can see something while long files are being loaded, that must be one of UI 101 bullet points :-) Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-report.c | 7 +-- tools/perf/util/debug.h | 16 +++++++ tools/perf/util/hist.c | 5 ++- tools/perf/util/hist.h | 2 +- tools/perf/util/newt.c | 103 ++++++++++++++++++++++++++++++++++---------- tools/perf/util/session.c | 6 +++ tools/perf/util/session.h | 12 ++++-- 7 files changed, 119 insertions(+), 32 deletions(-) (limited to 'tools/perf/util/newt.c') diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 6ab16980dd6..381918515a5 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -303,13 +303,14 @@ static int __cmd_report(void) next = rb_first(&session->stats_by_id); while (next) { struct event_stat_id *stats; + u64 nr_hists; stats = rb_entry(next, struct event_stat_id, rb_node); perf_session__collapse_resort(&stats->hists); - perf_session__output_resort(&stats->hists, stats->stats.total); - + nr_hists = perf_session__output_resort(&stats->hists, + stats->stats.total); if (use_browser) - perf_session__browse_hists(&stats->hists, + perf_session__browse_hists(&stats->hists, nr_hists, stats->stats.total, help); else { if (rb_first(&session->stats_by_id) == diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h index 0172edf3f15..5cb0a1b1401 100644 --- a/tools/perf/util/debug.h +++ b/tools/perf/util/debug.h @@ -10,13 +10,29 @@ extern int dump_trace; int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); void trace_event(event_t *event); +struct ui_progress; + #ifdef NO_NEWT_SUPPORT static inline int browser__show_help(const char *format __used, va_list ap __used) { return 0; } + +static inline struct ui_progress *ui_progress__new(const char *title __used, + u64 total __used) +{ + return (struct ui_progress *)1; +} + +static inline void ui_progress__update(struct ui_progress *self __used, + u64 curr __used) {} + +static inline void ui_progress__delete(struct ui_progress *self __used) {} #else int browser__show_help(const char *format, va_list ap); +struct ui_progress *ui_progress__new(const char *title, u64 total); +void ui_progress__update(struct ui_progress *self, u64 curr); +void ui_progress__delete(struct ui_progress *self); #endif #endif /* __PERF_DEBUG_H */ diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index de3190102cc..a46d0933246 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -185,12 +185,13 @@ static void perf_session__insert_output_hist_entry(struct rb_root *root, rb_insert_color(&he->rb_node, root); } -void perf_session__output_resort(struct rb_root *hists, u64 total_samples) +u64 perf_session__output_resort(struct rb_root *hists, u64 total_samples) { struct rb_root tmp; struct rb_node *next; struct hist_entry *n; u64 min_callchain_hits; + u64 nr_hists = 0; min_callchain_hits = total_samples * (callchain_param.min_percent / 100); @@ -205,9 +206,11 @@ void perf_session__output_resort(struct rb_root *hists, u64 total_samples) rb_erase(&n->rb_node, hists); perf_session__insert_output_hist_entry(&tmp, n, min_callchain_hits); + ++nr_hists; } *hists = tmp; + return nr_hists; } static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin) diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index fe366ce5db4..da6a8c1320f 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -25,7 +25,7 @@ size_t hist_entry__fprintf(struct hist_entry *self, u64 session_total); void hist_entry__free(struct hist_entry *); -void perf_session__output_resort(struct rb_root *hists, u64 total_samples); +u64 perf_session__output_resort(struct rb_root *hists, u64 total_samples); void perf_session__collapse_resort(struct rb_root *hists); size_t perf_session__fprintf_hists(struct rb_root *hists, struct perf_session *pair, diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index e99bcc8d193..b0210ae5b93 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -12,6 +12,72 @@ #include "sort.h" #include "symbol.h" +struct ui_progress { + newtComponent form, scale; +}; + +struct ui_progress *ui_progress__new(const char *title, u64 total) +{ + struct ui_progress *self = malloc(sizeof(*self)); + + if (self != NULL) { + int cols; + newtGetScreenSize(&cols, NULL); + cols -= 4; + newtCenteredWindow(cols, 1, title); + self->form = newtForm(NULL, NULL, 0); + if (self->form == NULL) + goto out_free_self; + self->scale = newtScale(0, 0, cols, total); + if (self->scale == NULL) + goto out_free_form; + newtFormAddComponents(self->form, self->scale, NULL); + newtRefresh(); + } + + return self; + +out_free_form: + newtFormDestroy(self->form); +out_free_self: + free(self); + return NULL; +} + +void ui_progress__update(struct ui_progress *self, u64 curr) +{ + newtScaleSet(self->scale, curr); + newtRefresh(); +} + +void ui_progress__delete(struct ui_progress *self) +{ + newtFormDestroy(self->form); + newtPopWindow(); + free(self); +} + +static char browser__last_msg[1024]; + +int browser__show_help(const char *format, va_list ap) +{ + int ret; + static int backlog; + + ret = vsnprintf(browser__last_msg + backlog, + sizeof(browser__last_msg) - backlog, format, ap); + backlog += ret; + + if (browser__last_msg[backlog - 1] == '\n') { + newtPopHelpLine(); + newtPushHelpLine(browser__last_msg); + newtRefresh(); + backlog = 0; + } + + return ret; +} + static void newt_form__set_exit_keys(newtComponent self) { newtFormAddHotKey(self, NEWT_KEY_ESCAPE); @@ -364,8 +430,8 @@ static void perf_session__selection(newtComponent self, void *data) *symbol_ptr = newt__symbol_tree_get_current(self); } -void perf_session__browse_hists(struct rb_root *hists, u64 session_total, - const char *helpline) +int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, + u64 session_total, const char *helpline) { struct sort_entry *se; struct rb_node *nd; @@ -378,6 +444,12 @@ void perf_session__browse_hists(struct rb_root *hists, u64 session_total, newtComponent form, tree; struct newtExitStruct es; const struct map_symbol *selection; + u64 curr_hist = 0; + struct ui_progress *progress; + + progress = ui_progress__new("Adding entries to the browser...", nr_hists); + if (progress == NULL) + return -1; snprintf(str, sizeof(str), "Samples: %Ld", session_total); newtDrawRootText(0, 0, str); @@ -419,8 +491,13 @@ void perf_session__browse_hists(struct rb_root *hists, u64 session_total, max_len = len; if (symbol_conf.use_callchain) hist_entry__append_callchain_browser(h, tree, session_total, idx++); + ++curr_hist; + if (curr_hist % 5) + ui_progress__update(progress, curr_hist); } + ui_progress__delete(progress); + if (max_len > cols) max_len = cols - 3; @@ -480,27 +557,7 @@ do_annotate: newtFormDestroy(form); newtPopWindow(); -} - -static char browser__last_msg[1024]; - -int browser__show_help(const char *format, va_list ap) -{ - int ret; - static int backlog; - - ret = vsnprintf(browser__last_msg + backlog, - sizeof(browser__last_msg) - backlog, format, ap); - backlog += ret; - - if (browser__last_msg[backlog - 1] == '\n') { - newtPopHelpLine(); - newtPushHelpLine(browser__last_msg); - newtRefresh(); - backlog = 0; - } - - return ret; + return 0; } void setup_browser(void) diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 76b4ac689df..32765cdca05 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -397,6 +397,10 @@ int __perf_session__process_events(struct perf_session *self, event_t *event; uint32_t size; char *buf; + struct ui_progress *progress = ui_progress__new("Processing events...", + self->size); + if (progress == NULL) + return -1; perf_event_ops__fill_defaults(ops); @@ -425,6 +429,7 @@ remap: more: event = (event_t *)(buf + head); + ui_progress__update(progress, offset); if (self->header.needs_swap) perf_event_header__bswap(&event->header); @@ -475,6 +480,7 @@ more: done: err = 0; out_err: + ui_progress__delete(progress); return err; } diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 631f8157fc1..6a15daeda57 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -88,11 +88,15 @@ static inline struct map * } #ifdef NO_NEWT_SUPPORT -static inline void perf_session__browse_hists(struct rb_root *hists __used, +static inline int perf_session__browse_hists(struct rb_root *hists __used, + u64 nr_hists __used, u64 session_total __used, - const char *helpline __used) {} + const char *helpline __used) +{ + return 0; +} #else -void perf_session__browse_hists(struct rb_root *hists, u64 session_total, - const char *helpline); +int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, + u64 session_total, const char *helpline); #endif #endif /* __PERF_SESSION_H */ -- cgit v1.2.3 From a4e3b956a820162b7c1d616117b4f23b6017f504 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 31 Mar 2010 11:33:40 -0300 Subject: perf hist: Replace ->print() routines by ->snprintf() equivalents MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Then hist_entry__fprintf will just us the newly introduced hist_entry__snprintf, add the newline and fprintf it to the supplied FILE descriptor. This allows us to remove the use_browser checking in the color_printf routines, that now got color_snprintf variants too. The newt TUI browser (and other GUIs that may come in the future) don't have to worry about stdio specific stuff in the strings they get from the se->snprintf routines and instead use whatever means to do the equivalent. Also the newt TUI browser don't have to use the fmemopen() hack, instead it can use the se->snprintf routines directly. For now tho use the hist_entry__snprintf routine to reduce the patch size. Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/color.c | 53 ++++++++++++++++++++++++++--- tools/perf/util/color.h | 4 +++ tools/perf/util/hist.c | 54 ++++++++++++++++++++---------- tools/perf/util/hist.h | 7 +++- tools/perf/util/newt.c | 53 +++-------------------------- tools/perf/util/sort.c | 88 ++++++++++++++++++++++++++----------------------- tools/perf/util/sort.h | 4 +-- 7 files changed, 149 insertions(+), 114 deletions(-) (limited to 'tools/perf/util/newt.c') diff --git a/tools/perf/util/color.c b/tools/perf/util/color.c index 9da01914e0a..e191eb9a667 100644 --- a/tools/perf/util/color.c +++ b/tools/perf/util/color.c @@ -166,6 +166,31 @@ int perf_color_default_config(const char *var, const char *value, void *cb) return perf_default_config(var, value, cb); } +static int __color_vsnprintf(char *bf, size_t size, const char *color, + const char *fmt, va_list args, const char *trail) +{ + int r = 0; + + /* + * Auto-detect: + */ + if (perf_use_color_default < 0) { + if (isatty(1) || pager_in_use()) + perf_use_color_default = 1; + else + perf_use_color_default = 0; + } + + if (perf_use_color_default && *color) + r += snprintf(bf, size, "%s", color); + r += vsnprintf(bf + r, size - r, fmt, args); + if (perf_use_color_default && *color) + r += snprintf(bf + r, size - r, "%s", PERF_COLOR_RESET); + if (trail) + r += snprintf(bf + r, size - r, "%s", trail); + return r; +} + static int __color_vfprintf(FILE *fp, const char *color, const char *fmt, va_list args, const char *trail) { @@ -191,11 +216,28 @@ static int __color_vfprintf(FILE *fp, const char *color, const char *fmt, return r; } +int color_vsnprintf(char *bf, size_t size, const char *color, + const char *fmt, va_list args) +{ + return __color_vsnprintf(bf, size, color, fmt, args, NULL); +} + int color_vfprintf(FILE *fp, const char *color, const char *fmt, va_list args) { return __color_vfprintf(fp, color, fmt, args, NULL); } +int color_snprintf(char *bf, size_t size, const char *color, + const char *fmt, ...) +{ + va_list args; + int r; + + va_start(args, fmt); + r = color_vsnprintf(bf, size, color, fmt, args); + va_end(args); + return r; +} int color_fprintf(FILE *fp, const char *color, const char *fmt, ...) { @@ -203,10 +245,7 @@ int color_fprintf(FILE *fp, const char *color, const char *fmt, ...) int r; va_start(args, fmt); - if (use_browser) - r = vfprintf(fp, fmt, args); - else - r = color_vfprintf(fp, color, fmt, args); + r = color_vfprintf(fp, color, fmt, args); va_end(args); return r; } @@ -277,3 +316,9 @@ int percent_color_fprintf(FILE *fp, const char *fmt, double percent) return r; } + +int percent_color_snprintf(char *bf, size_t size, const char *fmt, double percent) +{ + const char *color = get_percent_color(percent); + return color_snprintf(bf, size, color, fmt, percent); +} diff --git a/tools/perf/util/color.h b/tools/perf/util/color.h index 24e8809210b..dea082b7960 100644 --- a/tools/perf/util/color.h +++ b/tools/perf/util/color.h @@ -32,10 +32,14 @@ int perf_color_default_config(const char *var, const char *value, void *cb); int perf_config_colorbool(const char *var, const char *value, int stdout_is_tty); void color_parse(const char *value, const char *var, char *dst); void color_parse_mem(const char *value, int len, const char *var, char *dst); +int color_vsnprintf(char *bf, size_t size, const char *color, + const char *fmt, va_list args); int color_vfprintf(FILE *fp, const char *color, const char *fmt, va_list args); int color_fprintf(FILE *fp, const char *color, const char *fmt, ...); +int color_snprintf(char *bf, size_t size, const char *color, const char *fmt, ...); int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...); int color_fwrite_lines(FILE *fp, const char *color, size_t count, const char *buf); +int percent_color_snprintf(char *bf, size_t size, const char *fmt, double percent); int percent_color_fprintf(FILE *fp, const char *fmt, double percent); const char *get_percent_color(double percent); diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index a46d0933246..f0794913d57 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -455,16 +455,17 @@ static size_t hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self, return ret; } -size_t hist_entry__fprintf(struct hist_entry *self, +int hist_entry__snprintf(struct hist_entry *self, + char *s, size_t size, struct perf_session *pair_session, bool show_displacement, - long displacement, FILE *fp, + long displacement, bool color, u64 session_total) { struct sort_entry *se; u64 count, total; const char *sep = symbol_conf.field_sep; - size_t ret; + int ret; if (symbol_conf.exclude_other && !self->parent) return 0; @@ -477,17 +478,22 @@ size_t hist_entry__fprintf(struct hist_entry *self, total = session_total; } - if (total) - ret = percent_color_fprintf(fp, sep ? "%.2f" : " %6.2f%%", - (count * 100.0) / total); - else - ret = fprintf(fp, sep ? "%lld" : "%12lld ", count); + if (total) { + if (color) + ret = percent_color_snprintf(s, size, + sep ? "%.2f" : " %6.2f%%", + (count * 100.0) / total); + else + ret = snprintf(s, size, sep ? "%.2f" : " %6.2f%%", + (count * 100.0) / total); + } else + ret = snprintf(s, size, sep ? "%lld" : "%12lld ", count); if (symbol_conf.show_nr_samples) { if (sep) - ret += fprintf(fp, "%c%lld", *sep, count); + ret += snprintf(s + ret, size - ret, "%c%lld", *sep, count); else - ret += fprintf(fp, "%11lld", count); + ret += snprintf(s + ret, size - ret, "%11lld", count); } if (pair_session) { @@ -507,9 +513,9 @@ size_t hist_entry__fprintf(struct hist_entry *self, snprintf(bf, sizeof(bf), " "); if (sep) - ret += fprintf(fp, "%c%s", *sep, bf); + ret += snprintf(s + ret, size - ret, "%c%s", *sep, bf); else - ret += fprintf(fp, "%11.11s", bf); + ret += snprintf(s + ret, size - ret, "%11.11s", bf); if (show_displacement) { if (displacement) @@ -518,9 +524,9 @@ size_t hist_entry__fprintf(struct hist_entry *self, snprintf(bf, sizeof(bf), " "); if (sep) - ret += fprintf(fp, "%c%s", *sep, bf); + ret += snprintf(s + ret, size - ret, "%c%s", *sep, bf); else - ret += fprintf(fp, "%6.6s", bf); + ret += snprintf(s + ret, size - ret, "%6.6s", bf); } } @@ -528,11 +534,25 @@ size_t hist_entry__fprintf(struct hist_entry *self, if (se->elide) continue; - ret += fprintf(fp, "%s", sep ?: " "); - ret += se->print(fp, self, se->width ? *se->width : 0); + ret += snprintf(s + ret, size - ret, "%s", sep ?: " "); + ret += se->snprintf(self, s + ret, size - ret, + se->width ? *se->width : 0); } - return ret + fprintf(fp, "\n"); + return ret; +} + +int hist_entry__fprintf(struct hist_entry *self, + struct perf_session *pair_session, + bool show_displacement, + long displacement, FILE *fp, + u64 session_total) +{ + char bf[512]; + hist_entry__snprintf(self, bf, sizeof(bf), pair_session, + show_displacement, displacement, + true, session_total); + return fprintf(fp, "%s\n", bf); } static size_t hist_entry__fprintf_callchain(struct hist_entry *self, FILE *fp, diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index da6a8c1320f..ad17f0ad798 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -18,11 +18,16 @@ struct hist_entry *__perf_session__add_hist_entry(struct rb_root *hists, u64 count, bool *hit); extern int64_t hist_entry__cmp(struct hist_entry *, struct hist_entry *); extern int64_t hist_entry__collapse(struct hist_entry *, struct hist_entry *); -size_t hist_entry__fprintf(struct hist_entry *self, +int hist_entry__fprintf(struct hist_entry *self, struct perf_session *pair_session, bool show_displacement, long displacement, FILE *fp, u64 session_total); +int hist_entry__snprintf(struct hist_entry *self, + char *bf, size_t size, + struct perf_session *pair_session, + bool show_displacement, long displacement, + bool color, u64 session_total); void hist_entry__free(struct hist_entry *); u64 perf_session__output_resort(struct rb_root *hists, u64 total_samples); diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index b0210ae5b93..edd628f5337 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -294,60 +294,17 @@ static void hist_entry__append_callchain_browser(struct hist_entry *self, } } -/* - * FIXME: get lib/string.c linked with perf somehow - */ -static char *skip_spaces(const char *str) -{ - while (isspace(*str)) - ++str; - return (char *)str; -} - -static char *strim(char *s) -{ - size_t size; - char *end; - - s = skip_spaces(s); - size = strlen(s); - if (!size) - return s; - - end = s + size - 1; - while (end >= s && isspace(*end)) - end--; - *(end + 1) = '\0'; - - return s; -} - static size_t hist_entry__append_browser(struct hist_entry *self, newtComponent tree, u64 total) { - char bf[1024], *s; - FILE *fp; + char s[256]; + size_t ret; if (symbol_conf.exclude_other && !self->parent) return 0; - fp = fmemopen(bf, sizeof(bf), "w"); - if (fp == NULL) - return 0; - - hist_entry__fprintf(self, NULL, false, 0, fp, total); - fclose(fp); - - /* - * FIXME: We shouldn't need to trim, as the printing routines shouldn't - * add spaces it in the first place, the stdio output routines should - * call a __snprintf method instead of the current __print (that - * actually is a __fprintf) one, but get the raw string and _then_ add - * the newline, as this is a detail of stdio printing, not needed in - * other UIs, e.g. newt. - */ - s = strim(bf); - + ret = hist_entry__snprintf(self, s, sizeof(s), NULL, + false, 0, false, total); if (symbol_conf.use_callchain) { int indexes[2]; @@ -357,7 +314,7 @@ static size_t hist_entry__append_browser(struct hist_entry *self, } else newtListboxAppendEntry(tree, s, &self->ms); - return strlen(s); + return ret; } static void map_symbol__annotate_browser(const struct map_symbol *self) diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 9b80c13cae4..31329a1cd32 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -18,10 +18,21 @@ char * field_sep; LIST_HEAD(hist_entry__sort_list); +static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf, + size_t size, unsigned int width); +static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf, + size_t size, unsigned int width); +static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf, + size_t size, unsigned int width); +static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf, + size_t size, unsigned int width); +static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf, + size_t size, unsigned int width); + struct sort_entry sort_thread = { .header = "Command: Pid", .cmp = sort__thread_cmp, - .print = sort__thread_print, + .snprintf = hist_entry__thread_snprintf, .width = &threads__col_width, }; @@ -29,27 +40,27 @@ struct sort_entry sort_comm = { .header = "Command", .cmp = sort__comm_cmp, .collapse = sort__comm_collapse, - .print = sort__comm_print, + .snprintf = hist_entry__comm_snprintf, .width = &comms__col_width, }; struct sort_entry sort_dso = { .header = "Shared Object", .cmp = sort__dso_cmp, - .print = sort__dso_print, + .snprintf = hist_entry__dso_snprintf, .width = &dsos__col_width, }; struct sort_entry sort_sym = { .header = "Symbol", .cmp = sort__sym_cmp, - .print = sort__sym_print, + .snprintf = hist_entry__sym_snprintf, }; struct sort_entry sort_parent = { .header = "Parent symbol", .cmp = sort__parent_cmp, - .print = sort__parent_print, + .snprintf = hist_entry__parent_snprintf, .width = &parent_symbol__col_width, }; @@ -85,45 +96,38 @@ sort__thread_cmp(struct hist_entry *left, struct hist_entry *right) return right->thread->pid - left->thread->pid; } -int repsep_fprintf(FILE *fp, const char *fmt, ...) +static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...) { int n; va_list ap; va_start(ap, fmt); - if (!field_sep) - n = vfprintf(fp, fmt, ap); - else { - char *bf = NULL; - n = vasprintf(&bf, fmt, ap); - if (n > 0) { - char *sep = bf; - - while (1) { - sep = strchr(sep, *field_sep); - if (sep == NULL) - break; - *sep = '.'; - } + n = vsnprintf(bf, size, fmt, ap); + if (field_sep && n > 0) { + char *sep = bf; + + while (1) { + sep = strchr(sep, *field_sep); + if (sep == NULL) + break; + *sep = '.'; } - fputs(bf, fp); - free(bf); } va_end(ap); return n; } -size_t -sort__thread_print(FILE *fp, struct hist_entry *self, unsigned int width) +static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf, + size_t size, unsigned int width) { - return repsep_fprintf(fp, "%*s:%5d", width - 6, + return repsep_snprintf(bf, size, "%*s:%5d", width, self->thread->comm ?: "", self->thread->pid); } -size_t -sort__comm_print(FILE *fp, struct hist_entry *self, unsigned int width) +static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf, + size_t size, unsigned int width) { - return repsep_fprintf(fp, "%*s", width, self->thread->comm); + return repsep_snprintf(bf, size, "%*s", width, self->thread->comm); } /* --sort dso */ @@ -149,16 +153,16 @@ sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) return strcmp(dso_name_l, dso_name_r); } -size_t -sort__dso_print(FILE *fp, struct hist_entry *self, unsigned int width) +static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf, + size_t size, unsigned int width) { if (self->ms.map && self->ms.map->dso) { const char *dso_name = !verbose ? self->ms.map->dso->short_name : self->ms.map->dso->long_name; - return repsep_fprintf(fp, "%-*s", width, dso_name); + return repsep_snprintf(bf, size, "%-*s", width, dso_name); } - return repsep_fprintf(fp, "%*llx", width, (u64)self->ip); + return repsep_snprintf(bf, size, "%*Lx", width, self->ip); } /* --sort symbol */ @@ -177,22 +181,22 @@ sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) return (int64_t)(ip_r - ip_l); } - -size_t -sort__sym_print(FILE *fp, struct hist_entry *self, unsigned int width __used) +static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf, + size_t size, unsigned int width __used) { size_t ret = 0; if (verbose) { char o = self->ms.map ? dso__symtab_origin(self->ms.map->dso) : '!'; - ret += repsep_fprintf(fp, "%#018llx %c ", (u64)self->ip, o); + ret += repsep_snprintf(bf, size, "%#018llx %c ", self->ip, o); } - ret += repsep_fprintf(fp, "[%c] ", self->level); + ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", self->level); if (self->ms.sym) - ret += repsep_fprintf(fp, "%s", self->ms.sym->name); + ret += repsep_snprintf(bf + ret, size - ret, "%s", + self->ms.sym->name); else - ret += repsep_fprintf(fp, "%#016llx", (u64)self->ip); + ret += repsep_snprintf(bf + ret, size - ret, "%#016llx", self->ip); return ret; } @@ -231,10 +235,10 @@ sort__parent_cmp(struct hist_entry *left, struct hist_entry *right) return strcmp(sym_l->name, sym_r->name); } -size_t -sort__parent_print(FILE *fp, struct hist_entry *self, unsigned int width) +static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf, + size_t size, unsigned int width) { - return repsep_fprintf(fp, "%-*s", width, + return repsep_snprintf(bf, size, "%-*s", width, self->parent ? self->parent->name : "[other]"); } diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 598568696f9..439ec5fa0f5 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -76,7 +76,8 @@ struct sort_entry { int64_t (*cmp)(struct hist_entry *, struct hist_entry *); int64_t (*collapse)(struct hist_entry *, struct hist_entry *); - size_t (*print)(FILE *fp, struct hist_entry *, unsigned int width); + int (*snprintf)(struct hist_entry *self, char *bf, size_t size, + unsigned int width); unsigned int *width; bool elide; }; @@ -86,7 +87,6 @@ extern struct list_head hist_entry__sort_list; void setup_sorting(const char * const usagestr[], const struct option *opts); -extern int repsep_fprintf(FILE *fp, const char *fmt, ...); extern size_t sort__thread_print(FILE *, struct hist_entry *, unsigned int); extern size_t sort__comm_print(FILE *, struct hist_entry *, unsigned int); extern size_t sort__dso_print(FILE *, struct hist_entry *, unsigned int); -- cgit v1.2.3 From fb6b893180faec03e1d32149ef5cc412df9714df Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 3 Apr 2010 11:04:55 -0300 Subject: perf newt: Remove useless column width calculation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Not used in the TUI interface. Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/newt.c | 20 -------------------- 1 file changed, 20 deletions(-) (limited to 'tools/perf/util/newt.c') diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index edd628f5337..a2ba3fffe04 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -390,11 +390,8 @@ static void perf_session__selection(newtComponent self, void *data) int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, u64 session_total, const char *helpline) { - struct sort_entry *se; struct rb_node *nd; char seq[] = "."; - unsigned int width; - char *col_width = symbol_conf.col_width_list_str; int rows, cols, idx; int max_len = 0; char str[1024]; @@ -423,23 +420,6 @@ int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, newtComponentAddCallback(tree, perf_session__selection, &selection); - list_for_each_entry(se, &hist_entry__sort_list, list) { - if (se->elide) - continue; - width = strlen(se->header); - if (se->width) { - if (symbol_conf.col_width_list_str) { - if (col_width) { - *se->width = atoi(col_width); - col_width = strchr(col_width, ','); - if (col_width) - ++col_width; - } - } - *se->width = max(*se->width, width); - } - } - idx = 0; for (nd = rb_first(hists); nd; nd = rb_next(nd)) { struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); -- cgit v1.2.3 From e65713ea1e61e92d28284a55df2aa039ebe10003 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 3 Apr 2010 11:25:56 -0300 Subject: perf newt: Move the hist browser population bits to separare function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Next patches will use that when applying filtes to then repopulate the browser with the narrowed vision. Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/newt.c | 129 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 84 insertions(+), 45 deletions(-) (limited to 'tools/perf/util/newt.c') diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index a2ba3fffe04..509d921532e 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -381,53 +381,69 @@ static const void *newt__symbol_tree_get_current(newtComponent self) return newtListboxGetCurrent(self); } -static void perf_session__selection(newtComponent self, void *data) +static void hist_browser__selection(newtComponent self, void *data) { const struct map_symbol **symbol_ptr = data; *symbol_ptr = newt__symbol_tree_get_current(self); } -int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, - u64 session_total, const char *helpline) +struct hist_browser { + newtComponent form, tree; + const struct map_symbol *selection; +}; + +static struct hist_browser *hist_browser__new(void) +{ + struct hist_browser *self = malloc(sizeof(*self)); + + if (self != NULL) { + char seq[] = "."; + int rows; + + newtGetScreenSize(NULL, &rows); + + if (symbol_conf.use_callchain) + self->tree = newtCheckboxTreeMulti(0, 0, rows - 5, seq, + NEWT_FLAG_SCROLL); + else + self->tree = newtListbox(0, 0, rows - 5, + (NEWT_FLAG_SCROLL | + NEWT_FLAG_RETURNEXIT)); + newtComponentAddCallback(self->tree, hist_browser__selection, + &self->selection); + } + + return self; +} + +static void hist_browser__delete(struct hist_browser *self) +{ + newtFormDestroy(self->form); + newtPopWindow(); + free(self); +} + +static int hist_browser__populate(struct hist_browser *self, struct rb_root *hists, + u64 nr_hists, u64 session_total) { + int max_len = 0, idx, cols, rows; + struct ui_progress *progress; struct rb_node *nd; - char seq[] = "."; - int rows, cols, idx; - int max_len = 0; - char str[1024]; - newtComponent form, tree; - struct newtExitStruct es; - const struct map_symbol *selection; u64 curr_hist = 0; - struct ui_progress *progress; progress = ui_progress__new("Adding entries to the browser...", nr_hists); if (progress == NULL) return -1; - snprintf(str, sizeof(str), "Samples: %Ld", session_total); - newtDrawRootText(0, 0, str); - newtPushHelpLine(helpline); - - newtGetScreenSize(&cols, &rows); - - if (symbol_conf.use_callchain) - tree = newtCheckboxTreeMulti(0, 0, rows - 5, seq, - NEWT_FLAG_SCROLL); - else - tree = newtListbox(0, 0, rows - 5, (NEWT_FLAG_SCROLL | - NEWT_FLAG_RETURNEXIT)); - - newtComponentAddCallback(tree, perf_session__selection, &selection); - idx = 0; for (nd = rb_first(hists); nd; nd = rb_next(nd)) { struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); - int len = hist_entry__append_browser(h, tree, session_total); + int len = hist_entry__append_browser(h, self->tree, session_total); if (len > max_len) max_len = len; if (symbol_conf.use_callchain) - hist_entry__append_callchain_browser(h, tree, session_total, idx++); + hist_entry__append_callchain_browser(h, self->tree, + session_total, idx++); ++curr_hist; if (curr_hist % 5) ui_progress__update(progress, curr_hist); @@ -435,27 +451,50 @@ int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, ui_progress__delete(progress); + newtGetScreenSize(&cols, &rows); + if (max_len > cols) max_len = cols - 3; if (!symbol_conf.use_callchain) - newtListboxSetWidth(tree, max_len); + newtListboxSetWidth(self->tree, max_len); newtCenteredWindow(max_len + (symbol_conf.use_callchain ? 5 : 0), rows - 5, "Report"); - form = newt_form__new(); - newtFormAddHotKey(form, 'A'); - newtFormAddHotKey(form, 'a'); - newtFormAddHotKey(form, NEWT_KEY_RIGHT); - newtFormAddComponents(form, tree, NULL); - selection = newt__symbol_tree_get_current(tree); + self->form = newt_form__new(); + newtFormAddHotKey(self->form, 'A'); + newtFormAddHotKey(self->form, 'a'); + newtFormAddHotKey(self->form, NEWT_KEY_RIGHT); + newtFormAddComponents(self->form, self->tree, NULL); + self->selection = newt__symbol_tree_get_current(self->tree); + + return 0; +} + +int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, + u64 session_total, const char *helpline) +{ + struct newtExitStruct es; + char str[1024]; + int err = -1; + struct hist_browser *browser = hist_browser__new(); + + if (browser == NULL) + return -1; + + snprintf(str, sizeof(str), "Samples: %Ld", session_total); + newtDrawRootText(0, 0, str); + newtPushHelpLine(helpline); + + if (hist_browser__populate(browser, hists, nr_hists, session_total) < 0) + goto out; while (1) { char annotate[512]; const char *options[2]; int nr_options = 0, choice = 0; - newtFormRun(form, &es); + newtFormRun(browser->form, &es); if (es.reason == NEWT_EXIT_HOTKEY) { if (toupper(es.u.key) == 'A') goto do_annotate; @@ -469,9 +508,9 @@ int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, } } - if (selection->sym != NULL) { + if (browser->selection->sym != NULL) { snprintf(annotate, sizeof(annotate), - "Annotate %s", selection->sym->name); + "Annotate %s", browser->selection->sym->name); options[nr_options++] = annotate; } @@ -480,21 +519,21 @@ int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, if (choice == nr_options - 1) break; do_annotate: - if (selection->sym != NULL && choice >= 0) { - if (selection->map->dso->origin == DSO__ORIG_KERNEL) { + if (browser->selection->sym != NULL && choice >= 0) { + if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) { newtPopHelpLine(); newtPushHelpLine("No vmlinux file found, can't " "annotate with just a " "kallsyms file"); continue; } - map_symbol__annotate_browser(selection); + map_symbol__annotate_browser(browser->selection); } } - - newtFormDestroy(form); - newtPopWindow(); - return 0; + err = 0; +out: + hist_browser__delete(browser); + return err; } void setup_browser(void) -- cgit v1.2.3 From 533c46c31c0e82f19dbb087c77d85eaccd6fefdb Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 3 Apr 2010 11:54:35 -0300 Subject: perf newt: Pass the input_name to perf_session__browse_hists MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit So that it can use it in the 'perf annotate' command line, otherwise it'll use the default and not the specified -i filename passed to 'perf report'. Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-report.c | 3 ++- tools/perf/util/newt.c | 13 ++++++++----- tools/perf/util/session.h | 6 ++++-- 3 files changed, 14 insertions(+), 8 deletions(-) (limited to 'tools/perf/util/newt.c') diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 6615e09e336..e93c69a8e72 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -313,7 +313,8 @@ static int __cmd_report(void) stats->stats.total); if (use_browser) perf_session__browse_hists(&stats->hists, nr_hists, - stats->stats.total, help); + stats->stats.total, help, + input_name); else { if (rb_first(&session->stats_by_id) == rb_last(&session->stats_by_id)) diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 509d921532e..c93bc2a2d13 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -317,7 +317,8 @@ static size_t hist_entry__append_browser(struct hist_entry *self, return ret; } -static void map_symbol__annotate_browser(const struct map_symbol *self) +static void map_symbol__annotate_browser(const struct map_symbol *self, + const char *input_name) { FILE *fp; int cols, rows; @@ -331,8 +332,8 @@ static void map_symbol__annotate_browser(const struct map_symbol *self) if (self->sym == NULL) return; - if (asprintf(&str, "perf annotate -d \"%s\" %s 2>&1 | expand", - self->map->dso->name, self->sym->name) < 0) + if (asprintf(&str, "perf annotate -i \"%s\" -d \"%s\" %s 2>&1 | expand", + input_name, self->map->dso->name, self->sym->name) < 0) return; fp = popen(str, "r"); @@ -472,7 +473,8 @@ static int hist_browser__populate(struct hist_browser *self, struct rb_root *his } int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, - u64 session_total, const char *helpline) + u64 session_total, const char *helpline, + const char *input_name) { struct newtExitStruct es; char str[1024]; @@ -527,7 +529,8 @@ do_annotate: "kallsyms file"); continue; } - map_symbol__annotate_browser(browser->selection); + map_symbol__annotate_browser(browser->selection, + input_name); } } err = 0; diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index dffaff52ba4..27f4c2dc715 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -96,12 +96,14 @@ static inline struct map * static inline int perf_session__browse_hists(struct rb_root *hists __used, u64 nr_hists __used, u64 session_total __used, - const char *helpline __used) + const char *helpline __used, + const char *input_name __used) { return 0; } #else int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, - u64 session_total, const char *helpline); + u64 session_total, const char *helpline, + const char *input_name); #endif #endif /* __PERF_SESSION_H */ -- cgit v1.2.3 From 83753190c136901c916df267703937e60f24b8b8 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 3 Apr 2010 16:30:44 -0300 Subject: perf newt: Add a "Zoom into foo.so DSO" and reverse operations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clicking on -> will bring as one of the popup menu options a "Zoom into CURRENT DSO", i.e. CURRENT will be replaced by the name of the DSO in the current line. Choosing this option will filter out all samples that didn't took place in a symbol in this DSO. After that the option reverts to "Zoom out of CURRENT DSO", to allow going back to the more compreensive view, not filtered by DSO. Future similar operations will include zooming into a particular thread, COMM, CPU, "last minute", "last N usecs", etc. Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/newt.c | 119 ++++++++++++++++++++++++++++++++++++------------- tools/perf/util/sort.h | 3 +- 2 files changed, 90 insertions(+), 32 deletions(-) (limited to 'tools/perf/util/newt.c') diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index c93bc2a2d13..bbf725d4b38 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -94,7 +94,7 @@ static newtComponent newt_form__new(void) return self; } -static int popup_menu(int argc, const char *argv[]) +static int popup_menu(int argc, char * const argv[]) { struct newtExitStruct es; int i, rc = -1, max_len = 5; @@ -397,22 +397,8 @@ static struct hist_browser *hist_browser__new(void) { struct hist_browser *self = malloc(sizeof(*self)); - if (self != NULL) { - char seq[] = "."; - int rows; - - newtGetScreenSize(NULL, &rows); - - if (symbol_conf.use_callchain) - self->tree = newtCheckboxTreeMulti(0, 0, rows - 5, seq, - NEWT_FLAG_SCROLL); - else - self->tree = newtListbox(0, 0, rows - 5, - (NEWT_FLAG_SCROLL | - NEWT_FLAG_RETURNEXIT)); - newtComponentAddCallback(self->tree, hist_browser__selection, - &self->selection); - } + if (self != NULL) + self->form = NULL; return self; } @@ -431,6 +417,30 @@ static int hist_browser__populate(struct hist_browser *self, struct rb_root *his struct ui_progress *progress; struct rb_node *nd; u64 curr_hist = 0; + char seq[] = "."; + char str[256]; + + if (self->form) { + newtFormDestroy(self->form); + newtPopWindow(); + } + + snprintf(str, sizeof(str), "Samples: %Ld ", + session_total); + newtDrawRootText(0, 0, str); + + newtGetScreenSize(NULL, &rows); + + if (symbol_conf.use_callchain) + self->tree = newtCheckboxTreeMulti(0, 0, rows - 5, seq, + NEWT_FLAG_SCROLL); + else + self->tree = newtListbox(0, 0, rows - 5, + (NEWT_FLAG_SCROLL | + NEWT_FLAG_RETURNEXIT)); + + newtComponentAddCallback(self->tree, hist_browser__selection, + &self->selection); progress = ui_progress__new("Adding entries to the browser...", nr_hists); if (progress == NULL) @@ -439,7 +449,12 @@ static int hist_browser__populate(struct hist_browser *self, struct rb_root *his idx = 0; for (nd = rb_first(hists); nd; nd = rb_next(nd)) { struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); - int len = hist_entry__append_browser(h, self->tree, session_total); + int len; + + if (h->filtered) + continue; + + len = hist_entry__append_browser(h, self->tree, session_total); if (len > max_len) max_len = len; if (symbol_conf.use_callchain) @@ -463,6 +478,9 @@ static int hist_browser__populate(struct hist_browser *self, struct rb_root *his newtCenteredWindow(max_len + (symbol_conf.use_callchain ? 5 : 0), rows - 5, "Report"); self->form = newt_form__new(); + if (self->form == NULL) + return -1; + newtFormAddHotKey(self->form, 'A'); newtFormAddHotKey(self->form, 'a'); newtFormAddHotKey(self->form, NEWT_KEY_RIGHT); @@ -472,29 +490,50 @@ static int hist_browser__populate(struct hist_browser *self, struct rb_root *his return 0; } +static u64 hists__filter_by_dso(struct rb_root *hists, struct dso *dso, + u64 *session_total) +{ + struct rb_node *nd; + u64 nr_hists = 0; + + *session_total = 0; + + for (nd = rb_first(hists); nd; nd = rb_next(nd)) { + struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); + + if (dso != NULL && (h->ms.map == NULL || h->ms.map->dso != dso)) { + h->filtered = true; + continue; + } + h->filtered = false; + ++nr_hists; + *session_total += h->count; + } + + return nr_hists; +} + int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, u64 session_total, const char *helpline, const char *input_name) { struct newtExitStruct es; - char str[1024]; + bool dso_filtered = false; int err = -1; struct hist_browser *browser = hist_browser__new(); if (browser == NULL) return -1; - snprintf(str, sizeof(str), "Samples: %Ld", session_total); - newtDrawRootText(0, 0, str); newtPushHelpLine(helpline); if (hist_browser__populate(browser, hists, nr_hists, session_total) < 0) goto out; while (1) { - char annotate[512]; - const char *options[2]; - int nr_options = 0, choice = 0; + char *options[16]; + int nr_options = 0, choice = 0, i, + annotate = -2, zoom_dso = -2; newtFormRun(browser->form, &es); if (es.reason == NEWT_EXIT_HOTKEY) { @@ -510,18 +549,29 @@ int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, } } - if (browser->selection->sym != NULL) { - snprintf(annotate, sizeof(annotate), - "Annotate %s", browser->selection->sym->name); - options[nr_options++] = annotate; - } + if (browser->selection->sym != NULL && + asprintf(&options[nr_options], "Annotate %s", + browser->selection->sym->name) > 0) + annotate = nr_options++; + + if (browser->selection->map != NULL && + asprintf(&options[nr_options], "Zoom %s %s DSO", + dso_filtered ? "out of" : "into", + (browser->selection->map->dso->kernel ? "the Kernel" : + browser->selection->map->dso->short_name)) > 0) + zoom_dso = nr_options++; + + options[nr_options++] = (char *)"Exit"; - options[nr_options++] = "Exit"; choice = popup_menu(nr_options, options); + + for (i = 0; i < nr_options - 1; ++i) + free(options[i]); + if (choice == nr_options - 1) break; do_annotate: - if (browser->selection->sym != NULL && choice >= 0) { + if (choice == annotate) { if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) { newtPopHelpLine(); newtPushHelpLine("No vmlinux file found, can't " @@ -531,6 +581,13 @@ do_annotate: } map_symbol__annotate_browser(browser->selection, input_name); + } if (choice == zoom_dso) { + hists__filter_by_dso(hists, + dso_filtered ? NULL : browser->selection->map->dso, + &session_total); + dso_filtered = !dso_filtered; + if (hist_browser__populate(browser, hists, nr_hists, session_total) < 0) + goto out; } } err = 0; diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 5bf2b744e7b..dce79d33e33 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -48,7 +48,8 @@ struct hist_entry { struct map_symbol ms; u64 ip; char level; - struct symbol *parent; + bool filtered; + struct symbol *parent; union { unsigned long position; struct hist_entry *pair; -- cgit v1.2.3 From a5e29aca02fcecd086ac160ea29244cae6b4305e Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 3 Apr 2010 22:44:37 -0300 Subject: perf TUI: Add a "Zoom into COMM(PID) thread" and reverse operations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now one can press the right arrow key and in addition to being able to filter by DSO, filter out by thread too, or a combination of both filters. With this one can start collecting events for the whole system, then focus on a subset of the collected data quickly. Cc: Avi Kivity Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/newt.c | 82 +++++++++++++++++++++++++++++++++++++++++++++----- tools/perf/util/sort.h | 9 ++++-- 2 files changed, 81 insertions(+), 10 deletions(-) (limited to 'tools/perf/util/newt.c') diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index bbf725d4b38..6d6e022d770 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -490,6 +490,11 @@ static int hist_browser__populate(struct hist_browser *self, struct rb_root *his return 0; } +enum hist_filter { + HIST_FILTER__DSO, + HIST_FILTER__THREAD, +}; + static u64 hists__filter_by_dso(struct rb_root *hists, struct dso *dso, u64 *session_total) { @@ -502,10 +507,10 @@ static u64 hists__filter_by_dso(struct rb_root *hists, struct dso *dso, struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); if (dso != NULL && (h->ms.map == NULL || h->ms.map->dso != dso)) { - h->filtered = true; + h->filtered |= (1 << HIST_FILTER__DSO); continue; } - h->filtered = false; + h->filtered &= ~(1 << HIST_FILTER__DSO); ++nr_hists; *session_total += h->count; } @@ -513,12 +518,54 @@ static u64 hists__filter_by_dso(struct rb_root *hists, struct dso *dso, return nr_hists; } +static u64 hists__filter_by_thread(struct rb_root *hists, const struct thread *thread, + u64 *session_total) +{ + struct rb_node *nd; + u64 nr_hists = 0; + + *session_total = 0; + + for (nd = rb_first(hists); nd; nd = rb_next(nd)) { + struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); + + if (thread != NULL && h->thread != thread) { + h->filtered |= (1 << HIST_FILTER__THREAD); + continue; + } + h->filtered &= ~(1 << HIST_FILTER__THREAD); + ++nr_hists; + *session_total += h->count; + } + + return nr_hists; +} + +static struct thread *hist_browser__selected_thread(struct hist_browser *self) +{ + int *indexes; + + if (!symbol_conf.use_callchain) + goto out; + + indexes = newtCheckboxTreeFindItem(self->tree, (void *)self->selection); + if (indexes) { + bool is_hist_entry = indexes[1] == NEWT_ARG_LAST; + free(indexes); + if (is_hist_entry) + goto out; + } + return NULL; +out: + return *(struct thread **)(self->selection + 1); +} + int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, u64 session_total, const char *helpline, const char *input_name) { struct newtExitStruct es; - bool dso_filtered = false; + bool dso_filtered = false, thread_filtered = false; int err = -1; struct hist_browser *browser = hist_browser__new(); @@ -531,9 +578,10 @@ int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, goto out; while (1) { + const struct thread *thread; char *options[16]; int nr_options = 0, choice = 0, i, - annotate = -2, zoom_dso = -2; + annotate = -2, zoom_dso = -2, zoom_thread = -2; newtFormRun(browser->form, &es); if (es.reason == NEWT_EXIT_HOTKEY) { @@ -561,6 +609,13 @@ int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, browser->selection->map->dso->short_name)) > 0) zoom_dso = nr_options++; + thread = hist_browser__selected_thread(browser); + if (thread != NULL && + asprintf(&options[nr_options], "Zoom %s %s(%d) thread", + (thread_filtered ? "out of" : "into"), + (thread->comm_set ? thread->comm : ""), thread->pid) > 0) + zoom_thread = nr_options++; + options[nr_options++] = (char *)"Exit"; choice = popup_menu(nr_options, options); @@ -570,6 +625,9 @@ int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, if (choice == nr_options - 1) break; + + if (choice == -1) + continue; do_annotate: if (choice == annotate) { if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) { @@ -581,13 +639,21 @@ do_annotate: } map_symbol__annotate_browser(browser->selection, input_name); - } if (choice == zoom_dso) { - hists__filter_by_dso(hists, - dso_filtered ? NULL : browser->selection->map->dso, - &session_total); + } else if (choice == zoom_dso) { + nr_hists = hists__filter_by_dso(hists, + (dso_filtered ? NULL : + browser->selection->map->dso), + &session_total); dso_filtered = !dso_filtered; if (hist_browser__populate(browser, hists, nr_hists, session_total) < 0) goto out; + } else if (choice == zoom_thread) { + nr_hists = hists__filter_by_thread(hists, + (thread_filtered ? NULL : thread), + &session_total); + thread_filtered = !thread_filtered; + if (hist_browser__populate(browser, hists, nr_hists, session_total) < 0) + goto out; } } err = 0; diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index dce79d33e33..6d7b4be7060 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -44,11 +44,16 @@ extern enum sort_type sort__first_dimension; struct hist_entry { struct rb_node rb_node; u64 count; - struct thread *thread; + /* + * XXX WARNING! + * thread _has_ to come after ms, see + * hist_browser__selected_thread in util/newt.c + */ struct map_symbol ms; + struct thread *thread; u64 ip; char level; - bool filtered; + u8 filtered; struct symbol *parent; union { unsigned long position; -- cgit v1.2.3 From 6e7ab4c649eb7ed403d970b8eda32ca3745e8024 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 5 Apr 2010 12:02:18 -0300 Subject: perf TUI: Show filters on the title and add help line about how to zoom out MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Suggested-by: Ingo Molnar Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/newt.c | 96 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 68 insertions(+), 28 deletions(-) (limited to 'tools/perf/util/newt.c') diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 6d6e022d770..c0e71aacfc1 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -411,7 +411,7 @@ static void hist_browser__delete(struct hist_browser *self) } static int hist_browser__populate(struct hist_browser *self, struct rb_root *hists, - u64 nr_hists, u64 session_total) + u64 nr_hists, u64 session_total, const char *title) { int max_len = 0, idx, cols, rows; struct ui_progress *progress; @@ -476,7 +476,7 @@ static int hist_browser__populate(struct hist_browser *self, struct rb_root *his newtListboxSetWidth(self->tree, max_len); newtCenteredWindow(max_len + (symbol_conf.use_callchain ? 5 : 0), - rows - 5, "Report"); + rows - 5, title); self->form = newt_form__new(); if (self->form == NULL) return -1; @@ -495,7 +495,7 @@ enum hist_filter { HIST_FILTER__THREAD, }; -static u64 hists__filter_by_dso(struct rb_root *hists, struct dso *dso, +static u64 hists__filter_by_dso(struct rb_root *hists, const struct dso *dso, u64 *session_total) { struct rb_node *nd; @@ -560,25 +560,47 @@ out: return *(struct thread **)(self->selection + 1); } +static int hist_browser__title(char *bf, size_t size, const char *input_name, + const struct dso *dso, const struct thread *thread) +{ + int printed = 0; + + if (thread) + printed += snprintf(bf + printed, size - printed, + "Thread: %s(%d)", + (thread->comm_set ? thread->comm : ""), + thread->pid); + if (dso) + printed += snprintf(bf + printed, size - printed, + "%sDSO: %s", thread ? " " : "", + dso->short_name); + return printed ?: snprintf(bf, size, "Report: %s", input_name); +} + int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, u64 session_total, const char *helpline, const char *input_name) { + struct hist_browser *browser = hist_browser__new(); + const struct thread *thread_filter = NULL; + const struct dso *dso_filter = NULL; struct newtExitStruct es; - bool dso_filtered = false, thread_filtered = false; + char msg[160]; int err = -1; - struct hist_browser *browser = hist_browser__new(); if (browser == NULL) return -1; newtPushHelpLine(helpline); - if (hist_browser__populate(browser, hists, nr_hists, session_total) < 0) + hist_browser__title(msg, sizeof(msg), input_name, + dso_filter, thread_filter); + if (hist_browser__populate(browser, hists, nr_hists, session_total, msg) < 0) goto out; while (1) { const struct thread *thread; + const struct dso *dso; char *options[16]; int nr_options = 0, choice = 0, i, annotate = -2, zoom_dso = -2, zoom_thread = -2; @@ -602,20 +624,21 @@ int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, browser->selection->sym->name) > 0) annotate = nr_options++; - if (browser->selection->map != NULL && - asprintf(&options[nr_options], "Zoom %s %s DSO", - dso_filtered ? "out of" : "into", - (browser->selection->map->dso->kernel ? "the Kernel" : - browser->selection->map->dso->short_name)) > 0) - zoom_dso = nr_options++; - thread = hist_browser__selected_thread(browser); if (thread != NULL && asprintf(&options[nr_options], "Zoom %s %s(%d) thread", - (thread_filtered ? "out of" : "into"), - (thread->comm_set ? thread->comm : ""), thread->pid) > 0) + (thread_filter ? "out of" : "into"), + (thread->comm_set ? thread->comm : ""), + thread->pid) > 0) zoom_thread = nr_options++; + dso = browser->selection->map ? browser->selection->map->dso : NULL; + if (dso != NULL && + asprintf(&options[nr_options], "Zoom %s %s DSO", + (dso_filter ? "out of" : "into"), + (dso->kernel ? "the Kernel" : dso->short_name)) > 0) + zoom_dso = nr_options++; + options[nr_options++] = (char *)"Exit"; choice = popup_menu(nr_options, options); @@ -637,22 +660,39 @@ do_annotate: "kallsyms file"); continue; } - map_symbol__annotate_browser(browser->selection, - input_name); + map_symbol__annotate_browser(browser->selection, input_name); } else if (choice == zoom_dso) { - nr_hists = hists__filter_by_dso(hists, - (dso_filtered ? NULL : - browser->selection->map->dso), - &session_total); - dso_filtered = !dso_filtered; - if (hist_browser__populate(browser, hists, nr_hists, session_total) < 0) + if (dso_filter) { + newtPopHelpLine(); + dso_filter = NULL; + } else { + snprintf(msg, sizeof(msg), + "To zoom out press -> + \"Zoom out of %s DSO\"", + dso->kernel ? "the Kernel" : dso->short_name); + newtPushHelpLine(msg); + dso_filter = dso; + } + nr_hists = hists__filter_by_dso(hists, dso_filter, &session_total); + hist_browser__title(msg, sizeof(msg), input_name, + dso_filter, thread_filter); + if (hist_browser__populate(browser, hists, nr_hists, session_total, msg) < 0) goto out; } else if (choice == zoom_thread) { - nr_hists = hists__filter_by_thread(hists, - (thread_filtered ? NULL : thread), - &session_total); - thread_filtered = !thread_filtered; - if (hist_browser__populate(browser, hists, nr_hists, session_total) < 0) + if (thread_filter) { + newtPopHelpLine(); + thread_filter = NULL; + } else { + snprintf(msg, sizeof(msg), + "To zoom out press -> + \"Zoom out of %s(%d) thread\"", + (thread->comm_set ? thread->comm : ""), + thread->pid); + newtPushHelpLine(msg); + thread_filter = thread; + } + nr_hists = hists__filter_by_thread(hists, thread_filter, &session_total); + hist_browser__title(msg, sizeof(msg), input_name, + dso_filter, thread_filter); + if (hist_browser__populate(browser, hists, nr_hists, session_total, msg) < 0) goto out; } } -- cgit v1.2.3 From c0ed55d2e4f600335193612725c0d7c8211a7a4a Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 5 Apr 2010 12:04:23 -0300 Subject: perf TUI: Move "Yes" button to before "No" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Esc + Enter should be enough warning to avoid accidentaly exiting from the browser. Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/newt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools/perf/util/newt.c') diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index c0e71aacfc1..7a123a94e3f 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -132,7 +132,7 @@ static bool dialog_yesno(const char *msg) { /* newtWinChoice should really be accepting const char pointers... */ char yes[] = "Yes", no[] = "No"; - return newtWinChoice(NULL, no, yes, (char *)msg) == 2; + return newtWinChoice(NULL, yes, no, (char *)msg) == 1; } /* -- cgit v1.2.3 From 7f8264539c62378cccbdf9b598927b034bef4a92 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 10 May 2010 10:51:25 -0300 Subject: perf newt: Use newtAddComponent() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of newtAddComponents(just-one-entry, NULL), that is not needed if, like in this browser, we're adding just one component at a time. Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/newt.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'tools/perf/util/newt.c') diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 7a123a94e3f..e283a6e6b6e 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -31,7 +31,7 @@ struct ui_progress *ui_progress__new(const char *title, u64 total) self->scale = newtScale(0, 0, cols, total); if (self->scale == NULL) goto out_free_form; - newtFormAddComponents(self->form, self->scale, NULL); + newtFormAddComponent(self->form, self->scale); newtRefresh(); } @@ -107,7 +107,7 @@ static int popup_menu(int argc, char * const argv[]) if (listbox == NULL) goto out_destroy_form; - newtFormAddComponents(form, listbox, NULL); + newtFormAddComponent(form, listbox); for (i = 0; i < argc; ++i) { int len = strlen(argv[i]); @@ -365,7 +365,7 @@ static void map_symbol__annotate_browser(const struct map_symbol *self, newtCenteredWindow(max_line_len + 2, rows - 5, self->sym->name); form = newt_form__new(); - newtFormAddComponents(form, tree, NULL); + newtFormAddComponent(form, tree); newtFormRun(form, &es); newtFormDestroy(form); -- cgit v1.2.3 From b09e0190acf88c7fe3b05e3c331e1b2ef5310896 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 11 May 2010 11:10:15 -0300 Subject: perf hist: Adopt filter by dso and by thread methods from the newt browser MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Those are really not specific to the newt code, can be used by other UI frontends. Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-report.c | 5 +-- tools/perf/util/hist.c | 59 +++++++++++++++++++++++++++++++++ tools/perf/util/hist.h | 15 +++++++++ tools/perf/util/newt.c | 80 ++++++++------------------------------------- tools/perf/util/session.h | 15 --------- 5 files changed, 89 insertions(+), 85 deletions(-) (limited to 'tools/perf/util/newt.c') diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index d7c75291e78..3d67d6bf22c 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -301,10 +301,7 @@ static int __cmd_report(void) hists__collapse_resort(hists); hists__output_resort(hists); if (use_browser) - perf_session__browse_hists(&hists->entries, - hists->nr_entries, - hists->stats.total, help, - input_name); + hists__browse(hists, help, input_name); else { if (rb_first(&session->hists.entries) == rb_last(&session->hists.entries)) diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index e34fd248067..baa55be64d9 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -784,3 +784,62 @@ print_entries: return ret; } + +enum hist_filter { + HIST_FILTER__DSO, + HIST_FILTER__THREAD, +}; + +void hists__filter_by_dso(struct hists *self, const struct dso *dso) +{ + struct rb_node *nd; + + self->nr_entries = self->stats.total = 0; + self->max_sym_namelen = 0; + + for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { + struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); + + if (symbol_conf.exclude_other && !h->parent) + continue; + + if (dso != NULL && (h->ms.map == NULL || h->ms.map->dso != dso)) { + h->filtered |= (1 << HIST_FILTER__DSO); + continue; + } + + h->filtered &= ~(1 << HIST_FILTER__DSO); + if (!h->filtered) { + ++self->nr_entries; + self->stats.total += h->count; + if (h->ms.sym && + self->max_sym_namelen < h->ms.sym->namelen) + self->max_sym_namelen = h->ms.sym->namelen; + } + } +} + +void hists__filter_by_thread(struct hists *self, const struct thread *thread) +{ + struct rb_node *nd; + + self->nr_entries = self->stats.total = 0; + self->max_sym_namelen = 0; + + for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { + struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); + + if (thread != NULL && h->thread != thread) { + h->filtered |= (1 << HIST_FILTER__THREAD); + continue; + } + h->filtered &= ~(1 << HIST_FILTER__THREAD); + if (!h->filtered) { + ++self->nr_entries; + self->stats.total += h->count; + if (h->ms.sym && + self->max_sym_namelen < h->ms.sym->namelen) + self->max_sym_namelen = h->ms.sym->namelen; + } + } +} diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 1b18d04195d..1c5f93ac5ab 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -44,4 +44,19 @@ void hists__output_resort(struct hists *self); void hists__collapse_resort(struct hists *self); size_t hists__fprintf(struct hists *self, struct hists *pair, bool show_displacement, FILE *fp); + +void hists__filter_by_dso(struct hists *self, const struct dso *dso); +void hists__filter_by_thread(struct hists *self, const struct thread *thread); + +#ifdef NO_NEWT_SUPPORT +static inline int hists__browse(struct hists self __used, + const char *helpline __used, + const char *input_name __used) +{ + return 0; +} +#else +int hists__browse(struct hists *self, const char *helpline, + const char *input_name); +#endif #endif /* __PERF_HIST_H */ diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index e283a6e6b6e..638b519e72b 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -410,8 +410,8 @@ static void hist_browser__delete(struct hist_browser *self) free(self); } -static int hist_browser__populate(struct hist_browser *self, struct rb_root *hists, - u64 nr_hists, u64 session_total, const char *title) +static int hist_browser__populate(struct hist_browser *self, struct hists *hists, + const char *title) { int max_len = 0, idx, cols, rows; struct ui_progress *progress; @@ -426,7 +426,7 @@ static int hist_browser__populate(struct hist_browser *self, struct rb_root *his } snprintf(str, sizeof(str), "Samples: %Ld ", - session_total); + hists->stats.total); newtDrawRootText(0, 0, str); newtGetScreenSize(NULL, &rows); @@ -442,24 +442,25 @@ static int hist_browser__populate(struct hist_browser *self, struct rb_root *his newtComponentAddCallback(self->tree, hist_browser__selection, &self->selection); - progress = ui_progress__new("Adding entries to the browser...", nr_hists); + progress = ui_progress__new("Adding entries to the browser...", + hists->nr_entries); if (progress == NULL) return -1; idx = 0; - for (nd = rb_first(hists); nd; nd = rb_next(nd)) { + for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); int len; if (h->filtered) continue; - len = hist_entry__append_browser(h, self->tree, session_total); + len = hist_entry__append_browser(h, self->tree, hists->stats.total); if (len > max_len) max_len = len; if (symbol_conf.use_callchain) hist_entry__append_callchain_browser(h, self->tree, - session_total, idx++); + hists->stats.total, idx++); ++curr_hist; if (curr_hist % 5) ui_progress__update(progress, curr_hist); @@ -490,57 +491,6 @@ static int hist_browser__populate(struct hist_browser *self, struct rb_root *his return 0; } -enum hist_filter { - HIST_FILTER__DSO, - HIST_FILTER__THREAD, -}; - -static u64 hists__filter_by_dso(struct rb_root *hists, const struct dso *dso, - u64 *session_total) -{ - struct rb_node *nd; - u64 nr_hists = 0; - - *session_total = 0; - - for (nd = rb_first(hists); nd; nd = rb_next(nd)) { - struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); - - if (dso != NULL && (h->ms.map == NULL || h->ms.map->dso != dso)) { - h->filtered |= (1 << HIST_FILTER__DSO); - continue; - } - h->filtered &= ~(1 << HIST_FILTER__DSO); - ++nr_hists; - *session_total += h->count; - } - - return nr_hists; -} - -static u64 hists__filter_by_thread(struct rb_root *hists, const struct thread *thread, - u64 *session_total) -{ - struct rb_node *nd; - u64 nr_hists = 0; - - *session_total = 0; - - for (nd = rb_first(hists); nd; nd = rb_next(nd)) { - struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); - - if (thread != NULL && h->thread != thread) { - h->filtered |= (1 << HIST_FILTER__THREAD); - continue; - } - h->filtered &= ~(1 << HIST_FILTER__THREAD); - ++nr_hists; - *session_total += h->count; - } - - return nr_hists; -} - static struct thread *hist_browser__selected_thread(struct hist_browser *self) { int *indexes; @@ -577,9 +527,7 @@ static int hist_browser__title(char *bf, size_t size, const char *input_name, return printed ?: snprintf(bf, size, "Report: %s", input_name); } -int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, - u64 session_total, const char *helpline, - const char *input_name) +int hists__browse(struct hists *self, const char *helpline, const char *input_name) { struct hist_browser *browser = hist_browser__new(); const struct thread *thread_filter = NULL; @@ -595,7 +543,7 @@ int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, hist_browser__title(msg, sizeof(msg), input_name, dso_filter, thread_filter); - if (hist_browser__populate(browser, hists, nr_hists, session_total, msg) < 0) + if (hist_browser__populate(browser, self, msg) < 0) goto out; while (1) { @@ -672,10 +620,10 @@ do_annotate: newtPushHelpLine(msg); dso_filter = dso; } - nr_hists = hists__filter_by_dso(hists, dso_filter, &session_total); + hists__filter_by_dso(self, dso_filter); hist_browser__title(msg, sizeof(msg), input_name, dso_filter, thread_filter); - if (hist_browser__populate(browser, hists, nr_hists, session_total, msg) < 0) + if (hist_browser__populate(browser, self, msg) < 0) goto out; } else if (choice == zoom_thread) { if (thread_filter) { @@ -689,10 +637,10 @@ do_annotate: newtPushHelpLine(msg); thread_filter = thread; } - nr_hists = hists__filter_by_thread(hists, thread_filter, &session_total); + hists__filter_by_thread(self, thread_filter); hist_browser__title(msg, sizeof(msg), input_name, dso_filter, thread_filter); - if (hist_browser__populate(browser, hists, nr_hists, session_total, msg) < 0) + if (hist_browser__populate(browser, self, msg) < 0) goto out; } } diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 46190f94b54..ce00fa6cded 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -102,21 +102,6 @@ int perf_session__create_kernel_maps(struct perf_session *self); int do_read(int fd, void *buf, size_t size); void perf_session__update_sample_type(struct perf_session *self); -#ifdef NO_NEWT_SUPPORT -static inline int perf_session__browse_hists(struct rb_root *hists __used, - u64 nr_hists __used, - u64 session_total __used, - const char *helpline __used, - const char *input_name __used) -{ - return 0; -} -#else -int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, - u64 session_total, const char *helpline, - const char *input_name); -#endif - static inline struct machine *perf_session__find_host_machine(struct perf_session *self) { -- cgit v1.2.3 From 3798ed7bc7ade26d3f59506cd06288615dfc7585 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 11 May 2010 18:01:23 -0300 Subject: perf ui: Add ui_helpline methods MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Initially this was just to be able to have a printf like method to prepare the formatted string and then pass to newtPushHelpLine, but as we already have for ui_progress, etc, its a step in identifying a restricted, highlevel set of widgets we can then have implementations for multiple widget sets (GTK, etc). Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/newt.c | 69 ++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 50 insertions(+), 19 deletions(-) (limited to 'tools/perf/util/newt.c') diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 638b519e72b..daa86efffce 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -57,6 +57,43 @@ void ui_progress__delete(struct ui_progress *self) free(self); } +static void ui_helpline__pop(void) +{ + newtPopHelpLine(); +} + +static void ui_helpline__push(const char *msg) +{ + newtPushHelpLine(msg); +} + +static void ui_helpline__vpush(const char *fmt, va_list ap) +{ + char *s; + + if (vasprintf(&s, fmt, ap) < 0) + vfprintf(stderr, fmt, ap); + else { + ui_helpline__push(s); + free(s); + } +} + +static void ui_helpline__fpush(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + ui_helpline__vpush(fmt, ap); + va_end(ap); +} + +static void ui_helpline__puts(const char *msg) +{ + ui_helpline__pop(); + ui_helpline__push(msg); +} + static char browser__last_msg[1024]; int browser__show_help(const char *format, va_list ap) @@ -69,8 +106,7 @@ int browser__show_help(const char *format, va_list ap) backlog += ret; if (browser__last_msg[backlog - 1] == '\n') { - newtPopHelpLine(); - newtPushHelpLine(browser__last_msg); + ui_helpline__puts(browser__last_msg); newtRefresh(); backlog = 0; } @@ -340,7 +376,7 @@ static void map_symbol__annotate_browser(const struct map_symbol *self, if (fp == NULL) goto out_free_str; - newtPushHelpLine("Press ESC to exit"); + ui_helpline__push("Press ESC to exit"); newtGetScreenSize(&cols, &rows); tree = newtListbox(0, 0, rows - 5, NEWT_FLAG_SCROLL); @@ -370,7 +406,7 @@ static void map_symbol__annotate_browser(const struct map_symbol *self, newtFormRun(form, &es); newtFormDestroy(form); newtPopWindow(); - newtPopHelpLine(); + ui_helpline__pop(); out_free_str: free(str); } @@ -539,7 +575,7 @@ int hists__browse(struct hists *self, const char *helpline, const char *input_na if (browser == NULL) return -1; - newtPushHelpLine(helpline); + ui_helpline__push(helpline); hist_browser__title(msg, sizeof(msg), input_name, dso_filter, thread_filter); @@ -602,8 +638,7 @@ int hists__browse(struct hists *self, const char *helpline, const char *input_na do_annotate: if (choice == annotate) { if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) { - newtPopHelpLine(); - newtPushHelpLine("No vmlinux file found, can't " + ui_helpline__puts("No vmlinux file found, can't " "annotate with just a " "kallsyms file"); continue; @@ -611,13 +646,11 @@ do_annotate: map_symbol__annotate_browser(browser->selection, input_name); } else if (choice == zoom_dso) { if (dso_filter) { - newtPopHelpLine(); + ui_helpline__pop(); dso_filter = NULL; } else { - snprintf(msg, sizeof(msg), - "To zoom out press -> + \"Zoom out of %s DSO\"", - dso->kernel ? "the Kernel" : dso->short_name); - newtPushHelpLine(msg); + ui_helpline__fpush("To zoom out press -> + \"Zoom out of %s DSO\"", + dso->kernel ? "the Kernel" : dso->short_name); dso_filter = dso; } hists__filter_by_dso(self, dso_filter); @@ -627,14 +660,12 @@ do_annotate: goto out; } else if (choice == zoom_thread) { if (thread_filter) { - newtPopHelpLine(); + ui_helpline__pop(); thread_filter = NULL; } else { - snprintf(msg, sizeof(msg), - "To zoom out press -> + \"Zoom out of %s(%d) thread\"", - (thread->comm_set ? thread->comm : ""), - thread->pid); - newtPushHelpLine(msg); + ui_helpline__fpush("To zoom out press -> + \"Zoom out of %s(%d) thread\"", + thread->comm_set ? thread->comm : "", + thread->pid); thread_filter = thread; } hists__filter_by_thread(self, thread_filter); @@ -658,7 +689,7 @@ void setup_browser(void) use_browser = true; newtInit(); newtCls(); - newtPushHelpLine(" "); + ui_helpline__puts(" "); } void exit_browser(bool wait_for_ok) -- cgit v1.2.3 From ef7b93a11904c6ba10604233d318d9e8ec88cddc Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 11 May 2010 23:18:06 -0300 Subject: perf report: Librarize the annotation code and use it in the newt browser MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now we don't anymore use popen to run 'perf annotate' for the selected symbol, instead we collect per address samplings when processing samples in 'perf report' if we're using the newt browser, then we use this data directly to do annotation. Done this way we can actually traverse the objdump_line objects directly, matching the addresses to the collected samples and colouring them appropriately using lower level slang routines. The new ui_browser class will be reused for the main, callchain aware, histogram browser, when it will be made generic and don't assume that the objects are always instances of the objdump_line class maintained using list_heads. Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile | 5 + tools/perf/builtin-annotate.c | 198 +----------------------- tools/perf/builtin-report.c | 19 ++- tools/perf/util/hist.c | 184 ++++++++++++++++++++++ tools/perf/util/hist.h | 29 ++++ tools/perf/util/newt.c | 352 ++++++++++++++++++++++++++++++++++++------ tools/perf/util/sort.h | 6 - 7 files changed, 544 insertions(+), 249 deletions(-) (limited to 'tools/perf/util/newt.c') diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 0797786aa72..9c4dc30cdc1 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -560,6 +560,8 @@ ifneq ($(shell sh -c "(echo '\#include '; echo 'int main(void) { newtIni msg := $(warning newt not found, disables TUI support. Please install newt-devel or libnewt-dev); BASIC_CFLAGS += -DNO_NEWT_SUPPORT else + # Fedora has /usr/include/slang/slang.h, but ubuntu /usr/include/slang.h + BASIC_CFLAGS += -I/usr/include/slang EXTLIBS += -lnewt LIB_OBJS += $(OUTPUT)util/newt.o endif @@ -948,6 +950,9 @@ $(OUTPUT)builtin-init-db.o: builtin-init-db.c $(OUTPUT)PERF-CFLAGS $(OUTPUT)util/config.o: util/config.c $(OUTPUT)PERF-CFLAGS $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< +$(OUTPUT)util/newt.o: util/newt.c $(OUTPUT)PERF-CFLAGS + $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< + $(OUTPUT)util/rbtree.o: ../../lib/rbtree.c $(OUTPUT)PERF-CFLAGS $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 3940964161b..fd1b786c8f3 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -34,68 +34,8 @@ static bool full_paths; static bool print_line; -struct sym_hist { - u64 sum; - u64 ip[0]; -}; - -struct sym_ext { - struct rb_node node; - double percent; - char *path; -}; - -struct sym_priv { - struct sym_hist *hist; - struct sym_ext *ext; -}; - static const char *sym_hist_filter; -static int sym__alloc_hist(struct symbol *self) -{ - struct sym_priv *priv = symbol__priv(self); - const int size = (sizeof(*priv->hist) + - (self->end - self->start) * sizeof(u64)); - - priv->hist = zalloc(size); - return priv->hist == NULL ? -1 : 0; -} - -/* - * collect histogram counts - */ -static int annotate__hist_hit(struct hist_entry *he, u64 ip) -{ - unsigned int sym_size, offset; - struct symbol *sym = he->ms.sym; - struct sym_priv *priv; - struct sym_hist *h; - - if (!sym || !he->ms.map) - return 0; - - priv = symbol__priv(sym); - if (priv->hist == NULL && sym__alloc_hist(sym) < 0) - return -ENOMEM; - - sym_size = sym->end - sym->start; - offset = ip - sym->start; - - pr_debug3("%s: ip=%#Lx\n", __func__, he->ms.map->unmap_ip(he->ms.map, ip)); - - if (offset >= sym_size) - return 0; - - h = priv->hist; - h->sum++; - h->ip[offset]++; - - pr_debug3("%#Lx %s: count++ [ip: %#Lx, %#Lx] => %Ld\n", he->ms.sym->start, - he->ms.sym->name, ip, ip - he->ms.sym->start, h->ip[offset]); - return 0; -} - static int hists__add_entry(struct hists *self, struct addr_location *al) { struct hist_entry *he; @@ -115,7 +55,7 @@ static int hists__add_entry(struct hists *self, struct addr_location *al) if (he == NULL) return -ENOMEM; - return annotate__hist_hit(he, al->addr); + return hist_entry__inc_addr_samples(he, al->addr); } static int process_sample_event(event_t *event, struct perf_session *session) @@ -140,101 +80,6 @@ static int process_sample_event(event_t *event, struct perf_session *session) return 0; } -struct objdump_line { - struct list_head node; - s64 offset; - char *line; -}; - -static struct objdump_line *objdump_line__new(s64 offset, char *line) -{ - struct objdump_line *self = malloc(sizeof(*self)); - - if (self != NULL) { - self->offset = offset; - self->line = line; - } - - return self; -} - -static void objdump_line__free(struct objdump_line *self) -{ - free(self->line); - free(self); -} - -static void objdump__add_line(struct list_head *head, struct objdump_line *line) -{ - list_add_tail(&line->node, head); -} - -static struct objdump_line *objdump__get_next_ip_line(struct list_head *head, - struct objdump_line *pos) -{ - list_for_each_entry_continue(pos, head, node) - if (pos->offset >= 0) - return pos; - - return NULL; -} - -static int parse_line(FILE *file, struct hist_entry *he, - struct list_head *head) -{ - struct symbol *sym = he->ms.sym; - struct objdump_line *objdump_line; - char *line = NULL, *tmp, *tmp2; - size_t line_len; - s64 line_ip, offset = -1; - char *c; - - if (getline(&line, &line_len, file) < 0) - return -1; - - if (!line) - return -1; - - c = strchr(line, '\n'); - if (c) - *c = 0; - - line_ip = -1; - - /* - * Strip leading spaces: - */ - tmp = line; - while (*tmp) { - if (*tmp != ' ') - break; - tmp++; - } - - if (*tmp) { - /* - * Parse hexa addresses followed by ':' - */ - line_ip = strtoull(tmp, &tmp2, 16); - if (*tmp2 != ':') - line_ip = -1; - } - - if (line_ip != -1) { - u64 start = map__rip_2objdump(he->ms.map, sym->start); - offset = line_ip - start; - } - - objdump_line = objdump_line__new(offset, line); - if (objdump_line == NULL) { - free(line); - return -1; - } - objdump__add_line(head, objdump_line); - - return 0; -} - static int objdump_line__print(struct objdump_line *self, struct list_head *head, struct hist_entry *he, u64 len) @@ -439,27 +284,11 @@ static void annotate_sym(struct hist_entry *he) struct symbol *sym = he->ms.sym; const char *filename = dso->long_name, *d_filename; u64 len; - char command[PATH_MAX*2]; - FILE *file; LIST_HEAD(head); struct objdump_line *pos, *n; - if (!filename) - return; - - if (dso->origin == DSO__ORIG_KERNEL) { - if (dso->annotate_warned) - return; - 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); + if (hist_entry__annotate(he, &head) < 0) return; - } - - pr_debug("%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; @@ -477,29 +306,6 @@ static void annotate_sym(struct hist_entry *he) printf(" Percent | Source code & Disassembly of %s\n", d_filename); printf("------------------------------------------------\n"); - if (verbose >= 2) - 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", - map__rip_2objdump(map, sym->start), - map__rip_2objdump(map, sym->end), - filename, filename); - - if (verbose >= 3) - printf("doing: %s\n", command); - - file = popen(command, "r"); - if (!file) - return; - - while (!feof(file)) { - if (parse_line(file, he, &head) < 0) - break; - } - - pclose(file); - if (verbose) hist_entry__print_hits(he); diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 3d67d6bf22c..04de3387de3 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -106,8 +106,18 @@ static int perf_session__add_hist_entry(struct perf_session *self, if (he == NULL) goto out_free_syms; err = 0; - if (symbol_conf.use_callchain) + if (symbol_conf.use_callchain) { err = append_chain(he->callchain, data->callchain, syms); + if (err) + goto out_free_syms; + } + /* + * Only in the newt browser we are doing integrated annotation, + * so we don't allocated the extra space needed because the stdio + * code will not use it. + */ + if (use_browser) + err = hist_entry__inc_addr_samples(he, al->addr); out_free_syms: free(syms); return err; @@ -458,6 +468,13 @@ int cmd_report(int argc, const char **argv, const char *prefix __used) if (strcmp(input_name, "-") != 0) setup_browser(); + /* + * Only in the newt browser we are doing integrated annotation, + * so don't allocate extra space that won't be used in the stdio + * implementation. + */ + if (use_browser) + symbol_conf.priv_size = sizeof(struct sym_priv); if (symbol__init() < 0) return -1; diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index baa55be64d9..451d2e45d84 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -843,3 +843,187 @@ void hists__filter_by_thread(struct hists *self, const struct thread *thread) } } } + +static int symbol__alloc_hist(struct symbol *self) +{ + struct sym_priv *priv = symbol__priv(self); + const int size = (sizeof(*priv->hist) + + (self->end - self->start) * sizeof(u64)); + + priv->hist = zalloc(size); + return priv->hist == NULL ? -1 : 0; +} + +int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip) +{ + unsigned int sym_size, offset; + struct symbol *sym = self->ms.sym; + struct sym_priv *priv; + struct sym_hist *h; + + if (!sym || !self->ms.map) + return 0; + + priv = symbol__priv(sym); + if (priv->hist == NULL && symbol__alloc_hist(sym) < 0) + return -ENOMEM; + + sym_size = sym->end - sym->start; + offset = ip - sym->start; + + pr_debug3("%s: ip=%#Lx\n", __func__, self->ms.map->unmap_ip(self->ms.map, ip)); + + if (offset >= sym_size) + return 0; + + h = priv->hist; + h->sum++; + h->ip[offset]++; + + pr_debug3("%#Lx %s: count++ [ip: %#Lx, %#Lx] => %Ld\n", self->ms.sym->start, + self->ms.sym->name, ip, ip - self->ms.sym->start, h->ip[offset]); + return 0; +} + +static struct objdump_line *objdump_line__new(s64 offset, char *line) +{ + struct objdump_line *self = malloc(sizeof(*self)); + + if (self != NULL) { + self->offset = offset; + self->line = line; + } + + return self; +} + +void objdump_line__free(struct objdump_line *self) +{ + free(self->line); + free(self); +} + +static void objdump__add_line(struct list_head *head, struct objdump_line *line) +{ + list_add_tail(&line->node, head); +} + +struct objdump_line *objdump__get_next_ip_line(struct list_head *head, + struct objdump_line *pos) +{ + list_for_each_entry_continue(pos, head, node) + if (pos->offset >= 0) + return pos; + + return NULL; +} + +static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file, + struct list_head *head) +{ + struct symbol *sym = self->ms.sym; + struct objdump_line *objdump_line; + char *line = NULL, *tmp, *tmp2, *c; + size_t line_len; + s64 line_ip, offset = -1; + + if (getline(&line, &line_len, file) < 0) + return -1; + + if (!line) + return -1; + + while (line_len != 0 && isspace(line[line_len - 1])) + line[--line_len] = '\0'; + + c = strchr(line, '\n'); + if (c) + *c = 0; + + line_ip = -1; + + /* + * Strip leading spaces: + */ + tmp = line; + while (*tmp) { + if (*tmp != ' ') + break; + tmp++; + } + + if (*tmp) { + /* + * Parse hexa addresses followed by ':' + */ + line_ip = strtoull(tmp, &tmp2, 16); + if (*tmp2 != ':') + line_ip = -1; + } + + if (line_ip != -1) { + u64 start = map__rip_2objdump(self->ms.map, sym->start); + offset = line_ip - start; + } + + objdump_line = objdump_line__new(offset, line); + if (objdump_line == NULL) { + free(line); + return -1; + } + objdump__add_line(head, objdump_line); + + return 0; +} + +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 command[PATH_MAX * 2]; + FILE *file; + u64 len; + + if (!filename) + return -1; + + if (dso->origin == DSO__ORIG_KERNEL) { + if (dso->annotate_warned) + return 0; + 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; + } + + pr_debug("%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)); + + len = sym->end - sym->start; + + pr_debug("annotating [%p] %30s : [%p] %30s\n", + dso, dso->long_name, sym, sym->name); + + snprintf(command, sizeof(command), + "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s|grep -v %s|expand", + map__rip_2objdump(map, sym->start), + map__rip_2objdump(map, sym->end), + filename, filename); + + pr_debug("Executing: %s\n", command); + + file = popen(command, "r"); + if (!file) + return -1; + + while (!feof(file)) + if (hist_entry__parse_objdump_line(self, file, head) < 0) + break; + + pclose(file); + return 0; +} diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 1c5f93ac5ab..ed9c0673496 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -11,6 +11,32 @@ struct addr_location; struct symbol; struct rb_root; +struct objdump_line { + struct list_head node; + s64 offset; + char *line; +}; + +void objdump_line__free(struct objdump_line *self); +struct objdump_line *objdump__get_next_ip_line(struct list_head *head, + struct objdump_line *pos); + +struct sym_hist { + u64 sum; + u64 ip[0]; +}; + +struct sym_ext { + struct rb_node node; + double percent; + char *path; +}; + +struct sym_priv { + struct sym_hist *hist; + struct sym_ext *ext; +}; + struct events_stats { u64 total; u64 lost; @@ -45,6 +71,9 @@ void hists__collapse_resort(struct hists *self); size_t hists__fprintf(struct hists *self, struct hists *pair, bool show_displacement, FILE *fp); +int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip); +int hist_entry__annotate(struct hist_entry *self, struct list_head *head); + void hists__filter_by_dso(struct hists *self, const struct dso *dso); void hists__filter_by_thread(struct hists *self, const struct thread *thread); diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index daa86efffce..ba6acd04c08 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -2,6 +2,7 @@ #include #undef _GNU_SOURCE +#include #include #include #include @@ -171,6 +172,254 @@ static bool dialog_yesno(const char *msg) return newtWinChoice(NULL, yes, no, (char *)msg) == 1; } +#define HE_COLORSET_TOP 50 +#define HE_COLORSET_MEDIUM 51 +#define HE_COLORSET_NORMAL 52 +#define HE_COLORSET_SELECTED 53 +#define HE_COLORSET_CODE 54 + +static int ui_browser__percent_color(double percent, bool current) +{ + if (current) + return HE_COLORSET_SELECTED; + if (percent >= MIN_RED) + return HE_COLORSET_TOP; + if (percent >= MIN_GREEN) + return HE_COLORSET_MEDIUM; + return HE_COLORSET_NORMAL; +} + +struct ui_browser { + newtComponent form, sb; + u64 index, first_visible_entry_idx; + void *first_visible_entry, *entries; + u16 top, left, width, height; + void *priv; + u32 nr_entries; +}; + +static void ui_browser__refresh_dimensions(struct ui_browser *self) +{ + int cols, rows; + newtGetScreenSize(&cols, &rows); + + if (self->width > cols - 4) + self->width = cols - 4; + self->height = rows - 5; + if (self->height > self->nr_entries) + self->height = self->nr_entries; + self->top = (rows - self->height) / 2; + self->left = (cols - self->width) / 2; +} + +static void ui_browser__reset_index(struct ui_browser *self) +{ + self->index = self->first_visible_entry_idx = 0; + self->first_visible_entry = NULL; +} + +static int objdump_line__show(struct objdump_line *self, struct list_head *head, + int width, struct hist_entry *he, int len, + bool current_entry) +{ + if (self->offset != -1) { + struct symbol *sym = he->ms.sym; + unsigned int hits = 0; + double percent = 0.0; + int color; + struct sym_priv *priv = symbol__priv(sym); + struct sym_ext *sym_ext = priv->ext; + struct sym_hist *h = priv->hist; + s64 offset = self->offset; + struct objdump_line *next = objdump__get_next_ip_line(head, self); + + while (offset < (s64)len && + (next == NULL || offset < next->offset)) { + if (sym_ext) { + percent += sym_ext[offset].percent; + } else + hits += h->ip[offset]; + + ++offset; + } + + if (sym_ext == NULL && h->sum) + percent = 100.0 * hits / h->sum; + + color = ui_browser__percent_color(percent, current_entry); + SLsmg_set_color(color); + SLsmg_printf(" %7.2f ", percent); + if (!current_entry) + SLsmg_set_color(HE_COLORSET_CODE); + } else { + int color = ui_browser__percent_color(0, current_entry); + SLsmg_set_color(color); + SLsmg_write_nstring(" ", 9); + } + + SLsmg_write_char(':'); + SLsmg_write_nstring(" ", 8); + if (!*self->line) + SLsmg_write_nstring(" ", width - 18); + else + SLsmg_write_nstring(self->line, width - 18); + + return 0; +} + +static int ui_browser__refresh_entries(struct ui_browser *self) +{ + struct objdump_line *pos; + struct list_head *head = self->entries; + struct hist_entry *he = self->priv; + int row = 0; + int len = he->ms.sym->end - he->ms.sym->start; + + if (self->first_visible_entry == NULL || self->first_visible_entry == self->entries) + self->first_visible_entry = head->next; + + pos = list_entry(self->first_visible_entry, struct objdump_line, node); + + list_for_each_entry_from(pos, head, node) { + bool current_entry = (self->first_visible_entry_idx + row) == self->index; + SLsmg_gotorc(self->top + row, self->left); + objdump_line__show(pos, head, self->width, + he, len, current_entry); + if (++row == self->height) + break; + } + + SLsmg_set_color(HE_COLORSET_NORMAL); + SLsmg_fill_region(self->top + row, self->left, + self->height - row, self->width, ' '); + + return 0; +} + +static int ui_browser__run(struct ui_browser *self, const char *title, + struct newtExitStruct *es) +{ + if (self->form) { + newtFormDestroy(self->form); + newtPopWindow(); + } + + ui_browser__refresh_dimensions(self); + newtCenteredWindow(self->width + 2, self->height, title); + self->form = newt_form__new(); + if (self->form == NULL) + return -1; + + self->sb = newtVerticalScrollbar(self->width + 1, 0, self->height, + HE_COLORSET_NORMAL, + HE_COLORSET_SELECTED); + if (self->sb == NULL) + return -1; + + newtFormAddHotKey(self->form, NEWT_KEY_UP); + newtFormAddHotKey(self->form, NEWT_KEY_DOWN); + newtFormAddHotKey(self->form, NEWT_KEY_PGUP); + newtFormAddHotKey(self->form, NEWT_KEY_PGDN); + newtFormAddHotKey(self->form, NEWT_KEY_HOME); + newtFormAddHotKey(self->form, NEWT_KEY_END); + + if (ui_browser__refresh_entries(self) < 0) + return -1; + newtFormAddComponent(self->form, self->sb); + + while (1) { + unsigned int offset; + + newtFormRun(self->form, es); + + if (es->reason != NEWT_EXIT_HOTKEY) + break; + switch (es->u.key) { + case NEWT_KEY_DOWN: + if (self->index == self->nr_entries - 1) + break; + ++self->index; + if (self->index == self->first_visible_entry_idx + self->height) { + struct list_head *pos = self->first_visible_entry; + ++self->first_visible_entry_idx; + self->first_visible_entry = pos->next; + } + break; + case NEWT_KEY_UP: + if (self->index == 0) + break; + --self->index; + if (self->index < self->first_visible_entry_idx) { + struct list_head *pos = self->first_visible_entry; + --self->first_visible_entry_idx; + self->first_visible_entry = pos->prev; + } + break; + case NEWT_KEY_PGDN: + if (self->first_visible_entry_idx + self->height > self->nr_entries - 1) + break; + + offset = self->height; + if (self->index + offset > self->nr_entries - 1) + offset = self->nr_entries - 1 - self->index; + self->index += offset; + self->first_visible_entry_idx += offset; + + while (offset--) { + struct list_head *pos = self->first_visible_entry; + self->first_visible_entry = pos->next; + } + + break; + case NEWT_KEY_PGUP: + if (self->first_visible_entry_idx == 0) + break; + + if (self->first_visible_entry_idx < self->height) + offset = self->first_visible_entry_idx; + else + offset = self->height; + + self->index -= offset; + self->first_visible_entry_idx -= offset; + + while (offset--) { + struct list_head *pos = self->first_visible_entry; + self->first_visible_entry = pos->prev; + } + break; + case NEWT_KEY_HOME: + ui_browser__reset_index(self); + break; + case NEWT_KEY_END: { + struct list_head *head = self->entries; + offset = self->height - 1; + + if (offset > self->nr_entries) + offset = self->nr_entries; + + self->index = self->first_visible_entry_idx = self->nr_entries - 1 - offset; + self->first_visible_entry = head->prev; + while (offset-- != 0) { + struct list_head *pos = self->first_visible_entry; + self->first_visible_entry = pos->prev; + } + } + break; + case NEWT_KEY_ESCAPE: + case CTRL('c'): + case 'Q': + case 'q': + return 0; + default: + continue; + } + if (ui_browser__refresh_entries(self) < 0) + return -1; + } + return 0; +} + /* * When debugging newt problems it was useful to be able to "unroll" * the calls to newtCheckBoxTreeAdd{Array,Item}, so that we can generate @@ -353,62 +602,40 @@ static size_t hist_entry__append_browser(struct hist_entry *self, return ret; } -static void map_symbol__annotate_browser(const struct map_symbol *self, - const char *input_name) +static void hist_entry__annotate_browser(struct hist_entry *self) { - FILE *fp; - int cols, rows; - newtComponent form, tree; + struct ui_browser browser; struct newtExitStruct es; - char *str; - size_t line_len, max_line_len = 0; - size_t max_usable_width; - char *line = NULL; + struct objdump_line *pos, *n; + LIST_HEAD(head); - if (self->sym == NULL) + if (self->ms.sym == NULL) return; - if (asprintf(&str, "perf annotate -i \"%s\" -d \"%s\" %s 2>&1 | expand", - input_name, self->map->dso->name, self->sym->name) < 0) + if (hist_entry__annotate(self, &head) < 0) return; - fp = popen(str, "r"); - if (fp == NULL) - goto out_free_str; - ui_helpline__push("Press ESC to exit"); - newtGetScreenSize(&cols, &rows); - tree = newtListbox(0, 0, rows - 5, NEWT_FLAG_SCROLL); - - while (!feof(fp)) { - if (getline(&line, &line_len, fp) < 0 || !line_len) - break; - while (line_len != 0 && isspace(line[line_len - 1])) - line[--line_len] = '\0'; - if (line_len > max_line_len) - max_line_len = line_len; - newtListboxAppendEntry(tree, line, NULL); + memset(&browser, 0, sizeof(browser)); + browser.entries = &head; + browser.priv = self; + list_for_each_entry(pos, &head, node) { + size_t line_len = strlen(pos->line); + if (browser.width < line_len) + browser.width = line_len; + ++browser.nr_entries; } - fclose(fp); - free(line); - - max_usable_width = cols - 22; - if (max_line_len > max_usable_width) - max_line_len = max_usable_width; - - newtListboxSetWidth(tree, max_line_len); - newtCenteredWindow(max_line_len + 2, rows - 5, self->sym->name); - form = newt_form__new(); - newtFormAddComponent(form, tree); - - newtFormRun(form, &es); - newtFormDestroy(form); + browser.width += 18; /* Percentage */ + ui_browser__run(&browser, self->ms.sym->name, &es); + newtFormDestroy(browser.form); newtPopWindow(); + list_for_each_entry_safe(pos, n, &head, node) { + list_del(&pos->node); + objdump_line__free(pos); + } ui_helpline__pop(); -out_free_str: - free(str); } static const void *newt__symbol_tree_get_current(newtComponent self) @@ -527,7 +754,7 @@ static int hist_browser__populate(struct hist_browser *self, struct hists *hists return 0; } -static struct thread *hist_browser__selected_thread(struct hist_browser *self) +static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self) { int *indexes; @@ -543,7 +770,13 @@ static struct thread *hist_browser__selected_thread(struct hist_browser *self) } return NULL; out: - return *(struct thread **)(self->selection + 1); + return container_of(self->selection, struct hist_entry, ms); +} + +static struct thread *hist_browser__selected_thread(struct hist_browser *self) +{ + struct hist_entry *he = hist_browser__selected_entry(self); + return he ? he->thread : NULL; } static int hist_browser__title(char *bf, size_t size, const char *input_name, @@ -637,13 +870,20 @@ int hists__browse(struct hists *self, const char *helpline, const char *input_na continue; do_annotate: if (choice == annotate) { + struct hist_entry *he; + if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) { ui_helpline__puts("No vmlinux file found, can't " "annotate with just a " "kallsyms file"); continue; } - map_symbol__annotate_browser(browser->selection, input_name); + + he = hist_browser__selected_entry(browser); + if (he == NULL) + continue; + + hist_entry__annotate_browser(he); } else if (choice == zoom_dso) { if (dso_filter) { ui_helpline__pop(); @@ -681,8 +921,23 @@ out: return err; } +static struct newtPercentTreeColors { + const char *topColorFg, *topColorBg; + const char *mediumColorFg, *mediumColorBg; + const char *normalColorFg, *normalColorBg; + const char *selColorFg, *selColorBg; + const char *codeColorFg, *codeColorBg; +} defaultPercentTreeColors = { + "red", "lightgray", + "green", "lightgray", + "black", "lightgray", + "lightgray", "magenta", + "blue", "lightgray", +}; + void setup_browser(void) { + struct newtPercentTreeColors *c = &defaultPercentTreeColors; if (!isatty(1)) return; @@ -690,6 +945,11 @@ void setup_browser(void) newtInit(); newtCls(); ui_helpline__puts(" "); + SLtt_set_color(HE_COLORSET_TOP, NULL, c->topColorFg, c->topColorBg); + SLtt_set_color(HE_COLORSET_MEDIUM, NULL, c->mediumColorFg, c->mediumColorBg); + SLtt_set_color(HE_COLORSET_NORMAL, NULL, c->normalColorFg, c->normalColorBg); + SLtt_set_color(HE_COLORSET_SELECTED, NULL, c->selColorFg, c->selColorBg); + SLtt_set_color(HE_COLORSET_CODE, NULL, c->codeColorFg, c->codeColorBg); } void exit_browser(bool wait_for_ok) diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index b7c54eeed9c..af301acc461 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -48,12 +48,6 @@ struct hist_entry { u64 count_us; u64 count_guest_sys; u64 count_guest_us; - - /* - * XXX WARNING! - * thread _has_ to come after ms, see - * hist_browser__selected_thread in util/newt.c - */ struct map_symbol ms; struct thread *thread; u64 ip; -- cgit v1.2.3 From cee75ac7ecc27084accdb9d9d6fde65a09f047ae Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 14 May 2010 13:16:55 -0300 Subject: perf hist: Clarify events_stats fields usage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The events_stats.total field is too generic, rename it to .total_period, and also add a comment explaining that it is the sum of all the .period fields in samples, that is needed because we use auto-freq to avoid sampling artifacts. Ditto for events_stats.lost, that is the sum of all lost_event.lost fields, i.e. the number of events the kernel dropped. Looking at the users, builtin-sched.c can make use of these fields and stop doing it again. Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-diff.c | 2 +- tools/perf/builtin-report.c | 8 ++++---- tools/perf/builtin-sched.c | 17 ++++++----------- tools/perf/builtin-trace.c | 2 +- tools/perf/util/event.c | 2 +- tools/perf/util/hist.c | 20 ++++++++++---------- tools/perf/util/hist.h | 16 ++++++++++++++-- tools/perf/util/newt.c | 6 +++--- tools/perf/util/session.c | 2 +- 9 files changed, 41 insertions(+), 34 deletions(-) (limited to 'tools/perf/util/newt.c') diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 3a95a0260a5..6dd4bdae8a8 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -54,7 +54,7 @@ static int diff__process_sample_event(event_t *event, struct perf_session *sessi return -1; } - session->hists.stats.total += data.period; + session->hists.stats.total_period += data.period; return 0; } diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index f13cda1ef05..b8f47ded628 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -138,14 +138,14 @@ static int add_event_total(struct perf_session *session, if (!hists) return -ENOMEM; - hists->stats.total += data->period; + hists->stats.total_period += data->period; /* * FIXME: add_event_total should be moved from here to * perf_session__process_event so that the proper hist is passed to * the event_op methods. */ hists__inc_nr_events(hists, PERF_RECORD_SAMPLE); - session->hists.stats.total += data->period; + session->hists.stats.total_period += data->period; return 0; } @@ -322,10 +322,10 @@ static int __cmd_report(void) if (rb_first(&session->hists.entries) == rb_last(&session->hists.entries)) fprintf(stdout, "# Samples: %Ld\n#\n", - hists->stats.total); + hists->stats.total_period); else fprintf(stdout, "# Samples: %Ld %s\n#\n", - hists->stats.total, + hists->stats.total_period, __event_name(hists->type, hists->config)); hists__fprintf(hists, NULL, false, stdout); diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index aef6ed0e119..be7bc926471 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -1641,19 +1641,10 @@ static int process_sample_event(event_t *event, struct perf_session *session) return 0; } -static int process_lost_event(event_t *event __used, - struct perf_session *session __used) -{ - nr_lost_chunks++; - nr_lost_events += event->lost.lost; - - return 0; -} - static struct perf_event_ops event_ops = { .sample = process_sample_event, .comm = event__process_comm, - .lost = process_lost_event, + .lost = event__process_lost, .ordered_samples = true, }; @@ -1664,8 +1655,12 @@ static int read_events(void) if (session == NULL) return -ENOMEM; - if (perf_session__has_traces(session, "record -R")) + if (perf_session__has_traces(session, "record -R")) { err = perf_session__process_events(session, &event_ops); + nr_events = session->hists.stats.nr_events[0]; + nr_lost_events = session->hists.stats.total_lost; + nr_lost_chunks = session->hists.stats.nr_events[PERF_RECORD_LOST]; + } perf_session__delete(session); return err; diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 95fcb0517a9..dddf3f01b5a 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -109,7 +109,7 @@ static int process_sample_event(event_t *event, struct perf_session *session) data.time, thread->comm); } - session->hists.stats.total += data.period; + session->hists.stats.total_period += data.period; return 0; } diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 3e8fec17304..50771b5813e 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -385,7 +385,7 @@ int event__process_comm(event_t *self, struct perf_session *session) int event__process_lost(event_t *self, struct perf_session *session) { dump_printf(": id:%Ld: lost:%Ld\n", self->lost.id, self->lost.lost); - session->hists.stats.lost += self->lost.lost; + session->hists.stats.total_lost += self->lost.lost; return 0; } diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 1614ad71004..c5922451808 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -239,7 +239,7 @@ void hists__output_resort(struct hists *self) struct hist_entry *n; u64 min_callchain_hits; - min_callchain_hits = self->stats.total * (callchain_param.min_percent / 100); + min_callchain_hits = self->stats.total_period * (callchain_param.min_percent / 100); tmp = RB_ROOT; next = rb_first(&self->entries); @@ -525,7 +525,7 @@ int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, if (pair_hists) { count = self->pair ? self->pair->count : 0; - total = pair_hists->stats.total; + total = pair_hists->stats.total_period; count_sys = self->pair ? self->pair->count_sys : 0; count_us = self->pair ? self->pair->count_us : 0; count_guest_sys = self->pair ? self->pair->count_guest_sys : 0; @@ -769,10 +769,10 @@ print_entries: ++position; } ret += hist_entry__fprintf(h, pair, show_displacement, - displacement, fp, self->stats.total); + displacement, fp, self->stats.total_period); if (symbol_conf.use_callchain) - ret += hist_entry__fprintf_callchain(h, fp, self->stats.total); + ret += hist_entry__fprintf_callchain(h, fp, self->stats.total_period); if (h->ms.map == NULL && verbose > 1) { __map_groups__fprintf_maps(&h->thread->mg, @@ -795,7 +795,7 @@ void hists__filter_by_dso(struct hists *self, const struct dso *dso) { struct rb_node *nd; - self->nr_entries = self->stats.total = 0; + self->nr_entries = self->stats.total_period = 0; self->max_sym_namelen = 0; for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { @@ -812,7 +812,7 @@ void hists__filter_by_dso(struct hists *self, const struct dso *dso) h->filtered &= ~(1 << HIST_FILTER__DSO); if (!h->filtered) { ++self->nr_entries; - self->stats.total += h->count; + self->stats.total_period += h->count; if (h->ms.sym && self->max_sym_namelen < h->ms.sym->namelen) self->max_sym_namelen = h->ms.sym->namelen; @@ -824,7 +824,7 @@ void hists__filter_by_thread(struct hists *self, const struct thread *thread) { struct rb_node *nd; - self->nr_entries = self->stats.total = 0; + self->nr_entries = self->stats.total_period = 0; self->max_sym_namelen = 0; for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { @@ -837,7 +837,7 @@ void hists__filter_by_thread(struct hists *self, const struct thread *thread) h->filtered &= ~(1 << HIST_FILTER__THREAD); if (!h->filtered) { ++self->nr_entries; - self->stats.total += h->count; + self->stats.total_period += h->count; if (h->ms.sym && self->max_sym_namelen < h->ms.sym->namelen) self->max_sym_namelen = h->ms.sym->namelen; @@ -1031,8 +1031,8 @@ int hist_entry__annotate(struct hist_entry *self, struct list_head *head) void hists__inc_nr_events(struct hists *self, u32 type) { - ++self->hists.stats.nr_events[0]; - ++self->hists.stats.nr_events[type]; + ++self->stats.nr_events[0]; + ++self->stats.nr_events[type]; } size_t hists__fprintf_nr_events(struct hists *self, FILE *fp) diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 97b8962ff69..da6b84814a5 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -37,9 +37,21 @@ struct sym_priv { struct sym_ext *ext; }; +/* + * The kernel collects the number of events it couldn't send in a stretch and + * when possible sends this number in a PERF_RECORD_LOST event. The number of + * such "chunks" of lost events is stored in .nr_events[PERF_EVENT_LOST] while + * total_lost tells exactly how many events the kernel in fact lost, i.e. it is + * the sum of all struct lost_event.lost fields reported. + * + * The total_period is needed because by default auto-freq is used, so + * multipling nr_events[PERF_EVENT_SAMPLE] by a frequency isn't possible to get + * the total number of low level events, it is necessary to to sum all struct + * sample_event.period and stash the result in total_period. + */ struct events_stats { - u64 total; - u64 lost; + u64 total_period; + u64 total_lost; u32 nr_events[PERF_RECORD_HEADER_MAX]; u32 nr_unknown_events; }; diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index ba6acd04c08..010bacf4016 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -689,7 +689,7 @@ static int hist_browser__populate(struct hist_browser *self, struct hists *hists } snprintf(str, sizeof(str), "Samples: %Ld ", - hists->stats.total); + hists->stats.total_period); newtDrawRootText(0, 0, str); newtGetScreenSize(NULL, &rows); @@ -718,12 +718,12 @@ static int hist_browser__populate(struct hist_browser *self, struct hists *hists if (h->filtered) continue; - len = hist_entry__append_browser(h, self->tree, hists->stats.total); + len = hist_entry__append_browser(h, self->tree, hists->stats.total_period); if (len > max_len) max_len = len; if (symbol_conf.use_callchain) hist_entry__append_callchain_browser(h, self->tree, - hists->stats.total, idx++); + hists->stats.total_period, idx++); ++curr_hist; if (curr_hist % 5) ui_progress__update(progress, curr_hist); diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 7231f6b19fb..25bfca4f10f 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -549,7 +549,7 @@ static int perf_session__process_event(struct perf_session *self, dump_printf("%#Lx [%#x]: PERF_RECORD_%s", offset + head, event->header.size, event__name[event->header.type]); - hists__inc_nr_events(self, event->header.type); + hists__inc_nr_events(&self->hists, event->header.type); } if (self->header.needs_swap && event__swap_ops[event->header.type]) -- cgit v1.2.3 From c82ee828aa20487d254a5225d256cd422acee459 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 14 May 2010 14:19:35 -0300 Subject: perf report: Report number of events, not samples MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Number of samples is meaningless after we switched to auto-freq, so report the number of events, i.e. not the sum of the different periods, but the number PERF_RECORD_SAMPLE emitted by the kernel. While doing this I noticed that naming "count" to the sum of all the event periods can be confusing, so rename it to .period, just like in struct sample.data, so that we become more consistent. This helps with the next step, that was to record in struct hist_entry the number of sample events for each instance, we need that because we use it to generate the number of events when applying filters to the tree of hist entries like it is being done in the TUI report browser. Suggested-by: Ingo Molnar Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-diff.c | 6 ++-- tools/perf/builtin-report.c | 32 +++++++++++------ tools/perf/util/hist.c | 88 ++++++++++++++++++++++++--------------------- tools/perf/util/hist.h | 2 +- tools/perf/util/newt.c | 8 +++-- tools/perf/util/sort.h | 11 +++--- tools/perf/util/util.c | 22 ++++++++++++ tools/perf/util/util.h | 1 + 8 files changed, 107 insertions(+), 63 deletions(-) (limited to 'tools/perf/util/newt.c') diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 6dd4bdae8a8..a6e2fdc7a04 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -23,9 +23,9 @@ static bool force; static bool show_displacement; static int hists__add_entry(struct hists *self, - struct addr_location *al, u64 count) + struct addr_location *al, u64 period) { - if (__hists__add_entry(self, al, NULL, count) != NULL) + if (__hists__add_entry(self, al, NULL, period) != NULL) return 0; return -ENOMEM; } @@ -50,7 +50,7 @@ static int diff__process_sample_event(event_t *event, struct perf_session *sessi event__parse_sample(event, session->sample_type, &data); if (hists__add_entry(&session->hists, &al, data.period)) { - pr_warning("problem incrementing symbol count, skipping event\n"); + pr_warning("problem incrementing symbol period, skipping event\n"); return -1; } diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index b8f47ded628..68265120ee0 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -188,14 +188,14 @@ static int process_sample_event(event_t *event, struct perf_session *session) return 0; if (perf_session__add_hist_entry(session, &al, &data)) { - pr_debug("problem incrementing symbol count, skipping event\n"); + pr_debug("problem incrementing symbol period, skipping event\n"); return -1; } attr = perf_header__find_attr(data.id, &session->header); if (add_event_total(session, &data, attr)) { - pr_debug("problem adding event count\n"); + pr_debug("problem adding event period\n"); return -1; } @@ -269,11 +269,25 @@ static struct perf_event_ops event_ops = { extern volatile int session_done; -static void sig_handler(int sig __attribute__((__unused__))) +static void sig_handler(int sig __used) { session_done = 1; } +static size_t hists__fprintf_nr_sample_events(struct hists *self, + const char *evname, FILE *fp) +{ + size_t ret; + char unit; + unsigned long nr_events = self->stats.nr_events[PERF_RECORD_SAMPLE]; + + nr_events = convert_unit(nr_events, &unit); + ret = fprintf(fp, "# Events: %lu%c", nr_events, unit); + if (evname != NULL) + ret += fprintf(fp, " %s", evname); + return ret + fprintf(fp, "\n#\n"); +} + static int __cmd_report(void) { int ret = -EINVAL; @@ -319,14 +333,12 @@ static int __cmd_report(void) if (use_browser) hists__browse(hists, help, input_name); else { - if (rb_first(&session->hists.entries) == + const char *evname = NULL; + if (rb_first(&session->hists.entries) != rb_last(&session->hists.entries)) - fprintf(stdout, "# Samples: %Ld\n#\n", - hists->stats.total_period); - else - fprintf(stdout, "# Samples: %Ld %s\n#\n", - hists->stats.total_period, - __event_name(hists->type, hists->config)); + evname = __event_name(hists->type, hists->config); + + hists__fprintf_nr_sample_events(hists, evname, stdout); hists__fprintf(hists, NULL, false, stdout); fprintf(stdout, "\n\n"); diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index c5922451808..f75c5f62401 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -9,21 +9,21 @@ struct callchain_param callchain_param = { .min_percent = 0.5 }; -static void hist_entry__add_cpumode_count(struct hist_entry *self, - unsigned int cpumode, u64 count) +static void hist_entry__add_cpumode_period(struct hist_entry *self, + unsigned int cpumode, u64 period) { switch (cpumode) { case PERF_RECORD_MISC_KERNEL: - self->count_sys += count; + self->period_sys += period; break; case PERF_RECORD_MISC_USER: - self->count_us += count; + self->period_us += period; break; case PERF_RECORD_MISC_GUEST_KERNEL: - self->count_guest_sys += count; + self->period_guest_sys += period; break; case PERF_RECORD_MISC_GUEST_USER: - self->count_guest_us += count; + self->period_guest_us += period; break; default: break; @@ -31,7 +31,7 @@ static void hist_entry__add_cpumode_count(struct hist_entry *self, } /* - * histogram, sorted on item, collects counts + * histogram, sorted on item, collects periods */ static struct hist_entry *hist_entry__new(struct hist_entry *template) @@ -41,6 +41,7 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template) if (self != NULL) { *self = *template; + self->nr_events = 1; if (symbol_conf.use_callchain) callchain_init(self->callchain); } @@ -57,7 +58,7 @@ static void hists__inc_nr_entries(struct hists *self, struct hist_entry *entry) struct hist_entry *__hists__add_entry(struct hists *self, struct addr_location *al, - struct symbol *sym_parent, u64 count) + struct symbol *sym_parent, u64 period) { struct rb_node **p = &self->entries.rb_node; struct rb_node *parent = NULL; @@ -70,7 +71,7 @@ struct hist_entry *__hists__add_entry(struct hists *self, }, .ip = al->addr, .level = al->level, - .count = count, + .period = period, .parent = sym_parent, }; int cmp; @@ -82,7 +83,8 @@ struct hist_entry *__hists__add_entry(struct hists *self, cmp = hist_entry__cmp(&entry, he); if (!cmp) { - he->count += count; + he->period += period; + ++he->nr_events; goto out; } @@ -99,7 +101,7 @@ struct hist_entry *__hists__add_entry(struct hists *self, rb_insert_color(&he->rb_node, &self->entries); hists__inc_nr_entries(self, he); out: - hist_entry__add_cpumode_count(he, al->cpumode, count); + hist_entry__add_cpumode_period(he, al->cpumode, period); return he; } @@ -160,7 +162,7 @@ static bool collapse__insert_entry(struct rb_root *root, struct hist_entry *he) cmp = hist_entry__collapse(iter, he); if (!cmp) { - iter->count += he->count; + iter->period += he->period; hist_entry__free(he); return false; } @@ -203,7 +205,7 @@ void hists__collapse_resort(struct hists *self) } /* - * reverse the map, sort on count. + * reverse the map, sort on period. */ static void __hists__insert_output_entry(struct rb_root *entries, @@ -222,7 +224,7 @@ static void __hists__insert_output_entry(struct rb_root *entries, parent = *p; iter = rb_entry(parent, struct hist_entry, rb_node); - if (he->count > iter->count) + if (he->period > iter->period) p = &(*p)->rb_left; else p = &(*p)->rb_right; @@ -288,7 +290,7 @@ static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask, } static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, - int depth, int depth_mask, int count, + int depth, int depth_mask, int period, u64 total_samples, int hits, int left_margin) { @@ -301,7 +303,7 @@ static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, ret += fprintf(fp, "|"); else ret += fprintf(fp, " "); - if (!count && i == depth - 1) { + if (!period && i == depth - 1) { double percent; percent = hits * 100.0 / total_samples; @@ -516,7 +518,7 @@ int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, long displacement, bool color, u64 session_total) { struct sort_entry *se; - u64 count, total, count_sys, count_us, count_guest_sys, count_guest_us; + u64 period, total, period_sys, period_us, period_guest_sys, period_guest_us; const char *sep = symbol_conf.field_sep; int ret; @@ -524,57 +526,57 @@ int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, return 0; if (pair_hists) { - count = self->pair ? self->pair->count : 0; + period = self->pair ? self->pair->period : 0; total = pair_hists->stats.total_period; - count_sys = self->pair ? self->pair->count_sys : 0; - count_us = self->pair ? self->pair->count_us : 0; - count_guest_sys = self->pair ? self->pair->count_guest_sys : 0; - count_guest_us = self->pair ? self->pair->count_guest_us : 0; + period_sys = self->pair ? self->pair->period_sys : 0; + period_us = self->pair ? self->pair->period_us : 0; + period_guest_sys = self->pair ? self->pair->period_guest_sys : 0; + period_guest_us = self->pair ? self->pair->period_guest_us : 0; } else { - count = self->count; + period = self->period; total = session_total; - count_sys = self->count_sys; - count_us = self->count_us; - count_guest_sys = self->count_guest_sys; - count_guest_us = self->count_guest_us; + period_sys = self->period_sys; + period_us = self->period_us; + period_guest_sys = self->period_guest_sys; + period_guest_us = self->period_guest_us; } if (total) { if (color) ret = percent_color_snprintf(s, size, sep ? "%.2f" : " %6.2f%%", - (count * 100.0) / total); + (period * 100.0) / total); else ret = snprintf(s, size, sep ? "%.2f" : " %6.2f%%", - (count * 100.0) / total); + (period * 100.0) / total); if (symbol_conf.show_cpu_utilization) { ret += percent_color_snprintf(s + ret, size - ret, sep ? "%.2f" : " %6.2f%%", - (count_sys * 100.0) / total); + (period_sys * 100.0) / total); ret += percent_color_snprintf(s + ret, size - ret, sep ? "%.2f" : " %6.2f%%", - (count_us * 100.0) / total); + (period_us * 100.0) / total); if (perf_guest) { ret += percent_color_snprintf(s + ret, size - ret, sep ? "%.2f" : " %6.2f%%", - (count_guest_sys * 100.0) / + (period_guest_sys * 100.0) / total); ret += percent_color_snprintf(s + ret, size - ret, sep ? "%.2f" : " %6.2f%%", - (count_guest_us * 100.0) / + (period_guest_us * 100.0) / total); } } } else - ret = snprintf(s, size, sep ? "%lld" : "%12lld ", count); + ret = snprintf(s, size, sep ? "%lld" : "%12lld ", period); if (symbol_conf.show_nr_samples) { if (sep) - ret += snprintf(s + ret, size - ret, "%c%lld", *sep, count); + ret += snprintf(s + ret, size - ret, "%c%lld", *sep, period); else - ret += snprintf(s + ret, size - ret, "%11lld", count); + ret += snprintf(s + ret, size - ret, "%11lld", period); } if (pair_hists) { @@ -582,9 +584,9 @@ int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, double old_percent = 0, new_percent = 0, diff; if (total > 0) - old_percent = (count * 100.0) / total; + old_percent = (period * 100.0) / total; if (session_total > 0) - new_percent = (self->count * 100.0) / session_total; + new_percent = (self->period * 100.0) / session_total; diff = new_percent - old_percent; @@ -796,6 +798,7 @@ void hists__filter_by_dso(struct hists *self, const struct dso *dso) struct rb_node *nd; self->nr_entries = self->stats.total_period = 0; + self->stats.nr_events[PERF_RECORD_SAMPLE] = 0; self->max_sym_namelen = 0; for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { @@ -812,7 +815,8 @@ void hists__filter_by_dso(struct hists *self, const struct dso *dso) h->filtered &= ~(1 << HIST_FILTER__DSO); if (!h->filtered) { ++self->nr_entries; - self->stats.total_period += h->count; + self->stats.total_period += h->period; + self->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events; if (h->ms.sym && self->max_sym_namelen < h->ms.sym->namelen) self->max_sym_namelen = h->ms.sym->namelen; @@ -825,6 +829,7 @@ void hists__filter_by_thread(struct hists *self, const struct thread *thread) struct rb_node *nd; self->nr_entries = self->stats.total_period = 0; + self->stats.nr_events[PERF_RECORD_SAMPLE] = 0; self->max_sym_namelen = 0; for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { @@ -837,7 +842,8 @@ void hists__filter_by_thread(struct hists *self, const struct thread *thread) h->filtered &= ~(1 << HIST_FILTER__THREAD); if (!h->filtered) { ++self->nr_entries; - self->stats.total_period += h->count; + self->stats.total_period += h->period; + self->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events; if (h->ms.sym && self->max_sym_namelen < h->ms.sym->namelen) self->max_sym_namelen = h->ms.sym->namelen; @@ -881,7 +887,7 @@ int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip) h->sum++; h->ip[offset]++; - pr_debug3("%#Lx %s: count++ [ip: %#Lx, %#Lx] => %Ld\n", self->ms.sym->start, + pr_debug3("%#Lx %s: period++ [ip: %#Lx, %#Lx] => %Ld\n", self->ms.sym->start, self->ms.sym->name, ip, ip - self->ms.sym->start, h->ip[offset]); return 0; } diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index da6b84814a5..6f17dcd8412 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -69,7 +69,7 @@ struct hists { struct hist_entry *__hists__add_entry(struct hists *self, struct addr_location *al, - struct symbol *parent, u64 count); + struct symbol *parent, u64 period); extern int64_t hist_entry__cmp(struct hist_entry *, struct hist_entry *); extern int64_t hist_entry__collapse(struct hist_entry *, struct hist_entry *); int hist_entry__fprintf(struct hist_entry *self, struct hists *pair_hists, diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 010bacf4016..3402453812b 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -680,16 +680,18 @@ static int hist_browser__populate(struct hist_browser *self, struct hists *hists struct ui_progress *progress; struct rb_node *nd; u64 curr_hist = 0; - char seq[] = "."; + char seq[] = ".", unit; char str[256]; + unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE]; if (self->form) { newtFormDestroy(self->form); newtPopWindow(); } - snprintf(str, sizeof(str), "Samples: %Ld ", - hists->stats.total_period); + nr_events = convert_unit(nr_events, &unit); + snprintf(str, sizeof(str), "Events: %lu%c ", + nr_events, unit); newtDrawRootText(0, 0, str); newtGetScreenSize(NULL, &rows); diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index af301acc461..eab2e0b3b74 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -43,14 +43,15 @@ extern enum sort_type sort__first_dimension; struct hist_entry { struct rb_node rb_node; - u64 count; - u64 count_sys; - u64 count_us; - u64 count_guest_sys; - u64 count_guest_us; + u64 period; + u64 period_sys; + u64 period_us; + u64 period_guest_sys; + u64 period_guest_us; struct map_symbol ms; struct thread *thread; u64 ip; + u32 nr_events; char level; u8 filtered; struct symbol *parent; diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index f9b890fde68..214265674dd 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c @@ -92,3 +92,25 @@ out_close_from: out: return err; } + +unsigned long convert_unit(unsigned long value, char *unit) +{ + *unit = ' '; + + if (value > 1000) { + value /= 1000; + *unit = 'K'; + } + + if (value > 1000) { + value /= 1000; + *unit = 'M'; + } + + if (value > 1000) { + value /= 1000; + *unit = 'G'; + } + + return value; +} diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index fbf45d1b26f..0795bf304b1 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -423,6 +423,7 @@ char **argv_split(const char *str, int *argcp); void argv_free(char **argv); 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); #define _STR(x) #x #define STR(x) _STR(x) -- cgit v1.2.3 From 3e1bbdc3a721f4b1ed44f4554402a8dbc60fa97f Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 14 May 2010 20:05:21 -0300 Subject: perf newt: Make <- zoom out filters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After we use the filters to zoom into DSOs or threads, we can use <- (left arrow) to zoom out from the last filter applied. It is still possible to zoom out of order by using the popup menu. With this we now have the zoom out operation on the browsing fast path, by allowing fast navigation using just the four arrors and the enter key to expand collapse callchains. Suggested-by: Ingo Molnar Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile | 2 ++ tools/perf/util/newt.c | 34 ++++++++++++++++++++-- tools/perf/util/pstack.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/pstack.h | 12 ++++++++ 4 files changed, 120 insertions(+), 3 deletions(-) create mode 100644 tools/perf/util/pstack.c create mode 100644 tools/perf/util/pstack.h (limited to 'tools/perf/util/newt.c') diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 9c4dc30cdc1..a9281cca411 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -416,6 +416,7 @@ LIB_H += util/thread.h LIB_H += util/trace-event.h LIB_H += util/probe-finder.h LIB_H += util/probe-event.h +LIB_H += util/pstack.h LIB_H += util/cpumap.h LIB_OBJS += $(OUTPUT)util/abspath.o @@ -451,6 +452,7 @@ LIB_OBJS += $(OUTPUT)util/callchain.o LIB_OBJS += $(OUTPUT)util/values.o LIB_OBJS += $(OUTPUT)util/debug.o LIB_OBJS += $(OUTPUT)util/map.o +LIB_OBJS += $(OUTPUT)util/pstack.o LIB_OBJS += $(OUTPUT)util/session.o LIB_OBJS += $(OUTPUT)util/thread.o LIB_OBJS += $(OUTPUT)util/trace-event-parse.o diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 3402453812b..e74df1240ef 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -9,6 +9,7 @@ #include "cache.h" #include "hist.h" +#include "pstack.h" #include "session.h" #include "sort.h" #include "symbol.h" @@ -750,6 +751,7 @@ static int hist_browser__populate(struct hist_browser *self, struct hists *hists newtFormAddHotKey(self->form, 'A'); newtFormAddHotKey(self->form, 'a'); newtFormAddHotKey(self->form, NEWT_KEY_RIGHT); + newtFormAddHotKey(self->form, NEWT_KEY_LEFT); newtFormAddComponents(self->form, self->tree, NULL); self->selection = newt__symbol_tree_get_current(self->tree); @@ -801,6 +803,7 @@ static int hist_browser__title(char *bf, size_t size, const char *input_name, int hists__browse(struct hists *self, const char *helpline, const char *input_name) { struct hist_browser *browser = hist_browser__new(); + struct pstack *fstack = pstack__new(2); const struct thread *thread_filter = NULL; const struct dso *dso_filter = NULL; struct newtExitStruct es; @@ -810,12 +813,16 @@ int hists__browse(struct hists *self, const char *helpline, const char *input_na if (browser == NULL) return -1; + fstack = pstack__new(2); + if (fstack == NULL) + goto out; + ui_helpline__push(helpline); hist_browser__title(msg, sizeof(msg), input_name, dso_filter, thread_filter); if (hist_browser__populate(browser, self, msg) < 0) - goto out; + goto out_free_stack; while (1) { const struct thread *thread; @@ -836,6 +843,19 @@ int hists__browse(struct hists *self, const char *helpline, const char *input_na else continue; } + + if (es.u.key == NEWT_KEY_LEFT) { + const void *top; + + if (pstack__empty(fstack)) + continue; + top = pstack__pop(fstack); + if (top == &dso_filter) + goto zoom_out_dso; + if (top == &thread_filter) + goto zoom_out_thread; + continue; + } } if (browser->selection->sym != NULL && @@ -888,12 +908,15 @@ do_annotate: hist_entry__annotate_browser(he); } else if (choice == zoom_dso) { if (dso_filter) { + pstack__remove(fstack, &dso_filter); +zoom_out_dso: ui_helpline__pop(); dso_filter = NULL; } else { - ui_helpline__fpush("To zoom out press -> + \"Zoom out of %s DSO\"", + ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"", dso->kernel ? "the Kernel" : dso->short_name); dso_filter = dso; + pstack__push(fstack, &dso_filter); } hists__filter_by_dso(self, dso_filter); hist_browser__title(msg, sizeof(msg), input_name, @@ -902,13 +925,16 @@ do_annotate: goto out; } else if (choice == zoom_thread) { if (thread_filter) { + pstack__remove(fstack, &thread_filter); +zoom_out_thread: ui_helpline__pop(); thread_filter = NULL; } else { - ui_helpline__fpush("To zoom out press -> + \"Zoom out of %s(%d) thread\"", + ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"", thread->comm_set ? thread->comm : "", thread->pid); thread_filter = thread; + pstack__push(fstack, &thread_filter); } hists__filter_by_thread(self, thread_filter); hist_browser__title(msg, sizeof(msg), input_name, @@ -918,6 +944,8 @@ do_annotate: } } err = 0; +out_free_stack: + pstack__delete(fstack); out: hist_browser__delete(browser); return err; diff --git a/tools/perf/util/pstack.c b/tools/perf/util/pstack.c new file mode 100644 index 00000000000..13d36faf64e --- /dev/null +++ b/tools/perf/util/pstack.c @@ -0,0 +1,75 @@ +/* + * Simple pointer stack + * + * (c) 2010 Arnaldo Carvalho de Melo + */ + +#include "util.h" +#include "pstack.h" +#include +#include + +struct pstack { + unsigned short top; + unsigned short max_nr_entries; + void *entries[0]; +}; + +struct pstack *pstack__new(unsigned short max_nr_entries) +{ + struct pstack *self = zalloc((sizeof(*self) + + max_nr_entries * sizeof(void *))); + if (self != NULL) + self->max_nr_entries = max_nr_entries; + return self; +} + +void pstack__delete(struct pstack *self) +{ + free(self); +} + +bool pstack__empty(const struct pstack *self) +{ + return self->top == 0; +} + +void pstack__remove(struct pstack *self, void *key) +{ + unsigned short i = self->top, last_index = self->top - 1; + + while (i-- != 0) { + if (self->entries[i] == key) { + if (i < last_index) + memmove(self->entries + i, + self->entries + i + 1, + (last_index - i) * sizeof(void *)); + --self->top; + return; + } + } + pr_err("%s: %p not on the pstack!\n", __func__, key); +} + +void pstack__push(struct pstack *self, void *key) +{ + if (self->top == self->max_nr_entries) { + pr_err("%s: top=%d, overflow!\n", __func__, self->top); + return; + } + self->entries[self->top++] = key; +} + +void *pstack__pop(struct pstack *self) +{ + void *ret; + + if (self->top == 0) { + pr_err("%s: underflow!\n", __func__); + return NULL; + } + + ret = self->entries[--self->top]; + self->entries[self->top] = NULL; + return ret; +} diff --git a/tools/perf/util/pstack.h b/tools/perf/util/pstack.h new file mode 100644 index 00000000000..5ad07023504 --- /dev/null +++ b/tools/perf/util/pstack.h @@ -0,0 +1,12 @@ +#ifndef _PERF_PSTACK_ +#define _PERF_PSTACK_ + +struct pstack; +struct pstack *pstack__new(unsigned short max_nr_entries); +void pstack__delete(struct pstack *self); +bool pstack__empty(const struct pstack *self); +void pstack__remove(struct pstack *self, void *key); +void pstack__push(struct pstack *self, void *key); +void *pstack__pop(struct pstack *self); + +#endif /* _PERF_PSTACK_ */ -- cgit v1.2.3 From 605539034f208d365f76af8e2152cb25f702367d Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 15 May 2010 20:40:34 -0300 Subject: perf newt: Make <- exit the ui_browser MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Right now that means that pressing the left arrow willl make the symbol annotation window to exit back to the main symbol histogram browser. This is another improvement on the UI fastpath, i.e. just the arrows and enter are enough for most browsing. Suggested-by: Ingo Molnar Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/newt.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'tools/perf/util/newt.c') diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index e74df1240ef..5ded2f0d3e4 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -323,6 +323,7 @@ static int ui_browser__run(struct ui_browser *self, const char *title, newtFormAddHotKey(self->form, NEWT_KEY_PGDN); newtFormAddHotKey(self->form, NEWT_KEY_HOME); newtFormAddHotKey(self->form, NEWT_KEY_END); + newtFormAddHotKey(self->form, NEWT_KEY_LEFT); if (ui_browser__refresh_entries(self) < 0) return -1; @@ -408,6 +409,7 @@ static int ui_browser__run(struct ui_browser *self, const char *title, } break; case NEWT_KEY_ESCAPE: + case NEWT_KEY_LEFT: case CTRL('c'): case 'Q': case 'q': @@ -616,7 +618,7 @@ static void hist_entry__annotate_browser(struct hist_entry *self) if (hist_entry__annotate(self, &head) < 0) return; - ui_helpline__push("Press ESC to exit"); + ui_helpline__push("Press <- or ESC to exit"); memset(&browser, 0, sizeof(browser)); browser.entries = &head; -- cgit v1.2.3 From c1ec5fefd9cd9ccb020966a49a3c7f44b25d9e84 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 15 May 2010 20:45:31 -0300 Subject: perf newt: Fix the 'A'/'a' shortcut for annotate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reported-by: Ingo Molnar Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/newt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tools/perf/util/newt.c') diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 5ded2f0d3e4..094887f45d0 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -892,10 +892,10 @@ int hists__browse(struct hists *self, const char *helpline, const char *input_na if (choice == -1) continue; -do_annotate: + if (choice == annotate) { struct hist_entry *he; - +do_annotate: if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) { ui_helpline__puts("No vmlinux file found, can't " "annotate with just a " -- cgit v1.2.3 From 29351db6a05e7e42be457569428425520a18beec Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 15 May 2010 21:06:58 -0300 Subject: perf newt: Exit browser unconditionally when CTRL+C, q or Q is pressed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ESC still asks for confirmation. Suggested-by: Ingo Molnar Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/newt.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'tools/perf/util/newt.c') diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 094887f45d0..139eb1a16cd 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -837,9 +837,10 @@ int hists__browse(struct hists *self, const char *helpline, const char *input_na if (es.reason == NEWT_EXIT_HOTKEY) { if (toupper(es.u.key) == 'A') goto do_annotate; - if (es.u.key == NEWT_KEY_ESCAPE || - toupper(es.u.key) == 'Q' || - es.u.key == CTRL('c')) { + 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?")) break; else -- cgit v1.2.3 From 9d192e118a094087494997ea1c8a2faf39af38c5 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 15 May 2010 21:15:01 -0300 Subject: perf newt: Add single key shortcuts for zoom into DSO and threads MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 'D'/'d' for zooming into the DSO in the current highlighted hist entry, 'T'/'t' for zooming into the current thread. Suggested-by: Ingo Molnar Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/newt.c | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) (limited to 'tools/perf/util/newt.c') diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 139eb1a16cd..6974431d212 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -752,6 +752,10 @@ static int hist_browser__populate(struct hist_browser *self, struct hists *hists newtFormAddHotKey(self->form, 'A'); newtFormAddHotKey(self->form, 'a'); + newtFormAddHotKey(self->form, 'D'); + newtFormAddHotKey(self->form, 'd'); + newtFormAddHotKey(self->form, 'T'); + newtFormAddHotKey(self->form, 't'); newtFormAddHotKey(self->form, NEWT_KEY_RIGHT); newtFormAddHotKey(self->form, NEWT_KEY_LEFT); newtFormAddComponents(self->form, self->tree, NULL); @@ -834,9 +838,20 @@ int hists__browse(struct hists *self, const char *helpline, const char *input_na annotate = -2, zoom_dso = -2, zoom_thread = -2; newtFormRun(browser->form, &es); + + thread = hist_browser__selected_thread(browser); + dso = browser->selection->map ? browser->selection->map->dso : NULL; + if (es.reason == NEWT_EXIT_HOTKEY) { - if (toupper(es.u.key) == 'A') + switch (toupper(es.u.key)) { + case 'A': goto do_annotate; + case 'D': + goto zoom_dso; + case 'T': + goto zoom_thread; + default:; + } if (toupper(es.u.key) == 'Q' || es.u.key == CTRL('c')) break; @@ -866,7 +881,6 @@ int hists__browse(struct hists *self, const char *helpline, const char *input_na browser->selection->sym->name) > 0) annotate = nr_options++; - thread = hist_browser__selected_thread(browser); if (thread != NULL && asprintf(&options[nr_options], "Zoom %s %s(%d) thread", (thread_filter ? "out of" : "into"), @@ -874,7 +888,6 @@ int hists__browse(struct hists *self, const char *helpline, const char *input_na thread->pid) > 0) zoom_thread = nr_options++; - dso = browser->selection->map ? browser->selection->map->dso : NULL; if (dso != NULL && asprintf(&options[nr_options], "Zoom %s %s DSO", (dso_filter ? "out of" : "into"), @@ -910,12 +923,15 @@ do_annotate: hist_entry__annotate_browser(he); } else if (choice == zoom_dso) { +zoom_dso: if (dso_filter) { pstack__remove(fstack, &dso_filter); zoom_out_dso: ui_helpline__pop(); dso_filter = NULL; } else { + if (dso == NULL) + continue; ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"", dso->kernel ? "the Kernel" : dso->short_name); dso_filter = dso; @@ -927,6 +943,7 @@ zoom_out_dso: if (hist_browser__populate(browser, self, msg) < 0) goto out; } else if (choice == zoom_thread) { +zoom_thread: if (thread_filter) { pstack__remove(fstack, &thread_filter); zoom_out_thread: -- cgit v1.2.3 From a308f3a868185d4f804fe71d0400e2b058c6d9af Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 16 May 2010 20:29:38 -0300 Subject: perf tui: Make <- exit menus too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In fact it is now added to the hot key list when newt_form__new is used, allowing us to remove the explicit assignment in all its users. The visible change is that <- will exit the menu that pops up when -> is pressed (and Enter when callchains are not being used). Suggested-by: Ingo Molnar Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/newt.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'tools/perf/util/newt.c') diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 6974431d212..59a63e4405f 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -118,6 +118,7 @@ int browser__show_help(const char *format, va_list ap) static void newt_form__set_exit_keys(newtComponent self) { + newtFormAddHotKey(self, NEWT_KEY_LEFT); newtFormAddHotKey(self, NEWT_KEY_ESCAPE); newtFormAddHotKey(self, 'Q'); newtFormAddHotKey(self, 'q'); @@ -323,7 +324,6 @@ static int ui_browser__run(struct ui_browser *self, const char *title, newtFormAddHotKey(self->form, NEWT_KEY_PGDN); newtFormAddHotKey(self->form, NEWT_KEY_HOME); newtFormAddHotKey(self->form, NEWT_KEY_END); - newtFormAddHotKey(self->form, NEWT_KEY_LEFT); if (ui_browser__refresh_entries(self) < 0) return -1; @@ -757,7 +757,6 @@ static int hist_browser__populate(struct hist_browser *self, struct hists *hists newtFormAddHotKey(self->form, 'T'); newtFormAddHotKey(self->form, 't'); newtFormAddHotKey(self->form, NEWT_KEY_RIGHT); - newtFormAddHotKey(self->form, NEWT_KEY_LEFT); newtFormAddComponents(self->form, self->tree, NULL); self->selection = newt__symbol_tree_get_current(self->tree); -- cgit v1.2.3 From a9a4ab747e2d45bf08fddbc1568f080091486af9 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 16 May 2010 21:04:27 -0300 Subject: perf tui: Add help window to show key associations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Suggested-by: Ingo Molnar Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/newt.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) (limited to 'tools/perf/util/newt.c') diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 59a63e4405f..2001d26a557 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -167,6 +167,48 @@ out_destroy_form: return rc; } +static int ui__help_window(const char *text) +{ + struct newtExitStruct es; + newtComponent tb, form = newt_form__new(); + int rc = -1; + int max_len = 0, nr_lines = 0; + const char *t; + + if (form == NULL) + return -1; + + t = text; + while (1) { + const char *sep = strchr(t, '\n'); + int len; + + if (sep == NULL) + sep = strchr(t, '\0'); + len = sep - t; + if (max_len < len) + max_len = len; + ++nr_lines; + if (*sep == '\0') + break; + t = sep + 1; + } + + tb = newtTextbox(0, 0, max_len, nr_lines, 0); + if (tb == NULL) + goto out_destroy_form; + + newtTextboxSetText(tb, text); + newtFormAddComponent(form, tb); + newtCenteredWindow(max_len, nr_lines, NULL); + newtFormRun(form, &es); + newtPopWindow(); + rc = 0; +out_destroy_form: + newtFormDestroy(form); + return rc; +} + static bool dialog_yesno(const char *msg) { /* newtWinChoice should really be accepting const char pointers... */ @@ -756,6 +798,10 @@ static int hist_browser__populate(struct hist_browser *self, struct hists *hists newtFormAddHotKey(self->form, 'd'); newtFormAddHotKey(self->form, 'T'); newtFormAddHotKey(self->form, 't'); + newtFormAddHotKey(self->form, '?'); + newtFormAddHotKey(self->form, 'H'); + newtFormAddHotKey(self->form, 'h'); + newtFormAddHotKey(self->form, NEWT_KEY_F1); newtFormAddHotKey(self->form, NEWT_KEY_RIGHT); newtFormAddComponents(self->form, self->tree, NULL); self->selection = newt__symbol_tree_get_current(self->tree); @@ -842,6 +888,9 @@ int hists__browse(struct hists *self, const char *helpline, const char *input_na dso = browser->selection->map ? browser->selection->map->dso : NULL; if (es.reason == NEWT_EXIT_HOTKEY) { + if (es.u.key == NEWT_KEY_F1) + goto do_help; + switch (toupper(es.u.key)) { case 'A': goto do_annotate; @@ -849,6 +898,17 @@ int hists__browse(struct hists *self, const char *helpline, const char *input_na goto zoom_dso; case 'T': goto zoom_thread; + case 'H': + case '?': +do_help: + ui__help_window("-> Zoom into DSO/Threads & Annotate current symbol\n" + "<- Zoom out\n" + "a Annotate current symbol\n" + "h/?/F1 Show this window\n" + "d Zoom into current DSO\n" + "t Zoom into current Thread\n" + "q/CTRL+C Exit browser"); + continue; default:; } if (toupper(es.u.key) == 'Q' || -- cgit v1.2.3 From dc4ff19341126155c5714119396efbae62ab40bf Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 17 May 2010 12:25:09 -0300 Subject: perf tui: Add workaround for slang < 2.1.4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Older versions of the slang library didn't used the 'const' specifier, causing problems with modern compilers of this kind: util/newt.c:252: error: passing argument 1 of ‘SLsmg_printf’ discards qualifiers from pointer target type Fix it by using some wrappers that when needed const the affected parameters back to plain (char *). Reported-by: Lin Ming Cc: Frédéric Weisbecker Cc: Lin Ming Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: <20100517145421.GD29052@ghostprotocols.net> Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/newt.c | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) (limited to 'tools/perf/util/newt.c') diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 2001d26a557..ccb7c5bb269 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -14,6 +14,17 @@ #include "sort.h" #include "symbol.h" +#if SLANG_VERSION < 20104 +#define slsmg_printf(msg, args...) SLsmg_printf((char *)msg, ##args) +#define slsmg_write_nstring(msg, len) SLsmg_write_nstring((char *)msg, len) +#define sltt_set_color(obj, name, fg, bg) SLtt_set_color(obj,(char *)name,\ + (char *)fg, (char *)bg) +#else +#define slsmg_printf SLsmg_printf +#define slsmg_write_nstring SLsmg_write_nstring +#define sltt_set_color SLtt_set_color +#endif + struct ui_progress { newtComponent form, scale; }; @@ -292,21 +303,21 @@ static int objdump_line__show(struct objdump_line *self, struct list_head *head, color = ui_browser__percent_color(percent, current_entry); SLsmg_set_color(color); - SLsmg_printf(" %7.2f ", percent); + slsmg_printf(" %7.2f ", percent); if (!current_entry) SLsmg_set_color(HE_COLORSET_CODE); } else { int color = ui_browser__percent_color(0, current_entry); SLsmg_set_color(color); - SLsmg_write_nstring(" ", 9); + slsmg_write_nstring(" ", 9); } SLsmg_write_char(':'); - SLsmg_write_nstring(" ", 8); + slsmg_write_nstring(" ", 8); if (!*self->line) - SLsmg_write_nstring(" ", width - 18); + slsmg_write_nstring(" ", width - 18); else - SLsmg_write_nstring(self->line, width - 18); + slsmg_write_nstring(self->line, width - 18); return 0; } @@ -1054,11 +1065,11 @@ void setup_browser(void) newtInit(); newtCls(); ui_helpline__puts(" "); - SLtt_set_color(HE_COLORSET_TOP, NULL, c->topColorFg, c->topColorBg); - SLtt_set_color(HE_COLORSET_MEDIUM, NULL, c->mediumColorFg, c->mediumColorBg); - SLtt_set_color(HE_COLORSET_NORMAL, NULL, c->normalColorFg, c->normalColorBg); - SLtt_set_color(HE_COLORSET_SELECTED, NULL, c->selColorFg, c->selColorBg); - SLtt_set_color(HE_COLORSET_CODE, NULL, c->codeColorFg, c->codeColorBg); + sltt_set_color(HE_COLORSET_TOP, NULL, c->topColorFg, c->topColorBg); + sltt_set_color(HE_COLORSET_MEDIUM, NULL, c->mediumColorFg, c->mediumColorBg); + sltt_set_color(HE_COLORSET_NORMAL, NULL, c->normalColorFg, c->normalColorBg); + sltt_set_color(HE_COLORSET_SELECTED, NULL, c->selColorFg, c->selColorBg); + sltt_set_color(HE_COLORSET_CODE, NULL, c->codeColorFg, c->codeColorBg); } void exit_browser(bool wait_for_ok) -- cgit v1.2.3