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);
1405 struct popup_action {
1406 struct thread *thread;
1408 struct map_symbol ms;
1410 int (*fn)(struct hist_browser *browser, struct popup_action *act);
1414 do_annotate(struct hist_browser *browser, struct popup_action *act)
1416 struct perf_evsel *evsel;
1417 struct annotation *notes;
1418 struct hist_entry *he;
1421 if (!objdump_path && perf_session_env__lookup_objdump(browser->env))
1424 notes = symbol__annotation(act->ms.sym);
1428 evsel = hists_to_evsel(browser->hists);
1429 err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
1430 he = hist_browser__selected_entry(browser);
1432 * offer option to annotate the other branch source or target
1433 * (if they exists) when returning from annotate
1435 if ((err == 'q' || err == CTRL('c')) && he->branch_info)
1438 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1440 ui_browser__handle_resize(&browser->b);
1445 add_annotate_opt(struct hist_browser *browser __maybe_unused,
1446 struct popup_action *act, char **optstr,
1447 struct map *map, struct symbol *sym)
1449 if (sym == NULL || map->dso->annotate_warned)
1452 if (asprintf(optstr, "Annotate %s", sym->name) < 0)
1457 act->fn = do_annotate;
1462 do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
1464 struct thread *thread = act->thread;
1466 if (browser->hists->thread_filter) {
1467 pstack__remove(browser->pstack, &browser->hists->thread_filter);
1468 perf_hpp__set_elide(HISTC_THREAD, false);
1469 thread__zput(browser->hists->thread_filter);
1472 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1473 thread->comm_set ? thread__comm_str(thread) : "",
1475 browser->hists->thread_filter = thread__get(thread);
1476 perf_hpp__set_elide(HISTC_THREAD, false);
1477 pstack__push(browser->pstack, &browser->hists->thread_filter);
1480 hists__filter_by_thread(browser->hists);
1481 hist_browser__reset(browser);
1486 add_thread_opt(struct hist_browser *browser, struct popup_action *act,
1487 char **optstr, struct thread *thread)
1492 if (asprintf(optstr, "Zoom %s %s(%d) thread",
1493 browser->hists->thread_filter ? "out of" : "into",
1494 thread->comm_set ? thread__comm_str(thread) : "",
1498 act->thread = thread;
1499 act->fn = do_zoom_thread;
1504 do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
1506 struct dso *dso = act->dso;
1508 if (browser->hists->dso_filter) {
1509 pstack__remove(browser->pstack, &browser->hists->dso_filter);
1510 perf_hpp__set_elide(HISTC_DSO, false);
1511 browser->hists->dso_filter = NULL;
1516 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1517 dso->kernel ? "the Kernel" : dso->short_name);
1518 browser->hists->dso_filter = dso;
1519 perf_hpp__set_elide(HISTC_DSO, true);
1520 pstack__push(browser->pstack, &browser->hists->dso_filter);
1523 hists__filter_by_dso(browser->hists);
1524 hist_browser__reset(browser);
1529 add_dso_opt(struct hist_browser *browser, struct popup_action *act,
1530 char **optstr, struct dso *dso)
1535 if (asprintf(optstr, "Zoom %s %s DSO",
1536 browser->hists->dso_filter ? "out of" : "into",
1537 dso->kernel ? "the Kernel" : dso->short_name) < 0)
1541 act->fn = do_zoom_dso;
1546 do_browse_map(struct hist_browser *browser __maybe_unused,
1547 struct popup_action *act)
1549 map__browse(act->ms.map);
1554 add_map_opt(struct hist_browser *browser __maybe_unused,
1555 struct popup_action *act, char **optstr, struct map *map)
1560 if (asprintf(optstr, "Browse map details") < 0)
1564 act->fn = do_browse_map;
1569 do_run_script(struct hist_browser *browser __maybe_unused,
1570 struct popup_action *act)
1572 char script_opt[64];
1573 memset(script_opt, 0, sizeof(script_opt));
1576 scnprintf(script_opt, sizeof(script_opt), " -c %s ",
1577 thread__comm_str(act->thread));
1578 } else if (act->ms.sym) {
1579 scnprintf(script_opt, sizeof(script_opt), " -S %s ",
1583 script_browse(script_opt);
1588 add_script_opt(struct hist_browser *browser __maybe_unused,
1589 struct popup_action *act, char **optstr,
1590 struct thread *thread, struct symbol *sym)
1593 if (asprintf(optstr, "Run scripts for samples of thread [%s]",
1594 thread__comm_str(thread)) < 0)
1597 if (asprintf(optstr, "Run scripts for samples of symbol [%s]",
1601 if (asprintf(optstr, "Run scripts for all samples") < 0)
1605 act->thread = thread;
1607 act->fn = do_run_script;
1612 do_switch_data(struct hist_browser *browser __maybe_unused,
1613 struct popup_action *act __maybe_unused)
1615 if (switch_data_file()) {
1616 ui__warning("Won't switch the data files due to\n"
1617 "no valid data file get selected!\n");
1621 return K_SWITCH_INPUT_DATA;
1625 add_switch_opt(struct hist_browser *browser,
1626 struct popup_action *act, char **optstr)
1628 if (!is_report_browser(browser->hbt))
1631 if (asprintf(optstr, "Switch to another data file in PWD") < 0)
1634 act->fn = do_switch_data;
1639 do_exit_browser(struct hist_browser *browser __maybe_unused,
1640 struct popup_action *act __maybe_unused)
1646 add_exit_opt(struct hist_browser *browser __maybe_unused,
1647 struct popup_action *act, char **optstr)
1649 if (asprintf(optstr, "Exit") < 0)
1652 act->fn = do_exit_browser;
1656 static void hist_browser__update_nr_entries(struct hist_browser *hb)
1659 struct rb_node *nd = rb_first(&hb->hists->entries);
1661 if (hb->min_pcnt == 0) {
1662 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
1666 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
1671 hb->nr_non_filtered_entries = nr_entries;
1674 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
1675 const char *helpline,
1677 struct hist_browser_timer *hbt,
1679 struct perf_session_env *env)
1681 struct hists *hists = evsel__hists(evsel);
1682 struct hist_browser *browser = hist_browser__new(hists, hbt, env);
1683 struct branch_info *bi;
1684 #define MAX_OPTIONS 16
1685 char *options[MAX_OPTIONS];
1686 struct popup_action actions[MAX_OPTIONS];
1690 int delay_secs = hbt ? hbt->refresh : 0;
1691 struct perf_hpp_fmt *fmt;
1693 #define HIST_BROWSER_HELP_COMMON \
1694 "h/?/F1 Show this window\n" \
1696 "PGDN/SPACE Navigate\n" \
1697 "q/ESC/CTRL+C Exit browser\n\n" \
1698 "For multiple event sessions:\n\n" \
1699 "TAB/UNTAB Switch events\n\n" \
1700 "For symbolic views (--sort has sym):\n\n" \
1701 "-> Zoom into DSO/Threads & Annotate current symbol\n" \
1703 "a Annotate current symbol\n" \
1704 "C Collapse all callchains\n" \
1705 "d Zoom into current DSO\n" \
1706 "E Expand all callchains\n" \
1707 "F Toggle percentage of filtered entries\n" \
1708 "H Display column headers\n" \
1710 /* help messages are sorted by lexical order of the hotkey */
1711 const char report_help[] = HIST_BROWSER_HELP_COMMON
1712 "i Show header information\n"
1713 "P Print histograms to perf.hist.N\n"
1714 "r Run available scripts\n"
1715 "s Switch to another data file in PWD\n"
1716 "t Zoom into current Thread\n"
1717 "V Verbose (DSO names in callchains, etc)\n"
1718 "/ Filter symbol by name";
1719 const char top_help[] = HIST_BROWSER_HELP_COMMON
1720 "P Print histograms to perf.hist.N\n"
1721 "t Zoom into current Thread\n"
1722 "V Verbose (DSO names in callchains, etc)\n"
1723 "z Toggle zeroing of samples\n"
1724 "/ Filter symbol by name";
1726 if (browser == NULL)
1730 browser->min_pcnt = min_pcnt;
1731 hist_browser__update_nr_entries(browser);
1734 browser->pstack = pstack__new(2);
1735 if (browser->pstack == NULL)
1738 ui_helpline__push(helpline);
1740 memset(options, 0, sizeof(options));
1741 memset(actions, 0, sizeof(actions));
1743 perf_hpp__for_each_format(fmt)
1744 perf_hpp__reset_width(fmt, hists);
1746 if (symbol_conf.col_width_list_str)
1747 perf_hpp__set_user_width(symbol_conf.col_width_list_str);
1750 struct thread *thread = NULL;
1751 struct dso *dso = NULL;
1756 key = hist_browser__run(browser);
1758 if (browser->he_selection != NULL) {
1759 thread = hist_browser__selected_thread(browser);
1760 dso = browser->selection->map ? browser->selection->map->dso : NULL;
1768 * Exit the browser, let hists__browser_tree
1769 * go to the next or previous
1771 goto out_free_stack;
1773 if (!sort__has_sym) {
1774 ui_browser__warning(&browser->b, delay_secs * 2,
1775 "Annotation is only available for symbolic views, "
1776 "include \"sym*\" in --sort to use it.");
1780 if (browser->selection == NULL ||
1781 browser->selection->sym == NULL ||
1782 browser->selection->map->dso->annotate_warned)
1785 actions->ms.map = browser->selection->map;
1786 actions->ms.sym = browser->selection->sym;
1787 do_annotate(browser, actions);
1790 hist_browser__dump(browser);
1794 do_zoom_dso(browser, actions);
1797 browser->show_dso = !browser->show_dso;
1800 actions->thread = thread;
1801 do_zoom_thread(browser, actions);
1804 if (ui_browser__input_window("Symbol to show",
1805 "Please enter the name of symbol you want to see",
1806 buf, "ENTER: OK, ESC: Cancel",
1807 delay_secs * 2) == K_ENTER) {
1808 hists->symbol_filter_str = *buf ? buf : NULL;
1809 hists__filter_by_symbol(hists);
1810 hist_browser__reset(browser);
1814 if (is_report_browser(hbt)) {
1815 actions->thread = NULL;
1816 actions->ms.sym = NULL;
1817 do_run_script(browser, actions);
1821 if (is_report_browser(hbt)) {
1822 key = do_switch_data(browser, actions);
1823 if (key == K_SWITCH_INPUT_DATA)
1824 goto out_free_stack;
1828 /* env->arch is NULL for live-mode (i.e. perf top) */
1830 tui__header_window(env);
1833 symbol_conf.filter_relative ^= 1;
1836 if (!is_report_browser(hbt)) {
1837 struct perf_top *top = hbt->arg;
1839 top->zero = !top->zero;
1845 ui_browser__help_window(&browser->b,
1846 is_report_browser(hbt) ? report_help : top_help);
1855 if (pstack__empty(browser->pstack)) {
1857 * Go back to the perf_evsel_menu__run or other user
1860 goto out_free_stack;
1863 top = pstack__pop(browser->pstack);
1864 if (top == &browser->hists->dso_filter) {
1865 perf_hpp__set_elide(HISTC_DSO, false);
1866 browser->hists->dso_filter = NULL;
1867 hists__filter_by_dso(browser->hists);
1869 if (top == &browser->hists->thread_filter) {
1870 perf_hpp__set_elide(HISTC_THREAD, false);
1871 thread__zput(browser->hists->thread_filter);
1872 hists__filter_by_thread(browser->hists);
1875 hist_browser__reset(browser);
1880 !ui_browser__dialog_yesno(&browser->b,
1881 "Do you really want to exit?"))
1886 goto out_free_stack;
1892 goto add_exit_option;
1894 if (browser->selection == NULL)
1895 goto skip_annotation;
1897 if (sort__mode == SORT_MODE__BRANCH) {
1898 bi = browser->he_selection->branch_info;
1901 goto skip_annotation;
1903 nr_options += add_annotate_opt(browser,
1904 &actions[nr_options],
1905 &options[nr_options],
1908 if (bi->to.sym != bi->from.sym)
1909 nr_options += add_annotate_opt(browser,
1910 &actions[nr_options],
1911 &options[nr_options],
1915 nr_options += add_annotate_opt(browser,
1916 &actions[nr_options],
1917 &options[nr_options],
1918 browser->selection->map,
1919 browser->selection->sym);
1922 nr_options += add_thread_opt(browser, &actions[nr_options],
1923 &options[nr_options], thread);
1924 nr_options += add_dso_opt(browser, &actions[nr_options],
1925 &options[nr_options], dso);
1926 nr_options += add_map_opt(browser, &actions[nr_options],
1927 &options[nr_options],
1928 browser->selection->map);
1930 /* perf script support */
1931 if (browser->he_selection) {
1932 nr_options += add_script_opt(browser,
1933 &actions[nr_options],
1934 &options[nr_options],
1936 nr_options += add_script_opt(browser,
1937 &actions[nr_options],
1938 &options[nr_options],
1939 NULL, browser->selection->sym);
1941 nr_options += add_script_opt(browser, &actions[nr_options],
1942 &options[nr_options], NULL, NULL);
1943 nr_options += add_switch_opt(browser, &actions[nr_options],
1944 &options[nr_options]);
1946 nr_options += add_exit_opt(browser, &actions[nr_options],
1947 &options[nr_options]);
1950 struct popup_action *act;
1952 choice = ui__popup_menu(nr_options, options);
1953 if (choice == -1 || choice >= nr_options)
1956 act = &actions[choice];
1957 key = act->fn(browser, act);
1960 if (key == K_SWITCH_INPUT_DATA)
1964 pstack__delete(browser->pstack);
1966 hist_browser__delete(browser);
1967 free_popup_options(options, MAX_OPTIONS);
1971 struct perf_evsel_menu {
1972 struct ui_browser b;
1973 struct perf_evsel *selection;
1974 bool lost_events, lost_events_warned;
1976 struct perf_session_env *env;
1979 static void perf_evsel_menu__write(struct ui_browser *browser,
1980 void *entry, int row)
1982 struct perf_evsel_menu *menu = container_of(browser,
1983 struct perf_evsel_menu, b);
1984 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1985 struct hists *hists = evsel__hists(evsel);
1986 bool current_entry = ui_browser__is_current_entry(browser, row);
1987 unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1988 const char *ev_name = perf_evsel__name(evsel);
1990 const char *warn = " ";
1993 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
1994 HE_COLORSET_NORMAL);
1996 if (perf_evsel__is_group_event(evsel)) {
1997 struct perf_evsel *pos;
1999 ev_name = perf_evsel__group_name(evsel);
2001 for_each_group_member(pos, evsel) {
2002 struct hists *pos_hists = evsel__hists(pos);
2003 nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
2007 nr_events = convert_unit(nr_events, &unit);
2008 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
2009 unit, unit == ' ' ? "" : " ", ev_name);
2010 slsmg_printf("%s", bf);
2012 nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
2013 if (nr_events != 0) {
2014 menu->lost_events = true;
2016 ui_browser__set_color(browser, HE_COLORSET_TOP);
2017 nr_events = convert_unit(nr_events, &unit);
2018 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
2019 nr_events, unit, unit == ' ' ? "" : " ");
2023 slsmg_write_nstring(warn, browser->width - printed);
2026 menu->selection = evsel;
2029 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
2030 int nr_events, const char *help,
2031 struct hist_browser_timer *hbt)
2033 struct perf_evlist *evlist = menu->b.priv;
2034 struct perf_evsel *pos;
2035 const char *title = "Available samples";
2036 int delay_secs = hbt ? hbt->refresh : 0;
2039 if (ui_browser__show(&menu->b, title,
2040 "ESC: exit, ENTER|->: Browse histograms") < 0)
2044 key = ui_browser__run(&menu->b, delay_secs);
2048 hbt->timer(hbt->arg);
2050 if (!menu->lost_events_warned && menu->lost_events) {
2051 ui_browser__warn_lost_events(&menu->b);
2052 menu->lost_events_warned = true;
2057 if (!menu->selection)
2059 pos = menu->selection;
2061 perf_evlist__set_selected(evlist, pos);
2063 * Give the calling tool a chance to populate the non
2064 * default evsel resorted hists tree.
2067 hbt->timer(hbt->arg);
2068 key = perf_evsel__hists_browse(pos, nr_events, help,
2072 ui_browser__show_title(&menu->b, title);
2075 if (pos->node.next == &evlist->entries)
2076 pos = perf_evlist__first(evlist);
2078 pos = perf_evsel__next(pos);
2081 if (pos->node.prev == &evlist->entries)
2082 pos = perf_evlist__last(evlist);
2084 pos = perf_evsel__prev(pos);
2087 if (!ui_browser__dialog_yesno(&menu->b,
2088 "Do you really want to exit?"))
2091 case K_SWITCH_INPUT_DATA:
2101 if (!ui_browser__dialog_yesno(&menu->b,
2102 "Do you really want to exit?"))
2114 ui_browser__hide(&menu->b);
2118 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
2121 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
2123 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
2129 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
2130 int nr_entries, const char *help,
2131 struct hist_browser_timer *hbt,
2133 struct perf_session_env *env)
2135 struct perf_evsel *pos;
2136 struct perf_evsel_menu menu = {
2138 .entries = &evlist->entries,
2139 .refresh = ui_browser__list_head_refresh,
2140 .seek = ui_browser__list_head_seek,
2141 .write = perf_evsel_menu__write,
2142 .filter = filter_group_entries,
2143 .nr_entries = nr_entries,
2146 .min_pcnt = min_pcnt,
2150 ui_helpline__push("Press ESC to exit");
2152 evlist__for_each(evlist, pos) {
2153 const char *ev_name = perf_evsel__name(pos);
2154 size_t line_len = strlen(ev_name) + 7;
2156 if (menu.b.width < line_len)
2157 menu.b.width = line_len;
2160 return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
2163 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
2164 struct hist_browser_timer *hbt,
2166 struct perf_session_env *env)
2168 int nr_entries = evlist->nr_entries;
2171 if (nr_entries == 1) {
2172 struct perf_evsel *first = perf_evlist__first(evlist);
2174 return perf_evsel__hists_browse(first, nr_entries, help,
2175 false, hbt, min_pcnt,
2179 if (symbol_conf.event_group) {
2180 struct perf_evsel *pos;
2183 evlist__for_each(evlist, pos) {
2184 if (perf_evsel__is_group_leader(pos))
2188 if (nr_entries == 1)
2192 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
2193 hbt, min_pcnt, env);