2 #include "../libslang.h"
5 #include <linux/rbtree.h>
7 #include "../../util/evsel.h"
8 #include "../../util/evlist.h"
9 #include "../../util/hist.h"
10 #include "../../util/pstack.h"
11 #include "../../util/sort.h"
12 #include "../../util/util.h"
13 #include "../../arch/common.h"
15 #include "../browser.h"
16 #include "../helpline.h"
25 struct hist_entry *he_selection;
26 struct map_symbol *selection;
31 u64 nr_non_filtered_entries;
32 u64 nr_callchain_rows;
35 extern void hist_browser__init_hpp(void);
37 static int hists__browser_title(struct hists *hists, char *bf, size_t size);
38 static void hist_browser__update_nr_entries(struct hist_browser *hb);
40 static struct rb_node *hists__filter_entries(struct rb_node *nd,
43 static bool hist_browser__has_filter(struct hist_browser *hb)
45 return hists__has_filter(hb->hists) || hb->min_pcnt;
48 static u32 hist_browser__nr_entries(struct hist_browser *hb)
52 if (hist_browser__has_filter(hb))
53 nr_entries = hb->nr_non_filtered_entries;
55 nr_entries = hb->hists->nr_entries;
57 return nr_entries + hb->nr_callchain_rows;
60 static void hist_browser__update_rows(struct hist_browser *hb)
62 struct ui_browser *browser = &hb->b;
63 u16 header_offset = hb->show_headers ? 1 : 0, index_row;
65 browser->rows = browser->height - header_offset;
67 * Verify if we were at the last line and that line isn't
68 * visibe because we now show the header line(s).
70 index_row = browser->index - browser->top_idx;
71 if (index_row >= browser->rows)
72 browser->index -= index_row - browser->rows + 1;
75 static void hist_browser__refresh_dimensions(struct ui_browser *browser)
77 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
79 /* 3 == +/- toggle symbol before actual hist_entry rendering */
80 browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]"));
82 * FIXME: Just keeping existing behaviour, but this really should be
83 * before updating browser->width, as it will invalidate the
84 * calculation above. Fix this and the fallout in another
87 ui_browser__refresh_dimensions(browser);
88 hist_browser__update_rows(hb);
91 static void hist_browser__gotorc(struct hist_browser *browser, int row, int column)
93 u16 header_offset = browser->show_headers ? 1 : 0;
95 ui_browser__gotorc(&browser->b, row + header_offset, column);
98 static void hist_browser__reset(struct hist_browser *browser)
101 * The hists__remove_entry_filter() already folds non-filtered
102 * entries so we can assume it has 0 callchain rows.
104 browser->nr_callchain_rows = 0;
106 hist_browser__update_nr_entries(browser);
107 browser->b.nr_entries = hist_browser__nr_entries(browser);
108 hist_browser__refresh_dimensions(&browser->b);
109 ui_browser__reset_index(&browser->b);
112 static char tree__folded_sign(bool unfolded)
114 return unfolded ? '-' : '+';
117 static char map_symbol__folded(const struct map_symbol *ms)
119 return ms->has_children ? tree__folded_sign(ms->unfolded) : ' ';
122 static char hist_entry__folded(const struct hist_entry *he)
124 return map_symbol__folded(&he->ms);
127 static char callchain_list__folded(const struct callchain_list *cl)
129 return map_symbol__folded(&cl->ms);
132 static void map_symbol__set_folding(struct map_symbol *ms, bool unfold)
134 ms->unfolded = unfold ? ms->has_children : false;
137 static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
142 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
143 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
144 struct callchain_list *chain;
145 char folded_sign = ' '; /* No children */
147 list_for_each_entry(chain, &child->val, list) {
149 /* We need this because we may not have children */
150 folded_sign = callchain_list__folded(chain);
151 if (folded_sign == '+')
155 if (folded_sign == '-') /* Have children and they're unfolded */
156 n += callchain_node__count_rows_rb_tree(child);
162 static int callchain_node__count_rows(struct callchain_node *node)
164 struct callchain_list *chain;
165 bool unfolded = false;
168 list_for_each_entry(chain, &node->val, list) {
170 unfolded = chain->ms.unfolded;
174 n += callchain_node__count_rows_rb_tree(node);
179 static int callchain__count_rows(struct rb_root *chain)
184 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
185 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
186 n += callchain_node__count_rows(node);
192 static bool map_symbol__toggle_fold(struct map_symbol *ms)
197 if (!ms->has_children)
200 ms->unfolded = !ms->unfolded;
204 static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
206 struct rb_node *nd = rb_first(&node->rb_root);
208 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
209 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
210 struct callchain_list *chain;
213 list_for_each_entry(chain, &child->val, list) {
216 chain->ms.has_children = chain->list.next != &child->val ||
217 !RB_EMPTY_ROOT(&child->rb_root);
219 chain->ms.has_children = chain->list.next == &child->val &&
220 !RB_EMPTY_ROOT(&child->rb_root);
223 callchain_node__init_have_children_rb_tree(child);
227 static void callchain_node__init_have_children(struct callchain_node *node)
229 struct callchain_list *chain;
231 list_for_each_entry(chain, &node->val, list)
232 chain->ms.has_children = !RB_EMPTY_ROOT(&node->rb_root);
234 callchain_node__init_have_children_rb_tree(node);
237 static void callchain__init_have_children(struct rb_root *root)
241 for (nd = rb_first(root); nd; nd = rb_next(nd)) {
242 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
243 callchain_node__init_have_children(node);
247 static void hist_entry__init_have_children(struct hist_entry *he)
249 if (!he->init_have_children) {
250 he->ms.has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
251 callchain__init_have_children(&he->sorted_chain);
252 he->init_have_children = true;
256 static bool hist_browser__toggle_fold(struct hist_browser *browser)
258 if (map_symbol__toggle_fold(browser->selection)) {
259 struct hist_entry *he = browser->he_selection;
261 hist_entry__init_have_children(he);
262 browser->b.nr_entries -= he->nr_rows;
263 browser->nr_callchain_rows -= he->nr_rows;
266 he->nr_rows = callchain__count_rows(&he->sorted_chain);
270 browser->b.nr_entries += he->nr_rows;
271 browser->nr_callchain_rows += he->nr_rows;
276 /* If it doesn't have children, no toggling performed */
280 static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
285 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
286 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
287 struct callchain_list *chain;
288 bool has_children = false;
290 list_for_each_entry(chain, &child->val, list) {
292 map_symbol__set_folding(&chain->ms, unfold);
293 has_children = chain->ms.has_children;
297 n += callchain_node__set_folding_rb_tree(child, unfold);
303 static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
305 struct callchain_list *chain;
306 bool has_children = false;
309 list_for_each_entry(chain, &node->val, list) {
311 map_symbol__set_folding(&chain->ms, unfold);
312 has_children = chain->ms.has_children;
316 n += callchain_node__set_folding_rb_tree(node, unfold);
321 static int callchain__set_folding(struct rb_root *chain, bool unfold)
326 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
327 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
328 n += callchain_node__set_folding(node, unfold);
334 static void hist_entry__set_folding(struct hist_entry *he, bool unfold)
336 hist_entry__init_have_children(he);
337 map_symbol__set_folding(&he->ms, unfold);
339 if (he->ms.has_children) {
340 int n = callchain__set_folding(&he->sorted_chain, unfold);
341 he->nr_rows = unfold ? n : 0;
347 __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
350 struct hists *hists = browser->hists;
352 for (nd = rb_first(&hists->entries);
353 (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
355 struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
356 hist_entry__set_folding(he, unfold);
357 browser->nr_callchain_rows += he->nr_rows;
361 static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
363 browser->nr_callchain_rows = 0;
364 __hist_browser__set_folding(browser, unfold);
366 browser->b.nr_entries = hist_browser__nr_entries(browser);
367 /* Go to the start, we may be way after valid entries after a collapse */
368 ui_browser__reset_index(&browser->b);
371 static void ui_browser__warn_lost_events(struct ui_browser *browser)
373 ui_browser__warning(browser, 4,
374 "Events are being lost, check IO/CPU overload!\n\n"
375 "You may want to run 'perf' using a RT scheduler policy:\n\n"
376 " perf top -r 80\n\n"
377 "Or reduce the sampling frequency.");
380 static int hist_browser__run(struct hist_browser *browser,
381 struct hist_browser_timer *hbt)
385 int delay_secs = hbt ? hbt->refresh : 0;
387 browser->b.entries = &browser->hists->entries;
388 browser->b.nr_entries = hist_browser__nr_entries(browser);
390 hists__browser_title(browser->hists, title, sizeof(title));
392 if (ui_browser__show(&browser->b, title,
393 "Press '?' for help on key bindings") < 0)
397 key = ui_browser__run(&browser->b, delay_secs);
402 hbt->timer(hbt->arg);
404 if (hist_browser__has_filter(browser))
405 hist_browser__update_nr_entries(browser);
407 nr_entries = hist_browser__nr_entries(browser);
408 ui_browser__update_nr_entries(&browser->b, nr_entries);
410 if (browser->hists->stats.nr_lost_warned !=
411 browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
412 browser->hists->stats.nr_lost_warned =
413 browser->hists->stats.nr_events[PERF_RECORD_LOST];
414 ui_browser__warn_lost_events(&browser->b);
417 hists__browser_title(browser->hists, title, sizeof(title));
418 ui_browser__show_title(&browser->b, title);
421 case 'D': { /* Debug */
423 struct hist_entry *h = rb_entry(browser->b.top,
424 struct hist_entry, rb_node);
426 ui_helpline__fpush("%d: nr_ent=(%d,%d), rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
427 seq++, browser->b.nr_entries,
428 browser->hists->nr_entries,
432 h->row_offset, h->nr_rows);
436 /* Collapse the whole world. */
437 hist_browser__set_folding(browser, false);
440 /* Expand the whole world. */
441 hist_browser__set_folding(browser, true);
444 browser->show_headers = !browser->show_headers;
445 hist_browser__update_rows(browser);
448 if (hist_browser__toggle_fold(browser))
456 ui_browser__hide(&browser->b);
460 static char *callchain_list__sym_name(struct callchain_list *cl,
461 char *bf, size_t bfsize, bool show_dso)
466 printed = scnprintf(bf, bfsize, "%s", cl->ms.sym->name);
468 printed = scnprintf(bf, bfsize, "%#" PRIx64, cl->ip);
471 scnprintf(bf + printed, bfsize - printed, " %s",
472 cl->ms.map ? cl->ms.map->dso->short_name : "unknown");
477 #define LEVEL_OFFSET_STEP 3
479 static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browser,
480 struct callchain_node *chain_node,
481 u64 total, int level,
484 bool *is_current_entry)
486 struct rb_node *node;
487 int first_row = row, width, offset = level * LEVEL_OFFSET_STEP;
488 u64 new_total, remaining;
490 if (callchain_param.mode == CHAIN_GRAPH_REL)
491 new_total = chain_node->children_hit;
495 remaining = new_total;
496 node = rb_first(&chain_node->rb_root);
498 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
499 struct rb_node *next = rb_next(node);
500 u64 cumul = callchain_cumul_hits(child);
501 struct callchain_list *chain;
502 char folded_sign = ' ';
504 int extra_offset = 0;
508 list_for_each_entry(chain, &child->val, list) {
509 char bf[1024], *alloc_str;
512 bool was_first = first;
517 extra_offset = LEVEL_OFFSET_STEP;
519 folded_sign = callchain_list__folded(chain);
520 if (*row_offset != 0) {
526 str = callchain_list__sym_name(chain, bf, sizeof(bf),
529 double percent = cumul * 100.0 / new_total;
531 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
532 str = "Not enough memory!";
537 color = HE_COLORSET_NORMAL;
538 width = browser->b.width - (offset + extra_offset + 2);
539 if (ui_browser__is_current_entry(&browser->b, row)) {
540 browser->selection = &chain->ms;
541 color = HE_COLORSET_SELECTED;
542 *is_current_entry = true;
545 ui_browser__set_color(&browser->b, color);
546 hist_browser__gotorc(browser, row, 0);
547 slsmg_write_nstring(" ", offset + extra_offset);
548 slsmg_printf("%c ", folded_sign);
549 slsmg_write_nstring(str, width);
552 if (++row == browser->b.rows)
555 if (folded_sign == '+')
559 if (folded_sign == '-') {
560 const int new_level = level + (extra_offset ? 2 : 1);
561 row += hist_browser__show_callchain_node_rb_tree(browser, child, new_total,
562 new_level, row, row_offset,
565 if (row == browser->b.rows)
570 return row - first_row;
573 static int hist_browser__show_callchain_node(struct hist_browser *browser,
574 struct callchain_node *node,
575 int level, unsigned short row,
577 bool *is_current_entry)
579 struct callchain_list *chain;
581 offset = level * LEVEL_OFFSET_STEP,
582 width = browser->b.width - offset;
583 char folded_sign = ' ';
585 list_for_each_entry(chain, &node->val, list) {
589 folded_sign = callchain_list__folded(chain);
591 if (*row_offset != 0) {
596 color = HE_COLORSET_NORMAL;
597 if (ui_browser__is_current_entry(&browser->b, row)) {
598 browser->selection = &chain->ms;
599 color = HE_COLORSET_SELECTED;
600 *is_current_entry = true;
603 s = callchain_list__sym_name(chain, bf, sizeof(bf),
605 hist_browser__gotorc(browser, row, 0);
606 ui_browser__set_color(&browser->b, color);
607 slsmg_write_nstring(" ", offset);
608 slsmg_printf("%c ", folded_sign);
609 slsmg_write_nstring(s, width - 2);
611 if (++row == browser->b.rows)
615 if (folded_sign == '-')
616 row += hist_browser__show_callchain_node_rb_tree(browser, node,
617 browser->hists->stats.total_period,
622 return row - first_row;
625 static int hist_browser__show_callchain(struct hist_browser *browser,
626 struct rb_root *chain,
627 int level, unsigned short row,
629 bool *is_current_entry)
634 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
635 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
637 row += hist_browser__show_callchain_node(browser, node, level,
640 if (row == browser->b.rows)
644 return row - first_row;
648 struct ui_browser *b;
653 static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
655 struct hpp_arg *arg = hpp->ptr;
661 percent = va_arg(args, double);
664 ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
666 ret = scnprintf(hpp->buf, hpp->size, fmt, percent);
667 slsmg_printf("%s", hpp->buf);
669 advance_hpp(hpp, ret);
673 #define __HPP_COLOR_PERCENT_FN(_type, _field) \
674 static u64 __hpp_get_##_field(struct hist_entry *he) \
676 return he->stat._field; \
680 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,\
681 struct perf_hpp *hpp, \
682 struct hist_entry *he) \
684 return __hpp__fmt(hpp, he, __hpp_get_##_field, " %6.2f%%", \
685 __hpp__slsmg_color_printf, true); \
688 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
689 static u64 __hpp_get_acc_##_field(struct hist_entry *he) \
691 return he->stat_acc->_field; \
695 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,\
696 struct perf_hpp *hpp, \
697 struct hist_entry *he) \
699 if (!symbol_conf.cumulate_callchain) { \
700 int ret = scnprintf(hpp->buf, hpp->size, "%8s", "N/A"); \
701 slsmg_printf("%s", hpp->buf); \
705 return __hpp__fmt(hpp, he, __hpp_get_acc_##_field, " %6.2f%%", \
706 __hpp__slsmg_color_printf, true); \
709 __HPP_COLOR_PERCENT_FN(overhead, period)
710 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
711 __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
712 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
713 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
714 __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
716 #undef __HPP_COLOR_PERCENT_FN
717 #undef __HPP_COLOR_ACC_PERCENT_FN
719 void hist_browser__init_hpp(void)
721 perf_hpp__format[PERF_HPP__OVERHEAD].color =
722 hist_browser__hpp_color_overhead;
723 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
724 hist_browser__hpp_color_overhead_sys;
725 perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
726 hist_browser__hpp_color_overhead_us;
727 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
728 hist_browser__hpp_color_overhead_guest_sys;
729 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
730 hist_browser__hpp_color_overhead_guest_us;
731 perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
732 hist_browser__hpp_color_overhead_acc;
735 static int hist_browser__show_entry(struct hist_browser *browser,
736 struct hist_entry *entry,
741 int width = browser->b.width;
742 char folded_sign = ' ';
743 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
744 off_t row_offset = entry->row_offset;
746 struct perf_hpp_fmt *fmt;
749 browser->he_selection = entry;
750 browser->selection = &entry->ms;
753 if (symbol_conf.use_callchain) {
754 hist_entry__init_have_children(entry);
755 folded_sign = hist_entry__folded(entry);
758 if (row_offset == 0) {
759 struct hpp_arg arg = {
761 .folded_sign = folded_sign,
762 .current_entry = current_entry,
764 struct perf_hpp hpp = {
770 hist_browser__gotorc(browser, row, 0);
772 perf_hpp__for_each_format(fmt) {
773 if (perf_hpp__should_skip(fmt))
776 if (current_entry && browser->b.navkeypressed) {
777 ui_browser__set_color(&browser->b,
778 HE_COLORSET_SELECTED);
780 ui_browser__set_color(&browser->b,
785 if (symbol_conf.use_callchain) {
786 slsmg_printf("%c ", folded_sign);
796 width -= fmt->color(fmt, &hpp, entry);
798 width -= fmt->entry(fmt, &hpp, entry);
799 slsmg_printf("%s", s);
803 /* The scroll bar isn't being used */
804 if (!browser->b.navkeypressed)
807 slsmg_write_nstring("", width);
814 if (folded_sign == '-' && row != browser->b.rows) {
815 printed += hist_browser__show_callchain(browser, &entry->sorted_chain,
819 browser->he_selection = entry;
825 static int advance_hpp_check(struct perf_hpp *hpp, int inc)
827 advance_hpp(hpp, inc);
828 return hpp->size <= 0;
831 static int hists__scnprintf_headers(char *buf, size_t size, struct hists *hists)
833 struct perf_hpp dummy_hpp = {
837 struct perf_hpp_fmt *fmt;
840 if (symbol_conf.use_callchain) {
841 ret = scnprintf(buf, size, " ");
842 if (advance_hpp_check(&dummy_hpp, ret))
846 perf_hpp__for_each_format(fmt) {
847 if (perf_hpp__should_skip(fmt))
850 /* We need to add the length of the columns header. */
851 perf_hpp__reset_width(fmt, hists);
853 ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists));
854 if (advance_hpp_check(&dummy_hpp, ret))
857 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
858 if (advance_hpp_check(&dummy_hpp, ret))
865 static void hist_browser__show_headers(struct hist_browser *browser)
869 hists__scnprintf_headers(headers, sizeof(headers), browser->hists);
870 ui_browser__gotorc(&browser->b, 0, 0);
871 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
872 slsmg_write_nstring(headers, browser->b.width + 1);
875 static void ui_browser__hists_init_top(struct ui_browser *browser)
877 if (browser->top == NULL) {
878 struct hist_browser *hb;
880 hb = container_of(browser, struct hist_browser, b);
881 browser->top = rb_first(&hb->hists->entries);
885 static unsigned int hist_browser__refresh(struct ui_browser *browser)
888 u16 header_offset = 0;
890 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
892 if (hb->show_headers) {
893 hist_browser__show_headers(hb);
897 ui_browser__hists_init_top(browser);
899 for (nd = browser->top; nd; nd = rb_next(nd)) {
900 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
906 percent = hist_entry__get_percent_limit(h);
907 if (percent < hb->min_pcnt)
910 row += hist_browser__show_entry(hb, h, row);
911 if (row == browser->rows)
915 return row + header_offset;
918 static struct rb_node *hists__filter_entries(struct rb_node *nd,
922 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
923 float percent = hist_entry__get_percent_limit(h);
925 if (!h->filtered && percent >= min_pcnt)
934 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
938 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
939 float percent = hist_entry__get_percent_limit(h);
941 if (!h->filtered && percent >= min_pcnt)
950 static void ui_browser__hists_seek(struct ui_browser *browser,
951 off_t offset, int whence)
953 struct hist_entry *h;
956 struct hist_browser *hb;
958 hb = container_of(browser, struct hist_browser, b);
960 if (browser->nr_entries == 0)
963 ui_browser__hists_init_top(browser);
967 nd = hists__filter_entries(rb_first(browser->entries),
974 nd = hists__filter_prev_entries(rb_last(browser->entries),
983 * Moves not relative to the first visible entry invalidates its
986 h = rb_entry(browser->top, struct hist_entry, rb_node);
990 * Here we have to check if nd is expanded (+), if it is we can't go
991 * the next top level hist_entry, instead we must compute an offset of
992 * what _not_ to show and not change the first visible entry.
994 * This offset increments when we are going from top to bottom and
995 * decreases when we're going from bottom to top.
997 * As we don't have backpointers to the top level in the callchains
998 * structure, we need to always print the whole hist_entry callchain,
999 * skipping the first ones that are before the first visible entry
1000 * and stop when we printed enough lines to fill the screen.
1005 h = rb_entry(nd, struct hist_entry, rb_node);
1006 if (h->ms.unfolded) {
1007 u16 remaining = h->nr_rows - h->row_offset;
1008 if (offset > remaining) {
1009 offset -= remaining;
1012 h->row_offset += offset;
1018 nd = hists__filter_entries(rb_next(nd), hb->min_pcnt);
1023 } while (offset != 0);
1024 } else if (offset < 0) {
1026 h = rb_entry(nd, struct hist_entry, rb_node);
1027 if (h->ms.unfolded) {
1029 if (-offset > h->row_offset) {
1030 offset += h->row_offset;
1033 h->row_offset += offset;
1039 if (-offset > h->nr_rows) {
1040 offset += h->nr_rows;
1043 h->row_offset = h->nr_rows + offset;
1051 nd = hists__filter_prev_entries(rb_prev(nd),
1059 * Last unfiltered hist_entry, check if it is
1060 * unfolded, if it is then we should have
1061 * row_offset at its last entry.
1063 h = rb_entry(nd, struct hist_entry, rb_node);
1065 h->row_offset = h->nr_rows;
1072 h = rb_entry(nd, struct hist_entry, rb_node);
1077 static int hist_browser__fprintf_callchain_node_rb_tree(struct hist_browser *browser,
1078 struct callchain_node *chain_node,
1079 u64 total, int level,
1082 struct rb_node *node;
1083 int offset = level * LEVEL_OFFSET_STEP;
1084 u64 new_total, remaining;
1087 if (callchain_param.mode == CHAIN_GRAPH_REL)
1088 new_total = chain_node->children_hit;
1092 remaining = new_total;
1093 node = rb_first(&chain_node->rb_root);
1095 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1096 struct rb_node *next = rb_next(node);
1097 u64 cumul = callchain_cumul_hits(child);
1098 struct callchain_list *chain;
1099 char folded_sign = ' ';
1101 int extra_offset = 0;
1105 list_for_each_entry(chain, &child->val, list) {
1106 char bf[1024], *alloc_str;
1108 bool was_first = first;
1113 extra_offset = LEVEL_OFFSET_STEP;
1115 folded_sign = callchain_list__folded(chain);
1118 str = callchain_list__sym_name(chain, bf, sizeof(bf),
1121 double percent = cumul * 100.0 / new_total;
1123 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
1124 str = "Not enough memory!";
1129 printed += fprintf(fp, "%*s%c %s\n", offset + extra_offset, " ", folded_sign, str);
1131 if (folded_sign == '+')
1135 if (folded_sign == '-') {
1136 const int new_level = level + (extra_offset ? 2 : 1);
1137 printed += hist_browser__fprintf_callchain_node_rb_tree(browser, child, new_total,
1147 static int hist_browser__fprintf_callchain_node(struct hist_browser *browser,
1148 struct callchain_node *node,
1149 int level, FILE *fp)
1151 struct callchain_list *chain;
1152 int offset = level * LEVEL_OFFSET_STEP;
1153 char folded_sign = ' ';
1156 list_for_each_entry(chain, &node->val, list) {
1159 folded_sign = callchain_list__folded(chain);
1160 s = callchain_list__sym_name(chain, bf, sizeof(bf), browser->show_dso);
1161 printed += fprintf(fp, "%*s%c %s\n", offset, " ", folded_sign, s);
1164 if (folded_sign == '-')
1165 printed += hist_browser__fprintf_callchain_node_rb_tree(browser, node,
1166 browser->hists->stats.total_period,
1171 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1172 struct rb_root *chain, int level, FILE *fp)
1177 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
1178 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
1180 printed += hist_browser__fprintf_callchain_node(browser, node, level, fp);
1186 static int hist_browser__fprintf_entry(struct hist_browser *browser,
1187 struct hist_entry *he, FILE *fp)
1191 char folded_sign = ' ';
1192 struct perf_hpp hpp = {
1196 struct perf_hpp_fmt *fmt;
1200 if (symbol_conf.use_callchain)
1201 folded_sign = hist_entry__folded(he);
1203 if (symbol_conf.use_callchain)
1204 printed += fprintf(fp, "%c ", folded_sign);
1206 perf_hpp__for_each_format(fmt) {
1207 if (perf_hpp__should_skip(fmt))
1211 ret = scnprintf(hpp.buf, hpp.size, " ");
1212 advance_hpp(&hpp, ret);
1216 ret = fmt->entry(fmt, &hpp, he);
1217 advance_hpp(&hpp, ret);
1219 printed += fprintf(fp, "%s\n", rtrim(s));
1221 if (folded_sign == '-')
1222 printed += hist_browser__fprintf_callchain(browser, &he->sorted_chain, 1, fp);
1227 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1229 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
1234 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1236 printed += hist_browser__fprintf_entry(browser, h, fp);
1237 nd = hists__filter_entries(rb_next(nd), browser->min_pcnt);
1243 static int hist_browser__dump(struct hist_browser *browser)
1249 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
1250 if (access(filename, F_OK))
1253 * XXX: Just an arbitrary lazy upper limit
1255 if (++browser->print_seq == 8192) {
1256 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1261 fp = fopen(filename, "w");
1264 const char *err = strerror_r(errno, bf, sizeof(bf));
1265 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
1269 ++browser->print_seq;
1270 hist_browser__fprintf(browser, fp);
1272 ui_helpline__fpush("%s written!", filename);
1277 static struct hist_browser *hist_browser__new(struct hists *hists)
1279 struct hist_browser *browser = zalloc(sizeof(*browser));
1282 browser->hists = hists;
1283 browser->b.refresh = hist_browser__refresh;
1284 browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
1285 browser->b.seek = ui_browser__hists_seek;
1286 browser->b.use_navkeypressed = true;
1287 browser->show_headers = symbol_conf.show_hist_headers;
1293 static void hist_browser__delete(struct hist_browser *browser)
1298 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
1300 return browser->he_selection;
1303 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
1305 return browser->he_selection->thread;
1308 static int hists__browser_title(struct hists *hists, char *bf, size_t size)
1312 const struct dso *dso = hists->dso_filter;
1313 const struct thread *thread = hists->thread_filter;
1314 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1315 u64 nr_events = hists->stats.total_period;
1316 struct perf_evsel *evsel = hists_to_evsel(hists);
1317 const char *ev_name = perf_evsel__name(evsel);
1319 size_t buflen = sizeof(buf);
1321 if (symbol_conf.filter_relative) {
1322 nr_samples = hists->stats.nr_non_filtered_samples;
1323 nr_events = hists->stats.total_non_filtered_period;
1326 if (perf_evsel__is_group_event(evsel)) {
1327 struct perf_evsel *pos;
1329 perf_evsel__group_desc(evsel, buf, buflen);
1332 for_each_group_member(pos, evsel) {
1333 if (symbol_conf.filter_relative) {
1334 nr_samples += pos->hists.stats.nr_non_filtered_samples;
1335 nr_events += pos->hists.stats.total_non_filtered_period;
1337 nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1338 nr_events += pos->hists.stats.total_period;
1343 nr_samples = convert_unit(nr_samples, &unit);
1344 printed = scnprintf(bf, size,
1345 "Samples: %lu%c of event '%s', Event count (approx.): %lu",
1346 nr_samples, unit, ev_name, nr_events);
1349 if (hists->uid_filter_str)
1350 printed += snprintf(bf + printed, size - printed,
1351 ", UID: %s", hists->uid_filter_str);
1353 printed += scnprintf(bf + printed, size - printed,
1355 (thread->comm_set ? thread__comm_str(thread) : ""),
1358 printed += scnprintf(bf + printed, size - printed,
1359 ", DSO: %s", dso->short_name);
1363 static inline void free_popup_options(char **options, int n)
1367 for (i = 0; i < n; ++i)
1371 /* Check whether the browser is for 'top' or 'report' */
1372 static inline bool is_report_browser(void *timer)
1374 return timer == NULL;
1378 * Only runtime switching of perf data file will make "input_name" point
1379 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1380 * whether we need to call free() for current "input_name" during the switch.
1382 static bool is_input_name_malloced = false;
1384 static int switch_data_file(void)
1386 char *pwd, *options[32], *abs_path[32], *tmp;
1388 int nr_options = 0, choice = -1, ret = -1;
1389 struct dirent *dent;
1391 pwd = getenv("PWD");
1395 pwd_dir = opendir(pwd);
1399 memset(options, 0, sizeof(options));
1400 memset(options, 0, sizeof(abs_path));
1402 while ((dent = readdir(pwd_dir))) {
1403 char path[PATH_MAX];
1405 char *name = dent->d_name;
1408 if (!(dent->d_type == DT_REG))
1411 snprintf(path, sizeof(path), "%s/%s", pwd, name);
1413 file = fopen(path, "r");
1417 if (fread(&magic, 1, 8, file) < 8)
1418 goto close_file_and_continue;
1420 if (is_perf_magic(magic)) {
1421 options[nr_options] = strdup(name);
1422 if (!options[nr_options])
1423 goto close_file_and_continue;
1425 abs_path[nr_options] = strdup(path);
1426 if (!abs_path[nr_options]) {
1427 zfree(&options[nr_options]);
1428 ui__warning("Can't search all data files due to memory shortage.\n");
1436 close_file_and_continue:
1438 if (nr_options >= 32) {
1439 ui__warning("Too many perf data files in PWD!\n"
1440 "Only the first 32 files will be listed.\n");
1447 choice = ui__popup_menu(nr_options, options);
1448 if (choice < nr_options && choice >= 0) {
1449 tmp = strdup(abs_path[choice]);
1451 if (is_input_name_malloced)
1452 free((void *)input_name);
1454 is_input_name_malloced = true;
1457 ui__warning("Data switch failed due to memory shortage!\n");
1461 free_popup_options(options, nr_options);
1462 free_popup_options(abs_path, nr_options);
1466 static void hist_browser__update_nr_entries(struct hist_browser *hb)
1469 struct rb_node *nd = rb_first(&hb->hists->entries);
1471 if (hb->min_pcnt == 0) {
1472 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
1476 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
1481 hb->nr_non_filtered_entries = nr_entries;
1484 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
1485 const char *helpline,
1487 struct hist_browser_timer *hbt,
1489 struct perf_session_env *env)
1491 struct hists *hists = &evsel->hists;
1492 struct hist_browser *browser = hist_browser__new(hists);
1493 struct branch_info *bi;
1494 struct pstack *fstack;
1499 char script_opt[64];
1500 int delay_secs = hbt ? hbt->refresh : 0;
1502 #define HIST_BROWSER_HELP_COMMON \
1503 "h/?/F1 Show this window\n" \
1505 "PGDN/SPACE Navigate\n" \
1506 "q/ESC/CTRL+C Exit browser\n\n" \
1507 "For multiple event sessions:\n\n" \
1508 "TAB/UNTAB Switch events\n\n" \
1509 "For symbolic views (--sort has sym):\n\n" \
1510 "-> Zoom into DSO/Threads & Annotate current symbol\n" \
1512 "a Annotate current symbol\n" \
1513 "C Collapse all callchains\n" \
1514 "d Zoom into current DSO\n" \
1515 "E Expand all callchains\n" \
1516 "F Toggle percentage of filtered entries\n" \
1517 "H Display column headers\n" \
1519 /* help messages are sorted by lexical order of the hotkey */
1520 const char report_help[] = HIST_BROWSER_HELP_COMMON
1521 "i Show header information\n"
1522 "P Print histograms to perf.hist.N\n"
1523 "r Run available scripts\n"
1524 "s Switch to another data file in PWD\n"
1525 "t Zoom into current Thread\n"
1526 "V Verbose (DSO names in callchains, etc)\n"
1527 "/ Filter symbol by name";
1528 const char top_help[] = HIST_BROWSER_HELP_COMMON
1529 "P Print histograms to perf.hist.N\n"
1530 "t Zoom into current Thread\n"
1531 "V Verbose (DSO names in callchains, etc)\n"
1532 "/ Filter symbol by name";
1534 if (browser == NULL)
1538 browser->min_pcnt = min_pcnt;
1539 hist_browser__update_nr_entries(browser);
1542 fstack = pstack__new(2);
1546 ui_helpline__push(helpline);
1548 memset(options, 0, sizeof(options));
1551 const struct thread *thread = NULL;
1552 const struct dso *dso = NULL;
1554 annotate = -2, zoom_dso = -2, zoom_thread = -2,
1555 annotate_f = -2, annotate_t = -2, browse_map = -2;
1556 int scripts_comm = -2, scripts_symbol = -2,
1557 scripts_all = -2, switch_data = -2;
1561 key = hist_browser__run(browser, hbt);
1563 if (browser->he_selection != NULL) {
1564 thread = hist_browser__selected_thread(browser);
1565 dso = browser->selection->map ? browser->selection->map->dso : NULL;
1573 * Exit the browser, let hists__browser_tree
1574 * go to the next or previous
1576 goto out_free_stack;
1578 if (!sort__has_sym) {
1579 ui_browser__warning(&browser->b, delay_secs * 2,
1580 "Annotation is only available for symbolic views, "
1581 "include \"sym*\" in --sort to use it.");
1585 if (browser->selection == NULL ||
1586 browser->selection->sym == NULL ||
1587 browser->selection->map->dso->annotate_warned)
1591 hist_browser__dump(browser);
1596 browser->show_dso = !browser->show_dso;
1601 if (ui_browser__input_window("Symbol to show",
1602 "Please enter the name of symbol you want to see",
1603 buf, "ENTER: OK, ESC: Cancel",
1604 delay_secs * 2) == K_ENTER) {
1605 hists->symbol_filter_str = *buf ? buf : NULL;
1606 hists__filter_by_symbol(hists);
1607 hist_browser__reset(browser);
1611 if (is_report_browser(hbt))
1615 if (is_report_browser(hbt))
1616 goto do_data_switch;
1619 /* env->arch is NULL for live-mode (i.e. perf top) */
1621 tui__header_window(env);
1624 symbol_conf.filter_relative ^= 1;
1629 ui_browser__help_window(&browser->b,
1630 is_report_browser(hbt) ? report_help : top_help);
1639 if (pstack__empty(fstack)) {
1641 * Go back to the perf_evsel_menu__run or other user
1644 goto out_free_stack;
1647 top = pstack__pop(fstack);
1648 if (top == &browser->hists->dso_filter)
1650 if (top == &browser->hists->thread_filter)
1651 goto zoom_out_thread;
1656 !ui_browser__dialog_yesno(&browser->b,
1657 "Do you really want to exit?"))
1662 goto out_free_stack;
1668 goto add_exit_option;
1670 if (sort__mode == SORT_MODE__BRANCH) {
1671 bi = browser->he_selection->branch_info;
1672 if (browser->selection != NULL &&
1674 bi->from.sym != NULL &&
1675 !bi->from.map->dso->annotate_warned &&
1676 asprintf(&options[nr_options], "Annotate %s",
1677 bi->from.sym->name) > 0)
1678 annotate_f = nr_options++;
1680 if (browser->selection != NULL &&
1682 bi->to.sym != NULL &&
1683 !bi->to.map->dso->annotate_warned &&
1684 (bi->to.sym != bi->from.sym ||
1685 bi->to.map->dso != bi->from.map->dso) &&
1686 asprintf(&options[nr_options], "Annotate %s",
1687 bi->to.sym->name) > 0)
1688 annotate_t = nr_options++;
1690 if (browser->selection != NULL &&
1691 browser->selection->sym != NULL &&
1692 !browser->selection->map->dso->annotate_warned) {
1693 struct annotation *notes;
1695 notes = symbol__annotation(browser->selection->sym);
1698 asprintf(&options[nr_options], "Annotate %s",
1699 browser->selection->sym->name) > 0)
1700 annotate = nr_options++;
1704 if (thread != NULL &&
1705 asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
1706 (browser->hists->thread_filter ? "out of" : "into"),
1707 (thread->comm_set ? thread__comm_str(thread) : ""),
1709 zoom_thread = nr_options++;
1712 asprintf(&options[nr_options], "Zoom %s %s DSO",
1713 (browser->hists->dso_filter ? "out of" : "into"),
1714 (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
1715 zoom_dso = nr_options++;
1717 if (browser->selection != NULL &&
1718 browser->selection->map != NULL &&
1719 asprintf(&options[nr_options], "Browse map details") > 0)
1720 browse_map = nr_options++;
1722 /* perf script support */
1723 if (browser->he_selection) {
1726 if (asprintf(&options[nr_options], "Run scripts for samples of thread [%s]",
1727 thread__comm_str(browser->he_selection->thread)) > 0)
1728 scripts_comm = nr_options++;
1730 sym = browser->he_selection->ms.sym;
1731 if (sym && sym->namelen &&
1732 asprintf(&options[nr_options], "Run scripts for samples of symbol [%s]",
1734 scripts_symbol = nr_options++;
1737 if (asprintf(&options[nr_options], "Run scripts for all samples") > 0)
1738 scripts_all = nr_options++;
1740 if (is_report_browser(hbt) && asprintf(&options[nr_options],
1741 "Switch to another data file in PWD") > 0)
1742 switch_data = nr_options++;
1744 options[nr_options++] = (char *)"Exit";
1746 choice = ui__popup_menu(nr_options, options);
1748 if (choice == nr_options - 1)
1752 free_popup_options(options, nr_options - 1);
1756 if (choice == annotate || choice == annotate_t || choice == annotate_f) {
1757 struct hist_entry *he;
1758 struct annotation *notes;
1761 if (!objdump_path && perf_session_env__lookup_objdump(env))
1764 he = hist_browser__selected_entry(browser);
1769 * we stash the branch_info symbol + map into the
1770 * the ms so we don't have to rewrite all the annotation
1771 * code to use branch_info.
1772 * in branch mode, the ms struct is not used
1774 if (choice == annotate_f) {
1775 he->ms.sym = he->branch_info->from.sym;
1776 he->ms.map = he->branch_info->from.map;
1777 } else if (choice == annotate_t) {
1778 he->ms.sym = he->branch_info->to.sym;
1779 he->ms.map = he->branch_info->to.map;
1782 notes = symbol__annotation(he->ms.sym);
1787 * Don't let this be freed, say, by hists__decay_entry.
1790 err = hist_entry__tui_annotate(he, evsel, hbt);
1793 * offer option to annotate the other branch source or target
1794 * (if they exists) when returning from annotate
1796 if ((err == 'q' || err == CTRL('c'))
1797 && annotate_t != -2 && annotate_f != -2)
1798 goto retry_popup_menu;
1800 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1802 ui_browser__handle_resize(&browser->b);
1804 } else if (choice == browse_map)
1805 map__browse(browser->selection->map);
1806 else if (choice == zoom_dso) {
1808 if (browser->hists->dso_filter) {
1809 pstack__remove(fstack, &browser->hists->dso_filter);
1812 browser->hists->dso_filter = NULL;
1813 perf_hpp__set_elide(HISTC_DSO, false);
1817 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1818 dso->kernel ? "the Kernel" : dso->short_name);
1819 browser->hists->dso_filter = dso;
1820 perf_hpp__set_elide(HISTC_DSO, true);
1821 pstack__push(fstack, &browser->hists->dso_filter);
1823 hists__filter_by_dso(hists);
1824 hist_browser__reset(browser);
1825 } else if (choice == zoom_thread) {
1827 if (browser->hists->thread_filter) {
1828 pstack__remove(fstack, &browser->hists->thread_filter);
1831 browser->hists->thread_filter = NULL;
1832 perf_hpp__set_elide(HISTC_THREAD, false);
1834 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1835 thread->comm_set ? thread__comm_str(thread) : "",
1837 browser->hists->thread_filter = thread;
1838 perf_hpp__set_elide(HISTC_THREAD, false);
1839 pstack__push(fstack, &browser->hists->thread_filter);
1841 hists__filter_by_thread(hists);
1842 hist_browser__reset(browser);
1844 /* perf scripts support */
1845 else if (choice == scripts_all || choice == scripts_comm ||
1846 choice == scripts_symbol) {
1848 memset(script_opt, 0, 64);
1850 if (choice == scripts_comm)
1851 sprintf(script_opt, " -c %s ", thread__comm_str(browser->he_selection->thread));
1853 if (choice == scripts_symbol)
1854 sprintf(script_opt, " -S %s ", browser->he_selection->ms.sym->name);
1856 script_browse(script_opt);
1858 /* Switch to another data file */
1859 else if (choice == switch_data) {
1861 if (!switch_data_file()) {
1862 key = K_SWITCH_INPUT_DATA;
1865 ui__warning("Won't switch the data files due to\n"
1866 "no valid data file get selected!\n");
1870 pstack__delete(fstack);
1872 hist_browser__delete(browser);
1873 free_popup_options(options, nr_options - 1);
1877 struct perf_evsel_menu {
1878 struct ui_browser b;
1879 struct perf_evsel *selection;
1880 bool lost_events, lost_events_warned;
1882 struct perf_session_env *env;
1885 static void perf_evsel_menu__write(struct ui_browser *browser,
1886 void *entry, int row)
1888 struct perf_evsel_menu *menu = container_of(browser,
1889 struct perf_evsel_menu, b);
1890 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1891 bool current_entry = ui_browser__is_current_entry(browser, row);
1892 unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1893 const char *ev_name = perf_evsel__name(evsel);
1895 const char *warn = " ";
1898 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
1899 HE_COLORSET_NORMAL);
1901 if (perf_evsel__is_group_event(evsel)) {
1902 struct perf_evsel *pos;
1904 ev_name = perf_evsel__group_name(evsel);
1906 for_each_group_member(pos, evsel) {
1907 nr_events += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1911 nr_events = convert_unit(nr_events, &unit);
1912 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
1913 unit, unit == ' ' ? "" : " ", ev_name);
1914 slsmg_printf("%s", bf);
1916 nr_events = evsel->hists.stats.nr_events[PERF_RECORD_LOST];
1917 if (nr_events != 0) {
1918 menu->lost_events = true;
1920 ui_browser__set_color(browser, HE_COLORSET_TOP);
1921 nr_events = convert_unit(nr_events, &unit);
1922 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
1923 nr_events, unit, unit == ' ' ? "" : " ");
1927 slsmg_write_nstring(warn, browser->width - printed);
1930 menu->selection = evsel;
1933 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
1934 int nr_events, const char *help,
1935 struct hist_browser_timer *hbt)
1937 struct perf_evlist *evlist = menu->b.priv;
1938 struct perf_evsel *pos;
1939 const char *title = "Available samples";
1940 int delay_secs = hbt ? hbt->refresh : 0;
1943 if (ui_browser__show(&menu->b, title,
1944 "ESC: exit, ENTER|->: Browse histograms") < 0)
1948 key = ui_browser__run(&menu->b, delay_secs);
1952 hbt->timer(hbt->arg);
1954 if (!menu->lost_events_warned && menu->lost_events) {
1955 ui_browser__warn_lost_events(&menu->b);
1956 menu->lost_events_warned = true;
1961 if (!menu->selection)
1963 pos = menu->selection;
1965 perf_evlist__set_selected(evlist, pos);
1967 * Give the calling tool a chance to populate the non
1968 * default evsel resorted hists tree.
1971 hbt->timer(hbt->arg);
1972 key = perf_evsel__hists_browse(pos, nr_events, help,
1976 ui_browser__show_title(&menu->b, title);
1979 if (pos->node.next == &evlist->entries)
1980 pos = perf_evlist__first(evlist);
1982 pos = perf_evsel__next(pos);
1985 if (pos->node.prev == &evlist->entries)
1986 pos = perf_evlist__last(evlist);
1988 pos = perf_evsel__prev(pos);
1991 if (!ui_browser__dialog_yesno(&menu->b,
1992 "Do you really want to exit?"))
1995 case K_SWITCH_INPUT_DATA:
2005 if (!ui_browser__dialog_yesno(&menu->b,
2006 "Do you really want to exit?"))
2018 ui_browser__hide(&menu->b);
2022 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
2025 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
2027 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
2033 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
2034 int nr_entries, const char *help,
2035 struct hist_browser_timer *hbt,
2037 struct perf_session_env *env)
2039 struct perf_evsel *pos;
2040 struct perf_evsel_menu menu = {
2042 .entries = &evlist->entries,
2043 .refresh = ui_browser__list_head_refresh,
2044 .seek = ui_browser__list_head_seek,
2045 .write = perf_evsel_menu__write,
2046 .filter = filter_group_entries,
2047 .nr_entries = nr_entries,
2050 .min_pcnt = min_pcnt,
2054 ui_helpline__push("Press ESC to exit");
2056 evlist__for_each(evlist, pos) {
2057 const char *ev_name = perf_evsel__name(pos);
2058 size_t line_len = strlen(ev_name) + 7;
2060 if (menu.b.width < line_len)
2061 menu.b.width = line_len;
2064 return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
2067 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
2068 struct hist_browser_timer *hbt,
2070 struct perf_session_env *env)
2072 int nr_entries = evlist->nr_entries;
2075 if (nr_entries == 1) {
2076 struct perf_evsel *first = perf_evlist__first(evlist);
2078 return perf_evsel__hists_browse(first, nr_entries, help,
2079 false, hbt, min_pcnt,
2083 if (symbol_conf.event_group) {
2084 struct perf_evsel *pos;
2087 evlist__for_each(evlist, pos) {
2088 if (perf_evsel__is_group_leader(pos))
2092 if (nr_entries == 1)
2096 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
2097 hbt, min_pcnt, env);