aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorArnaldo Carvalho de Melo <acme@redhat.com>2011-02-22 10:02:07 -0500
committerArnaldo Carvalho de Melo <acme@redhat.com>2011-02-22 10:02:07 -0500
commitc97cf42219b7b6037d2f96c27a5f114f2383f828 (patch)
tree2180e3eae063a217d1681074a813eed42ad91a86 /tools
parent8635bf6ea3402154eec64763e6ed14972013c1c1 (diff)
perf top: Live TUI Annotation
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>
Diffstat (limited to 'tools')
-rw-r--r--tools/perf/builtin-top.c36
-rw-r--r--tools/perf/util/annotate.h6
-rw-r--r--tools/perf/util/top.h1
-rw-r--r--tools/perf/util/ui/browsers/annotate.c126
-rw-r--r--tools/perf/util/ui/browsers/top.c45
5 files changed, 156 insertions, 58 deletions
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index c9fd66d4a08..f88a2630e1f 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -92,7 +92,6 @@ static bool dump_symtab = false;
92static struct winsize winsize; 92static struct winsize winsize;
93 93
94static const char *sym_filter = NULL; 94static const char *sym_filter = NULL;
95struct sym_entry *sym_filter_entry = NULL;
96struct sym_entry *sym_filter_entry_sched = NULL; 95struct sym_entry *sym_filter_entry_sched = NULL;
97static int sym_pcnt_filter = 5; 96static int sym_pcnt_filter = 5;
98 97
@@ -168,18 +167,19 @@ static int parse_source(struct sym_entry *syme)
168 pthread_mutex_lock(&notes->lock); 167 pthread_mutex_lock(&notes->lock);
169 168
170 if (symbol__alloc_hist(sym, top.evlist->nr_entries) < 0) { 169 if (symbol__alloc_hist(sym, top.evlist->nr_entries) < 0) {
170 pthread_mutex_unlock(&notes->lock);
171 pr_err("Not enough memory for annotating '%s' symbol!\n", 171 pr_err("Not enough memory for annotating '%s' symbol!\n",
172 sym->name); 172 sym->name);
173 sleep(1); 173 sleep(1);
174 goto out_unlock; 174 return err;
175 } 175 }
176 176
177 err = symbol__annotate(sym, syme->map, 0); 177 err = symbol__annotate(sym, syme->map, 0);
178 if (err == 0) { 178 if (err == 0) {
179out_assign: 179out_assign:
180 sym_filter_entry = syme; 180 top.sym_filter_entry = syme;
181 } 181 }
182out_unlock: 182
183 pthread_mutex_unlock(&notes->lock); 183 pthread_mutex_unlock(&notes->lock);
184 return err; 184 return err;
185} 185}
@@ -195,7 +195,7 @@ static void record_precise_ip(struct sym_entry *syme, int counter, u64 ip)
195 struct annotation *notes; 195 struct annotation *notes;
196 struct symbol *sym; 196 struct symbol *sym;
197 197
198 if (syme != sym_filter_entry) 198 if (syme != top.sym_filter_entry)
199 return; 199 return;
200 200
201 sym = sym_entry__symbol(syme); 201 sym = sym_entry__symbol(syme);
@@ -275,8 +275,8 @@ static void print_sym_table(struct perf_session *session)
275 session->hists.stats.total_lost); 275 session->hists.stats.total_lost);
276 } 276 }
277 277
278 if (sym_filter_entry) { 278 if (top.sym_filter_entry) {
279 show_details(sym_filter_entry); 279 show_details(top.sym_filter_entry);
280 return; 280 return;
281 } 281 }
282 282
@@ -417,8 +417,8 @@ static void print_mapped_keys(void)
417{ 417{
418 char *name = NULL; 418 char *name = NULL;
419 419
420 if (sym_filter_entry) { 420 if (top.sym_filter_entry) {
421 struct symbol *sym = sym_entry__symbol(sym_filter_entry); 421 struct symbol *sym = sym_entry__symbol(top.sym_filter_entry);
422 name = sym->name; 422 name = sym->name;
423 } 423 }
424 424
@@ -549,15 +549,15 @@ static void handle_keypress(struct perf_session *session, int c)
549 perf_session__fprintf_dsos(session, stderr); 549 perf_session__fprintf_dsos(session, stderr);
550 exit(0); 550 exit(0);
551 case 's': 551 case 's':
552 prompt_symbol(&sym_filter_entry, "Enter details symbol"); 552 prompt_symbol(&top.sym_filter_entry, "Enter details symbol");
553 break; 553 break;
554 case 'S': 554 case 'S':
555 if (!sym_filter_entry) 555 if (!top.sym_filter_entry)
556 break; 556 break;
557 else { 557 else {
558 struct sym_entry *syme = sym_filter_entry; 558 struct sym_entry *syme = top.sym_filter_entry;
559 559
560 sym_filter_entry = NULL; 560 top.sym_filter_entry = NULL;
561 __zero_source_counters(syme); 561 __zero_source_counters(syme);
562 } 562 }
563 break; 563 break;
@@ -656,7 +656,7 @@ static int symbol_filter(struct map *map, struct symbol *sym)
656 syme->map = map; 656 syme->map = map;
657 symbol__annotate_init(map, sym); 657 symbol__annotate_init(map, sym);
658 658
659 if (!sym_filter_entry && sym_filter && !strcmp(name, sym_filter)) { 659 if (!top.sym_filter_entry && sym_filter && !strcmp(name, sym_filter)) {
660 /* schedule initial sym_filter_entry setup */ 660 /* schedule initial sym_filter_entry setup */
661 sym_filter_entry_sched = syme; 661 sym_filter_entry_sched = syme;
662 sym_filter = NULL; 662 sym_filter = NULL;
@@ -750,13 +750,13 @@ static void perf_event__process_sample(const union perf_event *event,
750 750
751 /* let's see, whether we need to install initial sym_filter_entry */ 751 /* let's see, whether we need to install initial sym_filter_entry */
752 if (sym_filter_entry_sched) { 752 if (sym_filter_entry_sched) {
753 sym_filter_entry = sym_filter_entry_sched; 753 top.sym_filter_entry = sym_filter_entry_sched;
754 sym_filter_entry_sched = NULL; 754 sym_filter_entry_sched = NULL;
755 if (parse_source(sym_filter_entry) < 0) { 755 if (parse_source(top.sym_filter_entry) < 0) {
756 struct symbol *sym = sym_entry__symbol(sym_filter_entry); 756 struct symbol *sym = sym_entry__symbol(top.sym_filter_entry);
757 757
758 pr_err("Can't annotate %s", sym->name); 758 pr_err("Can't annotate %s", sym->name);
759 if (sym_filter_entry->map->dso->origin == DSO__ORIG_KERNEL) { 759 if (top.sym_filter_entry->map->dso->origin == DSO__ORIG_KERNEL) {
760 pr_err(": No vmlinux file was found in the path:\n"); 760 pr_err(": No vmlinux file was found in the path:\n");
761 machine__fprintf_vmlinux_path(machine, stderr); 761 machine__fprintf_vmlinux_path(machine, stderr);
762 } else 762 } else
diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h
index e848803fcd4..c2c28689680 100644
--- a/tools/perf/util/annotate.h
+++ b/tools/perf/util/annotate.h
@@ -90,12 +90,14 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx,
90 90
91#ifdef NO_NEWT_SUPPORT 91#ifdef NO_NEWT_SUPPORT
92static inline int symbol__tui_annotate(struct symbol *sym __used, 92static inline int symbol__tui_annotate(struct symbol *sym __used,
93 struct map *map __used, int evidx __used) 93 struct map *map __used,
94 int evidx __used, int refresh __used)
94{ 95{
95 return 0; 96 return 0;
96} 97}
97#else 98#else
98int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx); 99int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
100 int refresh);
99#endif 101#endif
100 102
101#endif /* __PERF_ANNOTATE_H */ 103#endif /* __PERF_ANNOTATE_H */
diff --git a/tools/perf/util/top.h b/tools/perf/util/top.h
index 4f769f47e19..e8d28e2ecdb 100644
--- a/tools/perf/util/top.h
+++ b/tools/perf/util/top.h
@@ -44,6 +44,7 @@ struct perf_top {
44 pid_t target_pid, target_tid; 44 pid_t target_pid, target_tid;
45 bool hide_kernel_symbols, hide_user_symbols, zero; 45 bool hide_kernel_symbols, hide_user_symbols, zero;
46 const char *cpu_list; 46 const char *cpu_list;
47 struct sym_entry *sym_filter_entry;
47 struct perf_evsel *sym_evsel; 48 struct perf_evsel *sym_evsel;
48}; 49};
49 50
diff --git a/tools/perf/util/ui/browsers/annotate.c b/tools/perf/util/ui/browsers/annotate.c
index cfb5a27f15d..8c17a8730e4 100644
--- a/tools/perf/util/ui/browsers/annotate.c
+++ b/tools/perf/util/ui/browsers/annotate.c
@@ -6,6 +6,7 @@
6#include "../../sort.h" 6#include "../../sort.h"
7#include "../../symbol.h" 7#include "../../symbol.h"
8#include "../../annotate.h" 8#include "../../annotate.h"
9#include <pthread.h>
9 10
10static void ui__error_window(const char *fmt, ...) 11static void ui__error_window(const char *fmt, ...)
11{ 12{
@@ -138,46 +139,108 @@ static void annotate_browser__set_top(struct annotate_browser *self,
138 self->curr_hot = nd; 139 self->curr_hot = nd;
139} 140}
140 141
141static int annotate_browser__run(struct annotate_browser *self) 142static void annotate_browser__calc_percent(struct annotate_browser *browser,
143 int evidx)
142{ 144{
143 struct rb_node *nd; 145 struct symbol *sym = browser->b.priv;
146 struct annotation *notes = symbol__annotation(sym);
147 struct objdump_line *pos;
148
149 browser->entries = RB_ROOT;
150
151 pthread_mutex_lock(&notes->lock);
152
153 list_for_each_entry(pos, &notes->src->source, node) {
154 struct objdump_line_rb_node *rbpos = objdump_line__rb(pos);
155 rbpos->percent = objdump_line__calc_percent(pos, sym, evidx);
156 if (rbpos->percent < 0.01) {
157 RB_CLEAR_NODE(&rbpos->rb_node);
158 continue;
159 }
160 objdump__insert_line(&browser->entries, rbpos);
161 }
162 pthread_mutex_unlock(&notes->lock);
163
164 browser->curr_hot = rb_last(&browser->entries);
165}
166
167static int annotate_browser__run(struct annotate_browser *self, int evidx,
168 int refresh)
169{
170 struct rb_node *nd = NULL;
144 struct symbol *sym = self->b.priv; 171 struct symbol *sym = self->b.priv;
172 /*
173 * RIGHT To allow builtin-annotate to cycle thru multiple symbols by
174 * examining the exit key for this function.
175 */
176 int exit_keys[] = { 'H', NEWT_KEY_TAB, NEWT_KEY_UNTAB,
177 NEWT_KEY_RIGHT, 0 };
145 int key; 178 int key;
146 179
147 if (ui_browser__show(&self->b, sym->name, 180 if (ui_browser__show(&self->b, sym->name,
148 "<-, -> or ESC: exit, TAB/shift+TAB: cycle thru samples") < 0) 181 "<-, -> or ESC: exit, TAB/shift+TAB: "
182 "cycle hottest lines, H: Hottest") < 0)
149 return -1; 183 return -1;
150 /* 184
151 * To allow builtin-annotate to cycle thru multiple symbols by 185 ui_browser__add_exit_keys(&self->b, exit_keys);
152 * examining the exit key for this function. 186 annotate_browser__calc_percent(self, evidx);
153 */ 187
154 ui_browser__add_exit_key(&self->b, NEWT_KEY_RIGHT); 188 if (self->curr_hot)
189 annotate_browser__set_top(self, self->curr_hot);
155 190
156 nd = self->curr_hot; 191 nd = self->curr_hot;
157 if (nd) { 192
158 int tabs[] = { NEWT_KEY_TAB, NEWT_KEY_UNTAB, 0 }; 193 if (refresh != 0)
159 ui_browser__add_exit_keys(&self->b, tabs); 194 newtFormSetTimer(self->b.form, refresh);
160 }
161 195
162 while (1) { 196 while (1) {
163 key = ui_browser__run(&self->b); 197 key = ui_browser__run(&self->b);
164 198
199 if (refresh != 0) {
200 annotate_browser__calc_percent(self, evidx);
201 /*
202 * Current line focus got out of the list of most active
203 * lines, NULL it so that if TAB|UNTAB is pressed, we
204 * move to curr_hot (current hottest line).
205 */
206 if (nd != NULL && RB_EMPTY_NODE(nd))
207 nd = NULL;
208 }
209
165 switch (key) { 210 switch (key) {
211 case -1:
212 /*
213 * FIXME we need to check if it was
214 * es.reason == NEWT_EXIT_TIMER
215 */
216 if (refresh != 0)
217 symbol__annotate_decay_histogram(sym, evidx);
218 continue;
166 case NEWT_KEY_TAB: 219 case NEWT_KEY_TAB:
167 nd = rb_prev(nd); 220 if (nd != NULL) {
168 if (nd == NULL) 221 nd = rb_prev(nd);
169 nd = rb_last(&self->entries); 222 if (nd == NULL)
170 annotate_browser__set_top(self, nd); 223 nd = rb_last(&self->entries);
224 } else
225 nd = self->curr_hot;
171 break; 226 break;
172 case NEWT_KEY_UNTAB: 227 case NEWT_KEY_UNTAB:
173 nd = rb_next(nd); 228 if (nd != NULL)
174 if (nd == NULL) 229 nd = rb_next(nd);
175 nd = rb_first(&self->entries); 230 if (nd == NULL)
176 annotate_browser__set_top(self, nd); 231 nd = rb_first(&self->entries);
232 else
233 nd = self->curr_hot;
234 break;
235 case 'H':
236 nd = self->curr_hot;
177 break; 237 break;
178 default: 238 default:
179 goto out; 239 goto out;
180 } 240 }
241
242 if (nd != NULL)
243 annotate_browser__set_top(self, nd);
181 } 244 }
182out: 245out:
183 ui_browser__hide(&self->b); 246 ui_browser__hide(&self->b);
@@ -186,13 +249,13 @@ out:
186 249
187int hist_entry__tui_annotate(struct hist_entry *he, int evidx) 250int hist_entry__tui_annotate(struct hist_entry *he, int evidx)
188{ 251{
189 return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx); 252 return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx, 0);
190} 253}
191 254
192int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx) 255int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
256 int refresh)
193{ 257{
194 struct objdump_line *pos, *n; 258 struct objdump_line *pos, *n;
195 struct objdump_line_rb_node *rbpos;
196 struct annotation *notes = symbol__annotation(sym); 259 struct annotation *notes = symbol__annotation(sym);
197 struct annotate_browser browser = { 260 struct annotate_browser browser = {
198 .b = { 261 .b = {
@@ -211,7 +274,7 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx)
211 if (map->dso->annotate_warned) 274 if (map->dso->annotate_warned)
212 return -1; 275 return -1;
213 276
214 if (symbol__annotate(sym, map, sizeof(*rbpos)) < 0) { 277 if (symbol__annotate(sym, map, sizeof(struct objdump_line_rb_node)) < 0) {
215 ui__error_window(ui_helpline__last_msg); 278 ui__error_window(ui_helpline__last_msg);
216 return -1; 279 return -1;
217 } 280 }
@@ -219,26 +282,17 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx)
219 ui_helpline__push("Press <- or ESC to exit"); 282 ui_helpline__push("Press <- or ESC to exit");
220 283
221 list_for_each_entry(pos, &notes->src->source, node) { 284 list_for_each_entry(pos, &notes->src->source, node) {
285 struct objdump_line_rb_node *rbpos;
222 size_t line_len = strlen(pos->line); 286 size_t line_len = strlen(pos->line);
287
223 if (browser.b.width < line_len) 288 if (browser.b.width < line_len)
224 browser.b.width = line_len; 289 browser.b.width = line_len;
225 rbpos = objdump_line__rb(pos); 290 rbpos = objdump_line__rb(pos);
226 rbpos->idx = browser.b.nr_entries++; 291 rbpos->idx = browser.b.nr_entries++;
227 rbpos->percent = objdump_line__calc_percent(pos, sym, evidx);
228 if (rbpos->percent < 0.01)
229 continue;
230 objdump__insert_line(&browser.entries, rbpos);
231 } 292 }
232 293
233 /*
234 * Position the browser at the hottest line.
235 */
236 browser.curr_hot = rb_last(&browser.entries);
237 if (browser.curr_hot)
238 annotate_browser__set_top(&browser, browser.curr_hot);
239
240 browser.b.width += 18; /* Percentage */ 294 browser.b.width += 18; /* Percentage */
241 ret = annotate_browser__run(&browser); 295 ret = annotate_browser__run(&browser, evidx, refresh);
242 list_for_each_entry_safe(pos, n, &notes->src->source, node) { 296 list_for_each_entry_safe(pos, n, &notes->src->source, node) {
243 list_del(&pos->node); 297 list_del(&pos->node);
244 objdump_line__free(pos); 298 objdump_line__free(pos);
diff --git a/tools/perf/util/ui/browsers/top.c b/tools/perf/util/ui/browsers/top.c
index ca6062483a8..377ff58c9ae 100644
--- a/tools/perf/util/ui/browsers/top.c
+++ b/tools/perf/util/ui/browsers/top.c
@@ -7,6 +7,7 @@
7 * Released under the GPL v2. (and only v2, not any later version) 7 * Released under the GPL v2. (and only v2, not any later version)
8 */ 8 */
9#include "../browser.h" 9#include "../browser.h"
10#include "../../annotate.h"
10#include "../helpline.h" 11#include "../helpline.h"
11#include "../libslang.h" 12#include "../libslang.h"
12#include "../../evlist.h" 13#include "../../evlist.h"
@@ -18,6 +19,7 @@
18struct perf_top_browser { 19struct perf_top_browser {
19 struct ui_browser b; 20 struct ui_browser b;
20 struct rb_root root; 21 struct rb_root root;
22 struct sym_entry *selection;
21 float sum_ksamples; 23 float sum_ksamples;
22 int dso_width; 24 int dso_width;
23 int dso_short_width; 25 int dso_short_width;
@@ -60,6 +62,9 @@ static void perf_top_browser__write(struct ui_browser *browser, void *entry, int
60 slsmg_write_nstring(width >= syme->map->dso->long_name_len ? 62 slsmg_write_nstring(width >= syme->map->dso->long_name_len ?
61 syme->map->dso->long_name : 63 syme->map->dso->long_name :
62 syme->map->dso->short_name, width); 64 syme->map->dso->short_name, width);
65
66 if (current_entry)
67 top_browser->selection = syme;
63} 68}
64 69
65static void perf_top_browser__update_rb_tree(struct perf_top_browser *browser) 70static void perf_top_browser__update_rb_tree(struct perf_top_browser *browser)
@@ -80,21 +85,52 @@ static void perf_top_browser__update_rb_tree(struct perf_top_browser *browser)
80 browser->b.nr_entries = top->rb_entries; 85 browser->b.nr_entries = top->rb_entries;
81} 86}
82 87
88static void perf_top_browser__annotate(struct perf_top_browser *browser)
89{
90 struct sym_entry *syme = browser->selection;
91 struct symbol *sym = sym_entry__symbol(syme);
92 struct annotation *notes = symbol__annotation(sym);
93 struct perf_top *top = browser->b.priv;
94
95 if (notes->src != NULL)
96 goto do_annotation;
97
98 pthread_mutex_lock(&notes->lock);
99
100 top->sym_filter_entry = NULL;
101
102 if (symbol__alloc_hist(sym, top->evlist->nr_entries) < 0) {
103 pr_err("Not enough memory for annotating '%s' symbol!\n",
104 sym->name);
105 pthread_mutex_unlock(&notes->lock);
106 return;
107 }
108
109 top->sym_filter_entry = syme;
110
111 pthread_mutex_unlock(&notes->lock);
112do_annotation:
113 symbol__tui_annotate(sym, syme->map, 0, top->delay_secs * 1000);
114}
115
83static int perf_top_browser__run(struct perf_top_browser *browser) 116static int perf_top_browser__run(struct perf_top_browser *browser)
84{ 117{
85 int key; 118 int key;
86 char title[160]; 119 char title[160];
87 struct perf_top *top = browser->b.priv; 120 struct perf_top *top = browser->b.priv;
88 int delay_msecs = top->delay_secs * 1000; 121 int delay_msecs = top->delay_secs * 1000;
122 int exit_keys[] = { 'a', NEWT_KEY_ENTER, NEWT_KEY_RIGHT, 0, };
89 123
90 perf_top_browser__update_rb_tree(browser); 124 perf_top_browser__update_rb_tree(browser);
91 perf_top__header_snprintf(top, title, sizeof(title)); 125 perf_top__header_snprintf(top, title, sizeof(title));
92 perf_top__reset_sample_counters(top); 126 perf_top__reset_sample_counters(top);
93 127
94 if (ui_browser__show(&browser->b, title, "ESC: exit") < 0) 128 if (ui_browser__show(&browser->b, title,
129 "ESC: exit, ENTER|->|a: Live Annotate") < 0)
95 return -1; 130 return -1;
96 131
97 newtFormSetTimer(browser->b.form, delay_msecs); 132 newtFormSetTimer(browser->b.form, delay_msecs);
133 ui_browser__add_exit_keys(&browser->b, exit_keys);
98 134
99 while (1) { 135 while (1) {
100 key = ui_browser__run(&browser->b); 136 key = ui_browser__run(&browser->b);
@@ -109,7 +145,12 @@ static int perf_top_browser__run(struct perf_top_browser *browser)
109 SLsmg_gotorc(0, 0); 145 SLsmg_gotorc(0, 0);
110 slsmg_write_nstring(title, browser->b.width); 146 slsmg_write_nstring(title, browser->b.width);
111 break; 147 break;
112 case NEWT_KEY_TAB: 148 case 'a':
149 case NEWT_KEY_RIGHT:
150 case NEWT_KEY_ENTER:
151 if (browser->selection)
152 perf_top_browser__annotate(browser);
153 break;
113 default: 154 default:
114 goto out; 155 goto out;
115 } 156 }