perf tui: Allow disabling the TUI on a per command basis in ~/.perfconfig
[linux-2.6/cjktty.git] / tools / perf / util / newt.c
blob9d203c972f3b81e1ecf4810ad4cf7e550cae03b5
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>
18 #include "cache.h"
19 #include "hist.h"
20 #include "pstack.h"
21 #include "session.h"
22 #include "sort.h"
23 #include "symbol.h"
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
36 struct ui_progress {
37 newtComponent form, scale;
40 struct ui_progress *ui_progress__new(const char *title, u64 total)
42 struct ui_progress *self = malloc(sizeof(*self));
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();
59 return self;
61 out_free_form:
62 newtFormDestroy(self->form);
63 out_free_self:
64 free(self);
65 return NULL;
68 void ui_progress__update(struct ui_progress *self, u64 curr)
70 newtScaleSet(self->scale, curr);
71 newtRefresh();
74 void ui_progress__delete(struct ui_progress *self)
76 newtFormDestroy(self->form);
77 newtPopWindow();
78 free(self);
81 static void ui_helpline__pop(void)
83 newtPopHelpLine();
86 static void ui_helpline__push(const char *msg)
88 newtPushHelpLine(msg);
91 static void ui_helpline__vpush(const char *fmt, va_list ap)
93 char *s;
95 if (vasprintf(&s, fmt, ap) < 0)
96 vfprintf(stderr, fmt, ap);
97 else {
98 ui_helpline__push(s);
99 free(s);
103 static void ui_helpline__fpush(const char *fmt, ...)
105 va_list ap;
107 va_start(ap, fmt);
108 ui_helpline__vpush(fmt, ap);
109 va_end(ap);
112 static void ui_helpline__puts(const char *msg)
114 ui_helpline__pop();
115 ui_helpline__push(msg);
118 static char browser__last_msg[1024];
120 int browser__show_help(const char *format, va_list ap)
122 int ret;
123 static int backlog;
125 ret = vsnprintf(browser__last_msg + backlog,
126 sizeof(browser__last_msg) - backlog, format, ap);
127 backlog += ret;
129 if (browser__last_msg[backlog - 1] == '\n') {
130 ui_helpline__puts(browser__last_msg);
131 newtRefresh();
132 backlog = 0;
135 return ret;
138 static void newt_form__set_exit_keys(newtComponent self)
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'));
147 static newtComponent newt_form__new(void)
149 newtComponent self = newtForm(NULL, NULL, 0);
150 if (self)
151 newt_form__set_exit_keys(self);
152 return self;
155 static int popup_menu(int argc, char * const argv[])
157 struct newtExitStruct es;
158 int i, rc = -1, max_len = 5;
159 newtComponent listbox, form = newt_form__new();
161 if (form == NULL)
162 return -1;
164 listbox = newtListbox(0, 0, argc, NEWT_FLAG_RETURNEXIT);
165 if (listbox == NULL)
166 goto out_destroy_form;
168 newtFormAddComponent(form, listbox);
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;
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;
189 static int ui__help_window(const char *text)
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;
197 if (form == NULL)
198 return -1;
200 t = text;
201 while (1) {
202 const char *sep = strchr(t, '\n');
203 int len;
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;
216 tb = newtTextbox(0, 0, max_len, nr_lines, 0);
217 if (tb == NULL)
218 goto out_destroy_form;
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;
231 static bool dialog_yesno(const char *msg)
233 /* newtWinChoice should really be accepting const char pointers... */
234 char yes[] = "Yes", no[] = "No";
235 return newtWinChoice(NULL, yes, no, (char *)msg) == 1;
238 #define HE_COLORSET_TOP 50
239 #define HE_COLORSET_MEDIUM 51
240 #define HE_COLORSET_NORMAL 52
241 #define HE_COLORSET_SELECTED 53
242 #define HE_COLORSET_CODE 54
244 static int ui_browser__percent_color(double percent, bool current)
246 if (current)
247 return HE_COLORSET_SELECTED;
248 if (percent >= MIN_RED)
249 return HE_COLORSET_TOP;
250 if (percent >= MIN_GREEN)
251 return HE_COLORSET_MEDIUM;
252 return HE_COLORSET_NORMAL;
255 struct ui_browser {
256 newtComponent form, sb;
257 u64 index, first_visible_entry_idx;
258 void *first_visible_entry, *entries;
259 u16 top, left, width, height;
260 void *priv;
261 u32 nr_entries;
264 static void ui_browser__refresh_dimensions(struct ui_browser *self)
266 int cols, rows;
267 newtGetScreenSize(&cols, &rows);
269 if (self->width > cols - 4)
270 self->width = cols - 4;
271 self->height = rows - 5;
272 if (self->height > self->nr_entries)
273 self->height = self->nr_entries;
274 self->top = (rows - self->height) / 2;
275 self->left = (cols - self->width) / 2;
278 static void ui_browser__reset_index(struct ui_browser *self)
280 self->index = self->first_visible_entry_idx = 0;
281 self->first_visible_entry = NULL;
284 static int objdump_line__show(struct objdump_line *self, struct list_head *head,
285 int width, struct hist_entry *he, int len,
286 bool current_entry)
288 if (self->offset != -1) {
289 struct symbol *sym = he->ms.sym;
290 unsigned int hits = 0;
291 double percent = 0.0;
292 int color;
293 struct sym_priv *priv = symbol__priv(sym);
294 struct sym_ext *sym_ext = priv->ext;
295 struct sym_hist *h = priv->hist;
296 s64 offset = self->offset;
297 struct objdump_line *next = objdump__get_next_ip_line(head, self);
299 while (offset < (s64)len &&
300 (next == NULL || offset < next->offset)) {
301 if (sym_ext) {
302 percent += sym_ext[offset].percent;
303 } else
304 hits += h->ip[offset];
306 ++offset;
309 if (sym_ext == NULL && h->sum)
310 percent = 100.0 * hits / h->sum;
312 color = ui_browser__percent_color(percent, current_entry);
313 SLsmg_set_color(color);
314 slsmg_printf(" %7.2f ", percent);
315 if (!current_entry)
316 SLsmg_set_color(HE_COLORSET_CODE);
317 } else {
318 int color = ui_browser__percent_color(0, current_entry);
319 SLsmg_set_color(color);
320 slsmg_write_nstring(" ", 9);
323 SLsmg_write_char(':');
324 slsmg_write_nstring(" ", 8);
325 if (!*self->line)
326 slsmg_write_nstring(" ", width - 18);
327 else
328 slsmg_write_nstring(self->line, width - 18);
330 return 0;
333 static int ui_browser__refresh_entries(struct ui_browser *self)
335 struct objdump_line *pos;
336 struct list_head *head = self->entries;
337 struct hist_entry *he = self->priv;
338 int row = 0;
339 int len = he->ms.sym->end - he->ms.sym->start;
341 if (self->first_visible_entry == NULL || self->first_visible_entry == self->entries)
342 self->first_visible_entry = head->next;
344 pos = list_entry(self->first_visible_entry, struct objdump_line, node);
346 list_for_each_entry_from(pos, head, node) {
347 bool current_entry = (self->first_visible_entry_idx + row) == self->index;
348 SLsmg_gotorc(self->top + row, self->left);
349 objdump_line__show(pos, head, self->width,
350 he, len, current_entry);
351 if (++row == self->height)
352 break;
355 SLsmg_set_color(HE_COLORSET_NORMAL);
356 SLsmg_fill_region(self->top + row, self->left,
357 self->height - row, self->width, ' ');
359 return 0;
362 static int ui_browser__run(struct ui_browser *self, const char *title,
363 struct newtExitStruct *es)
365 if (self->form) {
366 newtFormDestroy(self->form);
367 newtPopWindow();
370 ui_browser__refresh_dimensions(self);
371 newtCenteredWindow(self->width + 2, self->height, title);
372 self->form = newt_form__new();
373 if (self->form == NULL)
374 return -1;
376 self->sb = newtVerticalScrollbar(self->width + 1, 0, self->height,
377 HE_COLORSET_NORMAL,
378 HE_COLORSET_SELECTED);
379 if (self->sb == NULL)
380 return -1;
382 newtFormAddHotKey(self->form, NEWT_KEY_UP);
383 newtFormAddHotKey(self->form, NEWT_KEY_DOWN);
384 newtFormAddHotKey(self->form, NEWT_KEY_PGUP);
385 newtFormAddHotKey(self->form, NEWT_KEY_PGDN);
386 newtFormAddHotKey(self->form, NEWT_KEY_HOME);
387 newtFormAddHotKey(self->form, NEWT_KEY_END);
389 if (ui_browser__refresh_entries(self) < 0)
390 return -1;
391 newtFormAddComponent(self->form, self->sb);
393 while (1) {
394 unsigned int offset;
396 newtFormRun(self->form, es);
398 if (es->reason != NEWT_EXIT_HOTKEY)
399 break;
400 switch (es->u.key) {
401 case NEWT_KEY_DOWN:
402 if (self->index == self->nr_entries - 1)
403 break;
404 ++self->index;
405 if (self->index == self->first_visible_entry_idx + self->height) {
406 struct list_head *pos = self->first_visible_entry;
407 ++self->first_visible_entry_idx;
408 self->first_visible_entry = pos->next;
410 break;
411 case NEWT_KEY_UP:
412 if (self->index == 0)
413 break;
414 --self->index;
415 if (self->index < self->first_visible_entry_idx) {
416 struct list_head *pos = self->first_visible_entry;
417 --self->first_visible_entry_idx;
418 self->first_visible_entry = pos->prev;
420 break;
421 case NEWT_KEY_PGDN:
422 if (self->first_visible_entry_idx + self->height > self->nr_entries - 1)
423 break;
425 offset = self->height;
426 if (self->index + offset > self->nr_entries - 1)
427 offset = self->nr_entries - 1 - self->index;
428 self->index += offset;
429 self->first_visible_entry_idx += offset;
431 while (offset--) {
432 struct list_head *pos = self->first_visible_entry;
433 self->first_visible_entry = pos->next;
436 break;
437 case NEWT_KEY_PGUP:
438 if (self->first_visible_entry_idx == 0)
439 break;
441 if (self->first_visible_entry_idx < self->height)
442 offset = self->first_visible_entry_idx;
443 else
444 offset = self->height;
446 self->index -= offset;
447 self->first_visible_entry_idx -= offset;
449 while (offset--) {
450 struct list_head *pos = self->first_visible_entry;
451 self->first_visible_entry = pos->prev;
453 break;
454 case NEWT_KEY_HOME:
455 ui_browser__reset_index(self);
456 break;
457 case NEWT_KEY_END: {
458 struct list_head *head = self->entries;
459 offset = self->height - 1;
461 if (offset > self->nr_entries)
462 offset = self->nr_entries;
464 self->index = self->first_visible_entry_idx = self->nr_entries - 1 - offset;
465 self->first_visible_entry = head->prev;
466 while (offset-- != 0) {
467 struct list_head *pos = self->first_visible_entry;
468 self->first_visible_entry = pos->prev;
471 break;
472 case NEWT_KEY_ESCAPE:
473 case NEWT_KEY_LEFT:
474 case CTRL('c'):
475 case 'Q':
476 case 'q':
477 return 0;
478 default:
479 continue;
481 if (ui_browser__refresh_entries(self) < 0)
482 return -1;
484 return 0;
488 * When debugging newt problems it was useful to be able to "unroll"
489 * the calls to newtCheckBoxTreeAdd{Array,Item}, so that we can generate
490 * a source file with the sequence of calls to these methods, to then
491 * tweak the arrays to get the intended results, so I'm keeping this code
492 * here, may be useful again in the future.
494 #undef NEWT_DEBUG
496 static void newt_checkbox_tree__add(newtComponent tree, const char *str,
497 void *priv, int *indexes)
499 #ifdef NEWT_DEBUG
500 /* Print the newtCheckboxTreeAddArray to tinker with its index arrays */
501 int i = 0, len = 40 - strlen(str);
503 fprintf(stderr,
504 "\tnewtCheckboxTreeAddItem(tree, %*.*s\"%s\", (void *)%p, 0, ",
505 len, len, " ", str, priv);
506 while (indexes[i] != NEWT_ARG_LAST) {
507 if (indexes[i] != NEWT_ARG_APPEND)
508 fprintf(stderr, " %d,", indexes[i]);
509 else
510 fprintf(stderr, " %s,", "NEWT_ARG_APPEND");
511 ++i;
513 fprintf(stderr, " %s", " NEWT_ARG_LAST);\n");
514 fflush(stderr);
515 #endif
516 newtCheckboxTreeAddArray(tree, str, priv, 0, indexes);
519 static char *callchain_list__sym_name(struct callchain_list *self,
520 char *bf, size_t bfsize)
522 if (self->ms.sym)
523 return self->ms.sym->name;
525 snprintf(bf, bfsize, "%#Lx", self->ip);
526 return bf;
529 static void __callchain__append_graph_browser(struct callchain_node *self,
530 newtComponent tree, u64 total,
531 int *indexes, int depth)
533 struct rb_node *node;
534 u64 new_total, remaining;
535 int idx = 0;
537 if (callchain_param.mode == CHAIN_GRAPH_REL)
538 new_total = self->children_hit;
539 else
540 new_total = total;
542 remaining = new_total;
543 node = rb_first(&self->rb_root);
544 while (node) {
545 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
546 struct rb_node *next = rb_next(node);
547 u64 cumul = cumul_hits(child);
548 struct callchain_list *chain;
549 int first = true, printed = 0;
550 int chain_idx = -1;
551 remaining -= cumul;
553 indexes[depth] = NEWT_ARG_APPEND;
554 indexes[depth + 1] = NEWT_ARG_LAST;
556 list_for_each_entry(chain, &child->val, list) {
557 char ipstr[BITS_PER_LONG / 4 + 1],
558 *alloc_str = NULL;
559 const char *str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
561 if (first) {
562 double percent = cumul * 100.0 / new_total;
564 first = false;
565 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
566 str = "Not enough memory!";
567 else
568 str = alloc_str;
569 } else {
570 indexes[depth] = idx;
571 indexes[depth + 1] = NEWT_ARG_APPEND;
572 indexes[depth + 2] = NEWT_ARG_LAST;
573 ++chain_idx;
575 newt_checkbox_tree__add(tree, str, &chain->ms, indexes);
576 free(alloc_str);
577 ++printed;
580 indexes[depth] = idx;
581 if (chain_idx != -1)
582 indexes[depth + 1] = chain_idx;
583 if (printed != 0)
584 ++idx;
585 __callchain__append_graph_browser(child, tree, new_total, indexes,
586 depth + (chain_idx != -1 ? 2 : 1));
587 node = next;
591 static void callchain__append_graph_browser(struct callchain_node *self,
592 newtComponent tree, u64 total,
593 int *indexes, int parent_idx)
595 struct callchain_list *chain;
596 int i = 0;
598 indexes[1] = NEWT_ARG_APPEND;
599 indexes[2] = NEWT_ARG_LAST;
601 list_for_each_entry(chain, &self->val, list) {
602 char ipstr[BITS_PER_LONG / 4 + 1], *str;
604 if (chain->ip >= PERF_CONTEXT_MAX)
605 continue;
607 if (!i++ && sort__first_dimension == SORT_SYM)
608 continue;
610 str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
611 newt_checkbox_tree__add(tree, str, &chain->ms, indexes);
614 indexes[1] = parent_idx;
615 indexes[2] = NEWT_ARG_APPEND;
616 indexes[3] = NEWT_ARG_LAST;
617 __callchain__append_graph_browser(self, tree, total, indexes, 2);
620 static void hist_entry__append_callchain_browser(struct hist_entry *self,
621 newtComponent tree, u64 total, int parent_idx)
623 struct rb_node *rb_node;
624 int indexes[1024] = { [0] = parent_idx, };
625 int idx = 0;
626 struct callchain_node *chain;
628 rb_node = rb_first(&self->sorted_chain);
629 while (rb_node) {
630 chain = rb_entry(rb_node, struct callchain_node, rb_node);
631 switch (callchain_param.mode) {
632 case CHAIN_FLAT:
633 break;
634 case CHAIN_GRAPH_ABS: /* falldown */
635 case CHAIN_GRAPH_REL:
636 callchain__append_graph_browser(chain, tree, total, indexes, idx++);
637 break;
638 case CHAIN_NONE:
639 default:
640 break;
642 rb_node = rb_next(rb_node);
646 static size_t hist_entry__append_browser(struct hist_entry *self,
647 newtComponent tree, u64 total)
649 char s[256];
650 size_t ret;
652 if (symbol_conf.exclude_other && !self->parent)
653 return 0;
655 ret = hist_entry__snprintf(self, s, sizeof(s), NULL,
656 false, 0, false, total);
657 if (symbol_conf.use_callchain) {
658 int indexes[2];
660 indexes[0] = NEWT_ARG_APPEND;
661 indexes[1] = NEWT_ARG_LAST;
662 newt_checkbox_tree__add(tree, s, &self->ms, indexes);
663 } else
664 newtListboxAppendEntry(tree, s, &self->ms);
666 return ret;
669 static void hist_entry__annotate_browser(struct hist_entry *self)
671 struct ui_browser browser;
672 struct newtExitStruct es;
673 struct objdump_line *pos, *n;
674 LIST_HEAD(head);
676 if (self->ms.sym == NULL)
677 return;
679 if (hist_entry__annotate(self, &head) < 0)
680 return;
682 ui_helpline__push("Press <- or ESC to exit");
684 memset(&browser, 0, sizeof(browser));
685 browser.entries = &head;
686 browser.priv = self;
687 list_for_each_entry(pos, &head, node) {
688 size_t line_len = strlen(pos->line);
689 if (browser.width < line_len)
690 browser.width = line_len;
691 ++browser.nr_entries;
694 browser.width += 18; /* Percentage */
695 ui_browser__run(&browser, self->ms.sym->name, &es);
696 newtFormDestroy(browser.form);
697 newtPopWindow();
698 list_for_each_entry_safe(pos, n, &head, node) {
699 list_del(&pos->node);
700 objdump_line__free(pos);
702 ui_helpline__pop();
705 static const void *newt__symbol_tree_get_current(newtComponent self)
707 if (symbol_conf.use_callchain)
708 return newtCheckboxTreeGetCurrent(self);
709 return newtListboxGetCurrent(self);
712 static void hist_browser__selection(newtComponent self, void *data)
714 const struct map_symbol **symbol_ptr = data;
715 *symbol_ptr = newt__symbol_tree_get_current(self);
718 struct hist_browser {
719 newtComponent form, tree;
720 const struct map_symbol *selection;
723 static struct hist_browser *hist_browser__new(void)
725 struct hist_browser *self = malloc(sizeof(*self));
727 if (self != NULL)
728 self->form = NULL;
730 return self;
733 static void hist_browser__delete(struct hist_browser *self)
735 newtFormDestroy(self->form);
736 newtPopWindow();
737 free(self);
740 static int hist_browser__populate(struct hist_browser *self, struct hists *hists,
741 const char *title)
743 int max_len = 0, idx, cols, rows;
744 struct ui_progress *progress;
745 struct rb_node *nd;
746 u64 curr_hist = 0;
747 char seq[] = ".", unit;
748 char str[256];
749 unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
751 if (self->form) {
752 newtFormDestroy(self->form);
753 newtPopWindow();
756 nr_events = convert_unit(nr_events, &unit);
757 snprintf(str, sizeof(str), "Events: %lu%c ",
758 nr_events, unit);
759 newtDrawRootText(0, 0, str);
761 newtGetScreenSize(NULL, &rows);
763 if (symbol_conf.use_callchain)
764 self->tree = newtCheckboxTreeMulti(0, 0, rows - 5, seq,
765 NEWT_FLAG_SCROLL);
766 else
767 self->tree = newtListbox(0, 0, rows - 5,
768 (NEWT_FLAG_SCROLL |
769 NEWT_FLAG_RETURNEXIT));
771 newtComponentAddCallback(self->tree, hist_browser__selection,
772 &self->selection);
774 progress = ui_progress__new("Adding entries to the browser...",
775 hists->nr_entries);
776 if (progress == NULL)
777 return -1;
779 idx = 0;
780 for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
781 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
782 int len;
784 if (h->filtered)
785 continue;
787 len = hist_entry__append_browser(h, self->tree, hists->stats.total_period);
788 if (len > max_len)
789 max_len = len;
790 if (symbol_conf.use_callchain)
791 hist_entry__append_callchain_browser(h, self->tree,
792 hists->stats.total_period, idx++);
793 ++curr_hist;
794 if (curr_hist % 5)
795 ui_progress__update(progress, curr_hist);
798 ui_progress__delete(progress);
800 newtGetScreenSize(&cols, &rows);
802 if (max_len > cols)
803 max_len = cols - 3;
805 if (!symbol_conf.use_callchain)
806 newtListboxSetWidth(self->tree, max_len);
808 newtCenteredWindow(max_len + (symbol_conf.use_callchain ? 5 : 0),
809 rows - 5, title);
810 self->form = newt_form__new();
811 if (self->form == NULL)
812 return -1;
814 newtFormAddHotKey(self->form, 'A');
815 newtFormAddHotKey(self->form, 'a');
816 newtFormAddHotKey(self->form, 'D');
817 newtFormAddHotKey(self->form, 'd');
818 newtFormAddHotKey(self->form, 'T');
819 newtFormAddHotKey(self->form, 't');
820 newtFormAddHotKey(self->form, '?');
821 newtFormAddHotKey(self->form, 'H');
822 newtFormAddHotKey(self->form, 'h');
823 newtFormAddHotKey(self->form, NEWT_KEY_F1);
824 newtFormAddHotKey(self->form, NEWT_KEY_RIGHT);
825 newtFormAddComponents(self->form, self->tree, NULL);
826 self->selection = newt__symbol_tree_get_current(self->tree);
828 return 0;
831 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self)
833 int *indexes;
835 if (!symbol_conf.use_callchain)
836 goto out;
838 indexes = newtCheckboxTreeFindItem(self->tree, (void *)self->selection);
839 if (indexes) {
840 bool is_hist_entry = indexes[1] == NEWT_ARG_LAST;
841 free(indexes);
842 if (is_hist_entry)
843 goto out;
845 return NULL;
846 out:
847 return container_of(self->selection, struct hist_entry, ms);
850 static struct thread *hist_browser__selected_thread(struct hist_browser *self)
852 struct hist_entry *he = hist_browser__selected_entry(self);
853 return he ? he->thread : NULL;
856 static int hist_browser__title(char *bf, size_t size, const char *input_name,
857 const struct dso *dso, const struct thread *thread)
859 int printed = 0;
861 if (thread)
862 printed += snprintf(bf + printed, size - printed,
863 "Thread: %s(%d)",
864 (thread->comm_set ? thread->comm : ""),
865 thread->pid);
866 if (dso)
867 printed += snprintf(bf + printed, size - printed,
868 "%sDSO: %s", thread ? " " : "",
869 dso->short_name);
870 return printed ?: snprintf(bf, size, "Report: %s", input_name);
873 int hists__browse(struct hists *self, const char *helpline, const char *input_name)
875 struct hist_browser *browser = hist_browser__new();
876 struct pstack *fstack = pstack__new(2);
877 const struct thread *thread_filter = NULL;
878 const struct dso *dso_filter = NULL;
879 struct newtExitStruct es;
880 char msg[160];
881 int err = -1;
883 if (browser == NULL)
884 return -1;
886 fstack = pstack__new(2);
887 if (fstack == NULL)
888 goto out;
890 ui_helpline__push(helpline);
892 hist_browser__title(msg, sizeof(msg), input_name,
893 dso_filter, thread_filter);
894 if (hist_browser__populate(browser, self, msg) < 0)
895 goto out_free_stack;
897 while (1) {
898 const struct thread *thread;
899 const struct dso *dso;
900 char *options[16];
901 int nr_options = 0, choice = 0, i,
902 annotate = -2, zoom_dso = -2, zoom_thread = -2;
904 newtFormRun(browser->form, &es);
906 thread = hist_browser__selected_thread(browser);
907 dso = browser->selection->map ? browser->selection->map->dso : NULL;
909 if (es.reason == NEWT_EXIT_HOTKEY) {
910 if (es.u.key == NEWT_KEY_F1)
911 goto do_help;
913 switch (toupper(es.u.key)) {
914 case 'A':
915 goto do_annotate;
916 case 'D':
917 goto zoom_dso;
918 case 'T':
919 goto zoom_thread;
920 case 'H':
921 case '?':
922 do_help:
923 ui__help_window("-> Zoom into DSO/Threads & Annotate current symbol\n"
924 "<- Zoom out\n"
925 "a Annotate current symbol\n"
926 "h/?/F1 Show this window\n"
927 "d Zoom into current DSO\n"
928 "t Zoom into current Thread\n"
929 "q/CTRL+C Exit browser");
930 continue;
931 default:;
933 if (toupper(es.u.key) == 'Q' ||
934 es.u.key == CTRL('c'))
935 break;
936 if (es.u.key == NEWT_KEY_ESCAPE) {
937 if (dialog_yesno("Do you really want to exit?"))
938 break;
939 else
940 continue;
943 if (es.u.key == NEWT_KEY_LEFT) {
944 const void *top;
946 if (pstack__empty(fstack))
947 continue;
948 top = pstack__pop(fstack);
949 if (top == &dso_filter)
950 goto zoom_out_dso;
951 if (top == &thread_filter)
952 goto zoom_out_thread;
953 continue;
957 if (browser->selection->sym != NULL &&
958 asprintf(&options[nr_options], "Annotate %s",
959 browser->selection->sym->name) > 0)
960 annotate = nr_options++;
962 if (thread != NULL &&
963 asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
964 (thread_filter ? "out of" : "into"),
965 (thread->comm_set ? thread->comm : ""),
966 thread->pid) > 0)
967 zoom_thread = nr_options++;
969 if (dso != NULL &&
970 asprintf(&options[nr_options], "Zoom %s %s DSO",
971 (dso_filter ? "out of" : "into"),
972 (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
973 zoom_dso = nr_options++;
975 options[nr_options++] = (char *)"Exit";
977 choice = popup_menu(nr_options, options);
979 for (i = 0; i < nr_options - 1; ++i)
980 free(options[i]);
982 if (choice == nr_options - 1)
983 break;
985 if (choice == -1)
986 continue;
988 if (choice == annotate) {
989 struct hist_entry *he;
990 do_annotate:
991 if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) {
992 ui_helpline__puts("No vmlinux file found, can't "
993 "annotate with just a "
994 "kallsyms file");
995 continue;
998 he = hist_browser__selected_entry(browser);
999 if (he == NULL)
1000 continue;
1002 hist_entry__annotate_browser(he);
1003 } else if (choice == zoom_dso) {
1004 zoom_dso:
1005 if (dso_filter) {
1006 pstack__remove(fstack, &dso_filter);
1007 zoom_out_dso:
1008 ui_helpline__pop();
1009 dso_filter = NULL;
1010 } else {
1011 if (dso == NULL)
1012 continue;
1013 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1014 dso->kernel ? "the Kernel" : dso->short_name);
1015 dso_filter = dso;
1016 pstack__push(fstack, &dso_filter);
1018 hists__filter_by_dso(self, dso_filter);
1019 hist_browser__title(msg, sizeof(msg), input_name,
1020 dso_filter, thread_filter);
1021 if (hist_browser__populate(browser, self, msg) < 0)
1022 goto out;
1023 } else if (choice == zoom_thread) {
1024 zoom_thread:
1025 if (thread_filter) {
1026 pstack__remove(fstack, &thread_filter);
1027 zoom_out_thread:
1028 ui_helpline__pop();
1029 thread_filter = NULL;
1030 } else {
1031 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1032 thread->comm_set ? thread->comm : "",
1033 thread->pid);
1034 thread_filter = thread;
1035 pstack__push(fstack, &thread_filter);
1037 hists__filter_by_thread(self, thread_filter);
1038 hist_browser__title(msg, sizeof(msg), input_name,
1039 dso_filter, thread_filter);
1040 if (hist_browser__populate(browser, self, msg) < 0)
1041 goto out;
1044 err = 0;
1045 out_free_stack:
1046 pstack__delete(fstack);
1047 out:
1048 hist_browser__delete(browser);
1049 return err;
1052 static struct newtPercentTreeColors {
1053 const char *topColorFg, *topColorBg;
1054 const char *mediumColorFg, *mediumColorBg;
1055 const char *normalColorFg, *normalColorBg;
1056 const char *selColorFg, *selColorBg;
1057 const char *codeColorFg, *codeColorBg;
1058 } defaultPercentTreeColors = {
1059 "red", "lightgray",
1060 "green", "lightgray",
1061 "black", "lightgray",
1062 "lightgray", "magenta",
1063 "blue", "lightgray",
1066 void setup_browser(void)
1068 struct newtPercentTreeColors *c = &defaultPercentTreeColors;
1070 if (!isatty(1) || !use_browser) {
1071 setup_pager();
1072 return;
1075 use_browser = 1;
1076 newtInit();
1077 newtCls();
1078 ui_helpline__puts(" ");
1079 sltt_set_color(HE_COLORSET_TOP, NULL, c->topColorFg, c->topColorBg);
1080 sltt_set_color(HE_COLORSET_MEDIUM, NULL, c->mediumColorFg, c->mediumColorBg);
1081 sltt_set_color(HE_COLORSET_NORMAL, NULL, c->normalColorFg, c->normalColorBg);
1082 sltt_set_color(HE_COLORSET_SELECTED, NULL, c->selColorFg, c->selColorBg);
1083 sltt_set_color(HE_COLORSET_CODE, NULL, c->codeColorFg, c->codeColorBg);
1086 void exit_browser(bool wait_for_ok)
1088 if (use_browser > 0) {
1089 if (wait_for_ok) {
1090 char title[] = "Fatal Error", ok[] = "Ok";
1091 newtWinMessage(title, ok, browser__last_msg);
1093 newtFinished();