Skip to content

Commit

Permalink
perf top: Live TUI Annotation
Browse files Browse the repository at this point in the history
Now one has just to press the right key, 'a' or Enter on the main 'perf
top --tui' screen to live annotate the symbol under the cursor.

The annotate window starts centered on the hottest line (the one with
most samples so far) then TAB and shift+TAB can be used to go to the
prev/next hot line.

Pressing 'H' at any point will center again the screen on the hottest
line.

Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Cc: Tom Zanussi <tzanussi@gmail.com>
LKML-Reference: <new-submission>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
  • Loading branch information
acmel committed Feb 22, 2011
1 parent 8635bf6 commit c97cf42
Show file tree
Hide file tree
Showing 5 changed files with 156 additions and 58 deletions.
36 changes: 18 additions & 18 deletions tools/perf/builtin-top.c
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@ static bool dump_symtab = false;
static struct winsize winsize;

static const char *sym_filter = NULL;
struct sym_entry *sym_filter_entry = NULL;
struct sym_entry *sym_filter_entry_sched = NULL;
static int sym_pcnt_filter = 5;

Expand Down Expand Up @@ -168,18 +167,19 @@ static int parse_source(struct sym_entry *syme)
pthread_mutex_lock(&notes->lock);

if (symbol__alloc_hist(sym, top.evlist->nr_entries) < 0) {
pthread_mutex_unlock(&notes->lock);
pr_err("Not enough memory for annotating '%s' symbol!\n",
sym->name);
sleep(1);
goto out_unlock;
return err;
}

err = symbol__annotate(sym, syme->map, 0);
if (err == 0) {
out_assign:
sym_filter_entry = syme;
top.sym_filter_entry = syme;
}
out_unlock:

pthread_mutex_unlock(&notes->lock);
return err;
}
Expand All @@ -195,7 +195,7 @@ static void record_precise_ip(struct sym_entry *syme, int counter, u64 ip)
struct annotation *notes;
struct symbol *sym;

if (syme != sym_filter_entry)
if (syme != top.sym_filter_entry)
return;

sym = sym_entry__symbol(syme);
Expand Down Expand Up @@ -275,8 +275,8 @@ static void print_sym_table(struct perf_session *session)
session->hists.stats.total_lost);
}

