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;
30 u64 nr_non_filtered_entries;
31 u64 nr_callchain_rows;
34 extern void hist_browser__init_hpp(void);
36 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,
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));
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));
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), rows=%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.rows)
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.rows)
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.rows)
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.rows)
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.rows) {
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->rows)
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)
1219 const struct dso *dso = hists->dso_filter;
1220 const struct thread *thread = hists->thread_filter;
1221 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1222 u64 nr_events = hists->stats.total_period;
1223 struct perf_evsel *evsel = hists_to_evsel(hists);
1224 const char *ev_name = perf_evsel__name(evsel);
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,
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, 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++;
1596 if (browser->selection != NULL &&
1597 browser->selection->sym != NULL &&
1598 !browser->selection->map->dso->annotate_warned) {
1599 struct annotation *notes;
1601 notes = symbol__annotation(browser->selection->sym);
1604 asprintf(&options[nr_options], "Annotate %s",
1605 browser->selection->sym->name) > 0)
1606 annotate = nr_options++;
1610 if (thread != NULL &&
1611 asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
1612 (browser->hists->thread_filter ? "out of" : "into"),
1613 (thread->comm_set ? thread__comm_str(thread) : ""),
1615 zoom_thread = nr_options++;
1618 asprintf(&options[nr_options], "Zoom %s %s DSO",
1619 (browser->hists->dso_filter ? "out of" : "into"),
1620 (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
1621 zoom_dso = nr_options++;
1623 if (browser->selection != NULL &&
1624 browser->selection->map != NULL &&
1625 asprintf(&options[nr_options], "Browse map details") > 0)
1626 browse_map = nr_options++;
1628 /* perf script support */
1629 if (browser->he_selection) {
1632 if (asprintf(&options[nr_options], "Run scripts for samples of thread [%s]",
1633 thread__comm_str(browser->he_selection->thread)) > 0)
1634 scripts_comm = nr_options++;
1636 sym = browser->he_selection->ms.sym;
1637 if (sym && sym->namelen &&
1638 asprintf(&options[nr_options], "Run scripts for samples of symbol [%s]",
1640 scripts_symbol = nr_options++;
1643 if (asprintf(&options[nr_options], "Run scripts for all samples") > 0)
1644 scripts_all = nr_options++;
1646 if (is_report_browser(hbt) && asprintf(&options[nr_options],
1647 "Switch to another data file in PWD") > 0)
1648 switch_data = nr_options++;
1650 options[nr_options++] = (char *)"Exit";
1652 choice = ui__popup_menu(nr_options, options);
1654 if (choice == nr_options - 1)
1658 free_popup_options(options, nr_options - 1);
1662 if (choice == annotate || choice == annotate_t || choice == annotate_f) {
1663 struct hist_entry *he;
1664 struct annotation *notes;
1667 if (!objdump_path && perf_session_env__lookup_objdump(env))
1670 he = hist_browser__selected_entry(browser);
1675 * we stash the branch_info symbol + map into the
1676 * the ms so we don't have to rewrite all the annotation
1677 * code to use branch_info.
1678 * in branch mode, the ms struct is not used
1680 if (choice == annotate_f) {
1681 he->ms.sym = he->branch_info->from.sym;
1682 he->ms.map = he->branch_info->from.map;
1683 } else if (choice == annotate_t) {
1684 he->ms.sym = he->branch_info->to.sym;
1685 he->ms.map = he->branch_info->to.map;
1688 notes = symbol__annotation(he->ms.sym);
1693 * Don't let this be freed, say, by hists__decay_entry.
1696 err = hist_entry__tui_annotate(he, evsel, hbt);
1699 * offer option to annotate the other branch source or target
1700 * (if they exists) when returning from annotate
1702 if ((err == 'q' || err == CTRL('c'))
1703 && annotate_t != -2 && annotate_f != -2)
1704 goto retry_popup_menu;
1706 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1708 ui_browser__handle_resize(&browser->b);
1710 } else if (choice == browse_map)
1711 map__browse(browser->selection->map);
1712 else if (choice == zoom_dso) {
1714 if (browser->hists->dso_filter) {
1715 pstack__remove(fstack, &browser->hists->dso_filter);
1718 browser->hists->dso_filter = NULL;
1719 perf_hpp__set_elide(HISTC_DSO, false);
1723 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1724 dso->kernel ? "the Kernel" : dso->short_name);
1725 browser->hists->dso_filter = dso;
1726 perf_hpp__set_elide(HISTC_DSO, true);
1727 pstack__push(fstack, &browser->hists->dso_filter);
1729 hists__filter_by_dso(hists);
1730 hist_browser__reset(browser);
1731 } else if (choice == zoom_thread) {
1733 if (browser->hists->thread_filter) {
1734 pstack__remove(fstack, &browser->hists->thread_filter);
1737 browser->hists->thread_filter = NULL;
1738 perf_hpp__set_elide(HISTC_THREAD, false);
1740 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1741 thread->comm_set ? thread__comm_str(thread) : "",
1743 browser->hists->thread_filter = thread;
1744 perf_hpp__set_elide(HISTC_THREAD, false);
1745 pstack__push(fstack, &browser->hists->thread_filter);
1747 hists__filter_by_thread(hists);
1748 hist_browser__reset(browser);
1750 /* perf scripts support */
1751 else if (choice == scripts_all || choice == scripts_comm ||
1752 choice == scripts_symbol) {
1754 memset(script_opt, 0, 64);
1756 if (choice == scripts_comm)
1757 sprintf(script_opt, " -c %s ", thread__comm_str(browser->he_selection->thread));
1759 if (choice == scripts_symbol)
1760 sprintf(script_opt, " -S %s ", browser->he_selection->ms.sym->name);
1762 script_browse(script_opt);
1764 /* Switch to another data file */
1765 else if (choice == switch_data) {
1767 if (!switch_data_file()) {
1768 key = K_SWITCH_INPUT_DATA;
1771 ui__warning("Won't switch the data files due to\n"
1772 "no valid data file get selected!\n");
1776 pstack__delete(fstack);
1778 hist_browser__delete(browser);
1779 free_popup_options(options, nr_options - 1);
1783 struct perf_evsel_menu {
1784 struct ui_browser b;
1785 struct perf_evsel *selection;
1786 bool lost_events, lost_events_warned;
1788 struct perf_session_env *env;
1791 static void perf_evsel_menu__write(struct ui_browser *browser,
1792 void *entry, int row)
1794 struct perf_evsel_menu *menu = container_of(browser,
1795 struct perf_evsel_menu, b);
1796 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1797 bool current_entry = ui_browser__is_current_entry(browser, row);
1798 unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1799 const char *ev_name = perf_evsel__name(evsel);
1801 const char *warn = " ";
1804 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
1805 HE_COLORSET_NORMAL);
1807 if (perf_evsel__is_group_event(evsel)) {
1808 struct perf_evsel *pos;
1810 ev_name = perf_evsel__group_name(evsel);
1812 for_each_group_member(pos, evsel) {
1813 nr_events += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1817 nr_events = convert_unit(nr_events, &unit);
1818 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
1819 unit, unit == ' ' ? "" : " ", ev_name);
1820 slsmg_printf("%s", bf);
1822 nr_events = evsel->hists.stats.nr_events[PERF_RECORD_LOST];
1823 if (nr_events != 0) {
1824 menu->lost_events = true;
1826 ui_browser__set_color(browser, HE_COLORSET_TOP);
1827 nr_events = convert_unit(nr_events, &unit);
1828 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
1829 nr_events, unit, unit == ' ' ? "" : " ");
1833 slsmg_write_nstring(warn, browser->width - printed);
1836 menu->selection = evsel;
1839 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
1840 int nr_events, const char *help,
1841 struct hist_browser_timer *hbt)
1843 struct perf_evlist *evlist = menu->b.priv;
1844 struct perf_evsel *pos;
1845 const char *title = "Available samples";
1846 int delay_secs = hbt ? hbt->refresh : 0;
1849 if (ui_browser__show(&menu->b, title,
1850 "ESC: exit, ENTER|->: Browse histograms") < 0)
1854 key = ui_browser__run(&menu->b, delay_secs);
1858 hbt->timer(hbt->arg);
1860 if (!menu->lost_events_warned && menu->lost_events) {
1861 ui_browser__warn_lost_events(&menu->b);
1862 menu->lost_events_warned = true;
1867 if (!menu->selection)
1869 pos = menu->selection;
1871 perf_evlist__set_selected(evlist, pos);
1873 * Give the calling tool a chance to populate the non
1874 * default evsel resorted hists tree.
1877 hbt->timer(hbt->arg);
1878 key = perf_evsel__hists_browse(pos, nr_events, help,
1882 ui_browser__show_title(&menu->b, title);
1885 if (pos->node.next == &evlist->entries)
1886 pos = perf_evlist__first(evlist);
1888 pos = perf_evsel__next(pos);
1891 if (pos->node.prev == &evlist->entries)
1892 pos = perf_evlist__last(evlist);
1894 pos = perf_evsel__prev(pos);
1897 if (!ui_browser__dialog_yesno(&menu->b,
1898 "Do you really want to exit?"))
1901 case K_SWITCH_INPUT_DATA:
1911 if (!ui_browser__dialog_yesno(&menu->b,
1912 "Do you really want to exit?"))
1924 ui_browser__hide(&menu->b);
1928 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
1931 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1933 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
1939 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
1940 int nr_entries, const char *help,
1941 struct hist_browser_timer *hbt,
1943 struct perf_session_env *env)
1945 struct perf_evsel *pos;
1946 struct perf_evsel_menu menu = {
1948 .entries = &evlist->entries,
1949 .refresh = ui_browser__list_head_refresh,
1950 .seek = ui_browser__list_head_seek,
1951 .write = perf_evsel_menu__write,
1952 .filter = filter_group_entries,
1953 .nr_entries = nr_entries,
1956 .min_pcnt = min_pcnt,
1960 ui_helpline__push("Press ESC to exit");
1962 evlist__for_each(evlist, pos) {
1963 const char *ev_name = perf_evsel__name(pos);
1964 size_t line_len = strlen(ev_name) + 7;
1966 if (menu.b.width < line_len)
1967 menu.b.width = line_len;
1970 return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
1973 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
1974 struct hist_browser_timer *hbt,
1976 struct perf_session_env *env)
1978 int nr_entries = evlist->nr_entries;
1981 if (nr_entries == 1) {
1982 struct perf_evsel *first = perf_evlist__first(evlist);
1984 return perf_evsel__hists_browse(first, nr_entries, help,
1985 false, hbt, min_pcnt,
1989 if (symbol_conf.event_group) {
1990 struct perf_evsel *pos;
1993 evlist__for_each(evlist, pos) {
1994 if (perf_evsel__is_group_leader(pos))
1998 if (nr_entries == 1)
2002 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
2003 hbt, min_pcnt, env);