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;
28 struct hist_browser_timer *hbt;
29 struct pstack *pstack;
30 struct perf_session_env *env;
35 u64 nr_non_filtered_entries;
36 u64 nr_callchain_rows;
39 extern void hist_browser__init_hpp(void);
41 static int hists__browser_title(struct hists *hists,
42 struct hist_browser_timer *hbt,
43 char *bf, size_t size);
44 static void hist_browser__update_nr_entries(struct hist_browser *hb);
46 static struct rb_node *hists__filter_entries(struct rb_node *nd,
49 static bool hist_browser__has_filter(struct hist_browser *hb)
51 return hists__has_filter(hb->hists) || hb->min_pcnt;
54 static int hist_browser__get_folding(struct hist_browser *browser)
57 struct hists *hists = browser->hists;
58 int unfolded_rows = 0;
60 for (nd = rb_first(&hists->entries);
61 (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
63 struct hist_entry *he =
64 rb_entry(nd, struct hist_entry, rb_node);
67 unfolded_rows += he->nr_rows;
72 static u32 hist_browser__nr_entries(struct hist_browser *hb)
76 if (hist_browser__has_filter(hb))
77 nr_entries = hb->nr_non_filtered_entries;
79 nr_entries = hb->hists->nr_entries;
81 hb->nr_callchain_rows = hist_browser__get_folding(hb);
82 return nr_entries + hb->nr_callchain_rows;
85 static void hist_browser__update_rows(struct hist_browser *hb)
87 struct ui_browser *browser = &hb->b;
88 u16 header_offset = hb->show_headers ? 1 : 0, index_row;
90 browser->rows = browser->height - header_offset;
92 * Verify if we were at the last line and that line isn't
93 * visibe because we now show the header line(s).
95 index_row = browser->index - browser->top_idx;
96 if (index_row >= browser->rows)
97 browser->index -= index_row - browser->rows + 1;
100 static void hist_browser__refresh_dimensions(struct ui_browser *browser)
102 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
104 /* 3 == +/- toggle symbol before actual hist_entry rendering */
105 browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]"));
107 * FIXME: Just keeping existing behaviour, but this really should be
108 * before updating browser->width, as it will invalidate the
109 * calculation above. Fix this and the fallout in another
112 ui_browser__refresh_dimensions(browser);
113 hist_browser__update_rows(hb);
116 static void hist_browser__gotorc(struct hist_browser *browser, int row, int column)
118 u16 header_offset = browser->show_headers ? 1 : 0;
120 ui_browser__gotorc(&browser->b, row + header_offset, column);
123 static void hist_browser__reset(struct hist_browser *browser)
126 * The hists__remove_entry_filter() already folds non-filtered
127 * entries so we can assume it has 0 callchain rows.
129 browser->nr_callchain_rows = 0;
131 hist_browser__update_nr_entries(browser);
132 browser->b.nr_entries = hist_browser__nr_entries(browser);
133 hist_browser__refresh_dimensions(&browser->b);
134 ui_browser__reset_index(&browser->b);
137 static char tree__folded_sign(bool unfolded)
139 return unfolded ? '-' : '+';
142 static char map_symbol__folded(const struct map_symbol *ms)
144 return ms->has_children ? tree__folded_sign(ms->unfolded) : ' ';
147 static char hist_entry__folded(const struct hist_entry *he)
149 return map_symbol__folded(&he->ms);
152 static char callchain_list__folded(const struct callchain_list *cl)
154 return map_symbol__folded(&cl->ms);
157 static void map_symbol__set_folding(struct map_symbol *ms, bool unfold)
159 ms->unfolded = unfold ? ms->has_children : false;
162 static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
167 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
168 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
169 struct callchain_list *chain;
170 char folded_sign = ' '; /* No children */
172 list_for_each_entry(chain, &child->val, list) {
174 /* We need this because we may not have children */
175 folded_sign = callchain_list__folded(chain);
176 if (folded_sign == '+')
180 if (folded_sign == '-') /* Have children and they're unfolded */
181 n += callchain_node__count_rows_rb_tree(child);
187 static int callchain_node__count_rows(struct callchain_node *node)
189 struct callchain_list *chain;
190 bool unfolded = false;
193 list_for_each_entry(chain, &node->val, list) {
195 unfolded = chain->ms.unfolded;
199 n += callchain_node__count_rows_rb_tree(node);
204 static int callchain__count_rows(struct rb_root *chain)
209 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
210 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
211 n += callchain_node__count_rows(node);
217 static bool map_symbol__toggle_fold(struct map_symbol *ms)
222 if (!ms->has_children)
225 ms->unfolded = !ms->unfolded;
229 static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
231 struct rb_node *nd = rb_first(&node->rb_root);
233 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
234 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
235 struct callchain_list *chain;
238 list_for_each_entry(chain, &child->val, list) {
241 chain->ms.has_children = chain->list.next != &child->val ||
242 !RB_EMPTY_ROOT(&child->rb_root);
244 chain->ms.has_children = chain->list.next == &child->val &&
245 !RB_EMPTY_ROOT(&child->rb_root);
248 callchain_node__init_have_children_rb_tree(child);
252 static void callchain_node__init_have_children(struct callchain_node *node,
255 struct callchain_list *chain;
257 chain = list_entry(node->val.next, struct callchain_list, list);
258 chain->ms.has_children = has_sibling;
260 if (!list_empty(&node->val)) {
261 chain = list_entry(node->val.prev, struct callchain_list, list);
262 chain->ms.has_children = !RB_EMPTY_ROOT(&node->rb_root);
265 callchain_node__init_have_children_rb_tree(node);
268 static void callchain__init_have_children(struct rb_root *root)
270 struct rb_node *nd = rb_first(root);
271 bool has_sibling = nd && rb_next(nd);
273 for (nd = rb_first(root); nd; nd = rb_next(nd)) {
274 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
275 callchain_node__init_have_children(node, has_sibling);
279 static void hist_entry__init_have_children(struct hist_entry *he)
281 if (!he->init_have_children) {
282 he->ms.has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
283 callchain__init_have_children(&he->sorted_chain);
284 he->init_have_children = true;
288 static bool hist_browser__toggle_fold(struct hist_browser *browser)
290 if (map_symbol__toggle_fold(browser->selection)) {
291 struct hist_entry *he = browser->he_selection;
293 hist_entry__init_have_children(he);
294 browser->b.nr_entries -= he->nr_rows;
295 browser->nr_callchain_rows -= he->nr_rows;
298 he->nr_rows = callchain__count_rows(&he->sorted_chain);
302 browser->b.nr_entries += he->nr_rows;
303 browser->nr_callchain_rows += he->nr_rows;
308 /* If it doesn't have children, no toggling performed */
312 static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
317 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
318 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
319 struct callchain_list *chain;
320 bool has_children = false;
322 list_for_each_entry(chain, &child->val, list) {
324 map_symbol__set_folding(&chain->ms, unfold);
325 has_children = chain->ms.has_children;
329 n += callchain_node__set_folding_rb_tree(child, unfold);
335 static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
337 struct callchain_list *chain;
338 bool has_children = false;
341 list_for_each_entry(chain, &node->val, list) {
343 map_symbol__set_folding(&chain->ms, unfold);
344 has_children = chain->ms.has_children;
348 n += callchain_node__set_folding_rb_tree(node, unfold);
353 static int callchain__set_folding(struct rb_root *chain, bool unfold)
358 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
359 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
360 n += callchain_node__set_folding(node, unfold);
366 static void hist_entry__set_folding(struct hist_entry *he, bool unfold)
368 hist_entry__init_have_children(he);
369 map_symbol__set_folding(&he->ms, unfold);
371 if (he->ms.has_children) {
372 int n = callchain__set_folding(&he->sorted_chain, unfold);
373 he->nr_rows = unfold ? n : 0;
379 __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
382 struct hists *hists = browser->hists;
384 for (nd = rb_first(&hists->entries);
385 (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
387 struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
388 hist_entry__set_folding(he, unfold);
389 browser->nr_callchain_rows += he->nr_rows;
393 static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
395 browser->nr_callchain_rows = 0;
396 __hist_browser__set_folding(browser, unfold);
398 browser->b.nr_entries = hist_browser__nr_entries(browser);
399 /* Go to the start, we may be way after valid entries after a collapse */
400 ui_browser__reset_index(&browser->b);
403 static void ui_browser__warn_lost_events(struct ui_browser *browser)
405 ui_browser__warning(browser, 4,
406 "Events are being lost, check IO/CPU overload!\n\n"
407 "You may want to run 'perf' using a RT scheduler policy:\n\n"
408 " perf top -r 80\n\n"
409 "Or reduce the sampling frequency.");
412 static int hist_browser__run(struct hist_browser *browser)
416 struct hist_browser_timer *hbt = browser->hbt;
417 int delay_secs = hbt ? hbt->refresh : 0;
419 browser->b.entries = &browser->hists->entries;
420 browser->b.nr_entries = hist_browser__nr_entries(browser);
422 hists__browser_title(browser->hists, hbt, title, sizeof(title));
424 if (ui_browser__show(&browser->b, title,
425 "Press '?' for help on key bindings") < 0)
429 key = ui_browser__run(&browser->b, delay_secs);
434 hbt->timer(hbt->arg);
436 if (hist_browser__has_filter(browser))
437 hist_browser__update_nr_entries(browser);
439 nr_entries = hist_browser__nr_entries(browser);
440 ui_browser__update_nr_entries(&browser->b, nr_entries);
442 if (browser->hists->stats.nr_lost_warned !=
443 browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
444 browser->hists->stats.nr_lost_warned =
445 browser->hists->stats.nr_events[PERF_RECORD_LOST];
446 ui_browser__warn_lost_events(&browser->b);
449 hists__browser_title(browser->hists,
450 hbt, title, sizeof(title));
451 ui_browser__show_title(&browser->b, title);
454 case 'D': { /* Debug */
456 struct hist_entry *h = rb_entry(browser->b.top,
457 struct hist_entry, rb_node);
459 ui_helpline__fpush("%d: nr_ent=(%d,%d), rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
460 seq++, browser->b.nr_entries,
461 browser->hists->nr_entries,
465 h->row_offset, h->nr_rows);
469 /* Collapse the whole world. */
470 hist_browser__set_folding(browser, false);
473 /* Expand the whole world. */
474 hist_browser__set_folding(browser, true);
477 browser->show_headers = !browser->show_headers;
478 hist_browser__update_rows(browser);
481 if (hist_browser__toggle_fold(browser))
489 ui_browser__hide(&browser->b);
493 struct callchain_print_arg {
494 /* for hists browser */
496 bool is_current_entry;
503 typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
504 struct callchain_list *chain,
505 const char *str, int offset,
507 struct callchain_print_arg *arg);
509 static void hist_browser__show_callchain_entry(struct hist_browser *browser,
510 struct callchain_list *chain,
511 const char *str, int offset,
513 struct callchain_print_arg *arg)
516 char folded_sign = callchain_list__folded(chain);
517 bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
519 color = HE_COLORSET_NORMAL;
520 width = browser->b.width - (offset + 2);
521 if (ui_browser__is_current_entry(&browser->b, row)) {
522 browser->selection = &chain->ms;
523 color = HE_COLORSET_SELECTED;
524 arg->is_current_entry = true;
527 ui_browser__set_color(&browser->b, color);
528 hist_browser__gotorc(browser, row, 0);
529 slsmg_write_nstring(" ", offset);
530 slsmg_printf("%c", folded_sign);
531 ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
532 slsmg_write_nstring(str, width);
535 static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
536 struct callchain_list *chain,
537 const char *str, int offset,
538 unsigned short row __maybe_unused,
539 struct callchain_print_arg *arg)
541 char folded_sign = callchain_list__folded(chain);
543 arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
547 typedef bool (*check_output_full_fn)(struct hist_browser *browser,
550 static bool hist_browser__check_output_full(struct hist_browser *browser,
553 return browser->b.rows == row;
556 static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
557 unsigned short row __maybe_unused)
562 #define LEVEL_OFFSET_STEP 3
564 static int hist_browser__show_callchain(struct hist_browser *browser,
565 struct rb_root *root, int level,
566 unsigned short row, u64 total,
567 print_callchain_entry_fn print,
568 struct callchain_print_arg *arg,
569 check_output_full_fn is_output_full)
571 struct rb_node *node;
572 int first_row = row, offset = level * LEVEL_OFFSET_STEP;
576 node = rb_first(root);
577 need_percent = node && rb_next(node);
580 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
581 struct rb_node *next = rb_next(node);
582 u64 cumul = callchain_cumul_hits(child);
583 struct callchain_list *chain;
584 char folded_sign = ' ';
586 int extra_offset = 0;
588 list_for_each_entry(chain, &child->val, list) {
589 char bf[1024], *alloc_str;
591 bool was_first = first;
595 else if (need_percent)
596 extra_offset = LEVEL_OFFSET_STEP;
598 folded_sign = callchain_list__folded(chain);
599 if (arg->row_offset != 0) {
605 str = callchain_list__sym_name(chain, bf, sizeof(bf),
608 if (was_first && need_percent) {
609 double percent = cumul * 100.0 / total;
611 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
612 str = "Not enough memory!";
617 print(browser, chain, str, offset + extra_offset, row, arg);
621 if (is_output_full(browser, ++row))
624 if (folded_sign == '+')
628 if (folded_sign == '-') {
629 const int new_level = level + (extra_offset ? 2 : 1);
631 if (callchain_param.mode == CHAIN_GRAPH_REL)
632 new_total = child->children_hit;
636 row += hist_browser__show_callchain(browser, &child->rb_root,
637 new_level, row, new_total,
638 print, arg, is_output_full);
640 if (is_output_full(browser, row))
645 return row - first_row;
649 struct ui_browser *b;
654 static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
656 struct hpp_arg *arg = hpp->ptr;
662 len = va_arg(args, int);
663 percent = va_arg(args, double);
666 ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
668 ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
669 slsmg_printf("%s", hpp->buf);
671 advance_hpp(hpp, ret);
675 #define __HPP_COLOR_PERCENT_FN(_type, _field) \
676 static u64 __hpp_get_##_field(struct hist_entry *he) \
678 return he->stat._field; \
682 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
683 struct perf_hpp *hpp, \
684 struct hist_entry *he) \
686 return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%", \
687 __hpp__slsmg_color_printf, true); \
690 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
691 static u64 __hpp_get_acc_##_field(struct hist_entry *he) \
693 return he->stat_acc->_field; \
697 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
698 struct perf_hpp *hpp, \
699 struct hist_entry *he) \
701 if (!symbol_conf.cumulate_callchain) { \
702 int len = fmt->user_len ?: fmt->len; \
703 int ret = scnprintf(hpp->buf, hpp->size, \
704 "%*s", len, "N/A"); \
705 slsmg_printf("%s", hpp->buf); \
709 return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field, \
710 " %*.2f%%", __hpp__slsmg_color_printf, true); \
713 __HPP_COLOR_PERCENT_FN(overhead, period)
714 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
715 __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
716 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
717 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
718 __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
720 #undef __HPP_COLOR_PERCENT_FN
721 #undef __HPP_COLOR_ACC_PERCENT_FN
723 void hist_browser__init_hpp(void)
725 perf_hpp__format[PERF_HPP__OVERHEAD].color =
726 hist_browser__hpp_color_overhead;
727 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
728 hist_browser__hpp_color_overhead_sys;
729 perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
730 hist_browser__hpp_color_overhead_us;
731 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
732 hist_browser__hpp_color_overhead_guest_sys;
733 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
734 hist_browser__hpp_color_overhead_guest_us;
735 perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
736 hist_browser__hpp_color_overhead_acc;
739 static int hist_browser__show_entry(struct hist_browser *browser,
740 struct hist_entry *entry,
745 int width = browser->b.width;
746 char folded_sign = ' ';
747 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
748 off_t row_offset = entry->row_offset;
750 struct perf_hpp_fmt *fmt;
753 browser->he_selection = entry;
754 browser->selection = &entry->ms;
757 if (symbol_conf.use_callchain) {
758 hist_entry__init_have_children(entry);
759 folded_sign = hist_entry__folded(entry);
762 if (row_offset == 0) {
763 struct hpp_arg arg = {
765 .folded_sign = folded_sign,
766 .current_entry = current_entry,
768 struct perf_hpp hpp = {
774 hist_browser__gotorc(browser, row, 0);
776 perf_hpp__for_each_format(fmt) {
777 if (perf_hpp__should_skip(fmt))
780 if (current_entry && browser->b.navkeypressed) {
781 ui_browser__set_color(&browser->b,
782 HE_COLORSET_SELECTED);
784 ui_browser__set_color(&browser->b,
789 if (symbol_conf.use_callchain) {
790 slsmg_printf("%c ", folded_sign);
800 width -= fmt->color(fmt, &hpp, entry);
802 width -= fmt->entry(fmt, &hpp, entry);
803 slsmg_printf("%s", s);
807 /* The scroll bar isn't being used */
808 if (!browser->b.navkeypressed)
811 slsmg_write_nstring("", width);
818 if (folded_sign == '-' && row != browser->b.rows) {
819 u64 total = hists__total_period(entry->hists);
820 struct callchain_print_arg arg = {
821 .row_offset = row_offset,
822 .is_current_entry = current_entry,
825 if (callchain_param.mode == CHAIN_GRAPH_REL) {
826 if (symbol_conf.cumulate_callchain)
827 total = entry->stat_acc->period;
829 total = entry->stat.period;
832 printed += hist_browser__show_callchain(browser,
833 &entry->sorted_chain, 1, row, total,
834 hist_browser__show_callchain_entry, &arg,
835 hist_browser__check_output_full);
837 if (arg.is_current_entry)
838 browser->he_selection = entry;
844 static int advance_hpp_check(struct perf_hpp *hpp, int inc)
846 advance_hpp(hpp, inc);
847 return hpp->size <= 0;
850 static int hists__scnprintf_headers(char *buf, size_t size, struct hists *hists)
852 struct perf_hpp dummy_hpp = {
856 struct perf_hpp_fmt *fmt;
859 if (symbol_conf.use_callchain) {
860 ret = scnprintf(buf, size, " ");
861 if (advance_hpp_check(&dummy_hpp, ret))
865 perf_hpp__for_each_format(fmt) {
866 if (perf_hpp__should_skip(fmt))
869 ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists));
870 if (advance_hpp_check(&dummy_hpp, ret))
873 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
874 if (advance_hpp_check(&dummy_hpp, ret))
881 static void hist_browser__show_headers(struct hist_browser *browser)
885 hists__scnprintf_headers(headers, sizeof(headers), browser->hists);
886 ui_browser__gotorc(&browser->b, 0, 0);
887 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
888 slsmg_write_nstring(headers, browser->b.width + 1);
891 static void ui_browser__hists_init_top(struct ui_browser *browser)
893 if (browser->top == NULL) {
894 struct hist_browser *hb;
896 hb = container_of(browser, struct hist_browser, b);
897 browser->top = rb_first(&hb->hists->entries);
901 static unsigned int hist_browser__refresh(struct ui_browser *browser)
904 u16 header_offset = 0;
906 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
908 if (hb->show_headers) {
909 hist_browser__show_headers(hb);
913 ui_browser__hists_init_top(browser);
915 for (nd = browser->top; nd; nd = rb_next(nd)) {
916 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
922 percent = hist_entry__get_percent_limit(h);
923 if (percent < hb->min_pcnt)
926 row += hist_browser__show_entry(hb, h, row);
927 if (row == browser->rows)
931 return row + header_offset;
934 static struct rb_node *hists__filter_entries(struct rb_node *nd,
938 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
939 float percent = hist_entry__get_percent_limit(h);
941 if (!h->filtered && percent >= min_pcnt)
950 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
954 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
955 float percent = hist_entry__get_percent_limit(h);
957 if (!h->filtered && percent >= min_pcnt)
966 static void ui_browser__hists_seek(struct ui_browser *browser,
967 off_t offset, int whence)
969 struct hist_entry *h;
972 struct hist_browser *hb;
974 hb = container_of(browser, struct hist_browser, b);
976 if (browser->nr_entries == 0)
979 ui_browser__hists_init_top(browser);
983 nd = hists__filter_entries(rb_first(browser->entries),
990 nd = hists__filter_prev_entries(rb_last(browser->entries),
999 * Moves not relative to the first visible entry invalidates its
1002 h = rb_entry(browser->top, struct hist_entry, rb_node);
1006 * Here we have to check if nd is expanded (+), if it is we can't go
1007 * the next top level hist_entry, instead we must compute an offset of
1008 * what _not_ to show and not change the first visible entry.
1010 * This offset increments when we are going from top to bottom and
1011 * decreases when we're going from bottom to top.
1013 * As we don't have backpointers to the top level in the callchains
1014 * structure, we need to always print the whole hist_entry callchain,
1015 * skipping the first ones that are before the first visible entry
1016 * and stop when we printed enough lines to fill the screen.
1021 h = rb_entry(nd, struct hist_entry, rb_node);
1022 if (h->ms.unfolded) {
1023 u16 remaining = h->nr_rows - h->row_offset;
1024 if (offset > remaining) {
1025 offset -= remaining;
1028 h->row_offset += offset;
1034 nd = hists__filter_entries(rb_next(nd), hb->min_pcnt);
1039 } while (offset != 0);
1040 } else if (offset < 0) {
1042 h = rb_entry(nd, struct hist_entry, rb_node);
1043 if (h->ms.unfolded) {
1045 if (-offset > h->row_offset) {
1046 offset += h->row_offset;
1049 h->row_offset += offset;
1055 if (-offset > h->nr_rows) {
1056 offset += h->nr_rows;
1059 h->row_offset = h->nr_rows + offset;
1067 nd = hists__filter_prev_entries(rb_prev(nd),
1075 * Last unfiltered hist_entry, check if it is
1076 * unfolded, if it is then we should have
1077 * row_offset at its last entry.
1079 h = rb_entry(nd, struct hist_entry, rb_node);
1081 h->row_offset = h->nr_rows;
1088 h = rb_entry(nd, struct hist_entry, rb_node);
1093 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1094 struct hist_entry *he, FILE *fp)
1096 u64 total = hists__total_period(he->hists);
1097 struct callchain_print_arg arg = {
1101 if (symbol_conf.cumulate_callchain)
1102 total = he->stat_acc->period;
1104 hist_browser__show_callchain(browser, &he->sorted_chain, 1, 0, total,
1105 hist_browser__fprintf_callchain_entry, &arg,
1106 hist_browser__check_dump_full);
1110 static int hist_browser__fprintf_entry(struct hist_browser *browser,
1111 struct hist_entry *he, FILE *fp)
1115 char folded_sign = ' ';
1116 struct perf_hpp hpp = {
1120 struct perf_hpp_fmt *fmt;
1124 if (symbol_conf.use_callchain)
1125 folded_sign = hist_entry__folded(he);
1127 if (symbol_conf.use_callchain)
1128 printed += fprintf(fp, "%c ", folded_sign);
1130 perf_hpp__for_each_format(fmt) {
1131 if (perf_hpp__should_skip(fmt))
1135 ret = scnprintf(hpp.buf, hpp.size, " ");
1136 advance_hpp(&hpp, ret);
1140 ret = fmt->entry(fmt, &hpp, he);
1141 advance_hpp(&hpp, ret);
1143 printed += fprintf(fp, "%s\n", rtrim(s));
1145 if (folded_sign == '-')
1146 printed += hist_browser__fprintf_callchain(browser, he, fp);
1151 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1153 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
1158 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1160 printed += hist_browser__fprintf_entry(browser, h, fp);
1161 nd = hists__filter_entries(rb_next(nd), browser->min_pcnt);
1167 static int hist_browser__dump(struct hist_browser *browser)
1173 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
1174 if (access(filename, F_OK))
1177 * XXX: Just an arbitrary lazy upper limit
1179 if (++browser->print_seq == 8192) {
1180 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1185 fp = fopen(filename, "w");
1188 const char *err = strerror_r(errno, bf, sizeof(bf));
1189 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
1193 ++browser->print_seq;
1194 hist_browser__fprintf(browser, fp);
1196 ui_helpline__fpush("%s written!", filename);
1201 static struct hist_browser *hist_browser__new(struct hists *hists,
1202 struct hist_browser_timer *hbt,
1203 struct perf_session_env *env)
1205 struct hist_browser *browser = zalloc(sizeof(*browser));
1208 browser->hists = hists;
1209 browser->b.refresh = hist_browser__refresh;
1210 browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
1211 browser->b.seek = ui_browser__hists_seek;
1212 browser->b.use_navkeypressed = true;
1213 browser->show_headers = symbol_conf.show_hist_headers;
1221 static void hist_browser__delete(struct hist_browser *browser)
1226 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
1228 return browser->he_selection;
1231 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
1233 return browser->he_selection->thread;
1236 /* Check whether the browser is for 'top' or 'report' */
1237 static inline bool is_report_browser(void *timer)
1239 return timer == NULL;
1242 static int hists__browser_title(struct hists *hists,
1243 struct hist_browser_timer *hbt,
1244 char *bf, size_t size)
1248 const struct dso *dso = hists->dso_filter;
1249 const struct thread *thread = hists->thread_filter;
1250 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1251 u64 nr_events = hists->stats.total_period;
1252 struct perf_evsel *evsel = hists_to_evsel(hists);
1253 const char *ev_name = perf_evsel__name(evsel);
1255 size_t buflen = sizeof(buf);
1257 if (symbol_conf.filter_relative) {
1258 nr_samples = hists->stats.nr_non_filtered_samples;
1259 nr_events = hists->stats.total_non_filtered_period;
1262 if (perf_evsel__is_group_event(evsel)) {
1263 struct perf_evsel *pos;
1265 perf_evsel__group_desc(evsel, buf, buflen);
1268 for_each_group_member(pos, evsel) {
1269 struct hists *pos_hists = evsel__hists(pos);
1271 if (symbol_conf.filter_relative) {
1272 nr_samples += pos_hists->stats.nr_non_filtered_samples;
1273 nr_events += pos_hists->stats.total_non_filtered_period;
1275 nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
1276 nr_events += pos_hists->stats.total_period;
1281 nr_samples = convert_unit(nr_samples, &unit);
1282 printed = scnprintf(bf, size,
1283 "Samples: %lu%c of event '%s', Event count (approx.): %" PRIu64,
1284 nr_samples, unit, ev_name, nr_events);
1287 if (hists->uid_filter_str)
1288 printed += snprintf(bf + printed, size - printed,
1289 ", UID: %s", hists->uid_filter_str);
1291 printed += scnprintf(bf + printed, size - printed,
1293 (thread->comm_set ? thread__comm_str(thread) : ""),
1296 printed += scnprintf(bf + printed, size - printed,
1297 ", DSO: %s", dso->short_name);
1298 if (!is_report_browser(hbt)) {
1299 struct perf_top *top = hbt->arg;
1302 printed += scnprintf(bf + printed, size - printed, " [z]");
1308 static inline void free_popup_options(char **options, int n)
1312 for (i = 0; i < n; ++i)
1317 * Only runtime switching of perf data file will make "input_name" point
1318 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1319 * whether we need to call free() for current "input_name" during the switch.
1321 static bool is_input_name_malloced = false;
1323 static int switch_data_file(void)
1325 char *pwd, *options[32], *abs_path[32], *tmp;
1327 int nr_options = 0, choice = -1, ret = -1;
1328 struct dirent *dent;
1330 pwd = getenv("PWD");
1334 pwd_dir = opendir(pwd);
1338 memset(options, 0, sizeof(options));
1339 memset(options, 0, sizeof(abs_path));
1341 while ((dent = readdir(pwd_dir))) {
1342 char path[PATH_MAX];
1344 char *name = dent->d_name;
1347 if (!(dent->d_type == DT_REG))
1350 snprintf(path, sizeof(path), "%s/%s", pwd, name);
1352 file = fopen(path, "r");
1356 if (fread(&magic, 1, 8, file) < 8)
1357 goto close_file_and_continue;
1359 if (is_perf_magic(magic)) {
1360 options[nr_options] = strdup(name);
1361 if (!options[nr_options])
1362 goto close_file_and_continue;
1364 abs_path[nr_options] = strdup(path);
1365 if (!abs_path[nr_options]) {
1366 zfree(&options[nr_options]);
1367 ui__warning("Can't search all data files due to memory shortage.\n");
1375 close_file_and_continue:
1377 if (nr_options >= 32) {
1378 ui__warning("Too many perf data files in PWD!\n"
1379 "Only the first 32 files will be listed.\n");
1386 choice = ui__popup_menu(nr_options, options);
1387 if (choice < nr_options && choice >= 0) {
1388 tmp = strdup(abs_path[choice]);
1390 if (is_input_name_malloced)
1391 free((void *)input_name);
1393 is_input_name_malloced = true;
1396 ui__warning("Data switch failed due to memory shortage!\n");
1400 free_popup_options(options, nr_options);
1401 free_popup_options(abs_path, nr_options);
1406 do_annotate(struct hist_browser *browser, struct map_symbol *ms)
1408 struct perf_evsel *evsel;
1409 struct annotation *notes;
1410 struct hist_entry *he;
1413 if (!objdump_path && perf_session_env__lookup_objdump(browser->env))
1416 notes = symbol__annotation(ms->sym);
1420 evsel = hists_to_evsel(browser->hists);
1421 err = map_symbol__tui_annotate(ms, evsel, browser->hbt);
1422 he = hist_browser__selected_entry(browser);
1424 * offer option to annotate the other branch source or target
1425 * (if they exists) when returning from annotate
1427 if ((err == 'q' || err == CTRL('c')) && he->branch_info)
1430 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1432 ui_browser__handle_resize(&browser->b);
1437 do_zoom_thread(struct hist_browser *browser, struct thread *thread)
1439 if (browser->hists->thread_filter) {
1440 pstack__remove(browser->pstack, &browser->hists->thread_filter);
1441 perf_hpp__set_elide(HISTC_THREAD, false);
1442 thread__zput(browser->hists->thread_filter);
1445 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1446 thread->comm_set ? thread__comm_str(thread) : "",
1448 browser->hists->thread_filter = thread__get(thread);
1449 perf_hpp__set_elide(HISTC_THREAD, false);
1450 pstack__push(browser->pstack, &browser->hists->thread_filter);
1453 hists__filter_by_thread(browser->hists);
1454 hist_browser__reset(browser);
1459 do_zoom_dso(struct hist_browser *browser, struct dso *dso)
1461 if (browser->hists->dso_filter) {
1462 pstack__remove(browser->pstack, &browser->hists->dso_filter);
1463 perf_hpp__set_elide(HISTC_DSO, false);
1464 browser->hists->dso_filter = NULL;
1469 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1470 dso->kernel ? "the Kernel" : dso->short_name);
1471 browser->hists->dso_filter = dso;
1472 perf_hpp__set_elide(HISTC_DSO, true);
1473 pstack__push(browser->pstack, &browser->hists->dso_filter);
1476 hists__filter_by_dso(browser->hists);
1477 hist_browser__reset(browser);
1482 do_browse_map(struct hist_browser *browser __maybe_unused, struct map *map)
1489 do_run_script(struct hist_browser *browser __maybe_unused,
1490 struct thread *thread, struct symbol *sym)
1492 char script_opt[64];
1493 memset(script_opt, 0, sizeof(script_opt));
1496 scnprintf(script_opt, sizeof(script_opt), " -c %s ",
1497 thread__comm_str(thread));
1499 scnprintf(script_opt, sizeof(script_opt), " -S %s ",
1503 script_browse(script_opt);
1508 do_switch_data(struct hist_browser *browser __maybe_unused, int key)
1510 if (switch_data_file()) {
1511 ui__warning("Won't switch the data files due to\n"
1512 "no valid data file get selected!\n");
1516 return K_SWITCH_INPUT_DATA;
1519 static void hist_browser__update_nr_entries(struct hist_browser *hb)
1522 struct rb_node *nd = rb_first(&hb->hists->entries);
1524 if (hb->min_pcnt == 0) {
1525 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
1529 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
1534 hb->nr_non_filtered_entries = nr_entries;
1537 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
1538 const char *helpline,
1540 struct hist_browser_timer *hbt,
1542 struct perf_session_env *env)
1544 struct hists *hists = evsel__hists(evsel);
1545 struct hist_browser *browser = hist_browser__new(hists, hbt, env);
1546 struct branch_info *bi;
1547 #define MAX_OPTIONS 16
1548 char *options[MAX_OPTIONS];
1552 int delay_secs = hbt ? hbt->refresh : 0;
1553 struct perf_hpp_fmt *fmt;
1555 #define HIST_BROWSER_HELP_COMMON \
1556 "h/?/F1 Show this window\n" \
1558 "PGDN/SPACE Navigate\n" \
1559 "q/ESC/CTRL+C Exit browser\n\n" \
1560 "For multiple event sessions:\n\n" \
1561 "TAB/UNTAB Switch events\n\n" \
1562 "For symbolic views (--sort has sym):\n\n" \
1563 "-> Zoom into DSO/Threads & Annotate current symbol\n" \
1565 "a Annotate current symbol\n" \
1566 "C Collapse all callchains\n" \
1567 "d Zoom into current DSO\n" \
1568 "E Expand all callchains\n" \
1569 "F Toggle percentage of filtered entries\n" \
1570 "H Display column headers\n" \
1572 /* help messages are sorted by lexical order of the hotkey */
1573 const char report_help[] = HIST_BROWSER_HELP_COMMON
1574 "i Show header information\n"
1575 "P Print histograms to perf.hist.N\n"
1576 "r Run available scripts\n"
1577 "s Switch to another data file in PWD\n"
1578 "t Zoom into current Thread\n"
1579 "V Verbose (DSO names in callchains, etc)\n"
1580 "/ Filter symbol by name";
1581 const char top_help[] = HIST_BROWSER_HELP_COMMON
1582 "P Print histograms to perf.hist.N\n"
1583 "t Zoom into current Thread\n"
1584 "V Verbose (DSO names in callchains, etc)\n"
1585 "z Toggle zeroing of samples\n"
1586 "/ Filter symbol by name";
1588 if (browser == NULL)
1592 browser->min_pcnt = min_pcnt;
1593 hist_browser__update_nr_entries(browser);
1596 browser->pstack = pstack__new(2);
1597 if (browser->pstack == NULL)
1600 ui_helpline__push(helpline);
1602 memset(options, 0, sizeof(options));
1604 perf_hpp__for_each_format(fmt)
1605 perf_hpp__reset_width(fmt, hists);
1607 if (symbol_conf.col_width_list_str)
1608 perf_hpp__set_user_width(symbol_conf.col_width_list_str);
1611 struct thread *thread = NULL;
1612 struct dso *dso = NULL;
1613 struct map_symbol ms;
1615 annotate = -2, zoom_dso = -2, zoom_thread = -2,
1616 annotate_f = -2, annotate_t = -2, browse_map = -2;
1617 int scripts_comm = -2, scripts_symbol = -2,
1618 scripts_all = -2, switch_data = -2;
1622 key = hist_browser__run(browser);
1624 if (browser->he_selection != NULL) {
1625 thread = hist_browser__selected_thread(browser);
1626 dso = browser->selection->map ? browser->selection->map->dso : NULL;
1634 * Exit the browser, let hists__browser_tree
1635 * go to the next or previous
1637 goto out_free_stack;
1639 if (!sort__has_sym) {
1640 ui_browser__warning(&browser->b, delay_secs * 2,
1641 "Annotation is only available for symbolic views, "
1642 "include \"sym*\" in --sort to use it.");
1646 if (browser->selection == NULL ||
1647 browser->selection->sym == NULL ||
1648 browser->selection->map->dso->annotate_warned)
1651 ms.map = browser->selection->map;
1652 ms.sym = browser->selection->sym;
1654 do_annotate(browser, &ms);
1657 hist_browser__dump(browser);
1660 do_zoom_dso(browser, dso);
1663 browser->show_dso = !browser->show_dso;
1666 do_zoom_thread(browser, thread);
1669 if (ui_browser__input_window("Symbol to show",
1670 "Please enter the name of symbol you want to see",
1671 buf, "ENTER: OK, ESC: Cancel",
1672 delay_secs * 2) == K_ENTER) {
1673 hists->symbol_filter_str = *buf ? buf : NULL;
1674 hists__filter_by_symbol(hists);
1675 hist_browser__reset(browser);
1679 if (is_report_browser(hbt))
1680 do_run_script(browser, NULL, NULL);
1683 if (is_report_browser(hbt)) {
1684 key = do_switch_data(browser, key);
1685 if (key == K_SWITCH_INPUT_DATA)
1686 goto out_free_stack;
1690 /* env->arch is NULL for live-mode (i.e. perf top) */
1692 tui__header_window(env);
1695 symbol_conf.filter_relative ^= 1;
1698 if (!is_report_browser(hbt)) {
1699 struct perf_top *top = hbt->arg;
1701 top->zero = !top->zero;
1707 ui_browser__help_window(&browser->b,
1708 is_report_browser(hbt) ? report_help : top_help);
1717 if (pstack__empty(browser->pstack)) {
1719 * Go back to the perf_evsel_menu__run or other user
1722 goto out_free_stack;
1725 top = pstack__pop(browser->pstack);
1726 if (top == &browser->hists->dso_filter) {
1727 perf_hpp__set_elide(HISTC_DSO, false);
1728 browser->hists->dso_filter = NULL;
1729 hists__filter_by_dso(browser->hists);
1731 if (top == &browser->hists->thread_filter) {
1732 perf_hpp__set_elide(HISTC_THREAD, false);
1733 thread__zput(browser->hists->thread_filter);
1734 hists__filter_by_thread(browser->hists);
1737 hist_browser__reset(browser);
1742 !ui_browser__dialog_yesno(&browser->b,
1743 "Do you really want to exit?"))
1748 goto out_free_stack;
1754 goto add_exit_option;
1756 if (browser->selection == NULL)
1757 goto skip_annotation;
1759 if (sort__mode == SORT_MODE__BRANCH) {
1760 bi = browser->he_selection->branch_info;
1763 goto skip_annotation;
1765 if (bi->from.sym != NULL &&
1766 !bi->from.map->dso->annotate_warned &&
1767 asprintf(&options[nr_options], "Annotate %s", bi->from.sym->name) > 0) {
1768 annotate_f = nr_options++;
1771 if (bi->to.sym != NULL &&
1772 !bi->to.map->dso->annotate_warned &&
1773 (bi->to.sym != bi->from.sym ||
1774 bi->to.map->dso != bi->from.map->dso) &&
1775 asprintf(&options[nr_options], "Annotate %s", bi->to.sym->name) > 0) {
1776 annotate_t = nr_options++;
1779 if (browser->selection->sym != NULL &&
1780 !browser->selection->map->dso->annotate_warned) {
1781 struct annotation *notes;
1783 notes = symbol__annotation(browser->selection->sym);
1786 asprintf(&options[nr_options], "Annotate %s",
1787 browser->selection->sym->name) > 0) {
1788 annotate = nr_options++;
1793 if (thread != NULL &&
1794 asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
1795 (browser->hists->thread_filter ? "out of" : "into"),
1796 (thread->comm_set ? thread__comm_str(thread) : ""),
1798 zoom_thread = nr_options++;
1801 asprintf(&options[nr_options], "Zoom %s %s DSO",
1802 (browser->hists->dso_filter ? "out of" : "into"),
1803 (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
1804 zoom_dso = nr_options++;
1806 if (browser->selection != NULL &&
1807 browser->selection->map != NULL &&
1808 asprintf(&options[nr_options], "Browse map details") > 0)
1809 browse_map = nr_options++;
1811 /* perf script support */
1812 if (browser->he_selection) {
1815 if (asprintf(&options[nr_options], "Run scripts for samples of thread [%s]",
1816 thread__comm_str(browser->he_selection->thread)) > 0)
1817 scripts_comm = nr_options++;
1819 sym = browser->he_selection->ms.sym;
1820 if (sym && sym->namelen &&
1821 asprintf(&options[nr_options], "Run scripts for samples of symbol [%s]",
1823 scripts_symbol = nr_options++;
1826 if (asprintf(&options[nr_options], "Run scripts for all samples") > 0)
1827 scripts_all = nr_options++;
1829 if (is_report_browser(hbt) && asprintf(&options[nr_options],
1830 "Switch to another data file in PWD") > 0)
1831 switch_data = nr_options++;
1833 if (asprintf(&options[nr_options], "Exit") > 0)
1836 choice = ui__popup_menu(nr_options, options);
1838 if (choice == nr_options - 1)
1842 free_popup_options(options, nr_options - 1);
1846 if (choice == annotate || choice == annotate_t || choice == annotate_f) {
1847 struct hist_entry *he;
1849 he = hist_browser__selected_entry(browser);
1853 if (choice == annotate_f) {
1854 ms.map = he->branch_info->from.map;
1855 ms.sym = he->branch_info->from.sym;
1856 } else if (choice == annotate_t) {
1857 ms.map = he->branch_info->to.map;
1858 ms.sym = he->branch_info->to.sym;
1860 ms = *browser->selection;
1863 if (do_annotate(browser, &ms) == 1)
1864 goto retry_popup_menu;
1865 } else if (choice == browse_map) {
1866 do_browse_map(browser, browser->selection->map);
1867 } else if (choice == zoom_dso) {
1868 do_zoom_dso(browser, dso);
1869 } else if (choice == zoom_thread) {
1870 do_zoom_thread(browser, thread);
1872 /* perf scripts support */
1873 else if (choice == scripts_all || choice == scripts_comm ||
1874 choice == scripts_symbol) {
1875 if (choice == scripts_comm)
1876 do_run_script(browser, browser->he_selection->thread, NULL);
1877 if (choice == scripts_symbol)
1878 do_run_script(browser, NULL, browser->he_selection->ms.sym);
1879 if (choice == scripts_all)
1880 do_run_script(browser, NULL, NULL);
1882 /* Switch to another data file */
1883 else if (choice == switch_data) {
1884 key = do_switch_data(browser, key);
1885 if (key == K_SWITCH_INPUT_DATA)
1890 pstack__delete(browser->pstack);
1892 hist_browser__delete(browser);
1893 free_popup_options(options, MAX_OPTIONS);
1897 struct perf_evsel_menu {
1898 struct ui_browser b;
1899 struct perf_evsel *selection;
1900 bool lost_events, lost_events_warned;
1902 struct perf_session_env *env;
1905 static void perf_evsel_menu__write(struct ui_browser *browser,
1906 void *entry, int row)
1908 struct perf_evsel_menu *menu = container_of(browser,
1909 struct perf_evsel_menu, b);
1910 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1911 struct hists *hists = evsel__hists(evsel);
1912 bool current_entry = ui_browser__is_current_entry(browser, row);
1913 unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1914 const char *ev_name = perf_evsel__name(evsel);
1916 const char *warn = " ";
1919 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
1920 HE_COLORSET_NORMAL);
1922 if (perf_evsel__is_group_event(evsel)) {
1923 struct perf_evsel *pos;
1925 ev_name = perf_evsel__group_name(evsel);
1927 for_each_group_member(pos, evsel) {
1928 struct hists *pos_hists = evsel__hists(pos);
1929 nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
1933 nr_events = convert_unit(nr_events, &unit);
1934 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
1935 unit, unit == ' ' ? "" : " ", ev_name);
1936 slsmg_printf("%s", bf);
1938 nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
1939 if (nr_events != 0) {
1940 menu->lost_events = true;
1942 ui_browser__set_color(browser, HE_COLORSET_TOP);
1943 nr_events = convert_unit(nr_events, &unit);
1944 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
1945 nr_events, unit, unit == ' ' ? "" : " ");
1949 slsmg_write_nstring(warn, browser->width - printed);
1952 menu->selection = evsel;
1955 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
1956 int nr_events, const char *help,
1957 struct hist_browser_timer *hbt)
1959 struct perf_evlist *evlist = menu->b.priv;
1960 struct perf_evsel *pos;
1961 const char *title = "Available samples";
1962 int delay_secs = hbt ? hbt->refresh : 0;
1965 if (ui_browser__show(&menu->b, title,
1966 "ESC: exit, ENTER|->: Browse histograms") < 0)
1970 key = ui_browser__run(&menu->b, delay_secs);
1974 hbt->timer(hbt->arg);
1976 if (!menu->lost_events_warned && menu->lost_events) {
1977 ui_browser__warn_lost_events(&menu->b);
1978 menu->lost_events_warned = true;
1983 if (!menu->selection)
1985 pos = menu->selection;
1987 perf_evlist__set_selected(evlist, pos);
1989 * Give the calling tool a chance to populate the non
1990 * default evsel resorted hists tree.
1993 hbt->timer(hbt->arg);
1994 key = perf_evsel__hists_browse(pos, nr_events, help,
1998 ui_browser__show_title(&menu->b, title);
2001 if (pos->node.next == &evlist->entries)
2002 pos = perf_evlist__first(evlist);
2004 pos = perf_evsel__next(pos);
2007 if (pos->node.prev == &evlist->entries)
2008 pos = perf_evlist__last(evlist);
2010 pos = perf_evsel__prev(pos);
2013 if (!ui_browser__dialog_yesno(&menu->b,
2014 "Do you really want to exit?"))
2017 case K_SWITCH_INPUT_DATA:
2027 if (!ui_browser__dialog_yesno(&menu->b,
2028 "Do you really want to exit?"))
2040 ui_browser__hide(&menu->b);
2044 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
2047 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
2049 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
2055 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
2056 int nr_entries, const char *help,
2057 struct hist_browser_timer *hbt,
2059 struct perf_session_env *env)
2061 struct perf_evsel *pos;
2062 struct perf_evsel_menu menu = {
2064 .entries = &evlist->entries,
2065 .refresh = ui_browser__list_head_refresh,
2066 .seek = ui_browser__list_head_seek,
2067 .write = perf_evsel_menu__write,
2068 .filter = filter_group_entries,
2069 .nr_entries = nr_entries,
2072 .min_pcnt = min_pcnt,
2076 ui_helpline__push("Press ESC to exit");
2078 evlist__for_each(evlist, pos) {
2079 const char *ev_name = perf_evsel__name(pos);
2080 size_t line_len = strlen(ev_name) + 7;
2082 if (menu.b.width < line_len)
2083 menu.b.width = line_len;
2086 return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
2089 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
2090 struct hist_browser_timer *hbt,
2092 struct perf_session_env *env)
2094 int nr_entries = evlist->nr_entries;
2097 if (nr_entries == 1) {
2098 struct perf_evsel *first = perf_evlist__first(evlist);
2100 return perf_evsel__hists_browse(first, nr_entries, help,
2101 false, hbt, min_pcnt,
2105 if (symbol_conf.event_group) {
2106 struct perf_evsel *pos;
2109 evlist__for_each(evlist, pos) {
2110 if (perf_evsel__is_group_leader(pos))
2114 if (nr_entries == 1)
2118 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
2119 hbt, min_pcnt, env);