2 #include "../libslang.h"
6 #include <linux/rbtree.h>
8 #include "../../evsel.h"
9 #include "../../evlist.h"
10 #include "../../hist.h"
11 #include "../../pstack.h"
12 #include "../../sort.h"
13 #include "../../util.h"
15 #include "../browser.h"
16 #include "../helpline.h"
24 struct hist_entry *he_selection;
25 struct map_symbol *selection;
29 static int hists__browser_title(struct hists *self, char *bf, size_t size,
32 static void hist_browser__refresh_dimensions(struct hist_browser *self)
34 /* 3 == +/- toggle symbol before actual hist_entry rendering */
35 self->b.width = 3 + (hists__sort_list_width(self->hists) +
39 static void hist_browser__reset(struct hist_browser *self)
41 self->b.nr_entries = self->hists->nr_entries;
42 hist_browser__refresh_dimensions(self);
43 ui_browser__reset_index(&self->b);
46 static char tree__folded_sign(bool unfolded)
48 return unfolded ? '-' : '+';
51 static char map_symbol__folded(const struct map_symbol *self)
53 return self->has_children ? tree__folded_sign(self->unfolded) : ' ';
56 static char hist_entry__folded(const struct hist_entry *self)
58 return map_symbol__folded(&self->ms);
61 static char callchain_list__folded(const struct callchain_list *self)
63 return map_symbol__folded(&self->ms);
66 static void map_symbol__set_folding(struct map_symbol *self, bool unfold)
68 self->unfolded = unfold ? self->has_children : false;
71 static int callchain_node__count_rows_rb_tree(struct callchain_node *self)
76 for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
77 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
78 struct callchain_list *chain;
79 char folded_sign = ' '; /* No children */
81 list_for_each_entry(chain, &child->val, list) {
83 /* We need this because we may not have children */
84 folded_sign = callchain_list__folded(chain);
85 if (folded_sign == '+')
89 if (folded_sign == '-') /* Have children and they're unfolded */
90 n += callchain_node__count_rows_rb_tree(child);
96 static int callchain_node__count_rows(struct callchain_node *node)
98 struct callchain_list *chain;
99 bool unfolded = false;
102 list_for_each_entry(chain, &node->val, list) {
104 unfolded = chain->ms.unfolded;
108 n += callchain_node__count_rows_rb_tree(node);
113 static int callchain__count_rows(struct rb_root *chain)
118 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
119 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
120 n += callchain_node__count_rows(node);
126 static bool map_symbol__toggle_fold(struct map_symbol *self)
128 if (!self->has_children)
131 self->unfolded = !self->unfolded;
135 static void callchain_node__init_have_children_rb_tree(struct callchain_node *self)
137 struct rb_node *nd = rb_first(&self->rb_root);
139 for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
140 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
141 struct callchain_list *chain;
144 list_for_each_entry(chain, &child->val, list) {
147 chain->ms.has_children = chain->list.next != &child->val ||
148 !RB_EMPTY_ROOT(&child->rb_root);
150 chain->ms.has_children = chain->list.next == &child->val &&
151 !RB_EMPTY_ROOT(&child->rb_root);
154 callchain_node__init_have_children_rb_tree(child);
158 static void callchain_node__init_have_children(struct callchain_node *self)
160 struct callchain_list *chain;
162 list_for_each_entry(chain, &self->val, list)
163 chain->ms.has_children = !RB_EMPTY_ROOT(&self->rb_root);
165 callchain_node__init_have_children_rb_tree(self);
168 static void callchain__init_have_children(struct rb_root *self)
172 for (nd = rb_first(self); nd; nd = rb_next(nd)) {
173 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
174 callchain_node__init_have_children(node);
178 static void hist_entry__init_have_children(struct hist_entry *self)
180 if (!self->init_have_children) {
181 self->ms.has_children = !RB_EMPTY_ROOT(&self->sorted_chain);
182 callchain__init_have_children(&self->sorted_chain);
183 self->init_have_children = true;
187 static bool hist_browser__toggle_fold(struct hist_browser *self)
189 if (map_symbol__toggle_fold(self->selection)) {
190 struct hist_entry *he = self->he_selection;
192 hist_entry__init_have_children(he);
193 self->hists->nr_entries -= he->nr_rows;
196 he->nr_rows = callchain__count_rows(&he->sorted_chain);
199 self->hists->nr_entries += he->nr_rows;
200 self->b.nr_entries = self->hists->nr_entries;
205 /* If it doesn't have children, no toggling performed */
209 static int callchain_node__set_folding_rb_tree(struct callchain_node *self, bool unfold)
214 for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
215 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
216 struct callchain_list *chain;
217 bool has_children = false;
219 list_for_each_entry(chain, &child->val, list) {
221 map_symbol__set_folding(&chain->ms, unfold);
222 has_children = chain->ms.has_children;
226 n += callchain_node__set_folding_rb_tree(child, unfold);
232 static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
234 struct callchain_list *chain;
235 bool has_children = false;
238 list_for_each_entry(chain, &node->val, list) {
240 map_symbol__set_folding(&chain->ms, unfold);
241 has_children = chain->ms.has_children;
245 n += callchain_node__set_folding_rb_tree(node, unfold);
250 static int callchain__set_folding(struct rb_root *chain, bool unfold)
255 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
256 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
257 n += callchain_node__set_folding(node, unfold);
263 static void hist_entry__set_folding(struct hist_entry *self, bool unfold)
265 hist_entry__init_have_children(self);
266 map_symbol__set_folding(&self->ms, unfold);
268 if (self->ms.has_children) {
269 int n = callchain__set_folding(&self->sorted_chain, unfold);
270 self->nr_rows = unfold ? n : 0;
275 static void hists__set_folding(struct hists *self, bool unfold)
279 self->nr_entries = 0;
281 for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) {
282 struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
283 hist_entry__set_folding(he, unfold);
284 self->nr_entries += 1 + he->nr_rows;
288 static void hist_browser__set_folding(struct hist_browser *self, bool unfold)
290 hists__set_folding(self->hists, unfold);
291 self->b.nr_entries = self->hists->nr_entries;
292 /* Go to the start, we may be way after valid entries after a collapse */
293 ui_browser__reset_index(&self->b);
296 static void ui_browser__warn_lost_events(struct ui_browser *browser)
298 ui_browser__warning(browser, 4,
299 "Events are being lost, check IO/CPU overload!\n\n"
300 "You may want to run 'perf' using a RT scheduler policy:\n\n"
301 " perf top -r 80\n\n"
302 "Or reduce the sampling frequency.");
305 static int hist_browser__run(struct hist_browser *self, const char *ev_name,
306 void(*timer)(void *arg), void *arg, int delay_secs)
311 self->b.entries = &self->hists->entries;
312 self->b.nr_entries = self->hists->nr_entries;
314 hist_browser__refresh_dimensions(self);
315 hists__browser_title(self->hists, title, sizeof(title), ev_name);
317 if (ui_browser__show(&self->b, title,
318 "Press '?' for help on key bindings") < 0)
322 key = ui_browser__run(&self->b, delay_secs);
327 ui_browser__update_nr_entries(&self->b, self->hists->nr_entries);
329 if (self->hists->stats.nr_lost_warned !=
330 self->hists->stats.nr_events[PERF_RECORD_LOST]) {
331 self->hists->stats.nr_lost_warned =
332 self->hists->stats.nr_events[PERF_RECORD_LOST];
333 ui_browser__warn_lost_events(&self->b);
336 hists__browser_title(self->hists, title, sizeof(title), ev_name);
337 ui_browser__show_title(&self->b, title);
339 case 'D': { /* Debug */
341 struct hist_entry *h = rb_entry(self->b.top,
342 struct hist_entry, rb_node);
344 ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
345 seq++, self->b.nr_entries,
346 self->hists->nr_entries,
350 h->row_offset, h->nr_rows);
354 /* Collapse the whole world. */
355 hist_browser__set_folding(self, false);
358 /* Expand the whole world. */
359 hist_browser__set_folding(self, true);
362 if (hist_browser__toggle_fold(self))
370 ui_browser__hide(&self->b);
374 static char *callchain_list__sym_name(struct callchain_list *self,
375 char *bf, size_t bfsize)
378 return self->ms.sym->name;
380 snprintf(bf, bfsize, "%#" PRIx64, self->ip);
384 #define LEVEL_OFFSET_STEP 3
386 static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self,
387 struct callchain_node *chain_node,
388 u64 total, int level,
391 bool *is_current_entry)
393 struct rb_node *node;
394 int first_row = row, width, offset = level * LEVEL_OFFSET_STEP;
395 u64 new_total, remaining;
397 if (callchain_param.mode == CHAIN_GRAPH_REL)
398 new_total = chain_node->children_hit;
402 remaining = new_total;
403 node = rb_first(&chain_node->rb_root);
405 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
406 struct rb_node *next = rb_next(node);
407 u64 cumul = callchain_cumul_hits(child);
408 struct callchain_list *chain;
409 char folded_sign = ' ';
411 int extra_offset = 0;
415 list_for_each_entry(chain, &child->val, list) {
416 char ipstr[BITS_PER_LONG / 4 + 1], *alloc_str;
419 bool was_first = first;
424 extra_offset = LEVEL_OFFSET_STEP;
426 folded_sign = callchain_list__folded(chain);
427 if (*row_offset != 0) {
433 str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
435 double percent = cumul * 100.0 / new_total;
437 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
438 str = "Not enough memory!";
443 color = HE_COLORSET_NORMAL;
444 width = self->b.width - (offset + extra_offset + 2);
445 if (ui_browser__is_current_entry(&self->b, row)) {
446 self->selection = &chain->ms;
447 color = HE_COLORSET_SELECTED;
448 *is_current_entry = true;
451 ui_browser__set_color(&self->b, color);
452 ui_browser__gotorc(&self->b, row, 0);
453 slsmg_write_nstring(" ", offset + extra_offset);
454 slsmg_printf("%c ", folded_sign);
455 slsmg_write_nstring(str, width);
458 if (++row == self->b.height)
461 if (folded_sign == '+')
465 if (folded_sign == '-') {
466 const int new_level = level + (extra_offset ? 2 : 1);
467 row += hist_browser__show_callchain_node_rb_tree(self, child, new_total,
468 new_level, row, row_offset,
471 if (row == self->b.height)
476 return row - first_row;
479 static int hist_browser__show_callchain_node(struct hist_browser *self,
480 struct callchain_node *node,
481 int level, unsigned short row,
483 bool *is_current_entry)
485 struct callchain_list *chain;
487 offset = level * LEVEL_OFFSET_STEP,
488 width = self->b.width - offset;
489 char folded_sign = ' ';
491 list_for_each_entry(chain, &node->val, list) {
492 char ipstr[BITS_PER_LONG / 4 + 1], *s;
495 folded_sign = callchain_list__folded(chain);
497 if (*row_offset != 0) {
502 color = HE_COLORSET_NORMAL;
503 if (ui_browser__is_current_entry(&self->b, row)) {
504 self->selection = &chain->ms;
505 color = HE_COLORSET_SELECTED;
506 *is_current_entry = true;
509 s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
510 ui_browser__gotorc(&self->b, row, 0);
511 ui_browser__set_color(&self->b, color);
512 slsmg_write_nstring(" ", offset);
513 slsmg_printf("%c ", folded_sign);
514 slsmg_write_nstring(s, width - 2);
516 if (++row == self->b.height)
520 if (folded_sign == '-')
521 row += hist_browser__show_callchain_node_rb_tree(self, node,
522 self->hists->stats.total_period,
527 return row - first_row;
530 static int hist_browser__show_callchain(struct hist_browser *self,
531 struct rb_root *chain,
532 int level, unsigned short row,
534 bool *is_current_entry)
539 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
540 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
542 row += hist_browser__show_callchain_node(self, node, level,
545 if (row == self->b.height)
549 return row - first_row;
552 static int hist_browser__show_entry(struct hist_browser *self,
553 struct hist_entry *entry,
559 int width = self->b.width - 6; /* The percentage */
560 char folded_sign = ' ';
561 bool current_entry = ui_browser__is_current_entry(&self->b, row);
562 off_t row_offset = entry->row_offset;
565 self->he_selection = entry;
566 self->selection = &entry->ms;
569 if (symbol_conf.use_callchain) {
570 hist_entry__init_have_children(entry);
571 folded_sign = hist_entry__folded(entry);
574 if (row_offset == 0) {
575 hist_entry__snprintf(entry, s, sizeof(s), self->hists);
576 percent = (entry->period * 100.0) / self->hists->stats.total_period;
578 ui_browser__set_percent_color(&self->b, percent, current_entry);
579 ui_browser__gotorc(&self->b, row, 0);
580 if (symbol_conf.use_callchain) {
581 slsmg_printf("%c ", folded_sign);
585 slsmg_printf(" %5.2f%%", percent);
587 /* The scroll bar isn't being used */
588 if (!self->b.navkeypressed)
591 if (!current_entry || !self->b.navkeypressed)
592 ui_browser__set_color(&self->b, HE_COLORSET_NORMAL);
594 if (symbol_conf.show_nr_samples) {
595 slsmg_printf(" %11u", entry->nr_events);
599 if (symbol_conf.show_total_period) {
600 slsmg_printf(" %12" PRIu64, entry->period);
604 slsmg_write_nstring(s, width);
610 if (folded_sign == '-' && row != self->b.height) {
611 printed += hist_browser__show_callchain(self, &entry->sorted_chain,
615 self->he_selection = entry;
621 static void ui_browser__hists_init_top(struct ui_browser *browser)
623 if (browser->top == NULL) {
624 struct hist_browser *hb;
626 hb = container_of(browser, struct hist_browser, b);
627 browser->top = rb_first(&hb->hists->entries);
631 static unsigned int hist_browser__refresh(struct ui_browser *self)
635 struct hist_browser *hb = container_of(self, struct hist_browser, b);
637 ui_browser__hists_init_top(self);
639 for (nd = self->top; nd; nd = rb_next(nd)) {
640 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
645 row += hist_browser__show_entry(hb, h, row);
646 if (row == self->height)
653 static struct rb_node *hists__filter_entries(struct rb_node *nd)
656 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
666 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd)
669 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
679 static void ui_browser__hists_seek(struct ui_browser *self,
680 off_t offset, int whence)
682 struct hist_entry *h;
686 if (self->nr_entries == 0)
689 ui_browser__hists_init_top(self);
693 nd = hists__filter_entries(rb_first(self->entries));
699 nd = hists__filter_prev_entries(rb_last(self->entries));
707 * Moves not relative to the first visible entry invalidates its
710 h = rb_entry(self->top, struct hist_entry, rb_node);
714 * Here we have to check if nd is expanded (+), if it is we can't go
715 * the next top level hist_entry, instead we must compute an offset of
716 * what _not_ to show and not change the first visible entry.
718 * This offset increments when we are going from top to bottom and
719 * decreases when we're going from bottom to top.
721 * As we don't have backpointers to the top level in the callchains
722 * structure, we need to always print the whole hist_entry callchain,
723 * skipping the first ones that are before the first visible entry
724 * and stop when we printed enough lines to fill the screen.
729 h = rb_entry(nd, struct hist_entry, rb_node);
730 if (h->ms.unfolded) {
731 u16 remaining = h->nr_rows - h->row_offset;
732 if (offset > remaining) {
736 h->row_offset += offset;
742 nd = hists__filter_entries(rb_next(nd));
747 } while (offset != 0);
748 } else if (offset < 0) {
750 h = rb_entry(nd, struct hist_entry, rb_node);
751 if (h->ms.unfolded) {
753 if (-offset > h->row_offset) {
754 offset += h->row_offset;
757 h->row_offset += offset;
763 if (-offset > h->nr_rows) {
764 offset += h->nr_rows;
767 h->row_offset = h->nr_rows + offset;
775 nd = hists__filter_prev_entries(rb_prev(nd));
782 * Last unfiltered hist_entry, check if it is
783 * unfolded, if it is then we should have
784 * row_offset at its last entry.
786 h = rb_entry(nd, struct hist_entry, rb_node);
788 h->row_offset = h->nr_rows;
795 h = rb_entry(nd, struct hist_entry, rb_node);
800 static struct hist_browser *hist_browser__new(struct hists *hists)
802 struct hist_browser *self = zalloc(sizeof(*self));
806 self->b.refresh = hist_browser__refresh;
807 self->b.seek = ui_browser__hists_seek;
808 self->b.use_navkeypressed = true;
809 if (sort__branch_mode == 1)
810 self->has_symbols = sort_sym_from.list.next != NULL;
812 self->has_symbols = sort_sym.list.next != NULL;
818 static void hist_browser__delete(struct hist_browser *self)
823 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self)
825 return self->he_selection;
828 static struct thread *hist_browser__selected_thread(struct hist_browser *self)
830 return self->he_selection->thread;
833 static int hists__browser_title(struct hists *self, char *bf, size_t size,
838 const struct dso *dso = self->dso_filter;
839 const struct thread *thread = self->thread_filter;
840 unsigned long nr_events = self->stats.nr_events[PERF_RECORD_SAMPLE];
842 nr_events = convert_unit(nr_events, &unit);
843 printed = scnprintf(bf, size, "Events: %lu%c %s", nr_events, unit, ev_name);
845 if (self->uid_filter_str)
846 printed += snprintf(bf + printed, size - printed,
847 ", UID: %s", self->uid_filter_str);
849 printed += scnprintf(bf + printed, size - printed,
851 (thread->comm_set ? thread->comm : ""),
854 printed += scnprintf(bf + printed, size - printed,
855 ", DSO: %s", dso->short_name);
859 static inline void free_popup_options(char **options, int n)
863 for (i = 0; i < n; ++i) {
869 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
870 const char *helpline, const char *ev_name,
872 void(*timer)(void *arg), void *arg,
875 struct hists *self = &evsel->hists;
876 struct hist_browser *browser = hist_browser__new(self);
877 struct branch_info *bi;
878 struct pstack *fstack;
887 fstack = pstack__new(2);
891 ui_helpline__push(helpline);
893 memset(options, 0, sizeof(options));
896 const struct thread *thread = NULL;
897 const struct dso *dso = NULL;
899 annotate = -2, zoom_dso = -2, zoom_thread = -2,
900 annotate_f = -2, annotate_t = -2, browse_map = -2;
904 key = hist_browser__run(browser, ev_name, timer, arg, delay_secs);
906 if (browser->he_selection != NULL) {
907 thread = hist_browser__selected_thread(browser);
908 dso = browser->selection->map ? browser->selection->map->dso : NULL;
916 * Exit the browser, let hists__browser_tree
917 * go to the next or previous
921 if (!browser->has_symbols) {
922 ui_browser__warning(&browser->b, delay_secs * 2,
923 "Annotation is only available for symbolic views, "
924 "include \"sym*\" in --sort to use it.");
928 if (browser->selection == NULL ||
929 browser->selection->sym == NULL ||
930 browser->selection->map->dso->annotate_warned)
938 if (ui_browser__input_window("Symbol to show",
939 "Please enter the name of symbol you want to see",
940 buf, "ENTER: OK, ESC: Cancel",
941 delay_secs * 2) == K_ENTER) {
942 self->symbol_filter_str = *buf ? buf : NULL;
943 hists__filter_by_symbol(self);
944 hist_browser__reset(browser);
950 ui_browser__help_window(&browser->b,
951 "h/?/F1 Show this window\n"
953 "PGDN/SPACE Navigate\n"
954 "q/ESC/CTRL+C Exit browser\n\n"
955 "For multiple event sessions:\n\n"
956 "TAB/UNTAB Switch events\n\n"
957 "For symbolic views (--sort has sym):\n\n"
958 "-> Zoom into DSO/Threads & Annotate current symbol\n"
960 "a Annotate current symbol\n"
961 "C Collapse all callchains\n"
962 "E Expand all callchains\n"
963 "d Zoom into current DSO\n"
964 "t Zoom into current Thread\n"
965 "s Filter symbol by name");
974 if (pstack__empty(fstack)) {
976 * Go back to the perf_evsel_menu__run or other user
982 top = pstack__pop(fstack);
983 if (top == &browser->hists->dso_filter)
985 if (top == &browser->hists->thread_filter)
986 goto zoom_out_thread;
991 !ui_browser__dialog_yesno(&browser->b,
992 "Do you really want to exit?"))
1002 if (!browser->has_symbols)
1003 goto add_exit_option;
1005 if (sort__branch_mode == 1) {
1006 bi = browser->he_selection->branch_info;
1007 if (browser->selection != NULL &&
1009 bi->from.sym != NULL &&
1010 !bi->from.map->dso->annotate_warned &&
1011 asprintf(&options[nr_options], "Annotate %s",
1012 bi->from.sym->name) > 0)
1013 annotate_f = nr_options++;
1015 if (browser->selection != NULL &&
1017 bi->to.sym != NULL &&
1018 !bi->to.map->dso->annotate_warned &&
1019 (bi->to.sym != bi->from.sym ||
1020 bi->to.map->dso != bi->from.map->dso) &&
1021 asprintf(&options[nr_options], "Annotate %s",
1022 bi->to.sym->name) > 0)
1023 annotate_t = nr_options++;
1026 if (browser->selection != NULL &&
1027 browser->selection->sym != NULL &&
1028 !browser->selection->map->dso->annotate_warned &&
1029 asprintf(&options[nr_options], "Annotate %s",
1030 browser->selection->sym->name) > 0)
1031 annotate = nr_options++;
1034 if (thread != NULL &&
1035 asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
1036 (browser->hists->thread_filter ? "out of" : "into"),
1037 (thread->comm_set ? thread->comm : ""),
1039 zoom_thread = nr_options++;
1042 asprintf(&options[nr_options], "Zoom %s %s DSO",
1043 (browser->hists->dso_filter ? "out of" : "into"),
1044 (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
1045 zoom_dso = nr_options++;
1047 if (browser->selection != NULL &&
1048 browser->selection->map != NULL &&
1049 asprintf(&options[nr_options], "Browse map details") > 0)
1050 browse_map = nr_options++;
1052 options[nr_options++] = (char *)"Exit";
1054 choice = ui__popup_menu(nr_options, options);
1056 if (choice == nr_options - 1)
1060 free_popup_options(options, nr_options - 1);
1064 if (choice == annotate || choice == annotate_t || choice == annotate_f) {
1065 struct hist_entry *he;
1068 he = hist_browser__selected_entry(browser);
1073 * we stash the branch_info symbol + map into the
1074 * the ms so we don't have to rewrite all the annotation
1075 * code to use branch_info.
1076 * in branch mode, the ms struct is not used
1078 if (choice == annotate_f) {
1079 he->ms.sym = he->branch_info->from.sym;
1080 he->ms.map = he->branch_info->from.map;
1081 } else if (choice == annotate_t) {
1082 he->ms.sym = he->branch_info->to.sym;
1083 he->ms.map = he->branch_info->to.map;
1087 * Don't let this be freed, say, by hists__decay_entry.
1090 err = hist_entry__tui_annotate(he, evsel->idx,
1091 timer, arg, delay_secs);
1094 * offer option to annotate the other branch source or target
1095 * (if they exists) when returning from annotate
1097 if ((err == 'q' || err == CTRL('c'))
1098 && annotate_t != -2 && annotate_f != -2)
1099 goto retry_popup_menu;
1101 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1103 ui_browser__handle_resize(&browser->b);
1105 } else if (choice == browse_map)
1106 map__browse(browser->selection->map);
1107 else if (choice == zoom_dso) {
1109 if (browser->hists->dso_filter) {
1110 pstack__remove(fstack, &browser->hists->dso_filter);
1113 browser->hists->dso_filter = NULL;
1114 sort_dso.elide = false;
1118 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1119 dso->kernel ? "the Kernel" : dso->short_name);
1120 browser->hists->dso_filter = dso;
1121 sort_dso.elide = true;
1122 pstack__push(fstack, &browser->hists->dso_filter);
1124 hists__filter_by_dso(self);
1125 hist_browser__reset(browser);
1126 } else if (choice == zoom_thread) {
1128 if (browser->hists->thread_filter) {
1129 pstack__remove(fstack, &browser->hists->thread_filter);
1132 browser->hists->thread_filter = NULL;
1133 sort_thread.elide = false;
1135 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1136 thread->comm_set ? thread->comm : "",
1138 browser->hists->thread_filter = thread;
1139 sort_thread.elide = true;
1140 pstack__push(fstack, &browser->hists->thread_filter);
1142 hists__filter_by_thread(self);
1143 hist_browser__reset(browser);
1147 pstack__delete(fstack);
1149 hist_browser__delete(browser);
1150 free_popup_options(options, nr_options - 1);
1154 struct perf_evsel_menu {
1155 struct ui_browser b;
1156 struct perf_evsel *selection;
1157 bool lost_events, lost_events_warned;
1160 static void perf_evsel_menu__write(struct ui_browser *browser,
1161 void *entry, int row)
1163 struct perf_evsel_menu *menu = container_of(browser,
1164 struct perf_evsel_menu, b);
1165 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1166 bool current_entry = ui_browser__is_current_entry(browser, row);
1167 unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1168 const char *ev_name = event_name(evsel);
1170 const char *warn = " ";
1173 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
1174 HE_COLORSET_NORMAL);
1176 nr_events = convert_unit(nr_events, &unit);
1177 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
1178 unit, unit == ' ' ? "" : " ", ev_name);
1179 slsmg_printf("%s", bf);
1181 nr_events = evsel->hists.stats.nr_events[PERF_RECORD_LOST];
1182 if (nr_events != 0) {
1183 menu->lost_events = true;
1185 ui_browser__set_color(browser, HE_COLORSET_TOP);
1186 nr_events = convert_unit(nr_events, &unit);
1187 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
1188 nr_events, unit, unit == ' ' ? "" : " ");
1192 slsmg_write_nstring(warn, browser->width - printed);
1195 menu->selection = evsel;
1198 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
1199 int nr_events, const char *help,
1200 void(*timer)(void *arg), void *arg, int delay_secs)
1202 struct perf_evlist *evlist = menu->b.priv;
1203 struct perf_evsel *pos;
1204 const char *ev_name, *title = "Available samples";
1207 if (ui_browser__show(&menu->b, title,
1208 "ESC: exit, ENTER|->: Browse histograms") < 0)
1212 key = ui_browser__run(&menu->b, delay_secs);
1218 if (!menu->lost_events_warned && menu->lost_events) {
1219 ui_browser__warn_lost_events(&menu->b);
1220 menu->lost_events_warned = true;
1225 if (!menu->selection)
1227 pos = menu->selection;
1229 perf_evlist__set_selected(evlist, pos);
1231 * Give the calling tool a chance to populate the non
1232 * default evsel resorted hists tree.
1236 ev_name = event_name(pos);
1237 key = perf_evsel__hists_browse(pos, nr_events, help,
1238 ev_name, true, timer,
1240 ui_browser__show_title(&menu->b, title);
1243 if (pos->node.next == &evlist->entries)
1244 pos = list_entry(evlist->entries.next, struct perf_evsel, node);
1246 pos = list_entry(pos->node.next, struct perf_evsel, node);
1249 if (pos->node.prev == &evlist->entries)
1250 pos = list_entry(evlist->entries.prev, struct perf_evsel, node);
1252 pos = list_entry(pos->node.prev, struct perf_evsel, node);
1255 if (!ui_browser__dialog_yesno(&menu->b,
1256 "Do you really want to exit?"))
1268 if (!ui_browser__dialog_yesno(&menu->b,
1269 "Do you really want to exit?"))
1281 ui_browser__hide(&menu->b);
1285 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
1287 void(*timer)(void *arg), void *arg,
1290 struct perf_evsel *pos;
1291 struct perf_evsel_menu menu = {
1293 .entries = &evlist->entries,
1294 .refresh = ui_browser__list_head_refresh,
1295 .seek = ui_browser__list_head_seek,
1296 .write = perf_evsel_menu__write,
1297 .nr_entries = evlist->nr_entries,
1302 ui_helpline__push("Press ESC to exit");
1304 list_for_each_entry(pos, &evlist->entries, node) {
1305 const char *ev_name = event_name(pos);
1306 size_t line_len = strlen(ev_name) + 7;
1308 if (menu.b.width < line_len)
1309 menu.b.width = line_len;
1311 * Cache the evsel name, tracepoints have a _high_ cost per
1312 * event_name() call.
1314 if (pos->name == NULL)
1315 pos->name = strdup(ev_name);
1318 return perf_evsel_menu__run(&menu, evlist->nr_entries, help, timer,
1322 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
1323 void(*timer)(void *arg), void *arg,
1327 if (evlist->nr_entries == 1) {
1328 struct perf_evsel *first = list_entry(evlist->entries.next,
1329 struct perf_evsel, node);
1330 const char *ev_name = event_name(first);
1331 return perf_evsel__hists_browse(first, evlist->nr_entries, help,
1332 ev_name, false, timer, arg,
1336 return __perf_evlist__tui_browse_hists(evlist, help,
1337 timer, arg, delay_secs);