aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf/util/newt.c
diff options
context:
space:
mode:
authorArnaldo Carvalho de Melo <acme@redhat.com>2010-05-11 22:18:06 -0400
committerArnaldo Carvalho de Melo <acme@redhat.com>2010-05-11 22:23:20 -0400
commitef7b93a11904c6ba10604233d318d9e8ec88cddc (patch)
tree7ae6fa9cbe19be8fbbc18c8fdeb7edfdb7bdab60 /tools/perf/util/newt.c
parent3798ed7bc7ade26d3f59506cd06288615dfc7585 (diff)
perf report: Librarize the annotation code and use it in the newt browser
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 <fweisbec@gmail.com> Cc: Mike Galbraith <efault@gmx.de> Cc: Paul Mackerras <paulus@samba.org> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl> Cc: Tom Zanussi <tzanussi@gmail.com> LKML-Reference: <new-submission> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Diffstat (limited to 'tools/perf/util/newt.c')
-rw-r--r--tools/perf/util/newt.c352
1 files changed, 306 insertions, 46 deletions
diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c
index daa86efffce6..ba6acd04c082 100644
--- a/tools/perf/util/newt.c
+++ b/tools/perf/util/newt.c
@@ -2,6 +2,7 @@
2#include <stdio.h> 2#include <stdio.h>
3#undef _GNU_SOURCE 3#undef _GNU_SOURCE
4 4
5#include <slang.h>
5#include <stdlib.h> 6#include <stdlib.h>
6#include <newt.h> 7#include <newt.h>
7#include <sys/ttydefaults.h> 8#include <sys/ttydefaults.h>
@@ -171,6 +172,254 @@ static bool dialog_yesno(const char *msg)
171 return newtWinChoice(NULL, yes, no, (char *)msg) == 1; 172 return newtWinChoice(NULL, yes, no, (char *)msg) == 1;
172} 173}
173 174
175#define HE_COLORSET_TOP 50
176#define HE_COLORSET_MEDIUM 51
177#define HE_COLORSET_NORMAL 52
178#define HE_COLORSET_SELECTED 53
179#define HE_COLORSET_CODE 54
180
181static int ui_browser__percent_color(double percent, bool current)
182{
183 if (current)
184 return HE_COLORSET_SELECTED;
185 if (percent >= MIN_RED)
186 return HE_COLORSET_TOP;
187 if (percent >= MIN_GREEN)
188 return HE_COLORSET_MEDIUM;
189 return HE_COLORSET_NORMAL;
190}
191
192struct ui_browser {
193 newtComponent form, sb;
194 u64 index, first_visible_entry_idx;
195 void *first_visible_entry, *entries;
196 u16 top, left, width, height;
197 void *priv;
198 u32 nr_entries;
199};
200
201static void ui_browser__refresh_dimensions(struct ui_browser *self)
202{
203 int cols, rows;
204 newtGetScreenSize(&cols, &rows);
205
206 if (self->width > cols - 4)
207 self->width = cols - 4;
208 self->height = rows - 5;
209 if (self->height > self->nr_entries)
210 self->height = self->nr_entries;
211 self->top = (rows - self->height) / 2;
212 self->left = (cols - self->width) / 2;
213}
214
215static void ui_browser__reset_index(struct ui_browser *self)
216{
217 self->index = self->first_visible_entry_idx = 0;
218 self->first_visible_entry = NULL;
219}
220
221static int objdump_line__show(struct objdump_line *self, struct list_head *head,
222 int width, struct hist_entry *he, int len,
223 bool current_entry)
224{
225 if (self->offset != -1) {
226 struct symbol *sym = he->ms.sym;
227 unsigned int hits = 0;
228 double percent = 0.0;
229 int color;
230 struct sym_priv *priv = symbol__priv(sym);
231 struct sym_ext *sym_ext = priv->ext;
232 struct sym_hist *h = priv->hist;
233 s64 offset = self->offset;
234 struct objdump_line *next = objdump__get_next_ip_line(head, self);
235
236 while (offset < (s64)len &&
237 (next == NULL || offset < next->offset)) {
238 if (sym_ext) {
239 percent += sym_ext[offset].percent;
240 } else
241 hits += h->ip[offset];
242
243 ++offset;
244 }
245
246 if (sym_ext == NULL && h->sum)
247 percent = 100.0 * hits / h->sum;
248
249 color = ui_browser__percent_color(percent, current_entry);
250 SLsmg_set_color(color);
251 SLsmg_printf(" %7.2f ", percent);
252 if (!current_entry)
253 SLsmg_set_color(HE_COLORSET_CODE);
254 } else {
255 int color = ui_browser__percent_color(0, current_entry);
256 SLsmg_set_color(color);
257 SLsmg_write_nstring(" ", 9);
258 }
259
260 SLsmg_write_char(':');
261 SLsmg_write_nstring(" ", 8);
262 if (!*self->line)
263 SLsmg_write_nstring(" ", width - 18);
264 else
265 SLsmg_write_nstring(self->line, width - 18);
266
267 return 0;
268}
269
270static int ui_browser__refresh_entries(struct ui_browser *self)
271{
272 struct objdump_line *pos;
273 struct list_head *head = self->entries;
274 struct hist_entry *he = self->priv;
275 int row = 0;
276 int len = he->ms.sym->end - he->ms.sym->start;
277
278 if (self->first_visible_entry == NULL || self->first_visible_entry == self->entries)
279 self->first_visible_entry = head->next;
280
281 pos = list_entry(self->first_visible_entry, struct objdump_line, node);
282
283 list_for_each_entry_from(pos, head, node) {
284 bool current_entry = (self->first_visible_entry_idx + row) == self->index;
285 SLsmg_gotorc(self->top + row, self->left);
286 objdump_line__show(pos, head, self->width,
287 he, len, current_entry);
288 if (++row == self->height)
289 break;
290 }
291
292 SLsmg_set_color(HE_COLORSET_NORMAL);
293 SLsmg_fill_region(self->top + row, self->left,
294 self->height - row, self->width, ' ');
295
296 return 0;
297}
298
299static int ui_browser__run(struct ui_browser *self, const char *title,
300 struct newtExitStruct *es)
301{
302 if (self->form) {
303 newtFormDestroy(self->form);
304 newtPopWindow();
305 }
306
307 ui_browser__refresh_dimensions(self);
308 newtCenteredWindow(self->width + 2, self->height, title);
309 self->form = newt_form__new();
310 if (self->form == NULL)
311 return -1;
312
313 self->sb = newtVerticalScrollbar(self->width + 1, 0, self->height,
314 HE_COLORSET_NORMAL,
315 HE_COLORSET_SELECTED);
316 if (self->sb == NULL)
317 return -1;
318
319 newtFormAddHotKey(self->form, NEWT_KEY_UP);
320 newtFormAddHotKey(self->form, NEWT_KEY_DOWN);
321 newtFormAddHotKey(self->form, NEWT_KEY_PGUP);
322 newtFormAddHotKey(self->form, NEWT_KEY_PGDN);
323 newtFormAddHotKey(self->form, NEWT_KEY_HOME);
324 newtFormAddHotKey(self->form, NEWT_KEY_END);
325
326 if (ui_browser__refresh_entries(self) < 0)
327 return -1;
328 newtFormAddComponent(self->form, self->sb);
329
330 while (1) {
331 unsigned int offset;
332
333 newtFormRun(self->form, es);
334
335 if (es->reason != NEWT_EXIT_HOTKEY)
336 break;
337 switch (es->u.key) {
338 case NEWT_KEY_DOWN:
339 if (self->index == self->nr_entries - 1)
340 break;
341 ++self->index;
342 if (self->index == self->first_visible_entry_idx + self->height) {
343 struct list_head *pos = self->first_visible_entry;
344 ++self->first_visible_entry_idx;
345 self->first_visible_entry = pos->next;
346 }
347 break;
348 case NEWT_KEY_UP:
349 if (self->index == 0)
350 break;
351 --self->index;
352 if (self->index < self->first_visible_entry_idx) {
353 struct list_head *pos = self->first_visible_entry;
354 --self->first_visible_entry_idx;
355 self->first_visible_entry = pos->prev;
356 }
357 break;
358 case NEWT_KEY_PGDN:
359 if (self->first_visible_entry_idx + self->height > self->nr_entries - 1)
360 break;
361
362 offset = self->height;
363 if (self->index + offset > self->nr_entries - 1)
364 offset = self->nr_entries - 1 - self->index;
365 self->index += offset;
366 self->first_visible_entry_idx += offset;
367
368 while (offset--) {
369 struct list_head *pos = self->first_visible_entry;
370 self->first_visible_entry = pos->next;
371 }
372
373 break;
374 case NEWT_KEY_PGUP:
375 if (self->first_visible_entry_idx == 0)
376 break;
377
378 if (self->first_visible_entry_idx < self->height)
379 offset = self->first_visible_entry_idx;
380 else
381 offset = self->height;
382
383 self->index -= offset;
384 self->first_visible_entry_idx -= offset;
385
386 while (offset--) {
387 struct list_head *pos = self->first_visible_entry;
388 self->first_visible_entry = pos->prev;
389 }
390 break;
391 case NEWT_KEY_HOME:
392 ui_browser__reset_index(self);
393 break;
394 case NEWT_KEY_END: {
395 struct list_head *head = self->entries;
396 offset = self->height - 1;
397
398 if (offset > self->nr_entries)
399 offset = self->nr_entries;
400
401 self->index = self->first_visible_entry_idx = self->nr_entries - 1 - offset;
402 self->first_visible_entry = head->prev;
403 while (offset-- != 0) {
404 struct list_head *pos = self->first_visible_entry;
405 self->first_visible_entry = pos->prev;
406 }
407 }
408 break;
409 case NEWT_KEY_ESCAPE:
410 case CTRL('c'):
411 case 'Q':
412 case 'q':
413 return 0;
414 default:
415 continue;
416 }
417 if (ui_browser__refresh_entries(self) < 0)
418 return -1;
419 }
420 return 0;
421}
422
174/* 423/*
175 * When debugging newt problems it was useful to be able to "unroll" 424 * When debugging newt problems it was useful to be able to "unroll"
176 * the calls to newtCheckBoxTreeAdd{Array,Item}, so that we can generate 425 * 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,
353 return ret; 602 return ret;
354} 603}
355 604
356static void map_symbol__annotate_browser(const struct map_symbol *self, 605static void hist_entry__annotate_browser(struct hist_entry *self)
357 const char *input_name)
358{ 606{
359 FILE *fp; 607 struct ui_browser browser;
360 int cols, rows;
361 newtComponent form, tree;
362 struct newtExitStruct es; 608 struct newtExitStruct es;
363 char *str; 609 struct objdump_line *pos, *n;
364 size_t line_len, max_line_len = 0; 610 LIST_HEAD(head);
365 size_t max_usable_width;
366 char *line = NULL;
367 611
368 if (self->sym == NULL) 612 if (self->ms.sym == NULL)
369 return; 613 return;
370 614
371 if (asprintf(&str, "perf annotate -i \"%s\" -d \"%s\" %s 2>&1 | expand", 615 if (hist_entry__annotate(self, &head) < 0)
372 input_name, self->map->dso->name, self->sym->name) < 0)
373 return; 616 return;
374 617
375 fp = popen(str, "r");
376 if (fp == NULL)
377 goto out_free_str;
378
379 ui_helpline__push("Press ESC to exit"); 618 ui_helpline__push("Press ESC to exit");
380 newtGetScreenSize(&cols, &rows);
381 tree = newtListbox(0, 0, rows - 5, NEWT_FLAG_SCROLL);
382
383 while (!feof(fp)) {
384 if (getline(&line, &line_len, fp) < 0 || !line_len)
385 break;
386 while (line_len != 0 && isspace(line[line_len - 1]))
387 line[--line_len] = '\0';
388 619
389 if (line_len > max_line_len) 620 memset(&browser, 0, sizeof(browser));
390 max_line_len = line_len; 621 browser.entries = &head;
391 newtListboxAppendEntry(tree, line, NULL); 622 browser.priv = self;
623 list_for_each_entry(pos, &head, node) {
624 size_t line_len = strlen(pos->line);
625 if (browser.width < line_len)
626 browser.width = line_len;
627 ++browser.nr_entries;
392 } 628 }
393 fclose(fp);
394 free(line);
395
396 max_usable_width = cols - 22;
397 if (max_line_len > max_usable_width)
398 max_line_len = max_usable_width;
399
400 newtListboxSetWidth(tree, max_line_len);
401 629
402 newtCenteredWindow(max_line_len + 2, rows - 5, self->sym->name); 630 browser.width += 18; /* Percentage */
403 form = newt_form__new(); 631 ui_browser__run(&browser, self->ms.sym->name, &es);
404 newtFormAddComponent(form, tree); 632 newtFormDestroy(browser.form);
405
406 newtFormRun(form, &es);
407 newtFormDestroy(form);
408 newtPopWindow(); 633 newtPopWindow();
634 list_for_each_entry_safe(pos, n, &head, node) {
635 list_del(&pos->node);
636 objdump_line__free(pos);
637 }
409 ui_helpline__pop(); 638 ui_helpline__pop();
410out_free_str:
411 free(str);
412} 639}
413 640
414static const void *newt__symbol_tree_get_current(newtComponent self) 641static 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
527 return 0; 754 return 0;
528} 755}
529 756
530static struct thread *hist_browser__selected_thread(struct hist_browser *self) 757static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self)
531{ 758{
532 int *indexes; 759 int *indexes;
533 760
@@ -543,7 +770,13 @@ static struct thread *hist_browser__selected_thread(struct hist_browser *self)
543 } 770 }
544 return NULL; 771 return NULL;
545out: 772out:
546 return *(struct thread **)(self->selection + 1); 773 return container_of(self->selection, struct hist_entry, ms);
774}
775
776static struct thread *hist_browser__selected_thread(struct hist_browser *self)
777{
778 struct hist_entry *he = hist_browser__selected_entry(self);
779 return he ? he->thread : NULL;
547} 780}
548 781
549static int hist_browser__title(char *bf, size_t size, const char *input_name, 782static 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
637 continue; 870 continue;
638do_annotate: 871do_annotate:
639 if (choice == annotate) { 872 if (choice == annotate) {
873 struct hist_entry *he;
874
640 if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) { 875 if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) {
641 ui_helpline__puts("No vmlinux file found, can't " 876 ui_helpline__puts("No vmlinux file found, can't "
642 "annotate with just a " 877 "annotate with just a "
643 "kallsyms file"); 878 "kallsyms file");
644 continue; 879 continue;
645 } 880 }
646 map_symbol__annotate_browser(browser->selection, input_name); 881
882 he = hist_browser__selected_entry(browser);
883 if (he == NULL)
884 continue;
885
886 hist_entry__annotate_browser(he);
647 } else if (choice == zoom_dso) { 887 } else if (choice == zoom_dso) {
648 if (dso_filter) { 888 if (dso_filter) {
649 ui_helpline__pop(); 889 ui_helpline__pop();
@@ -681,8 +921,23 @@ out:
681 return err; 921 return err;
682} 922}
683 923
924static struct newtPercentTreeColors {
925 const char *topColorFg, *topColorBg;
926 const char *mediumColorFg, *mediumColorBg;
927 const char *normalColorFg, *normalColorBg;
928 const char *selColorFg, *selColorBg;
929 const char *codeColorFg, *codeColorBg;
930} defaultPercentTreeColors = {
931 "red", "lightgray",
932 "green", "lightgray",
933 "black", "lightgray",
934 "lightgray", "magenta",
935 "blue", "lightgray",
936};
937
684void setup_browser(void) 938void setup_browser(void)
685{ 939{
940 struct newtPercentTreeColors *c = &defaultPercentTreeColors;
686 if (!isatty(1)) 941 if (!isatty(1))
687 return; 942 return;
688 943
@@ -690,6 +945,11 @@ void setup_browser(void)
690 newtInit(); 945 newtInit();
691 newtCls(); 946 newtCls();
692 ui_helpline__puts(" "); 947 ui_helpline__puts(" ");
948 SLtt_set_color(HE_COLORSET_TOP, NULL, c->topColorFg, c->topColorBg);
949 SLtt_set_color(HE_COLORSET_MEDIUM, NULL, c->mediumColorFg, c->mediumColorBg);
950 SLtt_set_color(HE_COLORSET_NORMAL, NULL, c->normalColorFg, c->normalColorBg);
951 SLtt_set_color(HE_COLORSET_SELECTED, NULL, c->selColorFg, c->selColorBg);
952 SLtt_set_color(HE_COLORSET_CODE, NULL, c->codeColorFg, c->codeColorBg);
693} 953}
694 954
695void exit_browser(bool wait_for_ok) 955void exit_browser(bool wait_for_ok)