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 "../../util/top.h"
14 #include "../../arch/common.h"
16 #include "../browser.h"
17 #include "../helpline.h"
26 struct hist_entry *he_selection;
27 struct map_symbol *selection;
32 u64 nr_non_filtered_entries;
33 u64 nr_callchain_rows;
36 extern void hist_browser__init_hpp(void);
38 static int hists__browser_title(struct hists *hists, char *bf, size_t size);
39 static void hist_browser__update_nr_entries(struct hist_browser *hb);
41 static struct rb_node *hists__filter_entries(struct rb_node *nd,
44 static bool hist_browser__has_filter(struct hist_browser *hb)
46 return hists__has_filter(hb->hists) || hb->min_pcnt;
49 static u32 hist_browser__nr_entries(struct hist_browser *hb)
53 if (hist_browser__has_filter(hb))
54 nr_entries = hb->nr_non_filtered_entries;
56 nr_entries = hb->hists->nr_entries;
58 return nr_entries + hb->nr_callchain_rows;
61 static void hist_browser__update_rows(struct hist_browser *hb)
63 struct ui_browser *browser = &hb->b;
64 u16 header_offset = hb->show_headers ? 1 : 0, index_row;
66 browser->rows = browser->height - header_offset;
68 * Verify if we were at the last line and that line isn't
69 * visibe because we now show the header line(s).
71 index_row = browser->index - browser->top_idx;
72 if (index_row >= browser->rows)
73 browser->index -= index_row - browser->rows + 1;
76 static void hist_browser__refresh_dimensions(struct ui_browser *browser)
78 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
80 /* 3 == +/- toggle symbol before actual hist_entry rendering */
81 browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]"));
83 * FIXME: Just keeping existing behaviour, but this really should be
84 * before updating browser->width, as it will invalidate the
85 * calculation above. Fix this and the fallout in another
88 ui_browser__refresh_dimensions(browser);
89 hist_browser__update_rows(hb);
92 static void hist_browser__gotorc(struct hist_browser *browser, int row, int column)
94 u16 header_offset = browser->show_headers ? 1 : 0;
96 ui_browser__gotorc(&browser->b, row + header_offset, column);
99 static void hist_browser__reset(struct hist_browser *browser)
102 * The hists__remove_entry_filter() already folds non-filtered
103 * entries so we can assume it has 0 callchain rows.
105 browser->nr_callchain_rows = 0;
107 hist_browser__update_nr_entries(browser);
108 browser->b.nr_entries = hist_browser__nr_entries(browser);
109 hist_browser__refresh_dimensions(&browser->b);
110 ui_browser__reset_index(&browser->b);
113 static char tree__folded_sign(bool unfolded)
115 return unfolded ? '-' : '+';
118 static char map_symbol__folded(const struct map_symbol *ms)
120 return ms->has_children ? tree__folded_sign(ms->unfolded) : ' ';
123 static char hist_entry__folded(const struct hist_entry *he)
125 return map_symbol__folded(&he->ms);
128 static char callchain_list__folded(const struct callchain_list *cl)
130 return map_symbol__folded(&cl->ms);
133 static void map_symbol__set_folding(struct map_symbol *ms, bool unfold)
135 ms->unfolded = unfold ? ms->has_children : false;
138 static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
143 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
144 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
145 struct callchain_list *chain;
146 char folded_sign = ' '; /* No children */
148 list_for_each_entry(chain, &child->val, list) {
150 /* We need this because we may not have children */
151 folded_sign = callchain_list__folded(chain);
152 if (folded_sign == '+')
156 if (folded_sign == '-') /* Have children and they're unfolded */
157 n += callchain_node__count_rows_rb_tree(child);
163 static int callchain_node__count_rows(struct callchain_node *node)
165 struct callchain_list *chain;
166 bool unfolded = false;
169 list_for_each_entry(chain, &node->val, list) {
171 unfolded = chain->ms.unfolded;
175 n += callchain_node__count_rows_rb_tree(node);
180 static int callchain__count_rows(struct rb_root *chain)
185 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
186 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
187 n += callchain_node__count_rows(node);
193 static bool map_symbol__toggle_fold(struct map_symbol *ms)
198 if (!ms->has_children)
201 ms->unfolded = !ms->unfolded;
205 static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
207 struct rb_node *nd = rb_first(&node->rb_root);
209 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
210 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
211 struct callchain_list *chain;
214 list_for_each_entry(chain, &child->val, list) {
217 chain->ms.has_children = chain->list.next != &child->val ||
218 !RB_EMPTY_ROOT(&child->rb_root);
220 chain->ms.has_children = chain->list.next == &child->val &&
221 !RB_EMPTY_ROOT(&child->rb_root);
224 callchain_node__init_have_children_rb_tree(child);
228 static void callchain_node__init_have_children(struct callchain_node *node)
230 struct callchain_list *chain;
232 if (!list_empty(&node->val)) {
233 chain = list_entry(node->val.prev, struct callchain_list, list);
234 chain->ms.has_children = !RB_EMPTY_ROOT(&node->rb_root);
237 callchain_node__init_have_children_rb_tree(node);
240 static void callchain__init_have_children(struct rb_root *root)
244 for (nd = rb_first(root); nd; nd = rb_next(nd)) {
245 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
246 callchain_node__init_have_children(node);
250 static void hist_entry__init_have_children(struct hist_entry *he)
252 if (!he->init_have_children) {
253 he->ms.has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
254 callchain__init_have_children(&he->sorted_chain);
255 he->init_have_children = true;
259 static bool hist_browser__toggle_fold(struct hist_browser *browser)
261 if (map_symbol__toggle_fold(browser->selection)) {
262 struct hist_entry *he = browser->he_selection;
264 hist_entry__init_have_children(he);
265 browser->b.nr_entries -= he->nr_rows;
266 browser->nr_callchain_rows -= he->nr_rows;
269 he->nr_rows = callchain__count_rows(&he->sorted_chain);
273 browser->b.nr_entries += he->nr_rows;
274 browser->nr_callchain_rows += he->nr_rows;
279 /* If it doesn't have children, no toggling performed */
283 static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
288 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
289 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
290 struct callchain_list *chain;
291 bool has_children = false;
293 list_for_each_entry(chain, &child->val, list) {
295 map_symbol__set_folding(&chain->ms, unfold);
296 has_children = chain->ms.has_children;
300 n += callchain_node__set_folding_rb_tree(child, unfold);
306 static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
308 struct callchain_list *chain;
309 bool has_children = false;
312 list_for_each_entry(chain, &node->val, list) {
314 map_symbol__set_folding(&chain->ms, unfold);
315 has_children = chain->ms.has_children;
319 n += callchain_node__set_folding_rb_tree(node, unfold);
324 static int callchain__set_folding(struct rb_root *chain, bool unfold)
329 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
330 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
331 n += callchain_node__set_folding(node, unfold);
337 static void hist_entry__set_folding(struct hist_entry *he, bool unfold)
339 hist_entry__init_have_children(he);
340 map_symbol__set_folding(&he->ms, unfold);
342 if (he->ms.has_children) {
343 int n = callchain__set_folding(&he->sorted_chain, unfold);
344 he->nr_rows = unfold ? n : 0;
350 __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
353 struct hists *hists = browser->hists;
355 for (nd = rb_first(&hists->entries);
356 (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
358 struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
359 hist_entry__set_folding(he, unfold);
360 browser->nr_callchain_rows += he->nr_rows;
364 static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
366 browser->nr_callchain_rows = 0;
367 __hist_browser__set_folding(browser, unfold);
369 browser->b.nr_entries = hist_browser__nr_entries(browser);
370 /* Go to the start, we may be way after valid entries after a collapse */
371 ui_browser__reset_index(&browser->b);
374 static void ui_browser__warn_lost_events(struct ui_browser *browser)
376 ui_browser__warning(browser, 4,
377 "Events are being lost, check IO/CPU overload!\n\n"
378 "You may want to run 'perf' using a RT scheduler policy:\n\n"
379 " perf top -r 80\n\n"
380 "Or reduce the sampling frequency.");
383 static int hist_browser__run(struct hist_browser *browser,
384 struct hist_browser_timer *hbt)
388 int delay_secs = hbt ? hbt->refresh : 0;
390 browser->b.entries = &browser->hists->entries;
391 browser->b.nr_entries = hist_browser__nr_entries(browser);
393 hists__browser_title(browser->hists, title, sizeof(title));
395 if (ui_browser__show(&browser->b, title,
396 "Press '?' for help on key bindings") < 0)
400 key = ui_browser__run(&browser->b, delay_secs);
405 hbt->timer(hbt->arg);
407 if (hist_browser__has_filter(browser))
408 hist_browser__update_nr_entries(browser);
410 nr_entries = hist_browser__nr_entries(browser);
411 ui_browser__update_nr_entries(&browser->b, nr_entries);
413 if (browser->hists->stats.nr_lost_warned !=
414 browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
415 browser->hists->stats.nr_lost_warned =
416 browser->hists->stats.nr_events[PERF_RECORD_LOST];
417 ui_browser__warn_lost_events(&browser->b);
420 hists__browser_title(browser->hists, title, sizeof(title));
421 ui_browser__show_title(&browser->b, title);
424 case 'D': { /* Debug */
426 struct hist_entry *h = rb_entry(browser->b.top,
427 struct hist_entry, rb_node);
429 ui_helpline__fpush("%d: nr_ent=(%d,%d), rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
430 seq++, browser->b.nr_entries,
431 browser->hists->nr_entries,
435 h->row_offset, h->nr_rows);
439 /* Collapse the whole world. */
440 hist_browser__set_folding(browser, false);
443 /* Expand the whole world. */
444 hist_browser__set_folding(browser, true);
447 browser->show_headers = !browser->show_headers;
448 hist_browser__update_rows(browser);
451 if (hist_browser__toggle_fold(browser))
459 ui_browser__hide(&browser->b);
463 static char *callchain_list__sym_name(struct callchain_list *cl,
464 char *bf, size_t bfsize, bool show_dso)
469 printed = scnprintf(bf, bfsize, "%s", cl->ms.sym->name);
471 printed = scnprintf(bf, bfsize, "%#" PRIx64, cl->ip);
474 scnprintf(bf + printed, bfsize - printed, " %s",
475 cl->ms.map ? cl->ms.map->dso->short_name : "unknown");
480 struct callchain_print_arg {
481 /* for hists browser */
483 bool is_current_entry;
490 typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
491 struct callchain_list *chain,
492 const char *str, int offset,
494 struct callchain_print_arg *arg);
496 static void hist_browser__show_callchain_entry(struct hist_browser *browser,
497 struct callchain_list *chain,
498 const char *str, int offset,
500 struct callchain_print_arg *arg)
503 char folded_sign = callchain_list__folded(chain);
505 color = HE_COLORSET_NORMAL;
506 width = browser->b.width - (offset + 2);
507 if (ui_browser__is_current_entry(&browser->b, row)) {
508 browser->selection = &chain->ms;
509 color = HE_COLORSET_SELECTED;
510 arg->is_current_entry = true;
513 ui_browser__set_color(&browser->b, color);
514 hist_browser__gotorc(browser, row, 0);
515 slsmg_write_nstring(" ", offset);
516 slsmg_printf("%c ", folded_sign);
517 slsmg_write_nstring(str, width);
520 static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
521 struct callchain_list *chain,
522 const char *str, int offset,
523 unsigned short row __maybe_unused,
524 struct callchain_print_arg *arg)
526 char folded_sign = callchain_list__folded(chain);
528 arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
532 typedef bool (*check_output_full_fn)(struct hist_browser *browser,
535 static bool hist_browser__check_output_full(struct hist_browser *browser,
538 return browser->b.rows == row;
541 static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
542 unsigned short row __maybe_unused)
547 #define LEVEL_OFFSET_STEP 3
549 static int hist_browser__show_callchain(struct hist_browser *browser,
550 struct rb_root *root, int level,
551 unsigned short row, u64 total,
552 print_callchain_entry_fn print,
553 struct callchain_print_arg *arg,
554 check_output_full_fn is_output_full)
556 struct rb_node *node;
557 int first_row = row, offset = level * LEVEL_OFFSET_STEP;
560 node = rb_first(root);
562 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
563 struct rb_node *next = rb_next(node);
564 u64 cumul = callchain_cumul_hits(child);
565 struct callchain_list *chain;
566 char folded_sign = ' ';
568 int extra_offset = 0;
570 list_for_each_entry(chain, &child->val, list) {
571 char bf[1024], *alloc_str;
573 bool was_first = first;
578 extra_offset = LEVEL_OFFSET_STEP;
580 folded_sign = callchain_list__folded(chain);
581 if (arg->row_offset != 0) {
587 str = callchain_list__sym_name(chain, bf, sizeof(bf),
590 if (was_first && level > 1) {
591 double percent = cumul * 100.0 / total;
593 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
594 str = "Not enough memory!";
599 print(browser, chain, str, offset + extra_offset, row, arg);
603 if (is_output_full(browser, ++row))
606 if (folded_sign == '+')
610 if (folded_sign == '-') {
611 const int new_level = level + (extra_offset ? 2 : 1);
613 if (callchain_param.mode == CHAIN_GRAPH_REL)
614 new_total = child->children_hit;
618 row += hist_browser__show_callchain(browser, &child->rb_root,
619 new_level, row, new_total,
620 print, arg, is_output_full);
622 if (is_output_full(browser, row))
627 return row - first_row;
631 struct ui_browser *b;
636 static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
638 struct hpp_arg *arg = hpp->ptr;
644 len = va_arg(args, int);
645 percent = va_arg(args, double);
648 ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
650 ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
651 slsmg_printf("%s", hpp->buf);
653 advance_hpp(hpp, ret);
657 #define __HPP_COLOR_PERCENT_FN(_type, _field) \
658 static u64 __hpp_get_##_field(struct hist_entry *he) \
660 return he->stat._field; \
664 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
665 struct perf_hpp *hpp, \
666 struct hist_entry *he) \
668 return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%", \
669 __hpp__slsmg_color_printf, true); \
672 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
673 static u64 __hpp_get_acc_##_field(struct hist_entry *he) \
675 return he->stat_acc->_field; \
679 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
680 struct perf_hpp *hpp, \
681 struct hist_entry *he) \
683 if (!symbol_conf.cumulate_callchain) { \
684 int len = fmt->user_len ?: fmt->len; \
685 int ret = scnprintf(hpp->buf, hpp->size, \
686 "%*s", len, "N/A"); \
687 slsmg_printf("%s", hpp->buf); \
691 return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field, \
692 " %*.2f%%", __hpp__slsmg_color_printf, true); \
695 __HPP_COLOR_PERCENT_FN(overhead, period)
696 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
697 __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
698 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
699 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
700 __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
702 #undef __HPP_COLOR_PERCENT_FN
703 #undef __HPP_COLOR_ACC_PERCENT_FN
705 void hist_browser__init_hpp(void)
707 perf_hpp__format[PERF_HPP__OVERHEAD].color =
708 hist_browser__hpp_color_overhead;
709 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
710 hist_browser__hpp_color_overhead_sys;
711 perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
712 hist_browser__hpp_color_overhead_us;
713 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
714 hist_browser__hpp_color_overhead_guest_sys;
715 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
716 hist_browser__hpp_color_overhead_guest_us;
717 perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
718 hist_browser__hpp_color_overhead_acc;
721 static int hist_browser__show_entry(struct hist_browser *browser,
722 struct hist_entry *entry,
727 int width = browser->b.width;
728 char folded_sign = ' ';
729 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
730 off_t row_offset = entry->row_offset;
732 struct perf_hpp_fmt *fmt;
735 browser->he_selection = entry;
736 browser->selection = &entry->ms;
739 if (symbol_conf.use_callchain) {
740 hist_entry__init_have_children(entry);
741 folded_sign = hist_entry__folded(entry);
744 if (row_offset == 0) {
745 struct hpp_arg arg = {
747 .folded_sign = folded_sign,
748 .current_entry = current_entry,
750 struct perf_hpp hpp = {
756 hist_browser__gotorc(browser, row, 0);
758 perf_hpp__for_each_format(fmt) {
759 if (perf_hpp__should_skip(fmt))
762 if (current_entry && browser->b.navkeypressed) {
763 ui_browser__set_color(&browser->b,
764 HE_COLORSET_SELECTED);
766 ui_browser__set_color(&browser->b,
771 if (symbol_conf.use_callchain) {
772 slsmg_printf("%c ", folded_sign);
782 width -= fmt->color(fmt, &hpp, entry);
784 width -= fmt->entry(fmt, &hpp, entry);
785 slsmg_printf("%s", s);
789 /* The scroll bar isn't being used */
790 if (!browser->b.navkeypressed)
793 slsmg_write_nstring("", width);
800 if (folded_sign == '-' && row != browser->b.rows) {
801 u64 total = hists__total_period(entry->hists);
802 struct callchain_print_arg arg = {
803 .row_offset = row_offset,
804 .is_current_entry = current_entry,
807 printed += hist_browser__show_callchain(browser,
808 &entry->sorted_chain, 1, row, total,
809 hist_browser__show_callchain_entry, &arg,
810 hist_browser__check_output_full);
812 if (arg.is_current_entry)
813 browser->he_selection = entry;
819 static int advance_hpp_check(struct perf_hpp *hpp, int inc)
821 advance_hpp(hpp, inc);
822 return hpp->size <= 0;
825 static int hists__scnprintf_headers(char *buf, size_t size, struct hists *hists)
827 struct perf_hpp dummy_hpp = {
831 struct perf_hpp_fmt *fmt;
834 if (symbol_conf.use_callchain) {
835 ret = scnprintf(buf, size, " ");
836 if (advance_hpp_check(&dummy_hpp, ret))
840 perf_hpp__for_each_format(fmt) {
841 if (perf_hpp__should_skip(fmt))
844 ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists));
845 if (advance_hpp_check(&dummy_hpp, ret))
848 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
849 if (advance_hpp_check(&dummy_hpp, ret))
856 static void hist_browser__show_headers(struct hist_browser *browser)
860 hists__scnprintf_headers(headers, sizeof(headers), browser->hists);
861 ui_browser__gotorc(&browser->b, 0, 0);
862 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
863 slsmg_write_nstring(headers, browser->b.width + 1);
866 static void ui_browser__hists_init_top(struct ui_browser *browser)
868 if (browser->top == NULL) {
869 struct hist_browser *hb;
871 hb = container_of(browser, struct hist_browser, b);
872 browser->top = rb_first(&hb->hists->entries);
876 static unsigned int hist_browser__refresh(struct ui_browser *browser)
879 u16 header_offset = 0;
881 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
883 if (hb->show_headers) {
884 hist_browser__show_headers(hb);
888 ui_browser__hists_init_top(browser);
890 for (nd = browser->top; nd; nd = rb_next(nd)) {
891 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
897 percent = hist_entry__get_percent_limit(h);
898 if (percent < hb->min_pcnt)
901 row += hist_browser__show_entry(hb, h, row);
902 if (row == browser->rows)
906 return row + header_offset;
909 static struct rb_node *hists__filter_entries(struct rb_node *nd,
913 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
914 float percent = hist_entry__get_percent_limit(h);
916 if (!h->filtered && percent >= min_pcnt)
925 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
929 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
930 float percent = hist_entry__get_percent_limit(h);
932 if (!h->filtered && percent >= min_pcnt)
941 static void ui_browser__hists_seek(struct ui_browser *browser,
942 off_t offset, int whence)
944 struct hist_entry *h;
947 struct hist_browser *hb;
949 hb = container_of(browser, struct hist_browser, b);
951 if (browser->nr_entries == 0)
954 ui_browser__hists_init_top(browser);
958 nd = hists__filter_entries(rb_first(browser->entries),
965 nd = hists__filter_prev_entries(rb_last(browser->entries),
974 * Moves not relative to the first visible entry invalidates its
977 h = rb_entry(browser->top, struct hist_entry, rb_node);
981 * Here we have to check if nd is expanded (+), if it is we can't go
982 * the next top level hist_entry, instead we must compute an offset of
983 * what _not_ to show and not change the first visible entry.
985 * This offset increments when we are going from top to bottom and
986 * decreases when we're going from bottom to top.
988 * As we don't have backpointers to the top level in the callchains
989 * structure, we need to always print the whole hist_entry callchain,
990 * skipping the first ones that are before the first visible entry
991 * and stop when we printed enough lines to fill the screen.
996 h = rb_entry(nd, struct hist_entry, rb_node);
997 if (h->ms.unfolded) {
998 u16 remaining = h->nr_rows - h->row_offset;
999 if (offset > remaining) {
1000 offset -= remaining;
1003 h->row_offset += offset;
1009 nd = hists__filter_entries(rb_next(nd), hb->min_pcnt);
1014 } while (offset != 0);
1015 } else if (offset < 0) {
1017 h = rb_entry(nd, struct hist_entry, rb_node);
1018 if (h->ms.unfolded) {
1020 if (-offset > h->row_offset) {
1021 offset += h->row_offset;
1024 h->row_offset += offset;
1030 if (-offset > h->nr_rows) {
1031 offset += h->nr_rows;
1034 h->row_offset = h->nr_rows + offset;
1042 nd = hists__filter_prev_entries(rb_prev(nd),
1050 * Last unfiltered hist_entry, check if it is
1051 * unfolded, if it is then we should have
1052 * row_offset at its last entry.
1054 h = rb_entry(nd, struct hist_entry, rb_node);
1056 h->row_offset = h->nr_rows;
1063 h = rb_entry(nd, struct hist_entry, rb_node);
1068 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1069 struct hist_entry *he, FILE *fp)
1071 u64 total = hists__total_period(he->hists);
1072 struct callchain_print_arg arg = {
1076 if (symbol_conf.cumulate_callchain)
1077 total = he->stat_acc->period;
1079 hist_browser__show_callchain(browser, &he->sorted_chain, 1, 0, total,
1080 hist_browser__fprintf_callchain_entry, &arg,
1081 hist_browser__check_dump_full);
1085 static int hist_browser__fprintf_entry(struct hist_browser *browser,
1086 struct hist_entry *he, FILE *fp)
1090 char folded_sign = ' ';
1091 struct perf_hpp hpp = {
1095 struct perf_hpp_fmt *fmt;
1099 if (symbol_conf.use_callchain)
1100 folded_sign = hist_entry__folded(he);
1102 if (symbol_conf.use_callchain)
1103 printed += fprintf(fp, "%c ", folded_sign);
1105 perf_hpp__for_each_format(fmt) {
1106 if (perf_hpp__should_skip(fmt))
1110 ret = scnprintf(hpp.buf, hpp.size, " ");
1111 advance_hpp(&hpp, ret);
1115 ret = fmt->entry(fmt, &hpp, he);
1116 advance_hpp(&hpp, ret);
1118 printed += fprintf(fp, "%s\n", rtrim(s));
1120 if (folded_sign == '-')
1121 printed += hist_browser__fprintf_callchain(browser, he, fp);
1126 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1128 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
1133 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1135 printed += hist_browser__fprintf_entry(browser, h, fp);
1136 nd = hists__filter_entries(rb_next(nd), browser->min_pcnt);
1142 static int hist_browser__dump(struct hist_browser *browser)
1148 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
1149 if (access(filename, F_OK))
1152 * XXX: Just an arbitrary lazy upper limit
1154 if (++browser->print_seq == 8192) {
1155 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1160 fp = fopen(filename, "w");
1163 const char *err = strerror_r(errno, bf, sizeof(bf));
1164 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
1168 ++browser->print_seq;
1169 hist_browser__fprintf(browser, fp);
1171 ui_helpline__fpush("%s written!", filename);
1176 static struct hist_browser *hist_browser__new(struct hists *hists)
1178 struct hist_browser *browser = zalloc(sizeof(*browser));
1181 browser->hists = hists;
1182 browser->b.refresh = hist_browser__refresh;
1183 browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
1184 browser->b.seek = ui_browser__hists_seek;
1185 browser->b.use_navkeypressed = true;
1186 browser->show_headers = symbol_conf.show_hist_headers;
1192 static void hist_browser__delete(struct hist_browser *browser)
1197 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
1199 return browser->he_selection;
1202 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
1204 return browser->he_selection->thread;
1207 static int hists__browser_title(struct hists *hists, char *bf, size_t size)
1211 const struct dso *dso = hists->dso_filter;
1212 const struct thread *thread = hists->thread_filter;
1213 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1214 u64 nr_events = hists->stats.total_period;
1215 struct perf_evsel *evsel = hists_to_evsel(hists);
1216 const char *ev_name = perf_evsel__name(evsel);
1218 size_t buflen = sizeof(buf);
1220 if (symbol_conf.filter_relative) {
1221 nr_samples = hists->stats.nr_non_filtered_samples;
1222 nr_events = hists->stats.total_non_filtered_period;
1225 if (perf_evsel__is_group_event(evsel)) {
1226 struct perf_evsel *pos;
1228 perf_evsel__group_desc(evsel, buf, buflen);
1231 for_each_group_member(pos, evsel) {
1232 struct hists *pos_hists = evsel__hists(pos);
1234 if (symbol_conf.filter_relative) {
1235 nr_samples += pos_hists->stats.nr_non_filtered_samples;
1236 nr_events += pos_hists->stats.total_non_filtered_period;
1238 nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
1239 nr_events += pos_hists->stats.total_period;
1244 nr_samples = convert_unit(nr_samples, &unit);
1245 printed = scnprintf(bf, size,
1246 "Samples: %lu%c of event '%s', Event count (approx.): %lu",
1247 nr_samples, unit, ev_name, nr_events);
1250 if (hists->uid_filter_str)
1251 printed += snprintf(bf + printed, size - printed,
1252 ", UID: %s", hists->uid_filter_str);
1254 printed += scnprintf(bf + printed, size - printed,
1256 (thread->comm_set ? thread__comm_str(thread) : ""),
1259 printed += scnprintf(bf + printed, size - printed,
1260 ", DSO: %s", dso->short_name);
1264 static inline void free_popup_options(char **options, int n)
1268 for (i = 0; i < n; ++i)
1272 /* Check whether the browser is for 'top' or 'report' */
1273 static inline bool is_report_browser(void *timer)
1275 return timer == NULL;
1279 * Only runtime switching of perf data file will make "input_name" point
1280 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1281 * whether we need to call free() for current "input_name" during the switch.
1283 static bool is_input_name_malloced = false;
1285 static int switch_data_file(void)
1287 char *pwd, *options[32], *abs_path[32], *tmp;
1289 int nr_options = 0, choice = -1, ret = -1;
1290 struct dirent *dent;
1292 pwd = getenv("PWD");
1296 pwd_dir = opendir(pwd);
1300 memset(options, 0, sizeof(options));
1301 memset(options, 0, sizeof(abs_path));
1303 while ((dent = readdir(pwd_dir))) {
1304 char path[PATH_MAX];
1306 char *name = dent->d_name;
1309 if (!(dent->d_type == DT_REG))
1312 snprintf(path, sizeof(path), "%s/%s", pwd, name);
1314 file = fopen(path, "r");
1318 if (fread(&magic, 1, 8, file) < 8)
1319 goto close_file_and_continue;
1321 if (is_perf_magic(magic)) {
1322 options[nr_options] = strdup(name);
1323 if (!options[nr_options])
1324 goto close_file_and_continue;
1326 abs_path[nr_options] = strdup(path);
1327 if (!abs_path[nr_options]) {
1328 zfree(&options[nr_options]);
1329 ui__warning("Can't search all data files due to memory shortage.\n");
1337 close_file_and_continue:
1339 if (nr_options >= 32) {
1340 ui__warning("Too many perf data files in PWD!\n"
1341 "Only the first 32 files will be listed.\n");
1348 choice = ui__popup_menu(nr_options, options);
1349 if (choice < nr_options && choice >= 0) {
1350 tmp = strdup(abs_path[choice]);
1352 if (is_input_name_malloced)
1353 free((void *)input_name);
1355 is_input_name_malloced = true;
1358 ui__warning("Data switch failed due to memory shortage!\n");
1362 free_popup_options(options, nr_options);
1363 free_popup_options(abs_path, nr_options);
1367 static void hist_browser__update_nr_entries(struct hist_browser *hb)
1370 struct rb_node *nd = rb_first(&hb->hists->entries);
1372 if (hb->min_pcnt == 0) {
1373 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
1377 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
1382 hb->nr_non_filtered_entries = nr_entries;
1385 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
1386 const char *helpline,
1388 struct hist_browser_timer *hbt,
1390 struct perf_session_env *env)
1392 struct hists *hists = evsel__hists(evsel);
1393 struct hist_browser *browser = hist_browser__new(hists);
1394 struct branch_info *bi;
1395 struct pstack *fstack;
1400 char script_opt[64];
1401 int delay_secs = hbt ? hbt->refresh : 0;
1402 struct perf_hpp_fmt *fmt;
1404 #define HIST_BROWSER_HELP_COMMON \
1405 "h/?/F1 Show this window\n" \
1407 "PGDN/SPACE Navigate\n" \
1408 "q/ESC/CTRL+C Exit browser\n\n" \
1409 "For multiple event sessions:\n\n" \
1410 "TAB/UNTAB Switch events\n\n" \
1411 "For symbolic views (--sort has sym):\n\n" \
1412 "-> Zoom into DSO/Threads & Annotate current symbol\n" \
1414 "a Annotate current symbol\n" \
1415 "C Collapse all callchains\n" \
1416 "d Zoom into current DSO\n" \
1417 "E Expand all callchains\n" \
1418 "F Toggle percentage of filtered entries\n" \
1419 "H Display column headers\n" \
1421 /* help messages are sorted by lexical order of the hotkey */
1422 const char report_help[] = HIST_BROWSER_HELP_COMMON
1423 "i Show header information\n"
1424 "P Print histograms to perf.hist.N\n"
1425 "r Run available scripts\n"
1426 "s Switch to another data file in PWD\n"
1427 "t Zoom into current Thread\n"
1428 "V Verbose (DSO names in callchains, etc)\n"
1429 "/ Filter symbol by name";
1430 const char top_help[] = HIST_BROWSER_HELP_COMMON
1431 "P Print histograms to perf.hist.N\n"
1432 "t Zoom into current Thread\n"
1433 "V Verbose (DSO names in callchains, etc)\n"
1434 "z Toggle zeroing of samples\n"
1435 "/ Filter symbol by name";
1437 if (browser == NULL)
1441 browser->min_pcnt = min_pcnt;
1442 hist_browser__update_nr_entries(browser);
1445 fstack = pstack__new(2);
1449 ui_helpline__push(helpline);
1451 memset(options, 0, sizeof(options));
1453 perf_hpp__for_each_format(fmt)
1454 perf_hpp__reset_width(fmt, hists);
1456 if (symbol_conf.col_width_list_str)
1457 perf_hpp__set_user_width(symbol_conf.col_width_list_str);
1460 const struct thread *thread = NULL;
1461 const struct dso *dso = NULL;
1463 annotate = -2, zoom_dso = -2, zoom_thread = -2,
1464 annotate_f = -2, annotate_t = -2, browse_map = -2;
1465 int scripts_comm = -2, scripts_symbol = -2,
1466 scripts_all = -2, switch_data = -2;
1470 key = hist_browser__run(browser, hbt);
1472 if (browser->he_selection != NULL) {
1473 thread = hist_browser__selected_thread(browser);
1474 dso = browser->selection->map ? browser->selection->map->dso : NULL;
1482 * Exit the browser, let hists__browser_tree
1483 * go to the next or previous
1485 goto out_free_stack;
1487 if (!sort__has_sym) {
1488 ui_browser__warning(&browser->b, delay_secs * 2,
1489 "Annotation is only available for symbolic views, "
1490 "include \"sym*\" in --sort to use it.");
1494 if (browser->selection == NULL ||
1495 browser->selection->sym == NULL ||
1496 browser->selection->map->dso->annotate_warned)
1500 hist_browser__dump(browser);
1505 browser->show_dso = !browser->show_dso;
1510 if (ui_browser__input_window("Symbol to show",
1511 "Please enter the name of symbol you want to see",
1512 buf, "ENTER: OK, ESC: Cancel",
1513 delay_secs * 2) == K_ENTER) {
1514 hists->symbol_filter_str = *buf ? buf : NULL;
1515 hists__filter_by_symbol(hists);
1516 hist_browser__reset(browser);
1520 if (is_report_browser(hbt))
1524 if (is_report_browser(hbt))
1525 goto do_data_switch;
1528 /* env->arch is NULL for live-mode (i.e. perf top) */
1530 tui__header_window(env);
1533 symbol_conf.filter_relative ^= 1;
1536 if (!is_report_browser(hbt)) {
1537 struct perf_top *top = hbt->arg;
1539 top->zero = !top->zero;
1545 ui_browser__help_window(&browser->b,
1546 is_report_browser(hbt) ? report_help : top_help);
1555 if (pstack__empty(fstack)) {
1557 * Go back to the perf_evsel_menu__run or other user
1560 goto out_free_stack;
1563 top = pstack__pop(fstack);
1564 if (top == &browser->hists->dso_filter)
1566 if (top == &browser->hists->thread_filter)
1567 goto zoom_out_thread;
1572 !ui_browser__dialog_yesno(&browser->b,
1573 "Do you really want to exit?"))
1578 goto out_free_stack;
1584 goto add_exit_option;
1586 if (sort__mode == SORT_MODE__BRANCH) {
1587 bi = browser->he_selection->branch_info;
1588 if (browser->selection != NULL &&
1590 bi->from.sym != NULL &&
1591 !bi->from.map->dso->annotate_warned &&
1592 asprintf(&options[nr_options], "Annotate %s",
1593 bi->from.sym->name) > 0)
1594 annotate_f = nr_options++;
1596 if (browser->selection != NULL &&
1598 bi->to.sym != NULL &&
1599 !bi->to.map->dso->annotate_warned &&
1600 (bi->to.sym != bi->from.sym ||
1601 bi->to.map->dso != bi->from.map->dso) &&
1602 asprintf(&options[nr_options], "Annotate %s",
1603 bi->to.sym->name) > 0)
1604 annotate_t = nr_options++;
1606 if (browser->selection != NULL &&
1607 browser->selection->sym != NULL &&
1608 !browser->selection->map->dso->annotate_warned) {
1609 struct annotation *notes;
1611 notes = symbol__annotation(browser->selection->sym);
1614 asprintf(&options[nr_options], "Annotate %s",
1615 browser->selection->sym->name) > 0)
1616 annotate = nr_options++;
1620 if (thread != NULL &&
1621 asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
1622 (browser->hists->thread_filter ? "out of" : "into"),
1623 (thread->comm_set ? thread__comm_str(thread) : ""),
1625 zoom_thread = nr_options++;
1628 asprintf(&options[nr_options], "Zoom %s %s DSO",
1629 (browser->hists->dso_filter ? "out of" : "into"),
1630 (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
1631 zoom_dso = nr_options++;
1633 if (browser->selection != NULL &&
1634 browser->selection->map != NULL &&
1635 asprintf(&options[nr_options], "Browse map details") > 0)
1636 browse_map = nr_options++;
1638 /* perf script support */
1639 if (browser->he_selection) {
1642 if (asprintf(&options[nr_options], "Run scripts for samples of thread [%s]",
1643 thread__comm_str(browser->he_selection->thread)) > 0)
1644 scripts_comm = nr_options++;
1646 sym = browser->he_selection->ms.sym;
1647 if (sym && sym->namelen &&
1648 asprintf(&options[nr_options], "Run scripts for samples of symbol [%s]",
1650 scripts_symbol = nr_options++;
1653 if (asprintf(&options[nr_options], "Run scripts for all samples") > 0)
1654 scripts_all = nr_options++;
1656 if (is_report_browser(hbt) && asprintf(&options[nr_options],
1657 "Switch to another data file in PWD") > 0)
1658 switch_data = nr_options++;
1660 options[nr_options++] = (char *)"Exit";
1662 choice = ui__popup_menu(nr_options, options);
1664 if (choice == nr_options - 1)
1668 free_popup_options(options, nr_options - 1);
1672 if (choice == annotate || choice == annotate_t || choice == annotate_f) {
1673 struct hist_entry *he;
1674 struct annotation *notes;
1677 if (!objdump_path && perf_session_env__lookup_objdump(env))
1680 he = hist_browser__selected_entry(browser);
1685 * we stash the branch_info symbol + map into the
1686 * the ms so we don't have to rewrite all the annotation
1687 * code to use branch_info.
1688 * in branch mode, the ms struct is not used
1690 if (choice == annotate_f) {
1691 he->ms.sym = he->branch_info->from.sym;
1692 he->ms.map = he->branch_info->from.map;
1693 } else if (choice == annotate_t) {
1694 he->ms.sym = he->branch_info->to.sym;
1695 he->ms.map = he->branch_info->to.map;
1698 notes = symbol__annotation(he->ms.sym);
1703 * Don't let this be freed, say, by hists__decay_entry.
1706 err = hist_entry__tui_annotate(he, evsel, hbt);
1709 * offer option to annotate the other branch source or target
1710 * (if they exists) when returning from annotate
1712 if ((err == 'q' || err == CTRL('c'))
1713 && annotate_t != -2 && annotate_f != -2)
1714 goto retry_popup_menu;
1716 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1718 ui_browser__handle_resize(&browser->b);
1720 } else if (choice == browse_map)
1721 map__browse(browser->selection->map);
1722 else if (choice == zoom_dso) {
1724 if (browser->hists->dso_filter) {
1725 pstack__remove(fstack, &browser->hists->dso_filter);
1728 browser->hists->dso_filter = NULL;
1729 perf_hpp__set_elide(HISTC_DSO, false);
1733 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1734 dso->kernel ? "the Kernel" : dso->short_name);
1735 browser->hists->dso_filter = dso;
1736 perf_hpp__set_elide(HISTC_DSO, true);
1737 pstack__push(fstack, &browser->hists->dso_filter);
1739 hists__filter_by_dso(hists);
1740 hist_browser__reset(browser);
1741 } else if (choice == zoom_thread) {
1743 if (browser->hists->thread_filter) {
1744 pstack__remove(fstack, &browser->hists->thread_filter);
1747 browser->hists->thread_filter = NULL;
1748 perf_hpp__set_elide(HISTC_THREAD, false);
1750 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1751 thread->comm_set ? thread__comm_str(thread) : "",
1753 browser->hists->thread_filter = thread;
1754 perf_hpp__set_elide(HISTC_THREAD, false);
1755 pstack__push(fstack, &browser->hists->thread_filter);
1757 hists__filter_by_thread(hists);
1758 hist_browser__reset(browser);
1760 /* perf scripts support */
1761 else if (choice == scripts_all || choice == scripts_comm ||
1762 choice == scripts_symbol) {
1764 memset(script_opt, 0, 64);
1766 if (choice == scripts_comm)
1767 sprintf(script_opt, " -c %s ", thread__comm_str(browser->he_selection->thread));
1769 if (choice == scripts_symbol)
1770 sprintf(script_opt, " -S %s ", browser->he_selection->ms.sym->name);
1772 script_browse(script_opt);
1774 /* Switch to another data file */
1775 else if (choice == switch_data) {
1777 if (!switch_data_file()) {
1778 key = K_SWITCH_INPUT_DATA;
1781 ui__warning("Won't switch the data files due to\n"
1782 "no valid data file get selected!\n");
1786 pstack__delete(fstack);
1788 hist_browser__delete(browser);
1789 free_popup_options(options, nr_options - 1);
1793 struct perf_evsel_menu {
1794 struct ui_browser b;
1795 struct perf_evsel *selection;
1796 bool lost_events, lost_events_warned;
1798 struct perf_session_env *env;
1801 static void perf_evsel_menu__write(struct ui_browser *browser,
1802 void *entry, int row)
1804 struct perf_evsel_menu *menu = container_of(browser,
1805 struct perf_evsel_menu, b);
1806 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1807 struct hists *hists = evsel__hists(evsel);
1808 bool current_entry = ui_browser__is_current_entry(browser, row);
1809 unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1810 const char *ev_name = perf_evsel__name(evsel);
1812 const char *warn = " ";
1815 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
1816 HE_COLORSET_NORMAL);
1818 if (perf_evsel__is_group_event(evsel)) {
1819 struct perf_evsel *pos;
1821 ev_name = perf_evsel__group_name(evsel);
1823 for_each_group_member(pos, evsel) {
1824 struct hists *pos_hists = evsel__hists(pos);
1825 nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
1829 nr_events = convert_unit(nr_events, &unit);
1830 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
1831 unit, unit == ' ' ? "" : " ", ev_name);
1832 slsmg_printf("%s", bf);
1834 nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
1835 if (nr_events != 0) {
1836 menu->lost_events = true;
1838 ui_browser__set_color(browser, HE_COLORSET_TOP);
1839 nr_events = convert_unit(nr_events, &unit);
1840 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
1841 nr_events, unit, unit == ' ' ? "" : " ");
1845 slsmg_write_nstring(warn, browser->width - printed);
1848 menu->selection = evsel;
1851 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
1852 int nr_events, const char *help,
1853 struct hist_browser_timer *hbt)
1855 struct perf_evlist *evlist = menu->b.priv;
1856 struct perf_evsel *pos;
1857 const char *title = "Available samples";
1858 int delay_secs = hbt ? hbt->refresh : 0;
1861 if (ui_browser__show(&menu->b, title,
1862 "ESC: exit, ENTER|->: Browse histograms") < 0)
1866 key = ui_browser__run(&menu->b, delay_secs);
1870 hbt->timer(hbt->arg);
1872 if (!menu->lost_events_warned && menu->lost_events) {
1873 ui_browser__warn_lost_events(&menu->b);
1874 menu->lost_events_warned = true;
1879 if (!menu->selection)
1881 pos = menu->selection;
1883 perf_evlist__set_selected(evlist, pos);
1885 * Give the calling tool a chance to populate the non
1886 * default evsel resorted hists tree.
1889 hbt->timer(hbt->arg);
1890 key = perf_evsel__hists_browse(pos, nr_events, help,
1894 ui_browser__show_title(&menu->b, title);
1897 if (pos->node.next == &evlist->entries)
1898 pos = perf_evlist__first(evlist);
1900 pos = perf_evsel__next(pos);
1903 if (pos->node.prev == &evlist->entries)
1904 pos = perf_evlist__last(evlist);
1906 pos = perf_evsel__prev(pos);
1909 if (!ui_browser__dialog_yesno(&menu->b,
1910 "Do you really want to exit?"))
1913 case K_SWITCH_INPUT_DATA:
1923 if (!ui_browser__dialog_yesno(&menu->b,
1924 "Do you really want to exit?"))
1936 ui_browser__hide(&menu->b);
1940 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
1943 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1945 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
1951 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
1952 int nr_entries, const char *help,
1953 struct hist_browser_timer *hbt,
1955 struct perf_session_env *env)
1957 struct perf_evsel *pos;
1958 struct perf_evsel_menu menu = {
1960 .entries = &evlist->entries,
1961 .refresh = ui_browser__list_head_refresh,
1962 .seek = ui_browser__list_head_seek,
1963 .write = perf_evsel_menu__write,
1964 .filter = filter_group_entries,
1965 .nr_entries = nr_entries,
1968 .min_pcnt = min_pcnt,
1972 ui_helpline__push("Press ESC to exit");
1974 evlist__for_each(evlist, pos) {
1975 const char *ev_name = perf_evsel__name(pos);
1976 size_t line_len = strlen(ev_name) + 7;
1978 if (menu.b.width < line_len)
1979 menu.b.width = line_len;
1982 return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
1985 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
1986 struct hist_browser_timer *hbt,
1988 struct perf_session_env *env)
1990 int nr_entries = evlist->nr_entries;
1993 if (nr_entries == 1) {
1994 struct perf_evsel *first = perf_evlist__first(evlist);
1996 return perf_evsel__hists_browse(first, nr_entries, help,
1997 false, hbt, min_pcnt,
2001 if (symbol_conf.event_group) {
2002 struct perf_evsel *pos;
2005 evlist__for_each(evlist, pos) {
2006 if (perf_evsel__is_group_leader(pos))
2010 if (nr_entries == 1)
2014 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
2015 hbt, min_pcnt, env);