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"
24 struct hist_entry *he_selection;
25 struct map_symbol *selection;
29 u64 nr_non_filtered_entries;
30 u64 nr_callchain_rows;
33 extern void hist_browser__init_hpp(void);
35 static int hists__browser_title(struct hists *hists, char *bf, size_t size,
37 static void hist_browser__update_nr_entries(struct hist_browser *hb);
39 static struct rb_node *hists__filter_entries(struct rb_node *nd,
42 static bool hist_browser__has_filter(struct hist_browser *hb)
44 return hists__has_filter(hb->hists) || hb->min_pcnt;
47 static u32 hist_browser__nr_entries(struct hist_browser *hb)
51 if (hist_browser__has_filter(hb))
52 nr_entries = hb->nr_non_filtered_entries;
54 nr_entries = hb->hists->nr_entries;
56 return nr_entries + hb->nr_callchain_rows;
59 static void hist_browser__refresh_dimensions(struct hist_browser *browser)
61 /* 3 == +/- toggle symbol before actual hist_entry rendering */
62 browser->b.width = 3 + (hists__sort_list_width(browser->hists) +
66 static void hist_browser__reset(struct hist_browser *browser)
69 * The hists__remove_entry_filter() already folds non-filtered
70 * entries so we can assume it has 0 callchain rows.
72 browser->nr_callchain_rows = 0;
74 hist_browser__update_nr_entries(browser);
75 browser->b.nr_entries = hist_browser__nr_entries(browser);
76 hist_browser__refresh_dimensions(browser);
77 ui_browser__reset_index(&browser->b);
80 static char tree__folded_sign(bool unfolded)
82 return unfolded ? '-' : '+';
85 static char map_symbol__folded(const struct map_symbol *ms)
87 return ms->has_children ? tree__folded_sign(ms->unfolded) : ' ';
90 static char hist_entry__folded(const struct hist_entry *he)
92 return map_symbol__folded(&he->ms);
95 static char callchain_list__folded(const struct callchain_list *cl)
97 return map_symbol__folded(&cl->ms);
100 static void map_symbol__set_folding(struct map_symbol *ms, bool unfold)
102 ms->unfolded = unfold ? ms->has_children : false;
105 static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
110 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
111 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
112 struct callchain_list *chain;
113 char folded_sign = ' '; /* No children */
115 list_for_each_entry(chain, &child->val, list) {
117 /* We need this because we may not have children */
118 folded_sign = callchain_list__folded(chain);
119 if (folded_sign == '+')
123 if (folded_sign == '-') /* Have children and they're unfolded */
124 n += callchain_node__count_rows_rb_tree(child);
130 static int callchain_node__count_rows(struct callchain_node *node)
132 struct callchain_list *chain;
133 bool unfolded = false;
136 list_for_each_entry(chain, &node->val, list) {
138 unfolded = chain->ms.unfolded;
142 n += callchain_node__count_rows_rb_tree(node);
147 static int callchain__count_rows(struct rb_root *chain)
152 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
153 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
154 n += callchain_node__count_rows(node);
160 static bool map_symbol__toggle_fold(struct map_symbol *ms)
165 if (!ms->has_children)
168 ms->unfolded = !ms->unfolded;
172 static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
174 struct rb_node *nd = rb_first(&node->rb_root);
176 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
177 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
178 struct callchain_list *chain;
181 list_for_each_entry(chain, &child->val, list) {
184 chain->ms.has_children = chain->list.next != &child->val ||
185 !RB_EMPTY_ROOT(&child->rb_root);
187 chain->ms.has_children = chain->list.next == &child->val &&
188 !RB_EMPTY_ROOT(&child->rb_root);
191 callchain_node__init_have_children_rb_tree(child);
195 static void callchain_node__init_have_children(struct callchain_node *node)
197 struct callchain_list *chain;
199 list_for_each_entry(chain, &node->val, list)
200 chain->ms.has_children = !RB_EMPTY_ROOT(&node->rb_root);
202 callchain_node__init_have_children_rb_tree(node);
205 static void callchain__init_have_children(struct rb_root *root)
209 for (nd = rb_first(root); nd; nd = rb_next(nd)) {
210 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
211 callchain_node__init_have_children(node);
215 static void hist_entry__init_have_children(struct hist_entry *he)
217 if (!he->init_have_children) {
218 he->ms.has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
219 callchain__init_have_children(&he->sorted_chain);
220 he->init_have_children = true;
224 static bool hist_browser__toggle_fold(struct hist_browser *browser)
226 if (map_symbol__toggle_fold(browser->selection)) {
227 struct hist_entry *he = browser->he_selection;
229 hist_entry__init_have_children(he);
230 browser->b.nr_entries -= he->nr_rows;
231 browser->nr_callchain_rows -= he->nr_rows;
234 he->nr_rows = callchain__count_rows(&he->sorted_chain);
238 browser->b.nr_entries += he->nr_rows;
239 browser->nr_callchain_rows += he->nr_rows;
244 /* If it doesn't have children, no toggling performed */
248 static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
253 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
254 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
255 struct callchain_list *chain;
256 bool has_children = false;
258 list_for_each_entry(chain, &child->val, list) {
260 map_symbol__set_folding(&chain->ms, unfold);
261 has_children = chain->ms.has_children;
265 n += callchain_node__set_folding_rb_tree(child, unfold);
271 static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
273 struct callchain_list *chain;
274 bool has_children = false;
277 list_for_each_entry(chain, &node->val, list) {
279 map_symbol__set_folding(&chain->ms, unfold);
280 has_children = chain->ms.has_children;
284 n += callchain_node__set_folding_rb_tree(node, unfold);
289 static int callchain__set_folding(struct rb_root *chain, bool unfold)
294 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
295 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
296 n += callchain_node__set_folding(node, unfold);
302 static void hist_entry__set_folding(struct hist_entry *he, bool unfold)
304 hist_entry__init_have_children(he);
305 map_symbol__set_folding(&he->ms, unfold);
307 if (he->ms.has_children) {
308 int n = callchain__set_folding(&he->sorted_chain, unfold);
309 he->nr_rows = unfold ? n : 0;
315 __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
318 struct hists *hists = browser->hists;
320 for (nd = rb_first(&hists->entries);
321 (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
323 struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
324 hist_entry__set_folding(he, unfold);
325 browser->nr_callchain_rows += he->nr_rows;
329 static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
331 browser->nr_callchain_rows = 0;
332 __hist_browser__set_folding(browser, unfold);
334 browser->b.nr_entries = hist_browser__nr_entries(browser);
335 /* Go to the start, we may be way after valid entries after a collapse */
336 ui_browser__reset_index(&browser->b);
339 static void ui_browser__warn_lost_events(struct ui_browser *browser)
341 ui_browser__warning(browser, 4,
342 "Events are being lost, check IO/CPU overload!\n\n"
343 "You may want to run 'perf' using a RT scheduler policy:\n\n"
344 " perf top -r 80\n\n"
345 "Or reduce the sampling frequency.");
348 static int hist_browser__run(struct hist_browser *browser, const char *ev_name,
349 struct hist_browser_timer *hbt)
353 int delay_secs = hbt ? hbt->refresh : 0;
355 browser->b.entries = &browser->hists->entries;
356 browser->b.nr_entries = hist_browser__nr_entries(browser);
358 hist_browser__refresh_dimensions(browser);
359 hists__browser_title(browser->hists, title, sizeof(title), ev_name);
361 if (ui_browser__show(&browser->b, title,
362 "Press '?' for help on key bindings") < 0)
366 key = ui_browser__run(&browser->b, delay_secs);
371 hbt->timer(hbt->arg);
373 if (hist_browser__has_filter(browser))
374 hist_browser__update_nr_entries(browser);
376 nr_entries = hist_browser__nr_entries(browser);
377 ui_browser__update_nr_entries(&browser->b, nr_entries);
379 if (browser->hists->stats.nr_lost_warned !=
380 browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
381 browser->hists->stats.nr_lost_warned =
382 browser->hists->stats.nr_events[PERF_RECORD_LOST];
383 ui_browser__warn_lost_events(&browser->b);
386 hists__browser_title(browser->hists, title, sizeof(title), ev_name);
387 ui_browser__show_title(&browser->b, title);
390 case 'D': { /* Debug */
392 struct hist_entry *h = rb_entry(browser->b.top,
393 struct hist_entry, rb_node);
395 ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
396 seq++, browser->b.nr_entries,
397 browser->hists->nr_entries,
401 h->row_offset, h->nr_rows);
405 /* Collapse the whole world. */
406 hist_browser__set_folding(browser, false);
409 /* Expand the whole world. */
410 hist_browser__set_folding(browser, true);
413 if (hist_browser__toggle_fold(browser))
421 ui_browser__hide(&browser->b);
425 static char *callchain_list__sym_name(struct callchain_list *cl,
426 char *bf, size_t bfsize, bool show_dso)
431 printed = scnprintf(bf, bfsize, "%s", cl->ms.sym->name);
433 printed = scnprintf(bf, bfsize, "%#" PRIx64, cl->ip);
436 scnprintf(bf + printed, bfsize - printed, " %s",
437 cl->ms.map ? cl->ms.map->dso->short_name : "unknown");
442 #define LEVEL_OFFSET_STEP 3
444 static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browser,
445 struct callchain_node *chain_node,
446 u64 total, int level,
449 bool *is_current_entry)
451 struct rb_node *node;
452 int first_row = row, width, offset = level * LEVEL_OFFSET_STEP;
453 u64 new_total, remaining;
455 if (callchain_param.mode == CHAIN_GRAPH_REL)
456 new_total = chain_node->children_hit;
460 remaining = new_total;
461 node = rb_first(&chain_node->rb_root);
463 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
464 struct rb_node *next = rb_next(node);
465 u64 cumul = callchain_cumul_hits(child);
466 struct callchain_list *chain;
467 char folded_sign = ' ';
469 int extra_offset = 0;
473 list_for_each_entry(chain, &child->val, list) {
474 char bf[1024], *alloc_str;
477 bool was_first = first;
482 extra_offset = LEVEL_OFFSET_STEP;
484 folded_sign = callchain_list__folded(chain);
485 if (*row_offset != 0) {
491 str = callchain_list__sym_name(chain, bf, sizeof(bf),
494 double percent = cumul * 100.0 / new_total;
496 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
497 str = "Not enough memory!";
502 color = HE_COLORSET_NORMAL;
503 width = browser->b.width - (offset + extra_offset + 2);
504 if (ui_browser__is_current_entry(&browser->b, row)) {
505 browser->selection = &chain->ms;
506 color = HE_COLORSET_SELECTED;
507 *is_current_entry = true;
510 ui_browser__set_color(&browser->b, color);
511 ui_browser__gotorc(&browser->b, row, 0);
512 slsmg_write_nstring(" ", offset + extra_offset);
513 slsmg_printf("%c ", folded_sign);
514 slsmg_write_nstring(str, width);
517 if (++row == browser->b.height)
520 if (folded_sign == '+')
524 if (folded_sign == '-') {
525 const int new_level = level + (extra_offset ? 2 : 1);
526 row += hist_browser__show_callchain_node_rb_tree(browser, child, new_total,
527 new_level, row, row_offset,
530 if (row == browser->b.height)
535 return row - first_row;
538 static int hist_browser__show_callchain_node(struct hist_browser *browser,
539 struct callchain_node *node,
540 int level, unsigned short row,
542 bool *is_current_entry)
544 struct callchain_list *chain;
546 offset = level * LEVEL_OFFSET_STEP,
547 width = browser->b.width - offset;
548 char folded_sign = ' ';
550 list_for_each_entry(chain, &node->val, list) {
554 folded_sign = callchain_list__folded(chain);
556 if (*row_offset != 0) {
561 color = HE_COLORSET_NORMAL;
562 if (ui_browser__is_current_entry(&browser->b, row)) {
563 browser->selection = &chain->ms;
564 color = HE_COLORSET_SELECTED;
565 *is_current_entry = true;
568 s = callchain_list__sym_name(chain, bf, sizeof(bf),
570 ui_browser__gotorc(&browser->b, row, 0);
571 ui_browser__set_color(&browser->b, color);
572 slsmg_write_nstring(" ", offset);
573 slsmg_printf("%c ", folded_sign);
574 slsmg_write_nstring(s, width - 2);
576 if (++row == browser->b.height)
580 if (folded_sign == '-')
581 row += hist_browser__show_callchain_node_rb_tree(browser, node,
582 browser->hists->stats.total_period,
587 return row - first_row;
590 static int hist_browser__show_callchain(struct hist_browser *browser,
591 struct rb_root *chain,
592 int level, unsigned short row,
594 bool *is_current_entry)
599 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
600 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
602 row += hist_browser__show_callchain_node(browser, node, level,
605 if (row == browser->b.height)
609 return row - first_row;
613 struct ui_browser *b;
618 static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
620 struct hpp_arg *arg = hpp->ptr;
626 percent = va_arg(args, double);
629 ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
631 ret = scnprintf(hpp->buf, hpp->size, fmt, percent);
632 slsmg_printf("%s", hpp->buf);
634 advance_hpp(hpp, ret);
638 #define __HPP_COLOR_PERCENT_FN(_type, _field) \
639 static u64 __hpp_get_##_field(struct hist_entry *he) \
641 return he->stat._field; \
645 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,\
646 struct perf_hpp *hpp, \
647 struct hist_entry *he) \
649 return __hpp__fmt(hpp, he, __hpp_get_##_field, " %6.2f%%", \
650 __hpp__slsmg_color_printf, true); \
653 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
654 static u64 __hpp_get_acc_##_field(struct hist_entry *he) \
656 return he->stat_acc->_field; \
660 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,\
661 struct perf_hpp *hpp, \
662 struct hist_entry *he) \
664 if (!symbol_conf.cumulate_callchain) { \
665 int ret = scnprintf(hpp->buf, hpp->size, "%8s", "N/A"); \
666 slsmg_printf("%s", hpp->buf); \
670 return __hpp__fmt(hpp, he, __hpp_get_acc_##_field, " %6.2f%%", \
671 __hpp__slsmg_color_printf, true); \
674 __HPP_COLOR_PERCENT_FN(overhead, period)
675 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
676 __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
677 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
678 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
679 __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
681 #undef __HPP_COLOR_PERCENT_FN
682 #undef __HPP_COLOR_ACC_PERCENT_FN
684 void hist_browser__init_hpp(void)
686 perf_hpp__format[PERF_HPP__OVERHEAD].color =
687 hist_browser__hpp_color_overhead;
688 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
689 hist_browser__hpp_color_overhead_sys;
690 perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
691 hist_browser__hpp_color_overhead_us;
692 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
693 hist_browser__hpp_color_overhead_guest_sys;
694 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
695 hist_browser__hpp_color_overhead_guest_us;
696 perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
697 hist_browser__hpp_color_overhead_acc;
700 static int hist_browser__show_entry(struct hist_browser *browser,
701 struct hist_entry *entry,
706 int width = browser->b.width;
707 char folded_sign = ' ';
708 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
709 off_t row_offset = entry->row_offset;
711 struct perf_hpp_fmt *fmt;
714 browser->he_selection = entry;
715 browser->selection = &entry->ms;
718 if (symbol_conf.use_callchain) {
719 hist_entry__init_have_children(entry);
720 folded_sign = hist_entry__folded(entry);
723 if (row_offset == 0) {
724 struct hpp_arg arg = {
726 .folded_sign = folded_sign,
727 .current_entry = current_entry,
729 struct perf_hpp hpp = {
735 ui_browser__gotorc(&browser->b, row, 0);
737 perf_hpp__for_each_format(fmt) {
738 if (perf_hpp__should_skip(fmt))
741 if (current_entry && browser->b.navkeypressed) {
742 ui_browser__set_color(&browser->b,
743 HE_COLORSET_SELECTED);
745 ui_browser__set_color(&browser->b,
750 if (symbol_conf.use_callchain) {
751 slsmg_printf("%c ", folded_sign);
761 width -= fmt->color(fmt, &hpp, entry);
763 width -= fmt->entry(fmt, &hpp, entry);
764 slsmg_printf("%s", s);
768 /* The scroll bar isn't being used */
769 if (!browser->b.navkeypressed)
772 slsmg_write_nstring("", width);
779 if (folded_sign == '-' && row != browser->b.height) {
780 printed += hist_browser__show_callchain(browser, &entry->sorted_chain,
784 browser->he_selection = entry;
790 static void ui_browser__hists_init_top(struct ui_browser *browser)
792 if (browser->top == NULL) {
793 struct hist_browser *hb;
795 hb = container_of(browser, struct hist_browser, b);
796 browser->top = rb_first(&hb->hists->entries);
800 static unsigned int hist_browser__refresh(struct ui_browser *browser)
804 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
806 ui_browser__hists_init_top(browser);
808 for (nd = browser->top; nd; nd = rb_next(nd)) {
809 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
815 percent = hist_entry__get_percent_limit(h);
816 if (percent < hb->min_pcnt)
819 row += hist_browser__show_entry(hb, h, row);
820 if (row == browser->height)
827 static struct rb_node *hists__filter_entries(struct rb_node *nd,
831 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
832 float percent = hist_entry__get_percent_limit(h);
834 if (!h->filtered && percent >= min_pcnt)
843 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
847 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
848 float percent = hist_entry__get_percent_limit(h);
850 if (!h->filtered && percent >= min_pcnt)
859 static void ui_browser__hists_seek(struct ui_browser *browser,
860 off_t offset, int whence)
862 struct hist_entry *h;
865 struct hist_browser *hb;
867 hb = container_of(browser, struct hist_browser, b);
869 if (browser->nr_entries == 0)
872 ui_browser__hists_init_top(browser);
876 nd = hists__filter_entries(rb_first(browser->entries),
883 nd = hists__filter_prev_entries(rb_last(browser->entries),
892 * Moves not relative to the first visible entry invalidates its
895 h = rb_entry(browser->top, struct hist_entry, rb_node);
899 * Here we have to check if nd is expanded (+), if it is we can't go
900 * the next top level hist_entry, instead we must compute an offset of
901 * what _not_ to show and not change the first visible entry.
903 * This offset increments when we are going from top to bottom and
904 * decreases when we're going from bottom to top.
906 * As we don't have backpointers to the top level in the callchains
907 * structure, we need to always print the whole hist_entry callchain,
908 * skipping the first ones that are before the first visible entry
909 * and stop when we printed enough lines to fill the screen.
914 h = rb_entry(nd, struct hist_entry, rb_node);
915 if (h->ms.unfolded) {
916 u16 remaining = h->nr_rows - h->row_offset;
917 if (offset > remaining) {
921 h->row_offset += offset;
927 nd = hists__filter_entries(rb_next(nd), hb->min_pcnt);
932 } while (offset != 0);
933 } else if (offset < 0) {
935 h = rb_entry(nd, struct hist_entry, rb_node);
936 if (h->ms.unfolded) {
938 if (-offset > h->row_offset) {
939 offset += h->row_offset;
942 h->row_offset += offset;
948 if (-offset > h->nr_rows) {
949 offset += h->nr_rows;
952 h->row_offset = h->nr_rows + offset;
960 nd = hists__filter_prev_entries(rb_prev(nd),
968 * Last unfiltered hist_entry, check if it is
969 * unfolded, if it is then we should have
970 * row_offset at its last entry.
972 h = rb_entry(nd, struct hist_entry, rb_node);
974 h->row_offset = h->nr_rows;
981 h = rb_entry(nd, struct hist_entry, rb_node);
986 static int hist_browser__fprintf_callchain_node_rb_tree(struct hist_browser *browser,
987 struct callchain_node *chain_node,
988 u64 total, int level,
991 struct rb_node *node;
992 int offset = level * LEVEL_OFFSET_STEP;
993 u64 new_total, remaining;
996 if (callchain_param.mode == CHAIN_GRAPH_REL)
997 new_total = chain_node->children_hit;
1001 remaining = new_total;
1002 node = rb_first(&chain_node->rb_root);
1004 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1005 struct rb_node *next = rb_next(node);
1006 u64 cumul = callchain_cumul_hits(child);
1007 struct callchain_list *chain;
1008 char folded_sign = ' ';
1010 int extra_offset = 0;
1014 list_for_each_entry(chain, &child->val, list) {
1015 char bf[1024], *alloc_str;
1017 bool was_first = first;
1022 extra_offset = LEVEL_OFFSET_STEP;
1024 folded_sign = callchain_list__folded(chain);
1027 str = callchain_list__sym_name(chain, bf, sizeof(bf),
1030 double percent = cumul * 100.0 / new_total;
1032 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
1033 str = "Not enough memory!";
1038 printed += fprintf(fp, "%*s%c %s\n", offset + extra_offset, " ", folded_sign, str);
1040 if (folded_sign == '+')
1044 if (folded_sign == '-') {
1045 const int new_level = level + (extra_offset ? 2 : 1);
1046 printed += hist_browser__fprintf_callchain_node_rb_tree(browser, child, new_total,
1056 static int hist_browser__fprintf_callchain_node(struct hist_browser *browser,
1057 struct callchain_node *node,
1058 int level, FILE *fp)
1060 struct callchain_list *chain;
1061 int offset = level * LEVEL_OFFSET_STEP;
1062 char folded_sign = ' ';
1065 list_for_each_entry(chain, &node->val, list) {
1068 folded_sign = callchain_list__folded(chain);
1069 s = callchain_list__sym_name(chain, bf, sizeof(bf), browser->show_dso);
1070 printed += fprintf(fp, "%*s%c %s\n", offset, " ", folded_sign, s);
1073 if (folded_sign == '-')
1074 printed += hist_browser__fprintf_callchain_node_rb_tree(browser, node,
1075 browser->hists->stats.total_period,
1080 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1081 struct rb_root *chain, int level, FILE *fp)
1086 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
1087 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
1089 printed += hist_browser__fprintf_callchain_node(browser, node, level, fp);
1095 static int hist_browser__fprintf_entry(struct hist_browser *browser,
1096 struct hist_entry *he, FILE *fp)
1100 char folded_sign = ' ';
1101 struct perf_hpp hpp = {
1105 struct perf_hpp_fmt *fmt;
1109 if (symbol_conf.use_callchain)
1110 folded_sign = hist_entry__folded(he);
1112 if (symbol_conf.use_callchain)
1113 printed += fprintf(fp, "%c ", folded_sign);
1115 perf_hpp__for_each_format(fmt) {
1116 if (perf_hpp__should_skip(fmt))
1120 ret = scnprintf(hpp.buf, hpp.size, " ");
1121 advance_hpp(&hpp, ret);
1125 ret = fmt->entry(fmt, &hpp, he);
1126 advance_hpp(&hpp, ret);
1128 printed += fprintf(fp, "%s\n", rtrim(s));
1130 if (folded_sign == '-')
1131 printed += hist_browser__fprintf_callchain(browser, &he->sorted_chain, 1, fp);
1136 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1138 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
1143 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1145 printed += hist_browser__fprintf_entry(browser, h, fp);
1146 nd = hists__filter_entries(rb_next(nd), browser->min_pcnt);
1152 static int hist_browser__dump(struct hist_browser *browser)
1158 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
1159 if (access(filename, F_OK))
1162 * XXX: Just an arbitrary lazy upper limit
1164 if (++browser->print_seq == 8192) {
1165 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1170 fp = fopen(filename, "w");
1173 const char *err = strerror_r(errno, bf, sizeof(bf));
1174 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
1178 ++browser->print_seq;
1179 hist_browser__fprintf(browser, fp);
1181 ui_helpline__fpush("%s written!", filename);
1186 static struct hist_browser *hist_browser__new(struct hists *hists)
1188 struct hist_browser *browser = zalloc(sizeof(*browser));
1191 browser->hists = hists;
1192 browser->b.refresh = hist_browser__refresh;
1193 browser->b.seek = ui_browser__hists_seek;
1194 browser->b.use_navkeypressed = true;
1200 static void hist_browser__delete(struct hist_browser *browser)
1205 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
1207 return browser->he_selection;
1210 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
1212 return browser->he_selection->thread;
1215 static int hists__browser_title(struct hists *hists, char *bf, size_t size,
1216 const char *ev_name)
1220 const struct dso *dso = hists->dso_filter;
1221 const struct thread *thread = hists->thread_filter;
1222 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1223 u64 nr_events = hists->stats.total_period;
1224 struct perf_evsel *evsel = hists_to_evsel(hists);
1226 size_t buflen = sizeof(buf);
1228 if (symbol_conf.filter_relative) {
1229 nr_samples = hists->stats.nr_non_filtered_samples;
1230 nr_events = hists->stats.total_non_filtered_period;
1233 if (perf_evsel__is_group_event(evsel)) {
1234 struct perf_evsel *pos;
1236 perf_evsel__group_desc(evsel, buf, buflen);
1239 for_each_group_member(pos, evsel) {
1240 if (symbol_conf.filter_relative) {
1241 nr_samples += pos->hists.stats.nr_non_filtered_samples;
1242 nr_events += pos->hists.stats.total_non_filtered_period;
1244 nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1245 nr_events += pos->hists.stats.total_period;
1250 nr_samples = convert_unit(nr_samples, &unit);
1251 printed = scnprintf(bf, size,
1252 "Samples: %lu%c of event '%s', Event count (approx.): %lu",
1253 nr_samples, unit, ev_name, nr_events);
1256 if (hists->uid_filter_str)
1257 printed += snprintf(bf + printed, size - printed,
1258 ", UID: %s", hists->uid_filter_str);
1260 printed += scnprintf(bf + printed, size - printed,
1262 (thread->comm_set ? thread__comm_str(thread) : ""),
1265 printed += scnprintf(bf + printed, size - printed,
1266 ", DSO: %s", dso->short_name);
1270 static inline void free_popup_options(char **options, int n)
1274 for (i = 0; i < n; ++i)
1278 /* Check whether the browser is for 'top' or 'report' */
1279 static inline bool is_report_browser(void *timer)
1281 return timer == NULL;
1285 * Only runtime switching of perf data file will make "input_name" point
1286 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1287 * whether we need to call free() for current "input_name" during the switch.
1289 static bool is_input_name_malloced = false;
1291 static int switch_data_file(void)
1293 char *pwd, *options[32], *abs_path[32], *tmp;
1295 int nr_options = 0, choice = -1, ret = -1;
1296 struct dirent *dent;
1298 pwd = getenv("PWD");
1302 pwd_dir = opendir(pwd);
1306 memset(options, 0, sizeof(options));
1307 memset(options, 0, sizeof(abs_path));
1309 while ((dent = readdir(pwd_dir))) {
1310 char path[PATH_MAX];
1312 char *name = dent->d_name;
1315 if (!(dent->d_type == DT_REG))
1318 snprintf(path, sizeof(path), "%s/%s", pwd, name);
1320 file = fopen(path, "r");
1324 if (fread(&magic, 1, 8, file) < 8)
1325 goto close_file_and_continue;
1327 if (is_perf_magic(magic)) {
1328 options[nr_options] = strdup(name);
1329 if (!options[nr_options])
1330 goto close_file_and_continue;
1332 abs_path[nr_options] = strdup(path);
1333 if (!abs_path[nr_options]) {
1334 zfree(&options[nr_options]);
1335 ui__warning("Can't search all data files due to memory shortage.\n");
1343 close_file_and_continue:
1345 if (nr_options >= 32) {
1346 ui__warning("Too many perf data files in PWD!\n"
1347 "Only the first 32 files will be listed.\n");
1354 choice = ui__popup_menu(nr_options, options);
1355 if (choice < nr_options && choice >= 0) {
1356 tmp = strdup(abs_path[choice]);
1358 if (is_input_name_malloced)
1359 free((void *)input_name);
1361 is_input_name_malloced = true;
1364 ui__warning("Data switch failed due to memory shortage!\n");
1368 free_popup_options(options, nr_options);
1369 free_popup_options(abs_path, nr_options);
1373 static void hist_browser__update_nr_entries(struct hist_browser *hb)
1376 struct rb_node *nd = rb_first(&hb->hists->entries);
1378 if (hb->min_pcnt == 0) {
1379 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
1383 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
1388 hb->nr_non_filtered_entries = nr_entries;
1391 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
1392 const char *helpline, const char *ev_name,
1394 struct hist_browser_timer *hbt,
1396 struct perf_session_env *env)
1398 struct hists *hists = &evsel->hists;
1399 struct hist_browser *browser = hist_browser__new(hists);
1400 struct branch_info *bi;
1401 struct pstack *fstack;
1406 char script_opt[64];
1407 int delay_secs = hbt ? hbt->refresh : 0;
1409 #define HIST_BROWSER_HELP_COMMON \
1410 "h/?/F1 Show this window\n" \
1412 "PGDN/SPACE Navigate\n" \
1413 "q/ESC/CTRL+C Exit browser\n\n" \
1414 "For multiple event sessions:\n\n" \
1415 "TAB/UNTAB Switch events\n\n" \
1416 "For symbolic views (--sort has sym):\n\n" \
1417 "-> Zoom into DSO/Threads & Annotate current symbol\n" \
1419 "a Annotate current symbol\n" \
1420 "C Collapse all callchains\n" \
1421 "d Zoom into current DSO\n" \
1422 "E Expand all callchains\n" \
1423 "F Toggle percentage of filtered entries\n" \
1425 /* help messages are sorted by lexical order of the hotkey */
1426 const char report_help[] = HIST_BROWSER_HELP_COMMON
1427 "i Show header information\n"
1428 "P Print histograms to perf.hist.N\n"
1429 "r Run available scripts\n"
1430 "s Switch to another data file in PWD\n"
1431 "t Zoom into current Thread\n"
1432 "V Verbose (DSO names in callchains, etc)\n"
1433 "/ Filter symbol by name";
1434 const char top_help[] = HIST_BROWSER_HELP_COMMON
1435 "P Print histograms to perf.hist.N\n"
1436 "t Zoom into current Thread\n"
1437 "V Verbose (DSO names in callchains, etc)\n"
1438 "/ Filter symbol by name";
1440 if (browser == NULL)
1444 browser->min_pcnt = min_pcnt;
1445 hist_browser__update_nr_entries(browser);
1448 fstack = pstack__new(2);
1452 ui_helpline__push(helpline);
1454 memset(options, 0, sizeof(options));
1457 const struct thread *thread = NULL;
1458 const struct dso *dso = NULL;
1460 annotate = -2, zoom_dso = -2, zoom_thread = -2,
1461 annotate_f = -2, annotate_t = -2, browse_map = -2;
1462 int scripts_comm = -2, scripts_symbol = -2,
1463 scripts_all = -2, switch_data = -2;
1467 key = hist_browser__run(browser, ev_name, hbt);
1469 if (browser->he_selection != NULL) {
1470 thread = hist_browser__selected_thread(browser);
1471 dso = browser->selection->map ? browser->selection->map->dso : NULL;
1479 * Exit the browser, let hists__browser_tree
1480 * go to the next or previous
1482 goto out_free_stack;
1484 if (!sort__has_sym) {
1485 ui_browser__warning(&browser->b, delay_secs * 2,
1486 "Annotation is only available for symbolic views, "
1487 "include \"sym*\" in --sort to use it.");
1491 if (browser->selection == NULL ||
1492 browser->selection->sym == NULL ||
1493 browser->selection->map->dso->annotate_warned)
1497 hist_browser__dump(browser);
1502 browser->show_dso = !browser->show_dso;
1507 if (ui_browser__input_window("Symbol to show",
1508 "Please enter the name of symbol you want to see",
1509 buf, "ENTER: OK, ESC: Cancel",
1510 delay_secs * 2) == K_ENTER) {
1511 hists->symbol_filter_str = *buf ? buf : NULL;
1512 hists__filter_by_symbol(hists);
1513 hist_browser__reset(browser);
1517 if (is_report_browser(hbt))
1521 if (is_report_browser(hbt))
1522 goto do_data_switch;
1525 /* env->arch is NULL for live-mode (i.e. perf top) */
1527 tui__header_window(env);
1530 symbol_conf.filter_relative ^= 1;
1535 ui_browser__help_window(&browser->b,
1536 is_report_browser(hbt) ? report_help : top_help);
1545 if (pstack__empty(fstack)) {
1547 * Go back to the perf_evsel_menu__run or other user
1550 goto out_free_stack;
1553 top = pstack__pop(fstack);
1554 if (top == &browser->hists->dso_filter)
1556 if (top == &browser->hists->thread_filter)
1557 goto zoom_out_thread;
1562 !ui_browser__dialog_yesno(&browser->b,
1563 "Do you really want to exit?"))
1568 goto out_free_stack;
1574 goto add_exit_option;
1576 if (sort__mode == SORT_MODE__BRANCH) {
1577 bi = browser->he_selection->branch_info;
1578 if (browser->selection != NULL &&
1580 bi->from.sym != NULL &&
1581 !bi->from.map->dso->annotate_warned &&
1582 asprintf(&options[nr_options], "Annotate %s",
1583 bi->from.sym->name) > 0)
1584 annotate_f = nr_options++;
1586 if (browser->selection != NULL &&
1588 bi->to.sym != NULL &&
1589 !bi->to.map->dso->annotate_warned &&
1590 (bi->to.sym != bi->from.sym ||
1591 bi->to.map->dso != bi->from.map->dso) &&
1592 asprintf(&options[nr_options], "Annotate %s",
1593 bi->to.sym->name) > 0)
1594 annotate_t = nr_options++;
1597 if (browser->selection != NULL &&
1598 browser->selection->sym != NULL &&
1599 !browser->selection->map->dso->annotate_warned &&
1600 asprintf(&options[nr_options], "Annotate %s",
1601 browser->selection->sym->name) > 0)
1602 annotate = nr_options++;
1605 if (thread != NULL &&
1606 asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
1607 (browser->hists->thread_filter ? "out of" : "into"),
1608 (thread->comm_set ? thread__comm_str(thread) : ""),
1610 zoom_thread = nr_options++;
1613 asprintf(&options[nr_options], "Zoom %s %s DSO",
1614 (browser->hists->dso_filter ? "out of" : "into"),
1615 (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
1616 zoom_dso = nr_options++;
1618 if (browser->selection != NULL &&
1619 browser->selection->map != NULL &&
1620 asprintf(&options[nr_options], "Browse map details") > 0)
1621 browse_map = nr_options++;
1623 /* perf script support */
1624 if (browser->he_selection) {
1627 if (asprintf(&options[nr_options], "Run scripts for samples of thread [%s]",
1628 thread__comm_str(browser->he_selection->thread)) > 0)
1629 scripts_comm = nr_options++;
1631 sym = browser->he_selection->ms.sym;
1632 if (sym && sym->namelen &&
1633 asprintf(&options[nr_options], "Run scripts for samples of symbol [%s]",
1635 scripts_symbol = nr_options++;
1638 if (asprintf(&options[nr_options], "Run scripts for all samples") > 0)
1639 scripts_all = nr_options++;
1641 if (is_report_browser(hbt) && asprintf(&options[nr_options],
1642 "Switch to another data file in PWD") > 0)
1643 switch_data = nr_options++;
1645 options[nr_options++] = (char *)"Exit";
1647 choice = ui__popup_menu(nr_options, options);
1649 if (choice == nr_options - 1)
1653 free_popup_options(options, nr_options - 1);
1657 if (choice == annotate || choice == annotate_t || choice == annotate_f) {
1658 struct hist_entry *he;
1661 if (!objdump_path && perf_session_env__lookup_objdump(env))
1664 he = hist_browser__selected_entry(browser);
1669 * we stash the branch_info symbol + map into the
1670 * the ms so we don't have to rewrite all the annotation
1671 * code to use branch_info.
1672 * in branch mode, the ms struct is not used
1674 if (choice == annotate_f) {
1675 he->ms.sym = he->branch_info->from.sym;
1676 he->ms.map = he->branch_info->from.map;
1677 } else if (choice == annotate_t) {
1678 he->ms.sym = he->branch_info->to.sym;
1679 he->ms.map = he->branch_info->to.map;
1683 * Don't let this be freed, say, by hists__decay_entry.
1686 err = hist_entry__tui_annotate(he, evsel, hbt);
1689 * offer option to annotate the other branch source or target
1690 * (if they exists) when returning from annotate
1692 if ((err == 'q' || err == CTRL('c'))
1693 && annotate_t != -2 && annotate_f != -2)
1694 goto retry_popup_menu;
1696 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1698 ui_browser__handle_resize(&browser->b);
1700 } else if (choice == browse_map)
1701 map__browse(browser->selection->map);
1702 else if (choice == zoom_dso) {
1704 if (browser->hists->dso_filter) {
1705 pstack__remove(fstack, &browser->hists->dso_filter);
1708 browser->hists->dso_filter = NULL;
1709 sort_dso.elide = false;
1713 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1714 dso->kernel ? "the Kernel" : dso->short_name);
1715 browser->hists->dso_filter = dso;
1716 sort_dso.elide = true;
1717 pstack__push(fstack, &browser->hists->dso_filter);
1719 hists__filter_by_dso(hists);
1720 hist_browser__reset(browser);
1721 } else if (choice == zoom_thread) {
1723 if (browser->hists->thread_filter) {
1724 pstack__remove(fstack, &browser->hists->thread_filter);
1727 browser->hists->thread_filter = NULL;
1728 sort_thread.elide = false;
1730 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1731 thread->comm_set ? thread__comm_str(thread) : "",
1733 browser->hists->thread_filter = thread;
1734 sort_thread.elide = true;
1735 pstack__push(fstack, &browser->hists->thread_filter);
1737 hists__filter_by_thread(hists);
1738 hist_browser__reset(browser);
1740 /* perf scripts support */
1741 else if (choice == scripts_all || choice == scripts_comm ||
1742 choice == scripts_symbol) {
1744 memset(script_opt, 0, 64);
1746 if (choice == scripts_comm)
1747 sprintf(script_opt, " -c %s ", thread__comm_str(browser->he_selection->thread));
1749 if (choice == scripts_symbol)
1750 sprintf(script_opt, " -S %s ", browser->he_selection->ms.sym->name);
1752 script_browse(script_opt);
1754 /* Switch to another data file */
1755 else if (choice == switch_data) {
1757 if (!switch_data_file()) {
1758 key = K_SWITCH_INPUT_DATA;
1761 ui__warning("Won't switch the data files due to\n"
1762 "no valid data file get selected!\n");
1766 pstack__delete(fstack);
1768 hist_browser__delete(browser);
1769 free_popup_options(options, nr_options - 1);
1773 struct perf_evsel_menu {
1774 struct ui_browser b;
1775 struct perf_evsel *selection;
1776 bool lost_events, lost_events_warned;
1778 struct perf_session_env *env;
1781 static void perf_evsel_menu__write(struct ui_browser *browser,
1782 void *entry, int row)
1784 struct perf_evsel_menu *menu = container_of(browser,
1785 struct perf_evsel_menu, b);
1786 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1787 bool current_entry = ui_browser__is_current_entry(browser, row);
1788 unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1789 const char *ev_name = perf_evsel__name(evsel);
1791 const char *warn = " ";
1794 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
1795 HE_COLORSET_NORMAL);
1797 if (perf_evsel__is_group_event(evsel)) {
1798 struct perf_evsel *pos;
1800 ev_name = perf_evsel__group_name(evsel);
1802 for_each_group_member(pos, evsel) {
1803 nr_events += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1807 nr_events = convert_unit(nr_events, &unit);
1808 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
1809 unit, unit == ' ' ? "" : " ", ev_name);
1810 slsmg_printf("%s", bf);
1812 nr_events = evsel->hists.stats.nr_events[PERF_RECORD_LOST];
1813 if (nr_events != 0) {
1814 menu->lost_events = true;
1816 ui_browser__set_color(browser, HE_COLORSET_TOP);
1817 nr_events = convert_unit(nr_events, &unit);
1818 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
1819 nr_events, unit, unit == ' ' ? "" : " ");
1823 slsmg_write_nstring(warn, browser->width - printed);
1826 menu->selection = evsel;
1829 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
1830 int nr_events, const char *help,
1831 struct hist_browser_timer *hbt)
1833 struct perf_evlist *evlist = menu->b.priv;
1834 struct perf_evsel *pos;
1835 const char *ev_name, *title = "Available samples";
1836 int delay_secs = hbt ? hbt->refresh : 0;
1839 if (ui_browser__show(&menu->b, title,
1840 "ESC: exit, ENTER|->: Browse histograms") < 0)
1844 key = ui_browser__run(&menu->b, delay_secs);
1848 hbt->timer(hbt->arg);
1850 if (!menu->lost_events_warned && menu->lost_events) {
1851 ui_browser__warn_lost_events(&menu->b);
1852 menu->lost_events_warned = true;
1857 if (!menu->selection)
1859 pos = menu->selection;
1861 perf_evlist__set_selected(evlist, pos);
1863 * Give the calling tool a chance to populate the non
1864 * default evsel resorted hists tree.
1867 hbt->timer(hbt->arg);
1868 ev_name = perf_evsel__name(pos);
1869 key = perf_evsel__hists_browse(pos, nr_events, help,
1873 ui_browser__show_title(&menu->b, title);
1876 if (pos->node.next == &evlist->entries)
1877 pos = perf_evlist__first(evlist);
1879 pos = perf_evsel__next(pos);
1882 if (pos->node.prev == &evlist->entries)
1883 pos = perf_evlist__last(evlist);
1885 pos = perf_evsel__prev(pos);
1888 if (!ui_browser__dialog_yesno(&menu->b,
1889 "Do you really want to exit?"))
1892 case K_SWITCH_INPUT_DATA:
1902 if (!ui_browser__dialog_yesno(&menu->b,
1903 "Do you really want to exit?"))
1915 ui_browser__hide(&menu->b);
1919 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
1922 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1924 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
1930 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
1931 int nr_entries, const char *help,
1932 struct hist_browser_timer *hbt,
1934 struct perf_session_env *env)
1936 struct perf_evsel *pos;
1937 struct perf_evsel_menu menu = {
1939 .entries = &evlist->entries,
1940 .refresh = ui_browser__list_head_refresh,
1941 .seek = ui_browser__list_head_seek,
1942 .write = perf_evsel_menu__write,
1943 .filter = filter_group_entries,
1944 .nr_entries = nr_entries,
1947 .min_pcnt = min_pcnt,
1951 ui_helpline__push("Press ESC to exit");
1953 evlist__for_each(evlist, pos) {
1954 const char *ev_name = perf_evsel__name(pos);
1955 size_t line_len = strlen(ev_name) + 7;
1957 if (menu.b.width < line_len)
1958 menu.b.width = line_len;
1961 return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
1964 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
1965 struct hist_browser_timer *hbt,
1967 struct perf_session_env *env)
1969 int nr_entries = evlist->nr_entries;
1972 if (nr_entries == 1) {
1973 struct perf_evsel *first = perf_evlist__first(evlist);
1974 const char *ev_name = perf_evsel__name(first);
1976 return perf_evsel__hists_browse(first, nr_entries, help,
1977 ev_name, false, hbt, min_pcnt,
1981 if (symbol_conf.event_group) {
1982 struct perf_evsel *pos;
1985 evlist__for_each(evlist, pos) {
1986 if (perf_evsel__is_group_leader(pos))
1990 if (nr_entries == 1)
1994 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
1995 hbt, min_pcnt, env);