perf report: Report number of events, not samples
[safe/jmp/linux-2.6] / tools / perf / util / newt.c
1 #define _GNU_SOURCE
2 #include <stdio.h>
3 #undef _GNU_SOURCE
4
5 #include <slang.h>
6 #include <stdlib.h>
7 #include <newt.h>
8 #include <sys/ttydefaults.h>
9
10 #include "cache.h"
11 #include "hist.h"
12 #include "session.h"
13 #include "sort.h"
14 #include "symbol.h"
15
16 struct ui_progress {
17         newtComponent form, scale;
18 };
19
20 struct ui_progress *ui_progress__new(const char *title, u64 total)
21 {
22         struct ui_progress *self = malloc(sizeof(*self));
23
24         if (self != NULL) {
25                 int cols;
26                 newtGetScreenSize(&cols, NULL);
27                 cols -= 4;
28                 newtCenteredWindow(cols, 1, title);
29                 self->form  = newtForm(NULL, NULL, 0);
30                 if (self->form == NULL)
31                         goto out_free_self;
32                 self->scale = newtScale(0, 0, cols, total);
33                 if (self->scale == NULL)
34                         goto out_free_form;
35                 newtFormAddComponent(self->form, self->scale);
36                 newtRefresh();
37         }
38
39         return self;
40
41 out_free_form:
42         newtFormDestroy(self->form);
43 out_free_self:
44         free(self);
45         return NULL;
46 }
47
48 void ui_progress__update(struct ui_progress *self, u64 curr)
49 {
50         newtScaleSet(self->scale, curr);
51         newtRefresh();
52 }
53
54 void ui_progress__delete(struct ui_progress *self)
55 {
56         newtFormDestroy(self->form);
57         newtPopWindow();
58         free(self);
59 }
60
61 static void ui_helpline__pop(void)
62 {
63         newtPopHelpLine();
64 }
65
66 static void ui_helpline__push(const char *msg)
67 {
68         newtPushHelpLine(msg);
69 }
70
71 static void ui_helpline__vpush(const char *fmt, va_list ap)
72 {
73         char *s;
74
75         if (vasprintf(&s, fmt, ap) < 0)
76                 vfprintf(stderr, fmt, ap);
77         else {
78                 ui_helpline__push(s);
79                 free(s);
80         }
81 }
82
83 static void ui_helpline__fpush(const char *fmt, ...)
84 {
85         va_list ap;
86
87         va_start(ap, fmt);
88         ui_helpline__vpush(fmt, ap);
89         va_end(ap);
90 }
91
92 static void ui_helpline__puts(const char *msg)
93 {
94         ui_helpline__pop();
95         ui_helpline__push(msg);
96 }
97
98 static char browser__last_msg[1024];
99
100 int browser__show_help(const char *format, va_list ap)
101 {
102         int ret;
103         static int backlog;
104
105         ret = vsnprintf(browser__last_msg + backlog,
106                         sizeof(browser__last_msg) - backlog, format, ap);
107         backlog += ret;
108
109         if (browser__last_msg[backlog - 1] == '\n') {
110                 ui_helpline__puts(browser__last_msg);
111                 newtRefresh();
112                 backlog = 0;
113         }
114
115         return ret;
116 }
117
118 static void newt_form__set_exit_keys(newtComponent self)
119 {
120         newtFormAddHotKey(self, NEWT_KEY_ESCAPE);
121         newtFormAddHotKey(self, 'Q');
122         newtFormAddHotKey(self, 'q');
123         newtFormAddHotKey(self, CTRL('c'));
124 }
125
126 static newtComponent newt_form__new(void)
127 {
128         newtComponent self = newtForm(NULL, NULL, 0);
129         if (self)
130                 newt_form__set_exit_keys(self);
131         return self;
132 }
133
134 static int popup_menu(int argc, char * const argv[])
135 {
136         struct newtExitStruct es;
137         int i, rc = -1, max_len = 5;
138         newtComponent listbox, form = newt_form__new();
139
140         if (form == NULL)
141                 return -1;
142
143         listbox = newtListbox(0, 0, argc, NEWT_FLAG_RETURNEXIT);
144         if (listbox == NULL)
145                 goto out_destroy_form;
146
147         newtFormAddComponent(form, listbox);
148
149         for (i = 0; i < argc; ++i) {
150                 int len = strlen(argv[i]);
151                 if (len > max_len)
152                         max_len = len;
153                 if (newtListboxAddEntry(listbox, argv[i], (void *)(long)i))
154                         goto out_destroy_form;
155         }
156
157         newtCenteredWindow(max_len, argc, NULL);
158         newtFormRun(form, &es);
159         rc = newtListboxGetCurrent(listbox) - NULL;
160         if (es.reason == NEWT_EXIT_HOTKEY)
161                 rc = -1;
162         newtPopWindow();
163 out_destroy_form:
164         newtFormDestroy(form);
165         return rc;
166 }
167
168 static bool dialog_yesno(const char *msg)
169 {
170         /* newtWinChoice should really be accepting const char pointers... */
171         char yes[] = "Yes", no[] = "No";
172         return newtWinChoice(NULL, yes, no, (char *)msg) == 1;
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
181 static 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
192 struct 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
201 static 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
215 static 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
221 static 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
270 static 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
299 static 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
423 /*
424  * When debugging newt problems it was useful to be able to "unroll"
425  * the calls to newtCheckBoxTreeAdd{Array,Item}, so that we can generate
426  * a source file with the sequence of calls to these methods, to then
427  * tweak the arrays to get the intended results, so I'm keeping this code
428  * here, may be useful again in the future.
429  */
430 #undef NEWT_DEBUG
431
432 static void newt_checkbox_tree__add(newtComponent tree, const char *str,
433                                     void *priv, int *indexes)
434 {
435 #ifdef NEWT_DEBUG
436         /* Print the newtCheckboxTreeAddArray to tinker with its index arrays */
437         int i = 0, len = 40 - strlen(str);
438
439         fprintf(stderr,
440                 "\tnewtCheckboxTreeAddItem(tree, %*.*s\"%s\", (void *)%p, 0, ",
441                 len, len, " ", str, priv);
442         while (indexes[i] != NEWT_ARG_LAST) {
443                 if (indexes[i] != NEWT_ARG_APPEND)
444                         fprintf(stderr, " %d,", indexes[i]);
445                 else
446                         fprintf(stderr, " %s,", "NEWT_ARG_APPEND");
447                 ++i;
448         }
449         fprintf(stderr, " %s", " NEWT_ARG_LAST);\n");
450         fflush(stderr);
451 #endif
452         newtCheckboxTreeAddArray(tree, str, priv, 0, indexes);
453 }
454
455 static char *callchain_list__sym_name(struct callchain_list *self,
456                                       char *bf, size_t bfsize)
457 {
458         if (self->ms.sym)
459                 return self->ms.sym->name;
460
461         snprintf(bf, bfsize, "%#Lx", self->ip);
462         return bf;
463 }
464
465 static void __callchain__append_graph_browser(struct callchain_node *self,
466                                               newtComponent tree, u64 total,
467                                               int *indexes, int depth)
468 {
469         struct rb_node *node;
470         u64 new_total, remaining;
471         int idx = 0;
472
473         if (callchain_param.mode == CHAIN_GRAPH_REL)
474                 new_total = self->children_hit;
475         else
476                 new_total = total;
477
478         remaining = new_total;
479         node = rb_first(&self->rb_root);
480         while (node) {
481                 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
482                 struct rb_node *next = rb_next(node);
483                 u64 cumul = cumul_hits(child);
484                 struct callchain_list *chain;
485                 int first = true, printed = 0;
486                 int chain_idx = -1;
487                 remaining -= cumul;
488
489                 indexes[depth] = NEWT_ARG_APPEND;
490                 indexes[depth + 1] = NEWT_ARG_LAST;
491
492                 list_for_each_entry(chain, &child->val, list) {
493                         char ipstr[BITS_PER_LONG / 4 + 1],
494                              *alloc_str = NULL;
495                         const char *str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
496
497                         if (first) {
498                                 double percent = cumul * 100.0 / new_total;
499
500                                 first = false;
501                                 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
502                                         str = "Not enough memory!";
503                                 else
504                                         str = alloc_str;
505                         } else {
506                                 indexes[depth] = idx;
507                                 indexes[depth + 1] = NEWT_ARG_APPEND;
508                                 indexes[depth + 2] = NEWT_ARG_LAST;
509                                 ++chain_idx;
510                         }
511                         newt_checkbox_tree__add(tree, str, &chain->ms, indexes);
512                         free(alloc_str);
513                         ++printed;
514                 }
515
516                 indexes[depth] = idx;
517                 if (chain_idx != -1)
518                         indexes[depth + 1] = chain_idx;
519                 if (printed != 0)
520                         ++idx;
521                 __callchain__append_graph_browser(child, tree, new_total, indexes,
522                                                   depth + (chain_idx != -1 ? 2 : 1));
523                 node = next;
524         }
525 }
526
527 static void callchain__append_graph_browser(struct callchain_node *self,
528                                             newtComponent tree, u64 total,
529                                             int *indexes, int parent_idx)
530 {
531         struct callchain_list *chain;
532         int i = 0;
533
534         indexes[1] = NEWT_ARG_APPEND;
535         indexes[2] = NEWT_ARG_LAST;
536
537         list_for_each_entry(chain, &self->val, list) {
538                 char ipstr[BITS_PER_LONG / 4 + 1], *str;
539
540                 if (chain->ip >= PERF_CONTEXT_MAX)
541                         continue;
542
543                 if (!i++ && sort__first_dimension == SORT_SYM)
544                         continue;
545
546                 str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
547                 newt_checkbox_tree__add(tree, str, &chain->ms, indexes);
548         }
549
550         indexes[1] = parent_idx;
551         indexes[2] = NEWT_ARG_APPEND;
552         indexes[3] = NEWT_ARG_LAST;
553         __callchain__append_graph_browser(self, tree, total, indexes, 2);
554 }
555
556 static void hist_entry__append_callchain_browser(struct hist_entry *self,
557                                                  newtComponent tree, u64 total, int parent_idx)
558 {
559         struct rb_node *rb_node;
560         int indexes[1024] = { [0] = parent_idx, };
561         int idx = 0;
562         struct callchain_node *chain;
563
564         rb_node = rb_first(&self->sorted_chain);
565         while (rb_node) {
566                 chain = rb_entry(rb_node, struct callchain_node, rb_node);
567                 switch (callchain_param.mode) {
568                 case CHAIN_FLAT:
569                         break;
570                 case CHAIN_GRAPH_ABS: /* falldown */
571                 case CHAIN_GRAPH_REL:
572                         callchain__append_graph_browser(chain, tree, total, indexes, idx++);
573                         break;
574                 case CHAIN_NONE:
575                 default:
576                         break;
577                 }
578                 rb_node = rb_next(rb_node);
579         }
580 }
581
582 static size_t hist_entry__append_browser(struct hist_entry *self,
583                                          newtComponent tree, u64 total)
584 {
585         char s[256];
586         size_t ret;
587
588         if (symbol_conf.exclude_other && !self->parent)
589                 return 0;
590
591         ret = hist_entry__snprintf(self, s, sizeof(s), NULL,
592                                    false, 0, false, total);
593         if (symbol_conf.use_callchain) {
594                 int indexes[2];
595
596                 indexes[0] = NEWT_ARG_APPEND;
597                 indexes[1] = NEWT_ARG_LAST;
598                 newt_checkbox_tree__add(tree, s, &self->ms, indexes);
599         } else
600                 newtListboxAppendEntry(tree, s, &self->ms);
601
602         return ret;
603 }
604
605 static void hist_entry__annotate_browser(struct hist_entry *self)
606 {
607         struct ui_browser browser;
608         struct newtExitStruct es;
609         struct objdump_line *pos, *n;
610         LIST_HEAD(head);
611
612         if (self->ms.sym == NULL)
613                 return;
614
615         if (hist_entry__annotate(self, &head) < 0)
616                 return;
617
618         ui_helpline__push("Press ESC to exit");
619
620         memset(&browser, 0, sizeof(browser));
621         browser.entries = &head;
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;
628         }
629
630         browser.width += 18; /* Percentage */
631         ui_browser__run(&browser, self->ms.sym->name, &es);
632         newtFormDestroy(browser.form);
633         newtPopWindow();
634         list_for_each_entry_safe(pos, n, &head, node) {
635                 list_del(&pos->node);
636                 objdump_line__free(pos);
637         }
638         ui_helpline__pop();
639 }
640
641 static const void *newt__symbol_tree_get_current(newtComponent self)
642 {
643         if (symbol_conf.use_callchain)
644                 return newtCheckboxTreeGetCurrent(self);
645         return newtListboxGetCurrent(self);
646 }
647
648 static void hist_browser__selection(newtComponent self, void *data)
649 {
650         const struct map_symbol **symbol_ptr = data;
651         *symbol_ptr = newt__symbol_tree_get_current(self);
652 }
653
654 struct hist_browser {
655         newtComponent           form, tree;
656         const struct map_symbol *selection;
657 };
658
659 static struct hist_browser *hist_browser__new(void)
660 {
661         struct hist_browser *self = malloc(sizeof(*self));
662
663         if (self != NULL)
664                 self->form = NULL;
665
666         return self;
667 }
668
669 static void hist_browser__delete(struct hist_browser *self)
670 {
671         newtFormDestroy(self->form);
672         newtPopWindow();
673         free(self);
674 }
675
676 static int hist_browser__populate(struct hist_browser *self, struct hists *hists,
677                                   const char *title)
678 {
679         int max_len = 0, idx, cols, rows;
680         struct ui_progress *progress;
681         struct rb_node *nd;
682         u64 curr_hist = 0;
683         char seq[] = ".", unit;
684         char str[256];
685         unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
686
687         if (self->form) {
688                 newtFormDestroy(self->form);
689                 newtPopWindow();
690         }
691
692         nr_events = convert_unit(nr_events, &unit);
693         snprintf(str, sizeof(str), "Events: %lu%c                            ",
694                  nr_events, unit);
695         newtDrawRootText(0, 0, str);
696
697         newtGetScreenSize(NULL, &rows);
698
699         if (symbol_conf.use_callchain)
700                 self->tree = newtCheckboxTreeMulti(0, 0, rows - 5, seq,
701                                                    NEWT_FLAG_SCROLL);
702         else
703                 self->tree = newtListbox(0, 0, rows - 5,
704                                         (NEWT_FLAG_SCROLL |
705                                          NEWT_FLAG_RETURNEXIT));
706
707         newtComponentAddCallback(self->tree, hist_browser__selection,
708                                  &self->selection);
709
710         progress = ui_progress__new("Adding entries to the browser...",
711                                     hists->nr_entries);
712         if (progress == NULL)
713                 return -1;
714
715         idx = 0;
716         for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
717                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
718                 int len;
719
720                 if (h->filtered)
721                         continue;
722
723                 len = hist_entry__append_browser(h, self->tree, hists->stats.total_period);
724                 if (len > max_len)
725                         max_len = len;
726                 if (symbol_conf.use_callchain)
727                         hist_entry__append_callchain_browser(h, self->tree,
728                                                              hists->stats.total_period, idx++);
729                 ++curr_hist;
730                 if (curr_hist % 5)
731                         ui_progress__update(progress, curr_hist);
732         }
733
734         ui_progress__delete(progress);
735
736         newtGetScreenSize(&cols, &rows);
737
738         if (max_len > cols)
739                 max_len = cols - 3;
740
741         if (!symbol_conf.use_callchain)
742                 newtListboxSetWidth(self->tree, max_len);
743
744         newtCenteredWindow(max_len + (symbol_conf.use_callchain ? 5 : 0),
745                            rows - 5, title);
746         self->form = newt_form__new();
747         if (self->form == NULL)
748                 return -1;
749
750         newtFormAddHotKey(self->form, 'A');
751         newtFormAddHotKey(self->form, 'a');
752         newtFormAddHotKey(self->form, NEWT_KEY_RIGHT);
753         newtFormAddComponents(self->form, self->tree, NULL);
754         self->selection = newt__symbol_tree_get_current(self->tree);
755
756         return 0;
757 }
758
759 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self)
760 {
761         int *indexes;
762
763         if (!symbol_conf.use_callchain)
764                 goto out;
765
766         indexes = newtCheckboxTreeFindItem(self->tree, (void *)self->selection);
767         if (indexes) {
768                 bool is_hist_entry = indexes[1] == NEWT_ARG_LAST;
769                 free(indexes);
770                 if (is_hist_entry)
771                         goto out;
772         }
773         return NULL;
774 out:
775         return container_of(self->selection, struct hist_entry, ms);
776 }
777
778 static struct thread *hist_browser__selected_thread(struct hist_browser *self)
779 {
780         struct hist_entry *he = hist_browser__selected_entry(self);
781         return he ? he->thread : NULL;
782 }
783
784 static int hist_browser__title(char *bf, size_t size, const char *input_name,
785                                const struct dso *dso, const struct thread *thread)
786 {
787         int printed = 0;
788
789         if (thread)
790                 printed += snprintf(bf + printed, size - printed,
791                                     "Thread: %s(%d)",
792                                     (thread->comm_set ?  thread->comm : ""),
793                                     thread->pid);
794         if (dso)
795                 printed += snprintf(bf + printed, size - printed,
796                                     "%sDSO: %s", thread ? " " : "",
797                                     dso->short_name);
798         return printed ?: snprintf(bf, size, "Report: %s", input_name);
799 }
800
801 int hists__browse(struct hists *self, const char *helpline, const char *input_name)
802 {
803         struct hist_browser *browser = hist_browser__new();
804         const struct thread *thread_filter = NULL;
805         const struct dso *dso_filter = NULL;
806         struct newtExitStruct es;
807         char msg[160];
808         int err = -1;
809
810         if (browser == NULL)
811                 return -1;
812
813         ui_helpline__push(helpline);
814
815         hist_browser__title(msg, sizeof(msg), input_name,
816                             dso_filter, thread_filter);
817         if (hist_browser__populate(browser, self, msg) < 0)
818                 goto out;
819
820         while (1) {
821                 const struct thread *thread;
822                 const struct dso *dso;
823                 char *options[16];
824                 int nr_options = 0, choice = 0, i,
825                     annotate = -2, zoom_dso = -2, zoom_thread = -2;
826
827                 newtFormRun(browser->form, &es);
828                 if (es.reason == NEWT_EXIT_HOTKEY) {
829                         if (toupper(es.u.key) == 'A')
830                                 goto do_annotate;
831                         if (es.u.key == NEWT_KEY_ESCAPE ||
832                             toupper(es.u.key) == 'Q' ||
833                             es.u.key == CTRL('c')) {
834                                 if (dialog_yesno("Do you really want to exit?"))
835                                         break;
836                                 else
837                                         continue;
838                         }
839                 }
840
841                 if (browser->selection->sym != NULL &&
842                     asprintf(&options[nr_options], "Annotate %s",
843                              browser->selection->sym->name) > 0)
844                         annotate = nr_options++;
845
846                 thread = hist_browser__selected_thread(browser);
847                 if (thread != NULL &&
848                     asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
849                              (thread_filter ? "out of" : "into"),
850                              (thread->comm_set ? thread->comm : ""),
851                              thread->pid) > 0)
852                         zoom_thread = nr_options++;
853
854                 dso = browser->selection->map ? browser->selection->map->dso : NULL;
855                 if (dso != NULL &&
856                     asprintf(&options[nr_options], "Zoom %s %s DSO",
857                              (dso_filter ? "out of" : "into"),
858                              (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
859                         zoom_dso = nr_options++;
860
861                 options[nr_options++] = (char *)"Exit";
862
863                 choice = popup_menu(nr_options, options);
864
865                 for (i = 0; i < nr_options - 1; ++i)
866                         free(options[i]);
867
868                 if (choice == nr_options - 1)
869                         break;
870
871                 if (choice == -1)
872                         continue;
873 do_annotate:
874                 if (choice == annotate) {
875                         struct hist_entry *he;
876
877                         if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) {
878                                 ui_helpline__puts("No vmlinux file found, can't "
879                                                  "annotate with just a "
880                                                  "kallsyms file");
881                                 continue;
882                         }
883
884                         he = hist_browser__selected_entry(browser);
885                         if (he == NULL)
886                                 continue;
887
888                         hist_entry__annotate_browser(he);
889                 } else if (choice == zoom_dso) {
890                         if (dso_filter) {
891                                 ui_helpline__pop();
892                                 dso_filter = NULL;
893                         } else {
894                                 ui_helpline__fpush("To zoom out press -> + \"Zoom out of %s DSO\"",
895                                                    dso->kernel ? "the Kernel" : dso->short_name);
896                                 dso_filter = dso;
897                         }
898                         hists__filter_by_dso(self, dso_filter);
899                         hist_browser__title(msg, sizeof(msg), input_name,
900                                             dso_filter, thread_filter);
901                         if (hist_browser__populate(browser, self, msg) < 0)
902                                 goto out;
903                 } else if (choice == zoom_thread) {
904                         if (thread_filter) {
905                                 ui_helpline__pop();
906                                 thread_filter = NULL;
907                         } else {
908                                 ui_helpline__fpush("To zoom out press -> + \"Zoom out of %s(%d) thread\"",
909                                                    thread->comm_set ? thread->comm : "",
910                                                    thread->pid);
911                                 thread_filter = thread;
912                         }
913                         hists__filter_by_thread(self, thread_filter);
914                         hist_browser__title(msg, sizeof(msg), input_name,
915                                             dso_filter, thread_filter);
916                         if (hist_browser__populate(browser, self, msg) < 0)
917                                 goto out;
918                 }
919         }
920         err = 0;
921 out:
922         hist_browser__delete(browser);
923         return err;
924 }
925
926 static struct newtPercentTreeColors {
927         const char *topColorFg, *topColorBg;
928         const char *mediumColorFg, *mediumColorBg;
929         const char *normalColorFg, *normalColorBg;
930         const char *selColorFg, *selColorBg;
931         const char *codeColorFg, *codeColorBg;
932 } defaultPercentTreeColors = {
933         "red",       "lightgray",
934         "green",     "lightgray",
935         "black",     "lightgray",
936         "lightgray", "magenta",
937         "blue",      "lightgray",
938 };
939
940 void setup_browser(void)
941 {
942         struct newtPercentTreeColors *c = &defaultPercentTreeColors;
943         if (!isatty(1))
944                 return;
945
946         use_browser = true;
947         newtInit();
948         newtCls();
949         ui_helpline__puts(" ");
950         SLtt_set_color(HE_COLORSET_TOP, NULL, c->topColorFg, c->topColorBg);
951         SLtt_set_color(HE_COLORSET_MEDIUM, NULL, c->mediumColorFg, c->mediumColorBg);
952         SLtt_set_color(HE_COLORSET_NORMAL, NULL, c->normalColorFg, c->normalColorBg);
953         SLtt_set_color(HE_COLORSET_SELECTED, NULL, c->selColorFg, c->selColorBg);
954         SLtt_set_color(HE_COLORSET_CODE, NULL, c->codeColorFg, c->codeColorBg);
955 }
956
957 void exit_browser(bool wait_for_ok)
958 {
959         if (use_browser) {
960                 if (wait_for_ok) {
961                         char title[] = "Fatal Error", ok[] = "Ok";
962                         newtWinMessage(title, ok, browser__last_msg);
963                 }
964                 newtFinished();
965         }
966 }