if (sym_filter_entry) {
show_details(sym_filter_entry);
if (top.sym_filter_entry) {
show_details(top.sym_filter_entry);
return;
}

Expand Down Expand Up @@ -417,8 +417,8 @@ static void print_mapped_keys(void)
{
char *name = NULL;

if (sym_filter_entry) {
struct symbol *sym = sym_entry__symbol(sym_filter_entry);
if (top.sym_filter_entry) {
struct symbol *sym = sym_entry__symbol(top.sym_filter_entry);
name = sym->name;
}

Expand Down Expand Up @@ -549,15 +549,15 @@ static void handle_keypress(struct perf_session *session, int c)
perf_session__fprintf_dsos(session, stderr);
exit(0);
case 's':
prompt_symbol(&sym_filter_entry, "Enter details symbol");
prompt_symbol(&top.sym_filter_entry, "Enter details symbol");
break;
case 'S':
if (!sym_filter_entry)
if (!top.sym_filter_entry)
break;
else {
struct sym_entry *syme = sym_filter_entry;
struct sym_entry *syme = top.sym_filter_entry;

sym_filter_entry = NULL;
top.sym_filter_entry = NULL;
__zero_source_counters(syme);
}
break;
Expand Down Expand Up @@ -656,7 +656,7 @@ static int symbol_filter(struct map *map, struct symbol *sym)
syme->map = map;
symbol__annotate_init(map, sym);

if (!sym_filter_entry && sym_filter && !strcmp(name, sym_filter)) {
if (!top.sym_filter_entry && sym_filter && !strcmp(name, sym_filter)) {
/* schedule initial sym_filter_entry setup */
sym_filter_entry_sched = syme;
sym_filter = NULL;
Expand Down Expand Up @@ -750,13 +750,13 @@ static void perf_event__process_sample(const union perf_event *event,

/* let's see, whether we need to install initial sym_filter_entry */
if (sym_filter_entry_sched) {
sym_filter_entry = sym_filter_entry_sched;
top.sym_filter_entry = sym_filter_entry_sched;
sym_filter_entry_sched = NULL;
if (parse_source(sym_filter_entry) < 0) {
struct symbol *sym = sym_entry__symbol(sym_filter_entry);
if (parse_source(top.sym_filter_entry) < 0) {
struct symbol *sym = sym_entry__symbol(top.sym_filter_entry);

pr_err("Can't annotate %s", sym->name);
if (sym_filter_entry->map->dso->origin == DSO__ORIG_KERNEL) {
if (top.sym_filter_entry->map->dso->origin == DSO__ORIG_KERNEL) {
pr_err(": No vmlinux file was found in the path:\n");
machine__fprintf_vmlinux_path(machine, stderr);
} else
Expand Down
6 changes: 4 additions & 2 deletions tools/perf/util/annotate.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,14 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx,

#ifdef NO_NEWT_SUPPORT
static inline int symbol__tui_annotate(struct symbol *sym __used,
struct map *map __used, int evidx __used)
struct map *map __used,
int evidx __used, int refresh __used)
{
return 0;
}
#else
int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx);
int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
int refresh);
#endif

#endif /* __PERF_ANNOTATE_H */
1 change: 1 addition & 0 deletions tools/perf/util/top.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ struct perf_top {
pid_t target_pid, target_tid;
bool hide_kernel_symbols, hide_user_symbols, zero;
const char *cpu_list;
struct sym_entry *sym_filter_entry;
struct perf_evsel *sym_evsel;
};

Expand Down
126 changes: 90 additions & 36 deletions tools/perf/util/ui/browsers/annotate.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "../../sort.h"
#include "../../symbol.h"
#include "../../annotate.h"
#include <pthread.h>

static void ui__error_window(const char *fmt, ...)
{
Expand Down Expand Up @@ -138,46 +139,108 @@ static void annotate_browser__set_top(struct annotate_browser *self,
self->curr_hot = nd;
}

static int annotate_browser__run(struct annotate_browser *self)
static void annotate_browser__calc_percent(struct annotate_browser *browser,
int evidx)
{
struct rb_node *nd;
struct symbol *sym = browser->b.priv;
struct annotation *notes = symbol__annotation(sym);
struct objdump_line *pos;

browser->entries = RB_ROOT;

pthread_mutex_lock(&notes->lock);

list_for_each_entry(pos, &notes->src->source, node) {
struct objdump_line_rb_node *rbpos = objdump_line__rb(pos);
rbpos->percent = objdump_line__calc_percent(pos, sym, evidx);
if (rbpos->percent < 0.01) {
RB_CLEAR_NODE(&rbpos->rb_node);
continue;
}
objdump__insert_line(&browser->entries, rbpos);
}
pthread_mutex_unlock(&notes->lock);

browser->curr_hot = rb_last(&browser->entries);
}

static int annotate_browser__run(struct annotate_browser *self, int evidx,
int refresh)
{
struct rb_node *nd = NULL;
struct symbol *sym = self->b.priv;
/*
* RIGHT To allow builtin-annotate to cycle thru multiple symbols by
* examining the exit key for this function.
*/
int exit_keys[] = { 'H', NEWT_KEY_TAB, NEWT_KEY_UNTAB,
NEWT_KEY_RIGHT, 0 };
int key;

if (ui_browser__show(&self->b, sym->name,
"<-, -> or ESC: exit, TAB/shift+TAB: cycle thru samples") < 0)
"<-, -> or ESC: exit, TAB/shift+TAB: "
"cycle hottest lines, H: Hottest") < 0)
return -1;
/*
* To allow builtin-annotate to cycle thru multiple symbols by
* examining the exit key for this function.
*/
ui_browser__add_exit_key(&self->b, NEWT_KEY_RIGHT);

ui_browser__add_exit_keys(&self->b, exit_keys);
annotate_browser__calc_percent(self, evidx);

if (self->curr_hot)
annotate_browser__set_top(self, self->curr_hot);

nd = self->curr_hot;
if (nd) {
int tabs[] = { NEWT_KEY_TAB, NEWT_KEY_UNTAB, 0 };
ui_browser__add_exit_keys(&self->b, tabs);
}

if (refresh != 0)
newtFormSetTimer(self->b.form, refresh);

while (1) {
key = ui_browser__run(&self->b);

if (refresh != 0) {
annotate_browser__calc_percent(self, evidx);
/*
* Current line focus got out of the list of most active
* lines, NULL it so that if TAB|UNTAB is pressed, we
* move to curr_hot (current hottest line).
*/
if (nd != NULL && RB_EMPTY_NODE(nd))
nd = NULL;
}

switch (key) {
case -1:
/*
* FIXME we need to check if it was
* es.reason == NEWT_EXIT_TIMER
*/
if (refresh != 0)
symbol__annotate_decay_histogram(sym, evidx);
continue;
case NEWT_KEY_TAB:
nd = rb_prev(nd);
if (nd == NULL)
nd = rb_last(&self->entries);
annotate_browser__set_top(self, nd);
if (nd != NULL) {
nd = rb_prev(nd);
if (nd == NULL)
nd = rb_last(&self->entries);
} else
nd = self->curr_hot;
break;
case NEWT_KEY_UNTAB:
nd = rb_next(nd);
if (nd == NULL)
nd = rb_first(&self->entries);
annotate_browser__set_top(self, nd);
if (nd != NULL)
nd = rb_next(nd);
if (nd == NULL)
nd = rb_first(&self->entries);
else
nd = self->curr_hot;
break;
case 'H':
nd = self->curr_hot;
break;
default:
goto out;
}

if (nd != NULL)
annotate_browser__set_top(self, nd);
}
out:
ui_browser__hide(&self->b);
Expand All @@ -186,13 +249,13 @@ static int annotate_browser__run(struct annotate_browser *self)

int hist_entry__tui_annotate(struct hist_entry *he, int evidx)
{
return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx);
return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx, 0);
}

int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx)
int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
int refresh)
{
struct objdump_line *pos, *n;
struct objdump_line_rb_node *rbpos;
struct annotation *notes = symbol__annotation(sym);
struct annotate_browser browser = {
.b = {
Expand All @@ -211,34 +274,25 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx)
if (map->dso->annotate_warned)
return -1;

if (symbol__annotate(sym, map, sizeof(*rbpos)) < 0) {
if (symbol__annotate(sym, map, sizeof(struct objdump_line_rb_node)) < 0) {
ui__error_window(ui_helpline__last_msg);
return -1;
}

ui_helpline__push("Press <- or ESC to exit");

list_for_each_entry(pos, &notes->src->source, node) {
struct objdump_line_rb_node *rbpos;
size_t line_len = strlen(pos->line);

if (browser.b.width < line_len)
browser.b.width = line_len;
rbpos = objdump_line__rb(pos);
rbpos->idx = browser.b.nr_entries++;
rbpos->percent = objdump_line__calc_percent(pos, sym, evidx);
if (rbpos->percent < 0.01)
continue;
objdump__insert_line(&browser.entries, rbpos);
}

/*
* Position the browser at the hottest line.
*/
browser.curr_hot = rb_last(&browser.entries);
if (browser.curr_hot)
annotate_browser__set_top(&browser, browser.curr_hot);

browser.b.width += 18; /* Percentage */
ret = annotate_browser__run(&browser);
ret = annotate_browser__run(&browser, evidx, refresh);
list_for_each_entry_safe(pos, n, &notes->src->source, node) {
list_del(&pos->node);
objdump_line__free(pos);
Expand Down
Loading

0 comments on commit c97cf42

Please sign in to comment.