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
10 #ifndef HAVE_LONG_LONG
11 #define HAVE_LONG_LONG __GLIBC_HAVE_LONG_LONG
18 #include <sys/ttydefaults.h>
26 #include "ui/browser.h"
28 #if SLANG_VERSION < 20104
29 #define slsmg_printf(msg, args...) SLsmg_printf((char *)msg, ##args)
30 #define slsmg_write_nstring(msg, len) SLsmg_write_nstring((char *)msg, len)
31 #define sltt_set_color(obj, name, fg, bg) SLtt_set_color(obj,(char *)name,\
32 (char *)fg, (char *)bg)
34 #define slsmg_printf SLsmg_printf
35 #define slsmg_write_nstring SLsmg_write_nstring
36 #define sltt_set_color SLtt_set_color
39 newtComponent newt_form__new(void);
42 newtComponent form, scale;
45 struct ui_progress *ui_progress__new(const char *title, u64 total)
47 struct ui_progress *self = malloc(sizeof(*self));
54 newtGetScreenSize(&cols, NULL);
56 newtCenteredWindow(cols, 1, title);
57 self->form = newtForm(NULL, NULL, 0);
58 if (self->form == NULL)
60 self->scale = newtScale(0, 0, cols, total);
61 if (self->scale == NULL)
63 newtFormAddComponent(self->form, self->scale);
70 newtFormDestroy(self->form);
76 void ui_progress__update(struct ui_progress *self, u64 curr)
79 * FIXME: We should have a per UI backend way of showing progress,
80 * stdio will just show a percentage as NN%, etc.
84 newtScaleSet(self->scale, curr);
88 void ui_progress__delete(struct ui_progress *self)
90 if (use_browser > 0) {
91 newtFormDestroy(self->form);
97 static void ui_helpline__pop(void)
102 static void ui_helpline__push(const char *msg)
104 newtPushHelpLine(msg);
107 static void ui_helpline__vpush(const char *fmt, va_list ap)
111 if (vasprintf(&s, fmt, ap) < 0)
112 vfprintf(stderr, fmt, ap);
114 ui_helpline__push(s);
119 static void ui_helpline__fpush(const char *fmt, ...)
124 ui_helpline__vpush(fmt, ap);
128 static void ui_helpline__puts(const char *msg)
131 ui_helpline__push(msg);
134 static int ui_entry__read(const char *title, char *bf, size_t size, int width)
136 struct newtExitStruct es;
137 newtComponent form, entry;
141 newtCenteredWindow(width, 1, title);
142 form = newtForm(NULL, NULL, 0);
146 entry = newtEntry(0, 0, "0x", width, &result, NEWT_FLAG_SCROLL);
150 newtFormAddComponent(form, entry);
151 newtFormAddHotKey(form, NEWT_KEY_ENTER);
152 newtFormAddHotKey(form, NEWT_KEY_ESCAPE);
153 newtFormAddHotKey(form, NEWT_KEY_LEFT);
154 newtFormAddHotKey(form, CTRL('c'));
155 newtFormRun(form, &es);
157 if (result != NULL) {
158 strncpy(bf, result, size);
163 newtFormDestroy(form);
167 static char browser__last_msg[1024];
169 int browser__show_help(const char *format, va_list ap)
174 ret = vsnprintf(browser__last_msg + backlog,
175 sizeof(browser__last_msg) - backlog, format, ap);
178 if (browser__last_msg[backlog - 1] == '\n') {
179 ui_helpline__puts(browser__last_msg);
187 static void newt_form__set_exit_keys(newtComponent self)
189 newtFormAddHotKey(self, NEWT_KEY_LEFT);
190 newtFormAddHotKey(self, NEWT_KEY_ESCAPE);
191 newtFormAddHotKey(self, 'Q');
192 newtFormAddHotKey(self, 'q');
193 newtFormAddHotKey(self, CTRL('c'));
196 newtComponent newt_form__new(void)
198 newtComponent self = newtForm(NULL, NULL, 0);
200 newt_form__set_exit_keys(self);
204 static int popup_menu(int argc, char * const argv[])
206 struct newtExitStruct es;
207 int i, rc = -1, max_len = 5;
208 newtComponent listbox, form = newt_form__new();
213 listbox = newtListbox(0, 0, argc, NEWT_FLAG_RETURNEXIT);
215 goto out_destroy_form;
217 newtFormAddComponent(form, listbox);
219 for (i = 0; i < argc; ++i) {
220 int len = strlen(argv[i]);
223 if (newtListboxAddEntry(listbox, argv[i], (void *)(long)i))
224 goto out_destroy_form;
227 newtCenteredWindow(max_len, argc, NULL);
228 newtFormRun(form, &es);
229 rc = newtListboxGetCurrent(listbox) - NULL;
230 if (es.reason == NEWT_EXIT_HOTKEY)
234 newtFormDestroy(form);
238 static int ui__help_window(const char *text)
240 struct newtExitStruct es;
241 newtComponent tb, form = newt_form__new();
243 int max_len = 0, nr_lines = 0;
251 const char *sep = strchr(t, '\n');
255 sep = strchr(t, '\0');
265 tb = newtTextbox(0, 0, max_len, nr_lines, 0);
267 goto out_destroy_form;
269 newtTextboxSetText(tb, text);
270 newtFormAddComponent(form, tb);
271 newtCenteredWindow(max_len, nr_lines, NULL);
272 newtFormRun(form, &es);
276 newtFormDestroy(form);
280 static bool dialog_yesno(const char *msg)
282 /* newtWinChoice should really be accepting const char pointers... */
283 char yes[] = "Yes", no[] = "No";
284 return newtWinChoice(NULL, yes, no, (char *)msg) == 1;
287 static void ui__error_window(const char *fmt, ...)
292 newtWinMessagev((char *)"Error", (char *)"Ok", (char *)fmt, ap);
296 static void annotate_browser__write(struct ui_browser *self, void *entry, int row)
298 struct objdump_line *ol = rb_entry(entry, struct objdump_line, node);
299 bool current_entry = ui_browser__is_current_entry(self, row);
300 int width = self->width;
302 if (ol->offset != -1) {
303 struct hist_entry *he = self->priv;
304 struct symbol *sym = he->ms.sym;
305 int len = he->ms.sym->end - he->ms.sym->start;
306 unsigned int hits = 0;
307 double percent = 0.0;
309 struct sym_priv *priv = symbol__priv(sym);
310 struct sym_ext *sym_ext = priv->ext;
311 struct sym_hist *h = priv->hist;
312 s64 offset = ol->offset;
313 struct objdump_line *next = objdump__get_next_ip_line(self->entries, ol);
315 while (offset < (s64)len &&
316 (next == NULL || offset < next->offset)) {
318 percent += sym_ext[offset].percent;
320 hits += h->ip[offset];
325 if (sym_ext == NULL && h->sum)
326 percent = 100.0 * hits / h->sum;
328 color = ui_browser__percent_color(percent, current_entry);
329 SLsmg_set_color(color);
330 slsmg_printf(" %7.2f ", percent);
332 SLsmg_set_color(HE_COLORSET_CODE);
334 int color = ui_browser__percent_color(0, current_entry);
335 SLsmg_set_color(color);
336 slsmg_write_nstring(" ", 9);
339 SLsmg_write_char(':');
340 slsmg_write_nstring(" ", 8);
342 slsmg_write_nstring(" ", width - 18);
344 slsmg_write_nstring(ol->line, width - 18);
347 static char *callchain_list__sym_name(struct callchain_list *self,
348 char *bf, size_t bfsize)
351 return self->ms.sym->name;
353 snprintf(bf, bfsize, "%#Lx", self->ip);
357 int hist_entry__tui_annotate(struct hist_entry *self)
359 struct newtExitStruct es;
360 struct objdump_line *pos, *n;
362 struct ui_browser browser = {
364 .refresh = ui_browser__list_head_refresh,
365 .seek = ui_browser__list_head_seek,
366 .write = annotate_browser__write,
371 if (self->ms.sym == NULL)
374 if (self->ms.map->dso->annotate_warned)
377 if (hist_entry__annotate(self, &head) < 0) {
378 ui__error_window(browser__last_msg);
382 ui_helpline__push("Press <- or ESC to exit");
384 list_for_each_entry(pos, &head, node) {
385 size_t line_len = strlen(pos->line);
386 if (browser.width < line_len)
387 browser.width = line_len;
388 ++browser.nr_entries;
391 browser.width += 18; /* Percentage */
392 ui_browser__show(&browser, self->ms.sym->name);
393 newtFormAddHotKey(browser.form, ' ');
394 ret = ui_browser__run(&browser, &es);
395 newtFormDestroy(browser.form);
397 list_for_each_entry_safe(pos, n, &head, node) {
398 list_del(&pos->node);
399 objdump_line__free(pos);
405 /* -------------------------------------------------------------------- */
414 static void map_browser__write(struct ui_browser *self, void *nd, int row)
416 struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
417 struct map_browser *mb = container_of(self, struct map_browser, b);
418 bool current_entry = ui_browser__is_current_entry(self, row);
419 int color = ui_browser__percent_color(0, current_entry);
421 SLsmg_set_color(color);
422 slsmg_printf("%*llx %*llx %c ",
423 mb->addrlen, sym->start, mb->addrlen, sym->end,
424 sym->binding == STB_GLOBAL ? 'g' :
425 sym->binding == STB_LOCAL ? 'l' : 'w');
426 slsmg_write_nstring(sym->name, mb->namelen);
429 /* FIXME uber-kludgy, see comment on cmd_report... */
430 static u32 *symbol__browser_index(struct symbol *self)
432 return ((void *)self) - sizeof(struct rb_node) - sizeof(u32);
435 static int map_browser__search(struct map_browser *self)
439 int err = ui_entry__read("Search by name/addr", target, sizeof(target), 40);
444 if (target[0] == '0' && tolower(target[1]) == 'x') {
445 u64 addr = strtoull(target, NULL, 16);
446 sym = map__find_symbol(self->map, addr, NULL);
448 sym = map__find_symbol_by_name(self->map, target, NULL);
451 u32 *idx = symbol__browser_index(sym);
453 self->b.first_visible_entry = &sym->rb_node;
454 self->b.index = self->b.first_visible_entry_idx = *idx;
456 ui_helpline__fpush("%s not found!", target);
461 static int map_browser__run(struct map_browser *self, struct newtExitStruct *es)
463 if (ui_browser__show(&self->b, self->map->dso->long_name) < 0)
466 ui_helpline__fpush("Press <- or ESC to exit, %s / to search",
467 verbose ? "" : "restart with -v to use");
468 newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT);
469 newtFormAddHotKey(self->b.form, NEWT_KEY_ENTER);
471 newtFormAddHotKey(self->b.form, '/');
474 ui_browser__run(&self->b, es);
476 if (es->reason != NEWT_EXIT_HOTKEY)
478 if (verbose && es->u.key == '/')
479 map_browser__search(self);
484 newtFormDestroy(self->b.form);
490 static int map__browse(struct map *self)
492 struct map_browser mb = {
494 .entries = &self->dso->symbols[self->type],
495 .refresh = ui_browser__rb_tree_refresh,
496 .seek = ui_browser__rb_tree_seek,
497 .write = map_browser__write,
501 struct newtExitStruct es;
503 char tmp[BITS_PER_LONG / 4];
506 for (nd = rb_first(mb.b.entries); nd; nd = rb_next(nd)) {
507 struct symbol *pos = rb_entry(nd, struct symbol, rb_node);
509 if (mb.namelen < pos->namelen)
510 mb.namelen = pos->namelen;
511 if (maxaddr < pos->end)
514 u32 *idx = symbol__browser_index(pos);
515 *idx = mb.b.nr_entries;
520 mb.addrlen = snprintf(tmp, sizeof(tmp), "%llx", maxaddr);
521 mb.b.width += mb.addrlen * 2 + 4 + mb.namelen;
522 return map_browser__run(&mb, &es);
525 /* -------------------------------------------------------------------- */
527 struct hist_browser {
530 struct hist_entry *he_selection;
531 struct map_symbol *selection;
534 static void hist_browser__reset(struct hist_browser *self);
535 static int hist_browser__run(struct hist_browser *self, const char *title,
536 struct newtExitStruct *es);
537 static unsigned int hist_browser__refresh(struct ui_browser *self);
538 static void ui_browser__hists_seek(struct ui_browser *self,
539 off_t offset, int whence);
541 static struct hist_browser *hist_browser__new(struct hists *hists)
543 struct hist_browser *self = zalloc(sizeof(*self));
547 self->b.refresh = hist_browser__refresh;
548 self->b.seek = ui_browser__hists_seek;
554 static void hist_browser__delete(struct hist_browser *self)
556 newtFormDestroy(self->b.form);
561 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self)
563 return self->he_selection;
566 static struct thread *hist_browser__selected_thread(struct hist_browser *self)
568 return self->he_selection->thread;
571 static int hist_browser__title(char *bf, size_t size, const char *ev_name,
572 const struct dso *dso, const struct thread *thread)
577 printed += snprintf(bf + printed, size - printed,
579 (thread->comm_set ? thread->comm : ""),
582 printed += snprintf(bf + printed, size - printed,
583 "%sDSO: %s", thread ? " " : "",
585 return printed ?: snprintf(bf, size, "Event: %s", ev_name);
588 int hists__browse(struct hists *self, const char *helpline, const char *ev_name)
590 struct hist_browser *browser = hist_browser__new(self);
591 struct pstack *fstack;
592 const struct thread *thread_filter = NULL;
593 const struct dso *dso_filter = NULL;
594 struct newtExitStruct es;
601 fstack = pstack__new(2);
605 ui_helpline__push(helpline);
607 hist_browser__title(msg, sizeof(msg), ev_name,
608 dso_filter, thread_filter);
611 const struct thread *thread;
612 const struct dso *dso;
614 int nr_options = 0, choice = 0, i,
615 annotate = -2, zoom_dso = -2, zoom_thread = -2,
618 if (hist_browser__run(browser, msg, &es))
621 thread = hist_browser__selected_thread(browser);
622 dso = browser->selection->map ? browser->selection->map->dso : NULL;
624 if (es.reason == NEWT_EXIT_HOTKEY) {
633 * Exit the browser, let hists__browser_tree
634 * go to the next or previous
643 if (browser->selection->map == NULL &&
644 browser->selection->map->dso->annotate_warned)
654 ui__help_window("-> Zoom into DSO/Threads & Annotate current symbol\n"
656 "a Annotate current symbol\n"
657 "h/?/F1 Show this window\n"
658 "d Zoom into current DSO\n"
659 "t Zoom into current Thread\n"
660 "q/CTRL+C Exit browser");
664 if (is_exit_key(key)) {
665 if (key == NEWT_KEY_ESCAPE) {
666 if (dialog_yesno("Do you really want to exit?"))
674 if (es.u.key == NEWT_KEY_LEFT) {
677 if (pstack__empty(fstack))
679 top = pstack__pop(fstack);
680 if (top == &dso_filter)
682 if (top == &thread_filter)
683 goto zoom_out_thread;
688 if (browser->selection->sym != NULL &&
689 !browser->selection->map->dso->annotate_warned &&
690 asprintf(&options[nr_options], "Annotate %s",
691 browser->selection->sym->name) > 0)
692 annotate = nr_options++;
694 if (thread != NULL &&
695 asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
696 (thread_filter ? "out of" : "into"),
697 (thread->comm_set ? thread->comm : ""),
699 zoom_thread = nr_options++;
702 asprintf(&options[nr_options], "Zoom %s %s DSO",
703 (dso_filter ? "out of" : "into"),
704 (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
705 zoom_dso = nr_options++;
707 if (browser->selection->map != NULL &&
708 asprintf(&options[nr_options], "Browse map details") > 0)
709 browse_map = nr_options++;
711 options[nr_options++] = (char *)"Exit";
713 choice = popup_menu(nr_options, options);
715 for (i = 0; i < nr_options - 1; ++i)
718 if (choice == nr_options - 1)
724 if (choice == annotate) {
725 struct hist_entry *he;
727 if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) {
728 browser->selection->map->dso->annotate_warned = 1;
729 ui_helpline__puts("No vmlinux file found, can't "
730 "annotate with just a "
735 he = hist_browser__selected_entry(browser);
739 hist_entry__tui_annotate(he);
740 } else if (choice == browse_map)
741 map__browse(browser->selection->map);
742 else if (choice == zoom_dso) {
745 pstack__remove(fstack, &dso_filter);
752 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
753 dso->kernel ? "the Kernel" : dso->short_name);
755 pstack__push(fstack, &dso_filter);
757 hists__filter_by_dso(self, dso_filter);
758 hist_browser__title(msg, sizeof(msg), ev_name,
759 dso_filter, thread_filter);
760 hist_browser__reset(browser);
761 } else if (choice == zoom_thread) {
764 pstack__remove(fstack, &thread_filter);
767 thread_filter = NULL;
769 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
770 thread->comm_set ? thread->comm : "",
772 thread_filter = thread;
773 pstack__push(fstack, &thread_filter);
775 hists__filter_by_thread(self, thread_filter);
776 hist_browser__title(msg, sizeof(msg), ev_name,
777 dso_filter, thread_filter);
778 hist_browser__reset(browser);
782 pstack__delete(fstack);
784 hist_browser__delete(browser);
788 int hists__tui_browse_tree(struct rb_root *self, const char *help)
790 struct rb_node *first = rb_first(self), *nd = first, *next;
794 struct hists *hists = rb_entry(nd, struct hists, rb_node);
795 const char *ev_name = __event_name(hists->type, hists->config);
797 key = hists__browse(hists, help, ev_name);
799 if (is_exit_key(key))
820 static void newt_suspend(void *d __used)
827 void setup_browser(void)
829 if (!isatty(1) || !use_browser || dump_trace) {
838 newtSetSuspendCallback(newt_suspend, NULL);
839 ui_helpline__puts(" ");
843 void exit_browser(bool wait_for_ok)
845 if (use_browser > 0) {
847 char title[] = "Fatal Error", ok[] = "Ok";
848 newtWinMessage(title, ok, browser__last_msg);
854 static void hist_browser__refresh_dimensions(struct hist_browser *self)
856 /* 3 == +/- toggle symbol before actual hist_entry rendering */
857 self->b.width = 3 + (hists__sort_list_width(self->hists) +
861 static void hist_browser__reset(struct hist_browser *self)
863 self->b.nr_entries = self->hists->nr_entries;
864 hist_browser__refresh_dimensions(self);
865 ui_browser__reset_index(&self->b);
868 static char tree__folded_sign(bool unfolded)
870 return unfolded ? '-' : '+';
873 static char map_symbol__folded(const struct map_symbol *self)
875 return self->has_children ? tree__folded_sign(self->unfolded) : ' ';
878 static char hist_entry__folded(const struct hist_entry *self)
880 return map_symbol__folded(&self->ms);
883 static char callchain_list__folded(const struct callchain_list *self)
885 return map_symbol__folded(&self->ms);
888 static bool map_symbol__toggle_fold(struct map_symbol *self)
890 if (!self->has_children)
893 self->unfolded = !self->unfolded;
897 #define LEVEL_OFFSET_STEP 3
899 static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self,
900 struct callchain_node *chain_node,
901 u64 total, int level,
904 bool *is_current_entry)
906 struct rb_node *node;
907 int first_row = row, width, offset = level * LEVEL_OFFSET_STEP;
908 u64 new_total, remaining;
910 if (callchain_param.mode == CHAIN_GRAPH_REL)
911 new_total = chain_node->children_hit;
915 remaining = new_total;
916 node = rb_first(&chain_node->rb_root);
918 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
919 struct rb_node *next = rb_next(node);
920 u64 cumul = cumul_hits(child);
921 struct callchain_list *chain;
922 char folded_sign = ' ';
924 int extra_offset = 0;
928 list_for_each_entry(chain, &child->val, list) {
929 char ipstr[BITS_PER_LONG / 4 + 1], *alloc_str;
932 bool was_first = first;
936 chain->ms.has_children = chain->list.next != &child->val ||
937 rb_first(&child->rb_root) != NULL;
939 extra_offset = LEVEL_OFFSET_STEP;
940 chain->ms.has_children = chain->list.next == &child->val &&
941 rb_first(&child->rb_root) != NULL;
944 folded_sign = callchain_list__folded(chain);
945 if (*row_offset != 0) {
951 str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
953 double percent = cumul * 100.0 / new_total;
955 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
956 str = "Not enough memory!";
961 color = HE_COLORSET_NORMAL;
962 width = self->b.width - (offset + extra_offset + 2);
963 if (ui_browser__is_current_entry(&self->b, row)) {
964 self->selection = &chain->ms;
965 color = HE_COLORSET_SELECTED;
966 *is_current_entry = true;
969 SLsmg_set_color(color);
970 SLsmg_gotorc(self->b.top + row, self->b.left);
971 slsmg_write_nstring(" ", offset + extra_offset);
972 slsmg_printf("%c ", folded_sign);
973 slsmg_write_nstring(str, width);
976 if (++row == self->b.height)
979 if (folded_sign == '+')
983 if (folded_sign == '-') {
984 const int new_level = level + (extra_offset ? 2 : 1);
985 row += hist_browser__show_callchain_node_rb_tree(self, child, new_total,
986 new_level, row, row_offset,
989 if (row == self->b.height)
994 return row - first_row;
997 static int hist_browser__show_callchain_node(struct hist_browser *self,
998 struct callchain_node *node,
999 int level, unsigned short row,
1001 bool *is_current_entry)
1003 struct callchain_list *chain;
1004 int first_row = row,
1005 offset = level * LEVEL_OFFSET_STEP,
1006 width = self->b.width - offset;
1007 char folded_sign = ' ';
1009 list_for_each_entry(chain, &node->val, list) {
1010 char ipstr[BITS_PER_LONG / 4 + 1], *s;
1013 * FIXME: This should be moved to somewhere else,
1014 * probably when the callchain is created, so as not to
1015 * traverse it all over again
1017 chain->ms.has_children = rb_first(&node->rb_root) != NULL;
1018 folded_sign = callchain_list__folded(chain);
1020 if (*row_offset != 0) {
1025 color = HE_COLORSET_NORMAL;
1026 if (ui_browser__is_current_entry(&self->b, row)) {
1027 self->selection = &chain->ms;
1028 color = HE_COLORSET_SELECTED;
1029 *is_current_entry = true;
1032 s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
1033 SLsmg_gotorc(self->b.top + row, self->b.left);
1034 SLsmg_set_color(color);
1035 slsmg_write_nstring(" ", offset);
1036 slsmg_printf("%c ", folded_sign);
1037 slsmg_write_nstring(s, width - 2);
1039 if (++row == self->b.height)
1043 if (folded_sign == '-')
1044 row += hist_browser__show_callchain_node_rb_tree(self, node,
1045 self->hists->stats.total_period,
1050 return row - first_row;
1053 static int hist_browser__show_callchain(struct hist_browser *self,
1054 struct rb_root *chain,
1055 int level, unsigned short row,
1057 bool *is_current_entry)
1060 int first_row = row;
1062 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
1063 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
1065 row += hist_browser__show_callchain_node(self, node, level,
1068 if (row == self->b.height)
1072 return row - first_row;
1075 static int hist_browser__show_entry(struct hist_browser *self,
1076 struct hist_entry *entry,
1082 int color, width = self->b.width;
1083 char folded_sign = ' ';
1084 bool current_entry = ui_browser__is_current_entry(&self->b, row);
1085 off_t row_offset = entry->row_offset;
1087 if (current_entry) {
1088 self->he_selection = entry;
1089 self->selection = &entry->ms;
1092 if (symbol_conf.use_callchain) {
1093 entry->ms.has_children = !RB_EMPTY_ROOT(&entry->sorted_chain);
1094 folded_sign = hist_entry__folded(entry);
1097 if (row_offset == 0) {
1098 hist_entry__snprintf(entry, s, sizeof(s), self->hists, NULL, false,
1099 0, false, self->hists->stats.total_period);
1100 percent = (entry->period * 100.0) / self->hists->stats.total_period;
1102 color = HE_COLORSET_SELECTED;
1103 if (!current_entry) {
1104 if (percent >= MIN_RED)
1105 color = HE_COLORSET_TOP;
1106 else if (percent >= MIN_GREEN)
1107 color = HE_COLORSET_MEDIUM;
1109 color = HE_COLORSET_NORMAL;
1112 SLsmg_set_color(color);
1113 SLsmg_gotorc(self->b.top + row, self->b.left);
1114 if (symbol_conf.use_callchain) {
1115 slsmg_printf("%c ", folded_sign);
1118 slsmg_write_nstring(s, width);
1124 if (folded_sign == '-' && row != self->b.height) {
1125 printed += hist_browser__show_callchain(self, &entry->sorted_chain,
1126 1, row, &row_offset,
1129 self->he_selection = entry;
1135 static unsigned int hist_browser__refresh(struct ui_browser *self)
1139 struct hist_browser *hb = container_of(self, struct hist_browser, b);
1141 if (self->first_visible_entry == NULL)
1142 self->first_visible_entry = rb_first(&hb->hists->entries);
1144 for (nd = self->first_visible_entry; nd; nd = rb_next(nd)) {
1145 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1150 row += hist_browser__show_entry(hb, h, row);
1151 if (row == self->height)
1158 static void callchain_node__init_have_children_rb_tree(struct callchain_node *self)
1160 struct rb_node *nd = rb_first(&self->rb_root);
1162 for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
1163 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
1164 struct callchain_list *chain;
1167 list_for_each_entry(chain, &child->val, list) {
1170 chain->ms.has_children = chain->list.next != &child->val ||
1171 rb_first(&child->rb_root) != NULL;
1173 chain->ms.has_children = chain->list.next == &child->val &&
1174 rb_first(&child->rb_root) != NULL;
1177 callchain_node__init_have_children_rb_tree(child);
1181 static void callchain_node__init_have_children(struct callchain_node *self)
1183 struct callchain_list *chain;
1185 list_for_each_entry(chain, &self->val, list)
1186 chain->ms.has_children = rb_first(&self->rb_root) != NULL;
1188 callchain_node__init_have_children_rb_tree(self);
1191 static void callchain__init_have_children(struct rb_root *self)
1195 for (nd = rb_first(self); nd; nd = rb_next(nd)) {
1196 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
1197 callchain_node__init_have_children(node);
1201 static void hist_entry__init_have_children(struct hist_entry *self)
1203 if (!self->init_have_children) {
1204 callchain__init_have_children(&self->sorted_chain);
1205 self->init_have_children = true;
1209 static struct rb_node *hists__filter_entries(struct rb_node *nd)
1211 while (nd != NULL) {
1212 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1222 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd)
1224 while (nd != NULL) {
1225 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1235 static void ui_browser__hists_seek(struct ui_browser *self,
1236 off_t offset, int whence)
1238 struct hist_entry *h;
1244 nd = hists__filter_entries(rb_first(self->entries));
1247 nd = self->first_visible_entry;
1250 nd = hists__filter_prev_entries(rb_last(self->entries));
1258 * Moves not relative to the first visible entry invalidates its
1261 h = rb_entry(self->first_visible_entry, struct hist_entry, rb_node);
1265 * Here we have to check if nd is expanded (+), if it is we can't go
1266 * the next top level hist_entry, instead we must compute an offset of
1267 * what _not_ to show and not change the first visible entry.
1269 * This offset increments when we are going from top to bottom and
1270 * decreases when we're going from bottom to top.
1272 * As we don't have backpointers to the top level in the callchains
1273 * structure, we need to always print the whole hist_entry callchain,
1274 * skipping the first ones that are before the first visible entry
1275 * and stop when we printed enough lines to fill the screen.
1280 h = rb_entry(nd, struct hist_entry, rb_node);
1281 if (h->ms.unfolded) {
1282 u16 remaining = h->nr_rows - h->row_offset;
1283 if (offset > remaining) {
1284 offset -= remaining;
1287 h->row_offset += offset;
1289 self->first_visible_entry = nd;
1293 nd = hists__filter_entries(rb_next(nd));
1297 self->first_visible_entry = nd;
1298 } while (offset != 0);
1299 } else if (offset < 0) {
1301 h = rb_entry(nd, struct hist_entry, rb_node);
1302 if (h->ms.unfolded) {
1304 if (-offset > h->row_offset) {
1305 offset += h->row_offset;
1308 h->row_offset += offset;
1310 self->first_visible_entry = nd;
1314 if (-offset > h->nr_rows) {
1315 offset += h->nr_rows;
1318 h->row_offset = h->nr_rows + offset;
1320 self->first_visible_entry = nd;
1326 nd = hists__filter_prev_entries(rb_prev(nd));
1330 self->first_visible_entry = nd;
1333 * Last unfiltered hist_entry, check if it is
1334 * unfolded, if it is then we should have
1335 * row_offset at its last entry.
1337 h = rb_entry(nd, struct hist_entry, rb_node);
1339 h->row_offset = h->nr_rows;
1345 self->first_visible_entry = nd;
1346 h = rb_entry(nd, struct hist_entry, rb_node);
1351 static int callchain_node__count_rows_rb_tree(struct callchain_node *self)
1356 for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
1357 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
1358 struct callchain_list *chain;
1359 char folded_sign = ' '; /* No children */
1361 list_for_each_entry(chain, &child->val, list) {
1363 /* We need this because we may not have children */
1364 folded_sign = callchain_list__folded(chain);
1365 if (folded_sign == '+')
1369 if (folded_sign == '-') /* Have children and they're unfolded */
1370 n += callchain_node__count_rows_rb_tree(child);
1376 static int callchain_node__count_rows(struct callchain_node *node)
1378 struct callchain_list *chain;
1379 bool unfolded = false;
1382 list_for_each_entry(chain, &node->val, list) {
1384 unfolded = chain->ms.unfolded;
1388 n += callchain_node__count_rows_rb_tree(node);
1393 static int callchain__count_rows(struct rb_root *chain)
1398 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
1399 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
1400 n += callchain_node__count_rows(node);
1406 static bool hist_browser__toggle_fold(struct hist_browser *self)
1408 if (map_symbol__toggle_fold(self->selection)) {
1409 struct hist_entry *he = self->he_selection;
1411 hist_entry__init_have_children(he);
1412 self->hists->nr_entries -= he->nr_rows;
1414 if (he->ms.unfolded)
1415 he->nr_rows = callchain__count_rows(&he->sorted_chain);
1418 self->hists->nr_entries += he->nr_rows;
1419 self->b.nr_entries = self->hists->nr_entries;
1424 /* If it doesn't have children, no toggling performed */
1428 static int hist_browser__run(struct hist_browser *self, const char *title,
1429 struct newtExitStruct *es)
1431 char str[256], unit;
1432 unsigned long nr_events = self->hists->stats.nr_events[PERF_RECORD_SAMPLE];
1434 self->b.entries = &self->hists->entries;
1435 self->b.nr_entries = self->hists->nr_entries;
1437 hist_browser__refresh_dimensions(self);
1439 nr_events = convert_unit(nr_events, &unit);
1440 snprintf(str, sizeof(str), "Events: %lu%c ",
1442 newtDrawRootText(0, 0, str);
1444 if (ui_browser__show(&self->b, title) < 0)
1447 newtFormAddHotKey(self->b.form, 'A');
1448 newtFormAddHotKey(self->b.form, 'a');
1449 newtFormAddHotKey(self->b.form, '?');
1450 newtFormAddHotKey(self->b.form, 'h');
1451 newtFormAddHotKey(self->b.form, 'H');
1452 newtFormAddHotKey(self->b.form, 'd');
1454 newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT);
1455 newtFormAddHotKey(self->b.form, NEWT_KEY_RIGHT);
1456 newtFormAddHotKey(self->b.form, NEWT_KEY_ENTER);
1459 ui_browser__run(&self->b, es);
1461 if (es->reason != NEWT_EXIT_HOTKEY)
1463 switch (es->u.key) {
1464 case 'd': { /* Debug */
1466 struct hist_entry *h = rb_entry(self->b.first_visible_entry,
1467 struct hist_entry, rb_node);
1469 ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
1470 seq++, self->b.nr_entries,
1471 self->hists->nr_entries,
1474 self->b.first_visible_entry_idx,
1475 h->row_offset, h->nr_rows);
1478 case NEWT_KEY_ENTER:
1479 if (hist_browser__toggle_fold(self))