Merge branch 'upstream-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jgarzi...
[pandora-kernel.git] / tools / perf / util / newt.c
1 #define _GNU_SOURCE
2 #include <stdio.h>
3 #undef _GNU_SOURCE
4 /*
5  * slang versions <= 2.0.6 have a "#if HAVE_LONG_LONG" that breaks
6  * the build if it isn't defined. Use the equivalent one that glibc
7  * has on features.h.
8  */
9 #include <features.h>
10 #ifndef HAVE_LONG_LONG
11 #define HAVE_LONG_LONG __GLIBC_HAVE_LONG_LONG
12 #endif
13 #include <slang.h>
14 #include <stdlib.h>
15 #include <newt.h>
16 #include <sys/ttydefaults.h>
17
18 #include "cache.h"
19 #include "hist.h"
20 #include "pstack.h"
21 #include "session.h"
22 #include "sort.h"
23 #include "symbol.h"
24
25 #if SLANG_VERSION < 20104
26 #define slsmg_printf(msg, args...) SLsmg_printf((char *)msg, ##args)
27 #define slsmg_write_nstring(msg, len) SLsmg_write_nstring((char *)msg, len)
28 #define sltt_set_color(obj, name, fg, bg) SLtt_set_color(obj,(char *)name,\
29                                                          (char *)fg, (char *)bg)
30 #else
31 #define slsmg_printf SLsmg_printf
32 #define slsmg_write_nstring SLsmg_write_nstring
33 #define sltt_set_color SLtt_set_color
34 #endif
35
36 struct ui_progress {
37         newtComponent form, scale;
38 };
39
40 struct ui_progress *ui_progress__new(const char *title, u64 total)
41 {
42         struct ui_progress *self = malloc(sizeof(*self));
43
44         if (self != NULL) {
45                 int cols;
46                 newtGetScreenSize(&cols, NULL);
47                 cols -= 4;
48                 newtCenteredWindow(cols, 1, title);
49                 self->form  = newtForm(NULL, NULL, 0);
50                 if (self->form == NULL)
51                         goto out_free_self;
52                 self->scale = newtScale(0, 0, cols, total);
53                 if (self->scale == NULL)
54                         goto out_free_form;
55                 newtFormAddComponent(self->form, self->scale);
56                 newtRefresh();
57         }
58
59         return self;
60
61 out_free_form:
62         newtFormDestroy(self->form);
63 out_free_self:
64         free(self);
65         return NULL;
66 }
67
68 void ui_progress__update(struct ui_progress *self, u64 curr)
69 {
70         newtScaleSet(self->scale, curr);
71         newtRefresh();
72 }
73
74 void ui_progress__delete(struct ui_progress *self)
75 {
76         newtFormDestroy(self->form);
77         newtPopWindow();
78         free(self);
79 }
80
81 static void ui_helpline__pop(void)
82 {
83         newtPopHelpLine();
84 }
85
86 static void ui_helpline__push(const char *msg)
87 {
88         newtPushHelpLine(msg);
89 }
90
91 static void ui_helpline__vpush(const char *fmt, va_list ap)
92 {
93         char *s;
94
95         if (vasprintf(&s, fmt, ap) < 0)
96                 vfprintf(stderr, fmt, ap);
97         else {
98                 ui_helpline__push(s);
99                 free(s);
100         }
101 }
102
103 static void ui_helpline__fpush(const char *fmt, ...)
104 {
105         va_list ap;
106
107         va_start(ap, fmt);
108         ui_helpline__vpush(fmt, ap);
109         va_end(ap);
110 }
111
112 static void ui_helpline__puts(const char *msg)
113 {
114         ui_helpline__pop();
115         ui_helpline__push(msg);
116 }
117
118 static char browser__last_msg[1024];
119
120 int browser__show_help(const char *format, va_list ap)
121 {
122         int ret;
123         static int backlog;
124
125         ret = vsnprintf(browser__last_msg + backlog,
126                         sizeof(browser__last_msg) - backlog, format, ap);
127         backlog += ret;
128
129         if (browser__last_msg[backlog - 1] == '\n') {
130                 ui_helpline__puts(browser__last_msg);
131                 newtRefresh();
132                 backlog = 0;
133         }
134
135         return ret;
136 }
137
138 static void newt_form__set_exit_keys(newtComponent self)
139 {
140         newtFormAddHotKey(self, NEWT_KEY_LEFT);
141         newtFormAddHotKey(self, NEWT_KEY_ESCAPE);
142         newtFormAddHotKey(self, 'Q');
143         newtFormAddHotKey(self, 'q');
144         newtFormAddHotKey(self, CTRL('c'));
145 }
146
147 static newtComponent newt_form__new(void)
148 {
149         newtComponent self = newtForm(NULL, NULL, 0);
150         if (self)
151                 newt_form__set_exit_keys(self);
152         return self;
153 }
154
155 static int popup_menu(int argc, char * const argv[])
156 {
157         struct newtExitStruct es;
158         int i, rc = -1, max_len = 5;
159         newtComponent listbox, form = newt_form__new();
160
161         if (form == NULL)
162                 return -1;
163
164         listbox = newtListbox(0, 0, argc, NEWT_FLAG_RETURNEXIT);
165         if (listbox == NULL)
166                 goto out_destroy_form;
167
168         newtFormAddComponent(form, listbox);
169
170         for (i = 0; i < argc; ++i) {
171                 int len = strlen(argv[i]);
172                 if (len > max_len)
173                         max_len = len;
174                 if (newtListboxAddEntry(listbox, argv[i], (void *)(long)i))
175                         goto out_destroy_form;
176         }
177
178         newtCenteredWindow(max_len, argc, NULL);
179         newtFormRun(form, &es);
180         rc = newtListboxGetCurrent(listbox) - NULL;
181         if (es.reason == NEWT_EXIT_HOTKEY)
182                 rc = -1;
183         newtPopWindow();
184 out_destroy_form:
185         newtFormDestroy(form);
186         return rc;
187 }
188
189 static int ui__help_window(const char *text)
190 {
191         struct newtExitStruct es;
192         newtComponent tb, form = newt_form__new();
193         int rc = -1;
194         int max_len = 0, nr_lines = 0;
195         const char *t;
196
197         if (form == NULL)
198                 return -1;
199
200         t = text;
201         while (1) {
202                 const char *sep = strchr(t, '\n');
203                 int len;
204
205                 if (sep == NULL)
206                         sep = strchr(t, '\0');
207                 len = sep - t;
208                 if (max_len < len)
209                         max_len = len;
210                 ++nr_lines;
211                 if (*sep == '\0')
212                         break;
213                 t = sep + 1;
214         }
215
216         tb = newtTextbox(0, 0, max_len, nr_lines, 0);
217         if (tb == NULL)
218                 goto out_destroy_form;
219
220         newtTextboxSetText(tb, text);
221         newtFormAddComponent(form, tb);
222         newtCenteredWindow(max_len, nr_lines, NULL);
223         newtFormRun(form, &es);
224         newtPopWindow();
225         rc = 0;
226 out_destroy_form:
227         newtFormDestroy(form);
228         return rc;
229 }
230
231 static bool dialog_yesno(const char *msg)
232 {
233         /* newtWinChoice should really be accepting const char pointers... */
234         char yes[] = "Yes", no[] = "No";
235         return newtWinChoice(NULL, yes, no, (char *)msg) == 1;
236 }
237
238 static void ui__error_window(const char *fmt, ...)
239 {
240         va_list ap;
241
242         va_start(ap, fmt);
243         newtWinMessagev((char *)"Error", (char *)"Ok", (char *)fmt, ap);
244         va_end(ap);
245 }
246
247 #define HE_COLORSET_TOP         50
248 #define HE_COLORSET_MEDIUM      51
249 #define HE_COLORSET_NORMAL      52
250 #define HE_COLORSET_SELECTED    53
251 #define HE_COLORSET_CODE        54
252
253 static int ui_browser__percent_color(double percent, bool current)
254 {
255         if (current)
256                 return HE_COLORSET_SELECTED;
257         if (percent >= MIN_RED)
258                 return HE_COLORSET_TOP;
259         if (percent >= MIN_GREEN)
260                 return HE_COLORSET_MEDIUM;
261         return HE_COLORSET_NORMAL;
262 }
263
264 struct ui_browser {
265         newtComponent   form, sb;
266         u64             index, first_visible_entry_idx;
267         void            *first_visible_entry, *entries;
268         u16             top, left, width, height;
269         void            *priv;
270         u32             nr_entries;
271 };
272
273 static void ui_browser__refresh_dimensions(struct ui_browser *self)
274 {
275         int cols, rows;
276         newtGetScreenSize(&cols, &rows);
277
278         if (self->width > cols - 4)
279                 self->width = cols - 4;
280         self->height = rows - 5;
281         if (self->height > self->nr_entries)
282                 self->height = self->nr_entries;
283         self->top  = (rows - self->height) / 2;
284         self->left = (cols - self->width) / 2;
285 }
286
287 static void ui_browser__reset_index(struct ui_browser *self)
288 {
289         self->index = self->first_visible_entry_idx = 0;
290         self->first_visible_entry = NULL;
291 }
292
293 static int objdump_line__show(struct objdump_line *self, struct list_head *head,
294                               int width, struct hist_entry *he, int len,
295                               bool current_entry)
296 {
297         if (self->offset != -1) {
298                 struct symbol *sym = he->ms.sym;
299                 unsigned int hits = 0;
300                 double percent = 0.0;
301                 int color;
302                 struct sym_priv *priv = symbol__priv(sym);
303                 struct sym_ext *sym_ext = priv->ext;
304                 struct sym_hist *h = priv->hist;
305                 s64 offset = self->offset;
306                 struct objdump_line *next = objdump__get_next_ip_line(head, self);
307
308                 while (offset < (s64)len &&
309                        (next == NULL || offset < next->offset)) {
310                         if (sym_ext) {
311                                 percent += sym_ext[offset].percent;
312                         } else
313                                 hits += h->ip[offset];
314
315                         ++offset;
316                 }
317
318                 if (sym_ext == NULL && h->sum)
319                         percent = 100.0 * hits / h->sum;
320
321                 color = ui_browser__percent_color(percent, current_entry);
322                 SLsmg_set_color(color);
323                 slsmg_printf(" %7.2f ", percent);
324                 if (!current_entry)
325                         SLsmg_set_color(HE_COLORSET_CODE);
326         } else {
327                 int color = ui_browser__percent_color(0, current_entry);
328                 SLsmg_set_color(color);
329                 slsmg_write_nstring(" ", 9);
330         }
331
332         SLsmg_write_char(':');
333         slsmg_write_nstring(" ", 8);
334         if (!*self->line)
335                 slsmg_write_nstring(" ", width - 18);
336         else
337                 slsmg_write_nstring(self->line, width - 18);
338
339         return 0;
340 }
341
342 static int ui_browser__refresh_entries(struct ui_browser *self)
343 {
344         struct objdump_line *pos;
345         struct list_head *head = self->entries;
346         struct hist_entry *he = self->priv;
347         int row = 0;
348         int len = he->ms.sym->end - he->ms.sym->start;
349
350         if (self->first_visible_entry == NULL || self->first_visible_entry == self->entries)
351                 self->first_visible_entry = head->next;
352
353         pos = list_entry(self->first_visible_entry, struct objdump_line, node);
354
355         list_for_each_entry_from(pos, head, node) {
356                 bool current_entry = (self->first_visible_entry_idx + row) == self->index;
357                 SLsmg_gotorc(self->top + row, self->left);
358                 objdump_line__show(pos, head, self->width,
359                                    he, len, current_entry);
360                 if (++row == self->height)
361                         break;
362         }
363
364         SLsmg_set_color(HE_COLORSET_NORMAL);
365         SLsmg_fill_region(self->top + row, self->left,
366                           self->height - row, self->width, ' ');
367
368         return 0;
369 }
370
371 static int ui_browser__run(struct ui_browser *self, const char *title,
372                            struct newtExitStruct *es)
373 {
374         if (self->form) {
375                 newtFormDestroy(self->form);
376                 newtPopWindow();
377         }
378
379         ui_browser__refresh_dimensions(self);
380         newtCenteredWindow(self->width + 2, self->height, title);
381         self->form = newt_form__new();
382         if (self->form == NULL)
383                 return -1;
384
385         self->sb = newtVerticalScrollbar(self->width + 1, 0, self->height,
386                                          HE_COLORSET_NORMAL,
387                                          HE_COLORSET_SELECTED);
388         if (self->sb == NULL)
389                 return -1;
390
391         newtFormAddHotKey(self->form, NEWT_KEY_UP);
392         newtFormAddHotKey(self->form, NEWT_KEY_DOWN);
393         newtFormAddHotKey(self->form, NEWT_KEY_PGUP);
394         newtFormAddHotKey(self->form, NEWT_KEY_PGDN);
395         newtFormAddHotKey(self->form, ' ');
396         newtFormAddHotKey(self->form, NEWT_KEY_HOME);
397         newtFormAddHotKey(self->form, NEWT_KEY_END);
398         newtFormAddHotKey(self->form, NEWT_KEY_TAB);
399         newtFormAddHotKey(self->form, NEWT_KEY_RIGHT);
400
401         if (ui_browser__refresh_entries(self) < 0)
402                 return -1;
403         newtFormAddComponent(self->form, self->sb);
404
405         while (1) {
406                 unsigned int offset;
407
408                 newtFormRun(self->form, es);
409
410                 if (es->reason != NEWT_EXIT_HOTKEY)
411                         break;
412                 if (is_exit_key(es->u.key))
413                         return es->u.key;
414                 switch (es->u.key) {
415                 case NEWT_KEY_DOWN:
416                         if (self->index == self->nr_entries - 1)
417                                 break;
418                         ++self->index;
419                         if (self->index == self->first_visible_entry_idx + self->height) {
420                                 struct list_head *pos = self->first_visible_entry;
421                                 ++self->first_visible_entry_idx;
422                                 self->first_visible_entry = pos->next;
423                         }
424                         break;
425                 case NEWT_KEY_UP:
426                         if (self->index == 0)
427                                 break;
428                         --self->index;
429                         if (self->index < self->first_visible_entry_idx) {
430                                 struct list_head *pos = self->first_visible_entry;
431                                 --self->first_visible_entry_idx;
432                                 self->first_visible_entry = pos->prev;
433                         }
434                         break;
435                 case NEWT_KEY_PGDN:
436                 case ' ':
437                         if (self->first_visible_entry_idx + self->height > self->nr_entries - 1)
438                                 break;
439
440                         offset = self->height;
441                         if (self->index + offset > self->nr_entries - 1)
442                                 offset = self->nr_entries - 1 - self->index;
443                         self->index += offset;
444                         self->first_visible_entry_idx += offset;
445
446                         while (offset--) {
447                                 struct list_head *pos = self->first_visible_entry;
448                                 self->first_visible_entry = pos->next;
449                         }
450
451                         break;
452                 case NEWT_KEY_PGUP:
453                         if (self->first_visible_entry_idx == 0)
454                                 break;
455
456                         if (self->first_visible_entry_idx < self->height)
457                                 offset = self->first_visible_entry_idx;
458                         else
459                                 offset = self->height;
460
461                         self->index -= offset;
462                         self->first_visible_entry_idx -= offset;
463
464                         while (offset--) {
465                                 struct list_head *pos = self->first_visible_entry;
466                                 self->first_visible_entry = pos->prev;
467                         }
468                         break;
469                 case NEWT_KEY_HOME:
470                         ui_browser__reset_index(self);
471                         break;
472                 case NEWT_KEY_END: {
473                         struct list_head *head = self->entries;
474                         offset = self->height - 1;
475
476                         if (offset > self->nr_entries)
477                                 offset = self->nr_entries;
478
479                         self->index = self->first_visible_entry_idx = self->nr_entries - 1 - offset;
480                         self->first_visible_entry = head->prev;
481                         while (offset-- != 0) {
482                                 struct list_head *pos = self->first_visible_entry;
483                                 self->first_visible_entry = pos->prev;
484                         }
485                 }
486                         break;
487                 case NEWT_KEY_RIGHT:
488                 case NEWT_KEY_LEFT:
489                 case NEWT_KEY_TAB:
490                         return es->u.key;
491                 default:
492                         continue;
493                 }
494                 if (ui_browser__refresh_entries(self) < 0)
495                         return -1;
496         }
497         return 0;
498 }
499
500 /*
501  * When debugging newt problems it was useful to be able to "unroll"
502  * the calls to newtCheckBoxTreeAdd{Array,Item}, so that we can generate
503  * a source file with the sequence of calls to these methods, to then
504  * tweak the arrays to get the intended results, so I'm keeping this code
505  * here, may be useful again in the future.
506  */
507 #undef NEWT_DEBUG
508
509 static void newt_checkbox_tree__add(newtComponent tree, const char *str,
510                                     void *priv, int *indexes)
511 {
512 #ifdef NEWT_DEBUG
513         /* Print the newtCheckboxTreeAddArray to tinker with its index arrays */
514         int i = 0, len = 40 - strlen(str);
515
516         fprintf(stderr,
517                 "\tnewtCheckboxTreeAddItem(tree, %*.*s\"%s\", (void *)%p, 0, ",
518                 len, len, " ", str, priv);
519         while (indexes[i] != NEWT_ARG_LAST) {
520                 if (indexes[i] != NEWT_ARG_APPEND)
521                         fprintf(stderr, " %d,", indexes[i]);
522                 else
523                         fprintf(stderr, " %s,", "NEWT_ARG_APPEND");
524                 ++i;
525         }
526         fprintf(stderr, " %s", " NEWT_ARG_LAST);\n");
527         fflush(stderr);
528 #endif
529         newtCheckboxTreeAddArray(tree, str, priv, 0, indexes);
530 }
531
532 static char *callchain_list__sym_name(struct callchain_list *self,
533                                       char *bf, size_t bfsize)
534 {
535         if (self->ms.sym)
536                 return self->ms.sym->name;
537
538         snprintf(bf, bfsize, "%#Lx", self->ip);
539         return bf;
540 }
541
542 static void __callchain__append_graph_browser(struct callchain_node *self,
543                                               newtComponent tree, u64 total,
544                                               int *indexes, int depth)
545 {
546         struct rb_node *node;
547         u64 new_total, remaining;
548         int idx = 0;
549
550         if (callchain_param.mode == CHAIN_GRAPH_REL)
551                 new_total = self->children_hit;
552         else
553                 new_total = total;
554
555         remaining = new_total;
556         node = rb_first(&self->rb_root);
557         while (node) {
558                 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
559                 struct rb_node *next = rb_next(node);
560                 u64 cumul = cumul_hits(child);
561                 struct callchain_list *chain;
562                 int first = true, printed = 0;
563                 int chain_idx = -1;
564                 remaining -= cumul;
565
566                 indexes[depth] = NEWT_ARG_APPEND;
567                 indexes[depth + 1] = NEWT_ARG_LAST;
568
569                 list_for_each_entry(chain, &child->val, list) {
570                         char ipstr[BITS_PER_LONG / 4 + 1],
571                              *alloc_str = NULL;
572                         const char *str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
573
574                         if (first) {
575                                 double percent = cumul * 100.0 / new_total;
576
577                                 first = false;
578                                 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
579                                         str = "Not enough memory!";
580                                 else
581                                         str = alloc_str;
582                         } else {
583                                 indexes[depth] = idx;
584                                 indexes[depth + 1] = NEWT_ARG_APPEND;
585                                 indexes[depth + 2] = NEWT_ARG_LAST;
586                                 ++chain_idx;
587                         }
588                         newt_checkbox_tree__add(tree, str, &chain->ms, indexes);
589                         free(alloc_str);
590                         ++printed;
591                 }
592
593                 indexes[depth] = idx;
594                 if (chain_idx != -1)
595                         indexes[depth + 1] = chain_idx;
596                 if (printed != 0)
597                         ++idx;
598                 __callchain__append_graph_browser(child, tree, new_total, indexes,
599                                                   depth + (chain_idx != -1 ? 2 : 1));
600                 node = next;
601         }
602 }
603
604 static void callchain__append_graph_browser(struct callchain_node *self,
605                                             newtComponent tree, u64 total,
606                                             int *indexes, int parent_idx)
607 {
608         struct callchain_list *chain;
609         int i = 0;
610
611         indexes[1] = NEWT_ARG_APPEND;
612         indexes[2] = NEWT_ARG_LAST;
613
614         list_for_each_entry(chain, &self->val, list) {
615                 char ipstr[BITS_PER_LONG / 4 + 1], *str;
616
617                 if (chain->ip >= PERF_CONTEXT_MAX)
618                         continue;
619
620                 if (!i++ && sort__first_dimension == SORT_SYM)
621                         continue;
622
623                 str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
624                 newt_checkbox_tree__add(tree, str, &chain->ms, indexes);
625         }
626
627         indexes[1] = parent_idx;
628         indexes[2] = NEWT_ARG_APPEND;
629         indexes[3] = NEWT_ARG_LAST;
630         __callchain__append_graph_browser(self, tree, total, indexes, 2);
631 }
632
633 static void hist_entry__append_callchain_browser(struct hist_entry *self,
634                                                  newtComponent tree, u64 total, int parent_idx)
635 {
636         struct rb_node *rb_node;
637         int indexes[1024] = { [0] = parent_idx, };
638         int idx = 0;
639         struct callchain_node *chain;
640
641         rb_node = rb_first(&self->sorted_chain);
642         while (rb_node) {
643                 chain = rb_entry(rb_node, struct callchain_node, rb_node);
644                 switch (callchain_param.mode) {
645                 case CHAIN_FLAT:
646                         break;
647                 case CHAIN_GRAPH_ABS: /* falldown */
648                 case CHAIN_GRAPH_REL:
649                         callchain__append_graph_browser(chain, tree, total, indexes, idx++);
650                         break;
651                 case CHAIN_NONE:
652                 default:
653                         break;
654                 }
655                 rb_node = rb_next(rb_node);
656         }
657 }
658
659 static size_t hist_entry__append_browser(struct hist_entry *self,
660                                          newtComponent tree, u64 total)
661 {
662         char s[256];
663         size_t ret;
664
665         if (symbol_conf.exclude_other && !self->parent)
666                 return 0;
667
668         ret = hist_entry__snprintf(self, s, sizeof(s), NULL,
669                                    false, 0, false, total);
670         if (symbol_conf.use_callchain) {
671                 int indexes[2];
672
673                 indexes[0] = NEWT_ARG_APPEND;
674                 indexes[1] = NEWT_ARG_LAST;
675                 newt_checkbox_tree__add(tree, s, &self->ms, indexes);
676         } else
677                 newtListboxAppendEntry(tree, s, &self->ms);
678
679         return ret;
680 }
681
682 int hist_entry__tui_annotate(struct hist_entry *self)
683 {
684         struct ui_browser browser;
685         struct newtExitStruct es;
686         struct objdump_line *pos, *n;
687         LIST_HEAD(head);
688         int ret;
689
690         if (self->ms.sym == NULL)
691                 return -1;
692
693         if (self->ms.map->dso->annotate_warned)
694                 return -1;
695
696         if (hist_entry__annotate(self, &head) < 0) {
697                 ui__error_window(browser__last_msg);
698                 return -1;
699         }
700
701         ui_helpline__push("Press <- or ESC to exit");
702
703         memset(&browser, 0, sizeof(browser));
704         browser.entries = &head;
705         browser.priv = self;
706         list_for_each_entry(pos, &head, node) {
707                 size_t line_len = strlen(pos->line);
708                 if (browser.width < line_len)
709                         browser.width = line_len;
710                 ++browser.nr_entries;
711         }
712
713         browser.width += 18; /* Percentage */
714         ret = ui_browser__run(&browser, self->ms.sym->name, &es);
715         newtFormDestroy(browser.form);
716         newtPopWindow();
717         list_for_each_entry_safe(pos, n, &head, node) {
718                 list_del(&pos->node);
719                 objdump_line__free(pos);
720         }
721         ui_helpline__pop();
722         return ret;
723 }
724
725 static const void *newt__symbol_tree_get_current(newtComponent self)
726 {
727         if (symbol_conf.use_callchain)
728                 return newtCheckboxTreeGetCurrent(self);
729         return newtListboxGetCurrent(self);
730 }
731
732 static void hist_browser__selection(newtComponent self, void *data)
733 {
734         const struct map_symbol **symbol_ptr = data;
735         *symbol_ptr = newt__symbol_tree_get_current(self);
736 }
737
738 struct hist_browser {
739         newtComponent           form, tree;
740         const struct map_symbol *selection;
741 };
742
743 static struct hist_browser *hist_browser__new(void)
744 {
745         struct hist_browser *self = malloc(sizeof(*self));
746
747         if (self != NULL)
748                 self->form = NULL;
749
750         return self;
751 }
752
753 static void hist_browser__delete(struct hist_browser *self)
754 {
755         newtFormDestroy(self->form);
756         newtPopWindow();
757         free(self);
758 }
759
760 static int hist_browser__populate(struct hist_browser *self, struct hists *hists,
761                                   const char *title)
762 {
763         int max_len = 0, idx, cols, rows;
764         struct ui_progress *progress;
765         struct rb_node *nd;
766         u64 curr_hist = 0;
767         char seq[] = ".", unit;
768         char str[256];
769         unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
770
771         if (self->form) {
772                 newtFormDestroy(self->form);
773                 newtPopWindow();
774         }
775
776         nr_events = convert_unit(nr_events, &unit);
777         snprintf(str, sizeof(str), "Events: %lu%c                            ",
778                  nr_events, unit);
779         newtDrawRootText(0, 0, str);
780
781         newtGetScreenSize(NULL, &rows);
782
783         if (symbol_conf.use_callchain)
784                 self->tree = newtCheckboxTreeMulti(0, 0, rows - 5, seq,
785                                                    NEWT_FLAG_SCROLL);
786         else
787                 self->tree = newtListbox(0, 0, rows - 5,
788                                         (NEWT_FLAG_SCROLL |
789                                          NEWT_FLAG_RETURNEXIT));
790
791         newtComponentAddCallback(self->tree, hist_browser__selection,
792                                  &self->selection);
793
794         progress = ui_progress__new("Adding entries to the browser...",
795                                     hists->nr_entries);
796         if (progress == NULL)
797                 return -1;
798
799         idx = 0;
800         for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
801                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
802                 int len;
803
804                 if (h->filtered)
805                         continue;
806
807                 len = hist_entry__append_browser(h, self->tree, hists->stats.total_period);
808                 if (len > max_len)
809                         max_len = len;
810                 if (symbol_conf.use_callchain)
811                         hist_entry__append_callchain_browser(h, self->tree,
812                                                              hists->stats.total_period, idx++);
813                 ++curr_hist;
814                 if (curr_hist % 5)
815                         ui_progress__update(progress, curr_hist);
816         }
817
818         ui_progress__delete(progress);
819
820         newtGetScreenSize(&cols, &rows);
821
822         if (max_len > cols)
823                 max_len = cols - 3;
824
825         if (!symbol_conf.use_callchain)
826                 newtListboxSetWidth(self->tree, max_len);
827
828         newtCenteredWindow(max_len + (symbol_conf.use_callchain ? 5 : 0),
829                            rows - 5, title);
830         self->form = newt_form__new();
831         if (self->form == NULL)
832                 return -1;
833
834         newtFormAddHotKey(self->form, 'A');
835         newtFormAddHotKey(self->form, 'a');
836         newtFormAddHotKey(self->form, 'D');
837         newtFormAddHotKey(self->form, 'd');
838         newtFormAddHotKey(self->form, 'T');
839         newtFormAddHotKey(self->form, 't');
840         newtFormAddHotKey(self->form, '?');
841         newtFormAddHotKey(self->form, 'H');
842         newtFormAddHotKey(self->form, 'h');
843         newtFormAddHotKey(self->form, NEWT_KEY_F1);
844         newtFormAddHotKey(self->form, NEWT_KEY_RIGHT);
845         newtFormAddHotKey(self->form, NEWT_KEY_TAB);
846         newtFormAddHotKey(self->form, NEWT_KEY_UNTAB);
847         newtFormAddComponents(self->form, self->tree, NULL);
848         self->selection = newt__symbol_tree_get_current(self->tree);
849
850         return 0;
851 }
852
853 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self)
854 {
855         int *indexes;
856
857         if (!symbol_conf.use_callchain)
858                 goto out;
859
860         indexes = newtCheckboxTreeFindItem(self->tree, (void *)self->selection);
861         if (indexes) {
862                 bool is_hist_entry = indexes[1] == NEWT_ARG_LAST;
863                 free(indexes);
864                 if (is_hist_entry)
865                         goto out;
866         }
867         return NULL;
868 out:
869         return container_of(self->selection, struct hist_entry, ms);
870 }
871
872 static struct thread *hist_browser__selected_thread(struct hist_browser *self)
873 {
874         struct hist_entry *he = hist_browser__selected_entry(self);
875         return he ? he->thread : NULL;
876 }
877
878 static int hist_browser__title(char *bf, size_t size, const char *ev_name,
879                                const struct dso *dso, const struct thread *thread)
880 {
881         int printed = 0;
882
883         if (thread)
884                 printed += snprintf(bf + printed, size - printed,
885                                     "Thread: %s(%d)",
886                                     (thread->comm_set ?  thread->comm : ""),
887                                     thread->pid);
888         if (dso)
889                 printed += snprintf(bf + printed, size - printed,
890                                     "%sDSO: %s", thread ? " " : "",
891                                     dso->short_name);
892         return printed ?: snprintf(bf, size, "Event: %s", ev_name);
893 }
894
895 int hists__browse(struct hists *self, const char *helpline, const char *ev_name)
896 {
897         struct hist_browser *browser = hist_browser__new();
898         struct pstack *fstack;
899         const struct thread *thread_filter = NULL;
900         const struct dso *dso_filter = NULL;
901         struct newtExitStruct es;
902         char msg[160];
903         int key = -1;
904
905         if (browser == NULL)
906                 return -1;
907
908         fstack = pstack__new(2);
909         if (fstack == NULL)
910                 goto out;
911
912         ui_helpline__push(helpline);
913
914         hist_browser__title(msg, sizeof(msg), ev_name,
915                             dso_filter, thread_filter);
916         if (hist_browser__populate(browser, self, msg) < 0)
917                 goto out_free_stack;
918
919         while (1) {
920                 const struct thread *thread;
921                 const struct dso *dso;
922                 char *options[16];
923                 int nr_options = 0, choice = 0, i,
924                     annotate = -2, zoom_dso = -2, zoom_thread = -2;
925
926                 newtFormRun(browser->form, &es);
927
928                 thread = hist_browser__selected_thread(browser);
929                 dso = browser->selection->map ? browser->selection->map->dso : NULL;
930
931                 if (es.reason == NEWT_EXIT_HOTKEY) {
932                         key = es.u.key;
933
934                         switch (key) {
935                         case NEWT_KEY_F1:
936                                 goto do_help;
937                         case NEWT_KEY_TAB:
938                         case NEWT_KEY_UNTAB:
939                                 /*
940                                  * Exit the browser, let hists__browser_tree
941                                  * go to the next or previous
942                                  */
943                                 goto out_free_stack;
944                         default:;
945                         }
946
947                         key = toupper(key);
948                         switch (key) {
949                         case 'A':
950                                 if (browser->selection->map == NULL &&
951                                     browser->selection->map->dso->annotate_warned)
952                                         continue;
953                                 goto do_annotate;
954                         case 'D':
955                                 goto zoom_dso;
956                         case 'T':
957                                 goto zoom_thread;
958                         case 'H':
959                         case '?':
960 do_help:
961                                 ui__help_window("->        Zoom into DSO/Threads & Annotate current symbol\n"
962                                                 "<-        Zoom out\n"
963                                                 "a         Annotate current symbol\n"
964                                                 "h/?/F1    Show this window\n"
965                                                 "d         Zoom into current DSO\n"
966                                                 "t         Zoom into current Thread\n"
967                                                 "q/CTRL+C  Exit browser");
968                                 continue;
969                         default:;
970                         }
971                         if (is_exit_key(key)) {
972                                 if (key == NEWT_KEY_ESCAPE) {
973                                         if (dialog_yesno("Do you really want to exit?"))
974                                                 break;
975                                         else
976                                                 continue;
977                                 } else
978                                         break;
979                         }
980
981                         if (es.u.key == NEWT_KEY_LEFT) {
982                                 const void *top;
983
984                                 if (pstack__empty(fstack))
985                                         continue;
986                                 top = pstack__pop(fstack);
987                                 if (top == &dso_filter)
988                                         goto zoom_out_dso;
989                                 if (top == &thread_filter)
990                                         goto zoom_out_thread;
991                                 continue;
992                         }
993                 }
994
995                 if (browser->selection->sym != NULL &&
996                     !browser->selection->map->dso->annotate_warned &&
997                     asprintf(&options[nr_options], "Annotate %s",
998                              browser->selection->sym->name) > 0)
999                         annotate = nr_options++;
1000
1001                 if (thread != NULL &&
1002                     asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
1003                              (thread_filter ? "out of" : "into"),
1004                              (thread->comm_set ? thread->comm : ""),
1005                              thread->pid) > 0)
1006                         zoom_thread = nr_options++;
1007
1008                 if (dso != NULL &&
1009                     asprintf(&options[nr_options], "Zoom %s %s DSO",
1010                              (dso_filter ? "out of" : "into"),
1011                              (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
1012                         zoom_dso = nr_options++;
1013
1014                 options[nr_options++] = (char *)"Exit";
1015
1016                 choice = popup_menu(nr_options, options);
1017
1018                 for (i = 0; i < nr_options - 1; ++i)
1019                         free(options[i]);
1020
1021                 if (choice == nr_options - 1)
1022                         break;
1023
1024                 if (choice == -1)
1025                         continue;
1026
1027                 if (choice == annotate) {
1028                         struct hist_entry *he;
1029 do_annotate:
1030                         if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) {
1031                                 browser->selection->map->dso->annotate_warned = 1;
1032                                 ui_helpline__puts("No vmlinux file found, can't "
1033                                                  "annotate with just a "
1034                                                  "kallsyms file");
1035                                 continue;
1036                         }
1037
1038                         he = hist_browser__selected_entry(browser);
1039                         if (he == NULL)
1040                                 continue;
1041
1042                         hist_entry__tui_annotate(he);
1043                 } else if (choice == zoom_dso) {
1044 zoom_dso:
1045                         if (dso_filter) {
1046                                 pstack__remove(fstack, &dso_filter);
1047 zoom_out_dso:
1048                                 ui_helpline__pop();
1049                                 dso_filter = NULL;
1050                         } else {
1051                                 if (dso == NULL)
1052                                         continue;
1053                                 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1054                                                    dso->kernel ? "the Kernel" : dso->short_name);
1055                                 dso_filter = dso;
1056                                 pstack__push(fstack, &dso_filter);
1057                         }
1058                         hists__filter_by_dso(self, dso_filter);
1059                         hist_browser__title(msg, sizeof(msg), ev_name,
1060                                             dso_filter, thread_filter);
1061                         if (hist_browser__populate(browser, self, msg) < 0)
1062                                 goto out;
1063                 } else if (choice == zoom_thread) {
1064 zoom_thread:
1065                         if (thread_filter) {
1066                                 pstack__remove(fstack, &thread_filter);
1067 zoom_out_thread:
1068                                 ui_helpline__pop();
1069                                 thread_filter = NULL;
1070                         } else {
1071                                 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1072                                                    thread->comm_set ? thread->comm : "",
1073                                                    thread->pid);
1074                                 thread_filter = thread;
1075                                 pstack__push(fstack, &thread_filter);
1076                         }
1077                         hists__filter_by_thread(self, thread_filter);
1078                         hist_browser__title(msg, sizeof(msg), ev_name,
1079                                             dso_filter, thread_filter);
1080                         if (hist_browser__populate(browser, self, msg) < 0)
1081                                 goto out;
1082                 }
1083         }
1084 out_free_stack:
1085         pstack__delete(fstack);
1086 out:
1087         hist_browser__delete(browser);
1088         return key;
1089 }
1090
1091 int hists__tui_browse_tree(struct rb_root *self, const char *help)
1092 {
1093         struct rb_node *first = rb_first(self), *nd = first, *next;
1094         int key = 0;
1095
1096         while (nd) {
1097                 struct hists *hists = rb_entry(nd, struct hists, rb_node);
1098                 const char *ev_name = __event_name(hists->type, hists->config);
1099
1100                 key = hists__browse(hists, help, ev_name);
1101
1102                 if (is_exit_key(key))
1103                         break;
1104
1105                 switch (key) {
1106                 case NEWT_KEY_TAB:
1107                         next = rb_next(nd);
1108                         if (next)
1109                                 nd = next;
1110                         break;
1111                 case NEWT_KEY_UNTAB:
1112                         if (nd == first)
1113                                 continue;
1114                         nd = rb_prev(nd);
1115                 default:
1116                         break;
1117                 }
1118         }
1119
1120         return key;
1121 }
1122
1123 static struct newtPercentTreeColors {
1124         const char *topColorFg, *topColorBg;
1125         const char *mediumColorFg, *mediumColorBg;
1126         const char *normalColorFg, *normalColorBg;
1127         const char *selColorFg, *selColorBg;
1128         const char *codeColorFg, *codeColorBg;
1129 } defaultPercentTreeColors = {
1130         "red",       "lightgray",
1131         "green",     "lightgray",
1132         "black",     "lightgray",
1133         "lightgray", "magenta",
1134         "blue",      "lightgray",
1135 };
1136
1137 void setup_browser(void)
1138 {
1139         struct newtPercentTreeColors *c = &defaultPercentTreeColors;
1140
1141         if (!isatty(1) || !use_browser || dump_trace) {
1142                 setup_pager();
1143                 return;
1144         }
1145
1146         use_browser = 1;
1147         newtInit();
1148         newtCls();
1149         ui_helpline__puts(" ");
1150         sltt_set_color(HE_COLORSET_TOP, NULL, c->topColorFg, c->topColorBg);
1151         sltt_set_color(HE_COLORSET_MEDIUM, NULL, c->mediumColorFg, c->mediumColorBg);
1152         sltt_set_color(HE_COLORSET_NORMAL, NULL, c->normalColorFg, c->normalColorBg);
1153         sltt_set_color(HE_COLORSET_SELECTED, NULL, c->selColorFg, c->selColorBg);
1154         sltt_set_color(HE_COLORSET_CODE, NULL, c->codeColorFg, c->codeColorBg);
1155 }
1156
1157 void exit_browser(bool wait_for_ok)
1158 {
1159         if (use_browser > 0) {
1160                 if (wait_for_ok) {
1161                         char title[] = "Fatal Error", ok[] = "Ok";
1162                         newtWinMessage(title, ok, browser__last_msg);
1163                 }
1164                 newtFinished();
1165         }
1166 }