4 #include <linux/rbtree.h>
6 #include "../../util/evsel.h"
7 #include "../../util/evlist.h"
8 #include "../../util/hist.h"
9 #include "../../util/pstack.h"
10 #include "../../util/sort.h"
11 #include "../../util/util.h"
12 #include "../../util/top.h"
13 #include "../../arch/common.h"
15 #include "../browser.h"
16 #include "../helpline.h"
25 struct hist_entry *he_selection;
26 struct map_symbol *selection;
27 struct hist_browser_timer *hbt;
28 struct pstack *pstack;
34 u64 nr_non_filtered_entries;
35 u64 nr_callchain_rows;
38 extern void hist_browser__init_hpp(void);
40 static int hists__browser_title(struct hists *hists,
41 struct hist_browser_timer *hbt,
42 char *bf, size_t size);
43 static void hist_browser__update_nr_entries(struct hist_browser *hb);
45 static struct rb_node *hists__filter_entries(struct rb_node *nd,
48 static bool hist_browser__has_filter(struct hist_browser *hb)
50 return hists__has_filter(hb->hists) || hb->min_pcnt || symbol_conf.has_filter;
53 static int hist_browser__get_folding(struct hist_browser *browser)
56 struct hists *hists = browser->hists;
57 int unfolded_rows = 0;
59 for (nd = rb_first(&hists->entries);
60 (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
62 struct hist_entry *he =
63 rb_entry(nd, struct hist_entry, rb_node);
66 unfolded_rows += he->nr_rows;
71 static u32 hist_browser__nr_entries(struct hist_browser *hb)
75 if (hist_browser__has_filter(hb))
76 nr_entries = hb->nr_non_filtered_entries;
78 nr_entries = hb->hists->nr_entries;
80 hb->nr_callchain_rows = hist_browser__get_folding(hb);
81 return nr_entries + hb->nr_callchain_rows;
84 static void hist_browser__update_rows(struct hist_browser *hb)
86 struct ui_browser *browser = &hb->b;
87 u16 header_offset = hb->show_headers ? 1 : 0, index_row;
89 browser->rows = browser->height - header_offset;
91 * Verify if we were at the last line and that line isn't
92 * visibe because we now show the header line(s).
94 index_row = browser->index - browser->top_idx;
95 if (index_row >= browser->rows)
96 browser->index -= index_row - browser->rows + 1;
99 static void hist_browser__refresh_dimensions(struct ui_browser *browser)
101 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
103 /* 3 == +/- toggle symbol before actual hist_entry rendering */
104 browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]"));
106 * FIXME: Just keeping existing behaviour, but this really should be
107 * before updating browser->width, as it will invalidate the
108 * calculation above. Fix this and the fallout in another
111 ui_browser__refresh_dimensions(browser);
112 hist_browser__update_rows(hb);
115 static void hist_browser__gotorc(struct hist_browser *browser, int row, int column)
117 u16 header_offset = browser->show_headers ? 1 : 0;
119 ui_browser__gotorc(&browser->b, row + header_offset, column);
122 static void hist_browser__reset(struct hist_browser *browser)
125 * The hists__remove_entry_filter() already folds non-filtered
126 * entries so we can assume it has 0 callchain rows.
128 browser->nr_callchain_rows = 0;
130 hist_browser__update_nr_entries(browser);
131 browser->b.nr_entries = hist_browser__nr_entries(browser);
132 hist_browser__refresh_dimensions(&browser->b);
133 ui_browser__reset_index(&browser->b);
136 static char tree__folded_sign(bool unfolded)
138 return unfolded ? '-' : '+';
141 static char hist_entry__folded(const struct hist_entry *he)
143 return he->has_children ? tree__folded_sign(he->unfolded) : ' ';
146 static char callchain_list__folded(const struct callchain_list *cl)
148 return cl->has_children ? tree__folded_sign(cl->unfolded) : ' ';
151 static void callchain_list__set_folding(struct callchain_list *cl, bool unfold)
153 cl->unfolded = unfold ? cl->has_children : false;
156 static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
161 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
162 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
163 struct callchain_list *chain;
164 char folded_sign = ' '; /* No children */
166 list_for_each_entry(chain, &child->val, list) {
168 /* We need this because we may not have children */
169 folded_sign = callchain_list__folded(chain);
170 if (folded_sign == '+')
174 if (folded_sign == '-') /* Have children and they're unfolded */
175 n += callchain_node__count_rows_rb_tree(child);
181 static int callchain_node__count_rows(struct callchain_node *node)
183 struct callchain_list *chain;
184 bool unfolded = false;
187 list_for_each_entry(chain, &node->val, list) {
189 unfolded = chain->unfolded;
193 n += callchain_node__count_rows_rb_tree(node);
198 static int callchain__count_rows(struct rb_root *chain)
203 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
204 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
205 n += callchain_node__count_rows(node);
211 static bool hist_entry__toggle_fold(struct hist_entry *he)
216 if (!he->has_children)
219 he->unfolded = !he->unfolded;
223 static bool callchain_list__toggle_fold(struct callchain_list *cl)
228 if (!cl->has_children)
231 cl->unfolded = !cl->unfolded;
235 static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
237 struct rb_node *nd = rb_first(&node->rb_root);
239 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
240 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
241 struct callchain_list *chain;
244 list_for_each_entry(chain, &child->val, list) {
247 chain->has_children = chain->list.next != &child->val ||
248 !RB_EMPTY_ROOT(&child->rb_root);
250 chain->has_children = chain->list.next == &child->val &&
251 !RB_EMPTY_ROOT(&child->rb_root);
254 callchain_node__init_have_children_rb_tree(child);
258 static void callchain_node__init_have_children(struct callchain_node *node,
261 struct callchain_list *chain;
263 chain = list_entry(node->val.next, struct callchain_list, list);
264 chain->has_children = has_sibling;
266 if (!list_empty(&node->val)) {
267 chain = list_entry(node->val.prev, struct callchain_list, list);
268 chain->has_children = !RB_EMPTY_ROOT(&node->rb_root);
271 callchain_node__init_have_children_rb_tree(node);
274 static void callchain__init_have_children(struct rb_root *root)
276 struct rb_node *nd = rb_first(root);
277 bool has_sibling = nd && rb_next(nd);
279 for (nd = rb_first(root); nd; nd = rb_next(nd)) {
280 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
281 callchain_node__init_have_children(node, has_sibling);
285 static void hist_entry__init_have_children(struct hist_entry *he)
287 if (!he->init_have_children) {
288 he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
289 callchain__init_have_children(&he->sorted_chain);
290 he->init_have_children = true;
294 static bool hist_browser__toggle_fold(struct hist_browser *browser)
296 struct hist_entry *he = browser->he_selection;
297 struct map_symbol *ms = browser->selection;
298 struct callchain_list *cl = container_of(ms, struct callchain_list, ms);
305 has_children = hist_entry__toggle_fold(he);
307 has_children = callchain_list__toggle_fold(cl);
310 hist_entry__init_have_children(he);
311 browser->b.nr_entries -= he->nr_rows;
312 browser->nr_callchain_rows -= he->nr_rows;
315 he->nr_rows = callchain__count_rows(&he->sorted_chain);
319 browser->b.nr_entries += he->nr_rows;
320 browser->nr_callchain_rows += he->nr_rows;
325 /* If it doesn't have children, no toggling performed */
329 static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
334 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
335 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
336 struct callchain_list *chain;
337 bool has_children = false;
339 list_for_each_entry(chain, &child->val, list) {
341 callchain_list__set_folding(chain, unfold);
342 has_children = chain->has_children;
346 n += callchain_node__set_folding_rb_tree(child, unfold);
352 static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
354 struct callchain_list *chain;
355 bool has_children = false;
358 list_for_each_entry(chain, &node->val, list) {
360 callchain_list__set_folding(chain, unfold);
361 has_children = chain->has_children;
365 n += callchain_node__set_folding_rb_tree(node, unfold);
370 static int callchain__set_folding(struct rb_root *chain, bool unfold)
375 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
376 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
377 n += callchain_node__set_folding(node, unfold);
383 static void hist_entry__set_folding(struct hist_entry *he, bool unfold)
385 hist_entry__init_have_children(he);
386 he->unfolded = unfold ? he->has_children : false;
388 if (he->has_children) {
389 int n = callchain__set_folding(&he->sorted_chain, unfold);
390 he->nr_rows = unfold ? n : 0;
396 __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
399 struct hists *hists = browser->hists;
401 for (nd = rb_first(&hists->entries);
402 (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
404 struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
405 hist_entry__set_folding(he, unfold);
406 browser->nr_callchain_rows += he->nr_rows;
410 static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
412 browser->nr_callchain_rows = 0;
413 __hist_browser__set_folding(browser, unfold);
415 browser->b.nr_entries = hist_browser__nr_entries(browser);
416 /* Go to the start, we may be way after valid entries after a collapse */
417 ui_browser__reset_index(&browser->b);
420 static void ui_browser__warn_lost_events(struct ui_browser *browser)
422 ui_browser__warning(browser, 4,
423 "Events are being lost, check IO/CPU overload!\n\n"
424 "You may want to run 'perf' using a RT scheduler policy:\n\n"
425 " perf top -r 80\n\n"
426 "Or reduce the sampling frequency.");
429 static int hist_browser__run(struct hist_browser *browser, const char *help)
433 struct hist_browser_timer *hbt = browser->hbt;
434 int delay_secs = hbt ? hbt->refresh : 0;
436 browser->b.entries = &browser->hists->entries;
437 browser->b.nr_entries = hist_browser__nr_entries(browser);
439 hists__browser_title(browser->hists, hbt, title, sizeof(title));
441 if (ui_browser__show(&browser->b, title, help) < 0)
445 key = ui_browser__run(&browser->b, delay_secs);
450 hbt->timer(hbt->arg);
452 if (hist_browser__has_filter(browser))
453 hist_browser__update_nr_entries(browser);
455 nr_entries = hist_browser__nr_entries(browser);
456 ui_browser__update_nr_entries(&browser->b, nr_entries);
458 if (browser->hists->stats.nr_lost_warned !=
459 browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
460 browser->hists->stats.nr_lost_warned =
461 browser->hists->stats.nr_events[PERF_RECORD_LOST];
462 ui_browser__warn_lost_events(&browser->b);
465 hists__browser_title(browser->hists,
466 hbt, title, sizeof(title));
467 ui_browser__show_title(&browser->b, title);
470 case 'D': { /* Debug */
472 struct hist_entry *h = rb_entry(browser->b.top,
473 struct hist_entry, rb_node);
475 ui_helpline__fpush("%d: nr_ent=(%d,%d), rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
476 seq++, browser->b.nr_entries,
477 browser->hists->nr_entries,
481 h->row_offset, h->nr_rows);
485 /* Collapse the whole world. */
486 hist_browser__set_folding(browser, false);
489 /* Expand the whole world. */
490 hist_browser__set_folding(browser, true);
493 browser->show_headers = !browser->show_headers;
494 hist_browser__update_rows(browser);
497 if (hist_browser__toggle_fold(browser))
505 ui_browser__hide(&browser->b);
509 struct callchain_print_arg {
510 /* for hists browser */
512 bool is_current_entry;
519 typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
520 struct callchain_list *chain,
521 const char *str, int offset,
523 struct callchain_print_arg *arg);
525 static void hist_browser__show_callchain_entry(struct hist_browser *browser,
526 struct callchain_list *chain,
527 const char *str, int offset,
529 struct callchain_print_arg *arg)
532 char folded_sign = callchain_list__folded(chain);
533 bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
535 color = HE_COLORSET_NORMAL;
536 width = browser->b.width - (offset + 2);
537 if (ui_browser__is_current_entry(&browser->b, row)) {
538 browser->selection = &chain->ms;
539 color = HE_COLORSET_SELECTED;
540 arg->is_current_entry = true;
543 ui_browser__set_color(&browser->b, color);
544 hist_browser__gotorc(browser, row, 0);
545 ui_browser__write_nstring(&browser->b, " ", offset);
546 ui_browser__printf(&browser->b, "%c", folded_sign);
547 ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
548 ui_browser__write_nstring(&browser->b, str, width);
551 static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
552 struct callchain_list *chain,
553 const char *str, int offset,
554 unsigned short row __maybe_unused,
555 struct callchain_print_arg *arg)
557 char folded_sign = callchain_list__folded(chain);
559 arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
563 typedef bool (*check_output_full_fn)(struct hist_browser *browser,
566 static bool hist_browser__check_output_full(struct hist_browser *browser,
569 return browser->b.rows == row;
572 static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
573 unsigned short row __maybe_unused)
578 #define LEVEL_OFFSET_STEP 3
580 static int hist_browser__show_callchain(struct hist_browser *browser,
581 struct rb_root *root, int level,
582 unsigned short row, u64 total,
583 print_callchain_entry_fn print,
584 struct callchain_print_arg *arg,
585 check_output_full_fn is_output_full)
587 struct rb_node *node;
588 int first_row = row, offset = level * LEVEL_OFFSET_STEP;
592 node = rb_first(root);
593 need_percent = node && rb_next(node);
596 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
597 struct rb_node *next = rb_next(node);
598 u64 cumul = callchain_cumul_hits(child);
599 struct callchain_list *chain;
600 char folded_sign = ' ';
602 int extra_offset = 0;
604 list_for_each_entry(chain, &child->val, list) {
605 char bf[1024], *alloc_str;
607 bool was_first = first;
611 else if (need_percent)
612 extra_offset = LEVEL_OFFSET_STEP;
614 folded_sign = callchain_list__folded(chain);
615 if (arg->row_offset != 0) {
621 str = callchain_list__sym_name(chain, bf, sizeof(bf),
624 if (was_first && need_percent) {
625 double percent = cumul * 100.0 / total;
627 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
628 str = "Not enough memory!";
633 print(browser, chain, str, offset + extra_offset, row, arg);
637 if (is_output_full(browser, ++row))
640 if (folded_sign == '+')
644 if (folded_sign == '-') {
645 const int new_level = level + (extra_offset ? 2 : 1);
647 if (callchain_param.mode == CHAIN_GRAPH_REL)
648 new_total = child->children_hit;
652 row += hist_browser__show_callchain(browser, &child->rb_root,
653 new_level, row, new_total,
654 print, arg, is_output_full);
656 if (is_output_full(browser, row))
661 return row - first_row;
665 struct ui_browser *b;
670 static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
672 struct hpp_arg *arg = hpp->ptr;
678 len = va_arg(args, int);
679 percent = va_arg(args, double);
682 ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
684 ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
685 ui_browser__printf(arg->b, "%s", hpp->buf);
687 advance_hpp(hpp, ret);
691 #define __HPP_COLOR_PERCENT_FN(_type, _field) \
692 static u64 __hpp_get_##_field(struct hist_entry *he) \
694 return he->stat._field; \
698 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
699 struct perf_hpp *hpp, \
700 struct hist_entry *he) \
702 return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%", \
703 __hpp__slsmg_color_printf, true); \
706 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
707 static u64 __hpp_get_acc_##_field(struct hist_entry *he) \
709 return he->stat_acc->_field; \
713 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
714 struct perf_hpp *hpp, \
715 struct hist_entry *he) \
717 if (!symbol_conf.cumulate_callchain) { \
718 struct hpp_arg *arg = hpp->ptr; \
719 int len = fmt->user_len ?: fmt->len; \
720 int ret = scnprintf(hpp->buf, hpp->size, \
721 "%*s", len, "N/A"); \
722 ui_browser__printf(arg->b, "%s", hpp->buf); \
726 return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field, \
727 " %*.2f%%", __hpp__slsmg_color_printf, true); \
730 __HPP_COLOR_PERCENT_FN(overhead, period)
731 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
732 __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
733 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
734 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
735 __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
737 #undef __HPP_COLOR_PERCENT_FN
738 #undef __HPP_COLOR_ACC_PERCENT_FN
740 void hist_browser__init_hpp(void)
742 perf_hpp__format[PERF_HPP__OVERHEAD].color =
743 hist_browser__hpp_color_overhead;
744 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
745 hist_browser__hpp_color_overhead_sys;
746 perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
747 hist_browser__hpp_color_overhead_us;
748 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
749 hist_browser__hpp_color_overhead_guest_sys;
750 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
751 hist_browser__hpp_color_overhead_guest_us;
752 perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
753 hist_browser__hpp_color_overhead_acc;
756 static int hist_browser__show_entry(struct hist_browser *browser,
757 struct hist_entry *entry,
762 int width = browser->b.width;
763 char folded_sign = ' ';
764 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
765 off_t row_offset = entry->row_offset;
767 struct perf_hpp_fmt *fmt;
770 browser->he_selection = entry;
771 browser->selection = &entry->ms;
774 if (symbol_conf.use_callchain) {
775 hist_entry__init_have_children(entry);
776 folded_sign = hist_entry__folded(entry);
779 if (row_offset == 0) {
780 struct hpp_arg arg = {
782 .folded_sign = folded_sign,
783 .current_entry = current_entry,
785 struct perf_hpp hpp = {
792 hist_browser__gotorc(browser, row, 0);
794 perf_hpp__for_each_format(fmt) {
795 if (perf_hpp__should_skip(fmt) || column++ < browser->b.horiz_scroll)
798 if (current_entry && browser->b.navkeypressed) {
799 ui_browser__set_color(&browser->b,
800 HE_COLORSET_SELECTED);
802 ui_browser__set_color(&browser->b,
807 if (symbol_conf.use_callchain) {
808 ui_browser__printf(&browser->b, "%c ", folded_sign);
813 ui_browser__printf(&browser->b, " ");
818 width -= fmt->color(fmt, &hpp, entry);
820 width -= fmt->entry(fmt, &hpp, entry);
821 ui_browser__printf(&browser->b, "%s", s);
825 /* The scroll bar isn't being used */
826 if (!browser->b.navkeypressed)
829 ui_browser__write_nstring(&browser->b, "", width);
836 if (folded_sign == '-' && row != browser->b.rows) {
837 u64 total = hists__total_period(entry->hists);
838 struct callchain_print_arg arg = {
839 .row_offset = row_offset,
840 .is_current_entry = current_entry,
843 if (callchain_param.mode == CHAIN_GRAPH_REL) {
844 if (symbol_conf.cumulate_callchain)
845 total = entry->stat_acc->period;
847 total = entry->stat.period;
850 printed += hist_browser__show_callchain(browser,
851 &entry->sorted_chain, 1, row, total,
852 hist_browser__show_callchain_entry, &arg,
853 hist_browser__check_output_full);
855 if (arg.is_current_entry)
856 browser->he_selection = entry;
862 static int advance_hpp_check(struct perf_hpp *hpp, int inc)
864 advance_hpp(hpp, inc);
865 return hpp->size <= 0;
868 static int hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf, size_t size)
870 struct hists *hists = browser->hists;
871 struct perf_hpp dummy_hpp = {
875 struct perf_hpp_fmt *fmt;
879 if (symbol_conf.use_callchain) {
880 ret = scnprintf(buf, size, " ");
881 if (advance_hpp_check(&dummy_hpp, ret))
885 perf_hpp__for_each_format(fmt) {
886 if (perf_hpp__should_skip(fmt) || column++ < browser->b.horiz_scroll)
889 ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists));
890 if (advance_hpp_check(&dummy_hpp, ret))
893 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
894 if (advance_hpp_check(&dummy_hpp, ret))
901 static void hist_browser__show_headers(struct hist_browser *browser)
905 hists_browser__scnprintf_headers(browser, headers, sizeof(headers));
906 ui_browser__gotorc(&browser->b, 0, 0);
907 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
908 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
911 static void ui_browser__hists_init_top(struct ui_browser *browser)
913 if (browser->top == NULL) {
914 struct hist_browser *hb;
916 hb = container_of(browser, struct hist_browser, b);
917 browser->top = rb_first(&hb->hists->entries);
921 static unsigned int hist_browser__refresh(struct ui_browser *browser)
924 u16 header_offset = 0;
926 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
928 if (hb->show_headers) {
929 hist_browser__show_headers(hb);
933 ui_browser__hists_init_top(browser);
934 hb->he_selection = NULL;
935 hb->selection = NULL;
937 for (nd = browser->top; nd; nd = rb_next(nd)) {
938 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
944 percent = hist_entry__get_percent_limit(h);
945 if (percent < hb->min_pcnt)
948 row += hist_browser__show_entry(hb, h, row);
949 if (row == browser->rows)
953 return row + header_offset;
956 static struct rb_node *hists__filter_entries(struct rb_node *nd,
960 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
961 float percent = hist_entry__get_percent_limit(h);
963 if (!h->filtered && percent >= min_pcnt)
972 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
976 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
977 float percent = hist_entry__get_percent_limit(h);
979 if (!h->filtered && percent >= min_pcnt)
988 static void ui_browser__hists_seek(struct ui_browser *browser,
989 off_t offset, int whence)
991 struct hist_entry *h;
994 struct hist_browser *hb;
996 hb = container_of(browser, struct hist_browser, b);
998 if (browser->nr_entries == 0)
1001 ui_browser__hists_init_top(browser);
1005 nd = hists__filter_entries(rb_first(browser->entries),
1012 nd = hists__filter_prev_entries(rb_last(browser->entries),
1021 * Moves not relative to the first visible entry invalidates its
1024 h = rb_entry(browser->top, struct hist_entry, rb_node);
1028 * Here we have to check if nd is expanded (+), if it is we can't go
1029 * the next top level hist_entry, instead we must compute an offset of
1030 * what _not_ to show and not change the first visible entry.
1032 * This offset increments when we are going from top to bottom and
1033 * decreases when we're going from bottom to top.
1035 * As we don't have backpointers to the top level in the callchains
1036 * structure, we need to always print the whole hist_entry callchain,
1037 * skipping the first ones that are before the first visible entry
1038 * and stop when we printed enough lines to fill the screen.
1046 h = rb_entry(nd, struct hist_entry, rb_node);
1048 u16 remaining = h->nr_rows - h->row_offset;
1049 if (offset > remaining) {
1050 offset -= remaining;
1053 h->row_offset += offset;
1059 nd = hists__filter_entries(rb_next(nd), hb->min_pcnt);
1064 } while (offset != 0);
1065 } else if (offset < 0) {
1067 h = rb_entry(nd, struct hist_entry, rb_node);
1070 if (-offset > h->row_offset) {
1071 offset += h->row_offset;
1074 h->row_offset += offset;
1080 if (-offset > h->nr_rows) {
1081 offset += h->nr_rows;
1084 h->row_offset = h->nr_rows + offset;
1092 nd = hists__filter_prev_entries(rb_prev(nd),
1100 * Last unfiltered hist_entry, check if it is
1101 * unfolded, if it is then we should have
1102 * row_offset at its last entry.
1104 h = rb_entry(nd, struct hist_entry, rb_node);
1106 h->row_offset = h->nr_rows;
1113 h = rb_entry(nd, struct hist_entry, rb_node);
1118 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1119 struct hist_entry *he, FILE *fp)
1121 u64 total = hists__total_period(he->hists);
1122 struct callchain_print_arg arg = {
1126 if (symbol_conf.cumulate_callchain)
1127 total = he->stat_acc->period;
1129 hist_browser__show_callchain(browser, &he->sorted_chain, 1, 0, total,
1130 hist_browser__fprintf_callchain_entry, &arg,
1131 hist_browser__check_dump_full);
1135 static int hist_browser__fprintf_entry(struct hist_browser *browser,
1136 struct hist_entry *he, FILE *fp)
1140 char folded_sign = ' ';
1141 struct perf_hpp hpp = {
1145 struct perf_hpp_fmt *fmt;
1149 if (symbol_conf.use_callchain)
1150 folded_sign = hist_entry__folded(he);
1152 if (symbol_conf.use_callchain)
1153 printed += fprintf(fp, "%c ", folded_sign);
1155 perf_hpp__for_each_format(fmt) {
1156 if (perf_hpp__should_skip(fmt))
1160 ret = scnprintf(hpp.buf, hpp.size, " ");
1161 advance_hpp(&hpp, ret);
1165 ret = fmt->entry(fmt, &hpp, he);
1166 advance_hpp(&hpp, ret);
1168 printed += fprintf(fp, "%s\n", rtrim(s));
1170 if (folded_sign == '-')
1171 printed += hist_browser__fprintf_callchain(browser, he, fp);
1176 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1178 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
1183 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1185 printed += hist_browser__fprintf_entry(browser, h, fp);
1186 nd = hists__filter_entries(rb_next(nd), browser->min_pcnt);
1192 static int hist_browser__dump(struct hist_browser *browser)
1198 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
1199 if (access(filename, F_OK))
1202 * XXX: Just an arbitrary lazy upper limit
1204 if (++browser->print_seq == 8192) {
1205 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1210 fp = fopen(filename, "w");
1213 const char *err = strerror_r(errno, bf, sizeof(bf));
1214 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
1218 ++browser->print_seq;
1219 hist_browser__fprintf(browser, fp);
1221 ui_helpline__fpush("%s written!", filename);
1226 static struct hist_browser *hist_browser__new(struct hists *hists,
1227 struct hist_browser_timer *hbt,
1228 struct perf_env *env)
1230 struct hist_browser *browser = zalloc(sizeof(*browser));
1233 browser->hists = hists;
1234 browser->b.refresh = hist_browser__refresh;
1235 browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
1236 browser->b.seek = ui_browser__hists_seek;
1237 browser->b.use_navkeypressed = true;
1238 browser->show_headers = symbol_conf.show_hist_headers;
1246 static void hist_browser__delete(struct hist_browser *browser)
1251 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
1253 return browser->he_selection;
1256 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
1258 return browser->he_selection->thread;
1261 /* Check whether the browser is for 'top' or 'report' */
1262 static inline bool is_report_browser(void *timer)
1264 return timer == NULL;
1267 static int hists__browser_title(struct hists *hists,
1268 struct hist_browser_timer *hbt,
1269 char *bf, size_t size)
1273 const struct dso *dso = hists->dso_filter;
1274 const struct thread *thread = hists->thread_filter;
1275 int socket_id = hists->socket_filter;
1276 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1277 u64 nr_events = hists->stats.total_period;
1278 struct perf_evsel *evsel = hists_to_evsel(hists);
1279 const char *ev_name = perf_evsel__name(evsel);
1281 size_t buflen = sizeof(buf);
1282 char ref[30] = " show reference callgraph, ";
1283 bool enable_ref = false;
1285 if (symbol_conf.filter_relative) {
1286 nr_samples = hists->stats.nr_non_filtered_samples;
1287 nr_events = hists->stats.total_non_filtered_period;
1290 if (perf_evsel__is_group_event(evsel)) {
1291 struct perf_evsel *pos;
1293 perf_evsel__group_desc(evsel, buf, buflen);
1296 for_each_group_member(pos, evsel) {
1297 struct hists *pos_hists = evsel__hists(pos);
1299 if (symbol_conf.filter_relative) {
1300 nr_samples += pos_hists->stats.nr_non_filtered_samples;
1301 nr_events += pos_hists->stats.total_non_filtered_period;
1303 nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
1304 nr_events += pos_hists->stats.total_period;
1309 if (symbol_conf.show_ref_callgraph &&
1310 strstr(ev_name, "call-graph=no"))
1312 nr_samples = convert_unit(nr_samples, &unit);
1313 printed = scnprintf(bf, size,
1314 "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64,
1315 nr_samples, unit, ev_name, enable_ref ? ref : " ", nr_events);
1318 if (hists->uid_filter_str)
1319 printed += snprintf(bf + printed, size - printed,
1320 ", UID: %s", hists->uid_filter_str);
1322 printed += scnprintf(bf + printed, size - printed,
1324 (thread->comm_set ? thread__comm_str(thread) : ""),
1327 printed += scnprintf(bf + printed, size - printed,
1328 ", DSO: %s", dso->short_name);
1330 printed += scnprintf(bf + printed, size - printed,
1331 ", Processor Socket: %d", socket_id);
1332 if (!is_report_browser(hbt)) {
1333 struct perf_top *top = hbt->arg;
1336 printed += scnprintf(bf + printed, size - printed, " [z]");
1342 static inline void free_popup_options(char **options, int n)
1346 for (i = 0; i < n; ++i)
1351 * Only runtime switching of perf data file will make "input_name" point
1352 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1353 * whether we need to call free() for current "input_name" during the switch.
1355 static bool is_input_name_malloced = false;
1357 static int switch_data_file(void)
1359 char *pwd, *options[32], *abs_path[32], *tmp;
1361 int nr_options = 0, choice = -1, ret = -1;
1362 struct dirent *dent;
1364 pwd = getenv("PWD");
1368 pwd_dir = opendir(pwd);
1372 memset(options, 0, sizeof(options));
1373 memset(options, 0, sizeof(abs_path));
1375 while ((dent = readdir(pwd_dir))) {
1376 char path[PATH_MAX];
1378 char *name = dent->d_name;
1381 if (!(dent->d_type == DT_REG))
1384 snprintf(path, sizeof(path), "%s/%s", pwd, name);
1386 file = fopen(path, "r");
1390 if (fread(&magic, 1, 8, file) < 8)
1391 goto close_file_and_continue;
1393 if (is_perf_magic(magic)) {
1394 options[nr_options] = strdup(name);
1395 if (!options[nr_options])
1396 goto close_file_and_continue;
1398 abs_path[nr_options] = strdup(path);
1399 if (!abs_path[nr_options]) {
1400 zfree(&options[nr_options]);
1401 ui__warning("Can't search all data files due to memory shortage.\n");
1409 close_file_and_continue:
1411 if (nr_options >= 32) {
1412 ui__warning("Too many perf data files in PWD!\n"
1413 "Only the first 32 files will be listed.\n");
1420 choice = ui__popup_menu(nr_options, options);
1421 if (choice < nr_options && choice >= 0) {
1422 tmp = strdup(abs_path[choice]);
1424 if (is_input_name_malloced)
1425 free((void *)input_name);
1427 is_input_name_malloced = true;
1430 ui__warning("Data switch failed due to memory shortage!\n");
1434 free_popup_options(options, nr_options);
1435 free_popup_options(abs_path, nr_options);
1439 struct popup_action {
1440 struct thread *thread;
1441 struct map_symbol ms;
1444 int (*fn)(struct hist_browser *browser, struct popup_action *act);
1448 do_annotate(struct hist_browser *browser, struct popup_action *act)
1450 struct perf_evsel *evsel;
1451 struct annotation *notes;
1452 struct hist_entry *he;
1455 if (!objdump_path && perf_env__lookup_objdump(browser->env))
1458 notes = symbol__annotation(act->ms.sym);
1462 evsel = hists_to_evsel(browser->hists);
1463 err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
1464 he = hist_browser__selected_entry(browser);
1466 * offer option to annotate the other branch source or target
1467 * (if they exists) when returning from annotate
1469 if ((err == 'q' || err == CTRL('c')) && he->branch_info)
1472 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1474 ui_browser__handle_resize(&browser->b);
1479 add_annotate_opt(struct hist_browser *browser __maybe_unused,
1480 struct popup_action *act, char **optstr,
1481 struct map *map, struct symbol *sym)
1483 if (sym == NULL || map->dso->annotate_warned)
1486 if (asprintf(optstr, "Annotate %s", sym->name) < 0)
1491 act->fn = do_annotate;
1496 do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
1498 struct thread *thread = act->thread;
1500 if (browser->hists->thread_filter) {
1501 pstack__remove(browser->pstack, &browser->hists->thread_filter);
1502 perf_hpp__set_elide(HISTC_THREAD, false);
1503 thread__zput(browser->hists->thread_filter);
1506 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
1507 thread->comm_set ? thread__comm_str(thread) : "",
1509 browser->hists->thread_filter = thread__get(thread);
1510 perf_hpp__set_elide(HISTC_THREAD, false);
1511 pstack__push(browser->pstack, &browser->hists->thread_filter);
1514 hists__filter_by_thread(browser->hists);
1515 hist_browser__reset(browser);
1520 add_thread_opt(struct hist_browser *browser, struct popup_action *act,
1521 char **optstr, struct thread *thread)
1526 if (asprintf(optstr, "Zoom %s %s(%d) thread",
1527 browser->hists->thread_filter ? "out of" : "into",
1528 thread->comm_set ? thread__comm_str(thread) : "",
1532 act->thread = thread;
1533 act->fn = do_zoom_thread;
1538 do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
1540 struct map *map = act->ms.map;
1542 if (browser->hists->dso_filter) {
1543 pstack__remove(browser->pstack, &browser->hists->dso_filter);
1544 perf_hpp__set_elide(HISTC_DSO, false);
1545 browser->hists->dso_filter = NULL;
1550 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
1551 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
1552 browser->hists->dso_filter = map->dso;
1553 perf_hpp__set_elide(HISTC_DSO, true);
1554 pstack__push(browser->pstack, &browser->hists->dso_filter);
1557 hists__filter_by_dso(browser->hists);
1558 hist_browser__reset(browser);
1563 add_dso_opt(struct hist_browser *browser, struct popup_action *act,
1564 char **optstr, struct map *map)
1569 if (asprintf(optstr, "Zoom %s %s DSO",
1570 browser->hists->dso_filter ? "out of" : "into",
1571 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
1575 act->fn = do_zoom_dso;
1580 do_browse_map(struct hist_browser *browser __maybe_unused,
1581 struct popup_action *act)
1583 map__browse(act->ms.map);
1588 add_map_opt(struct hist_browser *browser __maybe_unused,
1589 struct popup_action *act, char **optstr, struct map *map)
1594 if (asprintf(optstr, "Browse map details") < 0)
1598 act->fn = do_browse_map;
1603 do_run_script(struct hist_browser *browser __maybe_unused,
1604 struct popup_action *act)
1606 char script_opt[64];
1607 memset(script_opt, 0, sizeof(script_opt));
1610 scnprintf(script_opt, sizeof(script_opt), " -c %s ",
1611 thread__comm_str(act->thread));
1612 } else if (act->ms.sym) {
1613 scnprintf(script_opt, sizeof(script_opt), " -S %s ",
1617 script_browse(script_opt);
1622 add_script_opt(struct hist_browser *browser __maybe_unused,
1623 struct popup_action *act, char **optstr,
1624 struct thread *thread, struct symbol *sym)
1627 if (asprintf(optstr, "Run scripts for samples of thread [%s]",
1628 thread__comm_str(thread)) < 0)
1631 if (asprintf(optstr, "Run scripts for samples of symbol [%s]",
1635 if (asprintf(optstr, "Run scripts for all samples") < 0)
1639 act->thread = thread;
1641 act->fn = do_run_script;
1646 do_switch_data(struct hist_browser *browser __maybe_unused,
1647 struct popup_action *act __maybe_unused)
1649 if (switch_data_file()) {
1650 ui__warning("Won't switch the data files due to\n"
1651 "no valid data file get selected!\n");
1655 return K_SWITCH_INPUT_DATA;
1659 add_switch_opt(struct hist_browser *browser,
1660 struct popup_action *act, char **optstr)
1662 if (!is_report_browser(browser->hbt))
1665 if (asprintf(optstr, "Switch to another data file in PWD") < 0)
1668 act->fn = do_switch_data;
1673 do_exit_browser(struct hist_browser *browser __maybe_unused,
1674 struct popup_action *act __maybe_unused)
1680 add_exit_opt(struct hist_browser *browser __maybe_unused,
1681 struct popup_action *act, char **optstr)
1683 if (asprintf(optstr, "Exit") < 0)
1686 act->fn = do_exit_browser;
1691 do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
1693 if (browser->hists->socket_filter > -1) {
1694 pstack__remove(browser->pstack, &browser->hists->socket_filter);
1695 browser->hists->socket_filter = -1;
1696 perf_hpp__set_elide(HISTC_SOCKET, false);
1698 browser->hists->socket_filter = act->socket;
1699 perf_hpp__set_elide(HISTC_SOCKET, true);
1700 pstack__push(browser->pstack, &browser->hists->socket_filter);
1703 hists__filter_by_socket(browser->hists);
1704 hist_browser__reset(browser);
1709 add_socket_opt(struct hist_browser *browser, struct popup_action *act,
1710 char **optstr, int socket_id)
1715 if (asprintf(optstr, "Zoom %s Processor Socket %d",
1716 (browser->hists->socket_filter > -1) ? "out of" : "into",
1720 act->socket = socket_id;
1721 act->fn = do_zoom_socket;
1725 static void hist_browser__update_nr_entries(struct hist_browser *hb)
1728 struct rb_node *nd = rb_first(&hb->hists->entries);
1730 if (hb->min_pcnt == 0) {
1731 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
1735 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
1740 hb->nr_non_filtered_entries = nr_entries;
1743 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
1744 const char *helpline,
1746 struct hist_browser_timer *hbt,
1748 struct perf_env *env)
1750 struct hists *hists = evsel__hists(evsel);
1751 struct hist_browser *browser = hist_browser__new(hists, hbt, env);
1752 struct branch_info *bi;
1753 #define MAX_OPTIONS 16
1754 char *options[MAX_OPTIONS];
1755 struct popup_action actions[MAX_OPTIONS];
1759 int delay_secs = hbt ? hbt->refresh : 0;
1760 struct perf_hpp_fmt *fmt;
1762 #define HIST_BROWSER_HELP_COMMON \
1763 "h/?/F1 Show this window\n" \
1765 "PGDN/SPACE Navigate\n" \
1766 "q/ESC/CTRL+C Exit browser\n\n" \
1767 "For multiple event sessions:\n\n" \
1768 "TAB/UNTAB Switch events\n\n" \
1769 "For symbolic views (--sort has sym):\n\n" \
1770 "ENTER Zoom into DSO/Threads & Annotate current symbol\n" \
1772 "a Annotate current symbol\n" \
1773 "C Collapse all callchains\n" \
1774 "d Zoom into current DSO\n" \
1775 "E Expand all callchains\n" \
1776 "F Toggle percentage of filtered entries\n" \
1777 "H Display column headers\n" \
1778 "m Display context menu\n" \
1779 "S Zoom into current Processor Socket\n" \
1781 /* help messages are sorted by lexical order of the hotkey */
1782 const char report_help[] = HIST_BROWSER_HELP_COMMON
1783 "i Show header information\n"
1784 "P Print histograms to perf.hist.N\n"
1785 "r Run available scripts\n"
1786 "s Switch to another data file in PWD\n"
1787 "t Zoom into current Thread\n"
1788 "V Verbose (DSO names in callchains, etc)\n"
1789 "/ Filter symbol by name";
1790 const char top_help[] = HIST_BROWSER_HELP_COMMON
1791 "P Print histograms to perf.hist.N\n"
1792 "t Zoom into current Thread\n"
1793 "V Verbose (DSO names in callchains, etc)\n"
1794 "z Toggle zeroing of samples\n"
1795 "f Enable/Disable events\n"
1796 "/ Filter symbol by name";
1798 if (browser == NULL)
1801 /* reset abort key so that it can get Ctrl-C as a key */
1803 SLang_init_tty(0, 0, 0);
1806 browser->min_pcnt = min_pcnt;
1807 hist_browser__update_nr_entries(browser);
1810 browser->pstack = pstack__new(3);
1811 if (browser->pstack == NULL)
1814 ui_helpline__push(helpline);
1816 memset(options, 0, sizeof(options));
1817 memset(actions, 0, sizeof(actions));
1819 perf_hpp__for_each_format(fmt) {
1820 perf_hpp__reset_width(fmt, hists);
1822 * This is done just once, and activates the horizontal scrolling
1823 * code in the ui_browser code, it would be better to have a the
1824 * counter in the perf_hpp code, but I couldn't find doing it here
1825 * works, FIXME by setting this in hist_browser__new, for now, be
1828 ++browser->b.columns;
1831 if (symbol_conf.col_width_list_str)
1832 perf_hpp__set_user_width(symbol_conf.col_width_list_str);
1835 struct thread *thread = NULL;
1836 struct map *map = NULL;
1842 key = hist_browser__run(browser, helpline);
1844 if (browser->he_selection != NULL) {
1845 thread = hist_browser__selected_thread(browser);
1846 map = browser->selection->map;
1847 socked_id = browser->he_selection->socket;
1855 * Exit the browser, let hists__browser_tree
1856 * go to the next or previous
1858 goto out_free_stack;
1860 if (!sort__has_sym) {
1861 ui_browser__warning(&browser->b, delay_secs * 2,
1862 "Annotation is only available for symbolic views, "
1863 "include \"sym*\" in --sort to use it.");
1867 if (browser->selection == NULL ||
1868 browser->selection->sym == NULL ||
1869 browser->selection->map->dso->annotate_warned)
1872 actions->ms.map = browser->selection->map;
1873 actions->ms.sym = browser->selection->sym;
1874 do_annotate(browser, actions);
1877 hist_browser__dump(browser);
1880 actions->ms.map = map;
1881 do_zoom_dso(browser, actions);
1884 browser->show_dso = !browser->show_dso;
1887 actions->thread = thread;
1888 do_zoom_thread(browser, actions);
1891 actions->socket = socked_id;
1892 do_zoom_socket(browser, actions);
1895 if (ui_browser__input_window("Symbol to show",
1896 "Please enter the name of symbol you want to see.\n"
1897 "To remove the filter later, press / + ENTER.",
1898 buf, "ENTER: OK, ESC: Cancel",
1899 delay_secs * 2) == K_ENTER) {
1900 hists->symbol_filter_str = *buf ? buf : NULL;
1901 hists__filter_by_symbol(hists);
1902 hist_browser__reset(browser);
1906 if (is_report_browser(hbt)) {
1907 actions->thread = NULL;
1908 actions->ms.sym = NULL;
1909 do_run_script(browser, actions);
1913 if (is_report_browser(hbt)) {
1914 key = do_switch_data(browser, actions);
1915 if (key == K_SWITCH_INPUT_DATA)
1916 goto out_free_stack;
1920 /* env->arch is NULL for live-mode (i.e. perf top) */
1922 tui__header_window(env);
1925 symbol_conf.filter_relative ^= 1;
1928 if (!is_report_browser(hbt)) {
1929 struct perf_top *top = hbt->arg;
1931 top->zero = !top->zero;
1937 ui_browser__help_window(&browser->b,
1938 is_report_browser(hbt) ? report_help : top_help);
1949 if (pstack__empty(browser->pstack)) {
1951 * Go back to the perf_evsel_menu__run or other user
1954 goto out_free_stack;
1957 ui_browser__dialog_yesno(&browser->b,
1958 "Do you really want to exit?"))
1959 goto out_free_stack;
1963 top = pstack__peek(browser->pstack);
1964 if (top == &browser->hists->dso_filter) {
1966 * No need to set actions->dso here since
1967 * it's just to remove the current filter.
1968 * Ditto for thread below.
1970 do_zoom_dso(browser, actions);
1971 } else if (top == &browser->hists->thread_filter) {
1972 do_zoom_thread(browser, actions);
1973 } else if (top == &browser->hists->socket_filter) {
1974 do_zoom_socket(browser, actions);
1980 goto out_free_stack;
1982 if (!is_report_browser(hbt)) {
1983 struct perf_top *top = hbt->arg;
1985 perf_evlist__toggle_enable(top->evlist);
1987 * No need to refresh, resort/decay histogram
1988 * entries if we are not collecting samples:
1990 if (top->evlist->enabled) {
1991 helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
1992 hbt->refresh = delay_secs;
1994 helpline = "Press 'f' again to re-enable the events";
2001 helpline = "Press '?' for help on key bindings";
2006 goto add_exit_option;
2008 if (browser->selection == NULL)
2009 goto skip_annotation;
2011 if (sort__mode == SORT_MODE__BRANCH) {
2012 bi = browser->he_selection->branch_info;
2015 goto skip_annotation;
2017 nr_options += add_annotate_opt(browser,
2018 &actions[nr_options],
2019 &options[nr_options],
2022 if (bi->to.sym != bi->from.sym)
2023 nr_options += add_annotate_opt(browser,
2024 &actions[nr_options],
2025 &options[nr_options],
2029 nr_options += add_annotate_opt(browser,
2030 &actions[nr_options],
2031 &options[nr_options],
2032 browser->selection->map,
2033 browser->selection->sym);
2036 nr_options += add_thread_opt(browser, &actions[nr_options],
2037 &options[nr_options], thread);
2038 nr_options += add_dso_opt(browser, &actions[nr_options],
2039 &options[nr_options], map);
2040 nr_options += add_map_opt(browser, &actions[nr_options],
2041 &options[nr_options],
2042 browser->selection ?
2043 browser->selection->map : NULL);
2044 nr_options += add_socket_opt(browser, &actions[nr_options],
2045 &options[nr_options],
2047 /* perf script support */
2048 if (browser->he_selection) {
2049 nr_options += add_script_opt(browser,
2050 &actions[nr_options],
2051 &options[nr_options],
2054 * Note that browser->selection != NULL
2055 * when browser->he_selection is not NULL,
2056 * so we don't need to check browser->selection
2057 * before fetching browser->selection->sym like what
2058 * we do before fetching browser->selection->map.
2060 * See hist_browser__show_entry.
2062 nr_options += add_script_opt(browser,
2063 &actions[nr_options],
2064 &options[nr_options],
2065 NULL, browser->selection->sym);
2067 nr_options += add_script_opt(browser, &actions[nr_options],
2068 &options[nr_options], NULL, NULL);
2069 nr_options += add_switch_opt(browser, &actions[nr_options],
2070 &options[nr_options]);
2072 nr_options += add_exit_opt(browser, &actions[nr_options],
2073 &options[nr_options]);
2076 struct popup_action *act;
2078 choice = ui__popup_menu(nr_options, options);
2079 if (choice == -1 || choice >= nr_options)
2082 act = &actions[choice];
2083 key = act->fn(browser, act);
2086 if (key == K_SWITCH_INPUT_DATA)
2090 pstack__delete(browser->pstack);
2092 hist_browser__delete(browser);
2093 free_popup_options(options, MAX_OPTIONS);
2097 struct perf_evsel_menu {
2098 struct ui_browser b;
2099 struct perf_evsel *selection;
2100 bool lost_events, lost_events_warned;
2102 struct perf_env *env;
2105 static void perf_evsel_menu__write(struct ui_browser *browser,
2106 void *entry, int row)
2108 struct perf_evsel_menu *menu = container_of(browser,
2109 struct perf_evsel_menu, b);
2110 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
2111 struct hists *hists = evsel__hists(evsel);
2112 bool current_entry = ui_browser__is_current_entry(browser, row);
2113 unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
2114 const char *ev_name = perf_evsel__name(evsel);
2116 const char *warn = " ";
2119 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
2120 HE_COLORSET_NORMAL);
2122 if (perf_evsel__is_group_event(evsel)) {
2123 struct perf_evsel *pos;
2125 ev_name = perf_evsel__group_name(evsel);
2127 for_each_group_member(pos, evsel) {
2128 struct hists *pos_hists = evsel__hists(pos);
2129 nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
2133 nr_events = convert_unit(nr_events, &unit);
2134 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
2135 unit, unit == ' ' ? "" : " ", ev_name);
2136 ui_browser__printf(browser, "%s", bf);
2138 nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
2139 if (nr_events != 0) {
2140 menu->lost_events = true;
2142 ui_browser__set_color(browser, HE_COLORSET_TOP);
2143 nr_events = convert_unit(nr_events, &unit);
2144 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
2145 nr_events, unit, unit == ' ' ? "" : " ");
2149 ui_browser__write_nstring(browser, warn, browser->width - printed);
2152 menu->selection = evsel;
2155 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
2156 int nr_events, const char *help,
2157 struct hist_browser_timer *hbt)
2159 struct perf_evlist *evlist = menu->b.priv;
2160 struct perf_evsel *pos;
2161 const char *title = "Available samples";
2162 int delay_secs = hbt ? hbt->refresh : 0;
2165 if (ui_browser__show(&menu->b, title,
2166 "ESC: exit, ENTER|->: Browse histograms") < 0)
2170 key = ui_browser__run(&menu->b, delay_secs);
2174 hbt->timer(hbt->arg);
2176 if (!menu->lost_events_warned && menu->lost_events) {
2177 ui_browser__warn_lost_events(&menu->b);
2178 menu->lost_events_warned = true;
2183 if (!menu->selection)
2185 pos = menu->selection;
2187 perf_evlist__set_selected(evlist, pos);
2189 * Give the calling tool a chance to populate the non
2190 * default evsel resorted hists tree.
2193 hbt->timer(hbt->arg);
2194 key = perf_evsel__hists_browse(pos, nr_events, help,
2198 ui_browser__show_title(&menu->b, title);
2201 if (pos->node.next == &evlist->entries)
2202 pos = perf_evlist__first(evlist);
2204 pos = perf_evsel__next(pos);
2207 if (pos->node.prev == &evlist->entries)
2208 pos = perf_evlist__last(evlist);
2210 pos = perf_evsel__prev(pos);
2212 case K_SWITCH_INPUT_DATA:
2223 if (!ui_browser__dialog_yesno(&menu->b,
2224 "Do you really want to exit?"))
2236 ui_browser__hide(&menu->b);
2240 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
2243 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
2245 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
2251 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
2252 int nr_entries, const char *help,
2253 struct hist_browser_timer *hbt,
2255 struct perf_env *env)
2257 struct perf_evsel *pos;
2258 struct perf_evsel_menu menu = {
2260 .entries = &evlist->entries,
2261 .refresh = ui_browser__list_head_refresh,
2262 .seek = ui_browser__list_head_seek,
2263 .write = perf_evsel_menu__write,
2264 .filter = filter_group_entries,
2265 .nr_entries = nr_entries,
2268 .min_pcnt = min_pcnt,
2272 ui_helpline__push("Press ESC to exit");
2274 evlist__for_each(evlist, pos) {
2275 const char *ev_name = perf_evsel__name(pos);
2276 size_t line_len = strlen(ev_name) + 7;
2278 if (menu.b.width < line_len)
2279 menu.b.width = line_len;
2282 return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
2285 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
2286 struct hist_browser_timer *hbt,
2288 struct perf_env *env)
2290 int nr_entries = evlist->nr_entries;
2293 if (nr_entries == 1) {
2294 struct perf_evsel *first = perf_evlist__first(evlist);
2296 return perf_evsel__hists_browse(first, nr_entries, help,
2297 false, hbt, min_pcnt,
2301 if (symbol_conf.event_group) {
2302 struct perf_evsel *pos;
2305 evlist__for_each(evlist, pos) {
2306 if (perf_evsel__is_group_leader(pos))
2310 if (nr_entries == 1)
2314 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
2315 hbt, min_pcnt, env);