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);
690 #define __HPP_COLOR_PERCENT_FN(_type, _field) \
691 static u64 __hpp_get_##_field(struct hist_entry *he) \
693 return he->stat._field; \
697 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
698 struct perf_hpp *hpp, \
699 struct hist_entry *he) \
701 return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%", \
702 __hpp__slsmg_color_printf, true); \
705 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
706 static u64 __hpp_get_acc_##_field(struct hist_entry *he) \
708 return he->stat_acc->_field; \
712 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
713 struct perf_hpp *hpp, \
714 struct hist_entry *he) \
716 if (!symbol_conf.cumulate_callchain) { \
717 struct hpp_arg *arg = hpp->ptr; \
718 int len = fmt->user_len ?: fmt->len; \
719 int ret = scnprintf(hpp->buf, hpp->size, \
720 "%*s", len, "N/A"); \
721 ui_browser__printf(arg->b, "%s", hpp->buf); \
725 return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field, \
726 " %*.2f%%", __hpp__slsmg_color_printf, true); \
729 __HPP_COLOR_PERCENT_FN(overhead, period)
730 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
731 __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
732 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
733 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
734 __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
736 #undef __HPP_COLOR_PERCENT_FN
737 #undef __HPP_COLOR_ACC_PERCENT_FN
739 void hist_browser__init_hpp(void)
741 perf_hpp__format[PERF_HPP__OVERHEAD].color =
742 hist_browser__hpp_color_overhead;
743 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
744 hist_browser__hpp_color_overhead_sys;
745 perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
746 hist_browser__hpp_color_overhead_us;
747 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
748 hist_browser__hpp_color_overhead_guest_sys;
749 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
750 hist_browser__hpp_color_overhead_guest_us;
751 perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
752 hist_browser__hpp_color_overhead_acc;
755 static int hist_browser__show_entry(struct hist_browser *browser,
756 struct hist_entry *entry,
761 int width = browser->b.width;
762 char folded_sign = ' ';
763 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
764 off_t row_offset = entry->row_offset;
766 struct perf_hpp_fmt *fmt;
769 browser->he_selection = entry;
770 browser->selection = &entry->ms;
773 if (symbol_conf.use_callchain) {
774 hist_entry__init_have_children(entry);
775 folded_sign = hist_entry__folded(entry);
778 if (row_offset == 0) {
779 struct hpp_arg arg = {
781 .folded_sign = folded_sign,
782 .current_entry = current_entry,
784 struct perf_hpp hpp = {
791 hist_browser__gotorc(browser, row, 0);
793 perf_hpp__for_each_format(fmt) {
794 if (perf_hpp__should_skip(fmt) || column++ < browser->b.horiz_scroll)
797 if (current_entry && browser->b.navkeypressed) {
798 ui_browser__set_color(&browser->b,
799 HE_COLORSET_SELECTED);
801 ui_browser__set_color(&browser->b,
806 if (symbol_conf.use_callchain) {
807 ui_browser__printf(&browser->b, "%c ", folded_sign);
812 ui_browser__printf(&browser->b, " ");
817 width -= fmt->color(fmt, &hpp, entry);
819 width -= fmt->entry(fmt, &hpp, entry);
820 ui_browser__printf(&browser->b, "%s", s);
824 /* The scroll bar isn't being used */
825 if (!browser->b.navkeypressed)
828 ui_browser__write_nstring(&browser->b, "", width);
835 if (folded_sign == '-' && row != browser->b.rows) {
836 u64 total = hists__total_period(entry->hists);
837 struct callchain_print_arg arg = {
838 .row_offset = row_offset,
839 .is_current_entry = current_entry,
842 if (callchain_param.mode == CHAIN_GRAPH_REL) {
843 if (symbol_conf.cumulate_callchain)
844 total = entry->stat_acc->period;
846 total = entry->stat.period;
849 printed += hist_browser__show_callchain(browser,
850 &entry->sorted_chain, 1, row, total,
851 hist_browser__show_callchain_entry, &arg,
852 hist_browser__check_output_full);
854 if (arg.is_current_entry)
855 browser->he_selection = entry;
861 static int advance_hpp_check(struct perf_hpp *hpp, int inc)
863 advance_hpp(hpp, inc);
864 return hpp->size <= 0;
867 static int hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf, size_t size)
869 struct hists *hists = browser->hists;
870 struct perf_hpp dummy_hpp = {
874 struct perf_hpp_fmt *fmt;
878 if (symbol_conf.use_callchain) {
879 ret = scnprintf(buf, size, " ");
880 if (advance_hpp_check(&dummy_hpp, ret))
884 perf_hpp__for_each_format(fmt) {
885 if (perf_hpp__should_skip(fmt) || column++ < browser->b.horiz_scroll)
888 ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists));
889 if (advance_hpp_check(&dummy_hpp, ret))
892 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
893 if (advance_hpp_check(&dummy_hpp, ret))
900 static void hist_browser__show_headers(struct hist_browser *browser)
904 hists_browser__scnprintf_headers(browser, headers, sizeof(headers));
905 ui_browser__gotorc(&browser->b, 0, 0);
906 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
907 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
910 static void ui_browser__hists_init_top(struct ui_browser *browser)
912 if (browser->top == NULL) {
913 struct hist_browser *hb;
915 hb = container_of(browser, struct hist_browser, b);
916 browser->top = rb_first(&hb->hists->entries);
920 static unsigned int hist_browser__refresh(struct ui_browser *browser)
923 u16 header_offset = 0;
925 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
927 if (hb->show_headers) {
928 hist_browser__show_headers(hb);
932 ui_browser__hists_init_top(browser);
933 hb->he_selection = NULL;
934 hb->selection = NULL;
936 for (nd = browser->top; nd; nd = rb_next(nd)) {
937 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
943 percent = hist_entry__get_percent_limit(h);
944 if (percent < hb->min_pcnt)
947 row += hist_browser__show_entry(hb, h, row);
948 if (row == browser->rows)
952 return row + header_offset;
955 static struct rb_node *hists__filter_entries(struct rb_node *nd,
959 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
960 float percent = hist_entry__get_percent_limit(h);
962 if (!h->filtered && percent >= min_pcnt)
971 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
975 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
976 float percent = hist_entry__get_percent_limit(h);
978 if (!h->filtered && percent >= min_pcnt)
987 static void ui_browser__hists_seek(struct ui_browser *browser,
988 off_t offset, int whence)
990 struct hist_entry *h;
993 struct hist_browser *hb;
995 hb = container_of(browser, struct hist_browser, b);
997 if (browser->nr_entries == 0)
1000 ui_browser__hists_init_top(browser);
1004 nd = hists__filter_entries(rb_first(browser->entries),
1011 nd = hists__filter_prev_entries(rb_last(browser->entries),
1020 * Moves not relative to the first visible entry invalidates its
1023 h = rb_entry(browser->top, struct hist_entry, rb_node);
1027 * Here we have to check if nd is expanded (+), if it is we can't go
1028 * the next top level hist_entry, instead we must compute an offset of
1029 * what _not_ to show and not change the first visible entry.
1031 * This offset increments when we are going from top to bottom and
1032 * decreases when we're going from bottom to top.
1034 * As we don't have backpointers to the top level in the callchains
1035 * structure, we need to always print the whole hist_entry callchain,
1036 * skipping the first ones that are before the first visible entry
1037 * and stop when we printed enough lines to fill the screen.
1045 h = rb_entry(nd, struct hist_entry, rb_node);
1047 u16 remaining = h->nr_rows - h->row_offset;
1048 if (offset > remaining) {
1049 offset -= remaining;
1052 h->row_offset += offset;
1058 nd = hists__filter_entries(rb_next(nd), hb->min_pcnt);
1063 } while (offset != 0);
1064 } else if (offset < 0) {
1066 h = rb_entry(nd, struct hist_entry, rb_node);
1069 if (-offset > h->row_offset) {
1070 offset += h->row_offset;
1073 h->row_offset += offset;
1079 if (-offset > h->nr_rows) {
1080 offset += h->nr_rows;
1083 h->row_offset = h->nr_rows + offset;
1091 nd = hists__filter_prev_entries(rb_prev(nd),
1099 * Last unfiltered hist_entry, check if it is
1100 * unfolded, if it is then we should have
1101 * row_offset at its last entry.
1103 h = rb_entry(nd, struct hist_entry, rb_node);
1105 h->row_offset = h->nr_rows;
1112 h = rb_entry(nd, struct hist_entry, rb_node);
1117 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1118 struct hist_entry *he, FILE *fp)
1120 u64 total = hists__total_period(he->hists);
1121 struct callchain_print_arg arg = {
1125 if (symbol_conf.cumulate_callchain)
1126 total = he->stat_acc->period;
1128 hist_browser__show_callchain(browser, &he->sorted_chain, 1, 0, total,
1129 hist_browser__fprintf_callchain_entry, &arg,
1130 hist_browser__check_dump_full);
1134 static int hist_browser__fprintf_entry(struct hist_browser *browser,
1135 struct hist_entry *he, FILE *fp)
1139 char folded_sign = ' ';
1140 struct perf_hpp hpp = {
1144 struct perf_hpp_fmt *fmt;
1148 if (symbol_conf.use_callchain)
1149 folded_sign = hist_entry__folded(he);
1151 if (symbol_conf.use_callchain)
1152 printed += fprintf(fp, "%c ", folded_sign);
1154 perf_hpp__for_each_format(fmt) {
1155 if (perf_hpp__should_skip(fmt))
1159 ret = scnprintf(hpp.buf, hpp.size, " ");
1160 advance_hpp(&hpp, ret);
1164 ret = fmt->entry(fmt, &hpp, he);
1165 advance_hpp(&hpp, ret);
1167 printed += fprintf(fp, "%s\n", rtrim(s));
1169 if (folded_sign == '-')
1170 printed += hist_browser__fprintf_callchain(browser, he, fp);
1175 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1177 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
1182 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1184 printed += hist_browser__fprintf_entry(browser, h, fp);
1185 nd = hists__filter_entries(rb_next(nd), browser->min_pcnt);
1191 static int hist_browser__dump(struct hist_browser *browser)
1197 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
1198 if (access(filename, F_OK))
1201 * XXX: Just an arbitrary lazy upper limit
1203 if (++browser->print_seq == 8192) {
1204 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1209 fp = fopen(filename, "w");
1212 const char *err = strerror_r(errno, bf, sizeof(bf));
1213 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
1217 ++browser->print_seq;
1218 hist_browser__fprintf(browser, fp);
1220 ui_helpline__fpush("%s written!", filename);
1225 static struct hist_browser *hist_browser__new(struct hists *hists,
1226 struct hist_browser_timer *hbt,
1227 struct perf_env *env)
1229 struct hist_browser *browser = zalloc(sizeof(*browser));
1232 browser->hists = hists;
1233 browser->b.refresh = hist_browser__refresh;
1234 browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
1235 browser->b.seek = ui_browser__hists_seek;
1236 browser->b.use_navkeypressed = true;
1237 browser->show_headers = symbol_conf.show_hist_headers;
1245 static void hist_browser__delete(struct hist_browser *browser)
1250 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
1252 return browser->he_selection;
1255 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
1257 return browser->he_selection->thread;
1260 /* Check whether the browser is for 'top' or 'report' */
1261 static inline bool is_report_browser(void *timer)
1263 return timer == NULL;
1266 static int hists__browser_title(struct hists *hists,
1267 struct hist_browser_timer *hbt,
1268 char *bf, size_t size)
1272 const struct dso *dso = hists->dso_filter;
1273 const struct thread *thread = hists->thread_filter;
1274 int socket_id = hists->socket_filter;
1275 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1276 u64 nr_events = hists->stats.total_period;
1277 struct perf_evsel *evsel = hists_to_evsel(hists);
1278 const char *ev_name = perf_evsel__name(evsel);
1280 size_t buflen = sizeof(buf);
1281 char ref[30] = " show reference callgraph, ";
1282 bool enable_ref = false;
1284 if (symbol_conf.filter_relative) {
1285 nr_samples = hists->stats.nr_non_filtered_samples;
1286 nr_events = hists->stats.total_non_filtered_period;
1289 if (perf_evsel__is_group_event(evsel)) {
1290 struct perf_evsel *pos;
1292 perf_evsel__group_desc(evsel, buf, buflen);
1295 for_each_group_member(pos, evsel) {
1296 struct hists *pos_hists = evsel__hists(pos);
1298 if (symbol_conf.filter_relative) {
1299 nr_samples += pos_hists->stats.nr_non_filtered_samples;
1300 nr_events += pos_hists->stats.total_non_filtered_period;
1302 nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
1303 nr_events += pos_hists->stats.total_period;
1308 if (symbol_conf.show_ref_callgraph &&
1309 strstr(ev_name, "call-graph=no"))
1311 nr_samples = convert_unit(nr_samples, &unit);
1312 printed = scnprintf(bf, size,
1313 "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64,
1314 nr_samples, unit, ev_name, enable_ref ? ref : " ", nr_events);
1317 if (hists->uid_filter_str)
1318 printed += snprintf(bf + printed, size - printed,
1319 ", UID: %s", hists->uid_filter_str);
1321 printed += scnprintf(bf + printed, size - printed,
1323 (thread->comm_set ? thread__comm_str(thread) : ""),
1326 printed += scnprintf(bf + printed, size - printed,
1327 ", DSO: %s", dso->short_name);
1329 printed += scnprintf(bf + printed, size - printed,
1330 ", Processor Socket: %d", socket_id);
1331 if (!is_report_browser(hbt)) {
1332 struct perf_top *top = hbt->arg;
1335 printed += scnprintf(bf + printed, size - printed, " [z]");
1341 static inline void free_popup_options(char **options, int n)
1345 for (i = 0; i < n; ++i)
1350 * Only runtime switching of perf data file will make "input_name" point
1351 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1352 * whether we need to call free() for current "input_name" during the switch.
1354 static bool is_input_name_malloced = false;
1356 static int switch_data_file(void)
1358 char *pwd, *options[32], *abs_path[32], *tmp;
1360 int nr_options = 0, choice = -1, ret = -1;
1361 struct dirent *dent;
1363 pwd = getenv("PWD");
1367 pwd_dir = opendir(pwd);
1371 memset(options, 0, sizeof(options));
1372 memset(options, 0, sizeof(abs_path));
1374 while ((dent = readdir(pwd_dir))) {
1375 char path[PATH_MAX];
1377 char *name = dent->d_name;
1380 if (!(dent->d_type == DT_REG))
1383 snprintf(path, sizeof(path), "%s/%s", pwd, name);
1385 file = fopen(path, "r");
1389 if (fread(&magic, 1, 8, file) < 8)
1390 goto close_file_and_continue;
1392 if (is_perf_magic(magic)) {
1393 options[nr_options] = strdup(name);
1394 if (!options[nr_options])
1395 goto close_file_and_continue;
1397 abs_path[nr_options] = strdup(path);
1398 if (!abs_path[nr_options]) {
1399 zfree(&options[nr_options]);
1400 ui__warning("Can't search all data files due to memory shortage.\n");
1408 close_file_and_continue:
1410 if (nr_options >= 32) {
1411 ui__warning("Too many perf data files in PWD!\n"
1412 "Only the first 32 files will be listed.\n");
1419 choice = ui__popup_menu(nr_options, options);
1420 if (choice < nr_options && choice >= 0) {
1421 tmp = strdup(abs_path[choice]);
1423 if (is_input_name_malloced)
1424 free((void *)input_name);
1426 is_input_name_malloced = true;
1429 ui__warning("Data switch failed due to memory shortage!\n");
1433 free_popup_options(options, nr_options);
1434 free_popup_options(abs_path, nr_options);
1438 struct popup_action {
1439 struct thread *thread;
1440 struct map_symbol ms;
1443 int (*fn)(struct hist_browser *browser, struct popup_action *act);
1447 do_annotate(struct hist_browser *browser, struct popup_action *act)
1449 struct perf_evsel *evsel;
1450 struct annotation *notes;
1451 struct hist_entry *he;
1454 if (!objdump_path && perf_env__lookup_objdump(browser->env))
1457 notes = symbol__annotation(act->ms.sym);
1461 evsel = hists_to_evsel(browser->hists);
1462 err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
1463 he = hist_browser__selected_entry(browser);
1465 * offer option to annotate the other branch source or target
1466 * (if they exists) when returning from annotate
1468 if ((err == 'q' || err == CTRL('c')) && he->branch_info)
1471 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1473 ui_browser__handle_resize(&browser->b);
1478 add_annotate_opt(struct hist_browser *browser __maybe_unused,
1479 struct popup_action *act, char **optstr,
1480 struct map *map, struct symbol *sym)
1482 if (sym == NULL || map->dso->annotate_warned)
1485 if (asprintf(optstr, "Annotate %s", sym->name) < 0)
1490 act->fn = do_annotate;
1495 do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
1497 struct thread *thread = act->thread;
1499 if (browser->hists->thread_filter) {
1500 pstack__remove(browser->pstack, &browser->hists->thread_filter);
1501 perf_hpp__set_elide(HISTC_THREAD, false);
1502 thread__zput(browser->hists->thread_filter);
1505 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
1506 thread->comm_set ? thread__comm_str(thread) : "",
1508 browser->hists->thread_filter = thread__get(thread);
1509 perf_hpp__set_elide(HISTC_THREAD, false);
1510 pstack__push(browser->pstack, &browser->hists->thread_filter);
1513 hists__filter_by_thread(browser->hists);
1514 hist_browser__reset(browser);
1519 add_thread_opt(struct hist_browser *browser, struct popup_action *act,
1520 char **optstr, struct thread *thread)
1525 if (asprintf(optstr, "Zoom %s %s(%d) thread",
1526 browser->hists->thread_filter ? "out of" : "into",
1527 thread->comm_set ? thread__comm_str(thread) : "",
1531 act->thread = thread;
1532 act->fn = do_zoom_thread;
1537 do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
1539 struct map *map = act->ms.map;
1541 if (browser->hists->dso_filter) {
1542 pstack__remove(browser->pstack, &browser->hists->dso_filter);
1543 perf_hpp__set_elide(HISTC_DSO, false);
1544 browser->hists->dso_filter = NULL;
1549 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
1550 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
1551 browser->hists->dso_filter = map->dso;
1552 perf_hpp__set_elide(HISTC_DSO, true);
1553 pstack__push(browser->pstack, &browser->hists->dso_filter);
1556 hists__filter_by_dso(browser->hists);
1557 hist_browser__reset(browser);
1562 add_dso_opt(struct hist_browser *browser, struct popup_action *act,
1563 char **optstr, struct map *map)
1568 if (asprintf(optstr, "Zoom %s %s DSO",
1569 browser->hists->dso_filter ? "out of" : "into",
1570 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
1574 act->fn = do_zoom_dso;
1579 do_browse_map(struct hist_browser *browser __maybe_unused,
1580 struct popup_action *act)
1582 map__browse(act->ms.map);
1587 add_map_opt(struct hist_browser *browser __maybe_unused,
1588 struct popup_action *act, char **optstr, struct map *map)
1593 if (asprintf(optstr, "Browse map details") < 0)
1597 act->fn = do_browse_map;
1602 do_run_script(struct hist_browser *browser __maybe_unused,
1603 struct popup_action *act)
1605 char script_opt[64];
1606 memset(script_opt, 0, sizeof(script_opt));
1609 scnprintf(script_opt, sizeof(script_opt), " -c %s ",
1610 thread__comm_str(act->thread));
1611 } else if (act->ms.sym) {
1612 scnprintf(script_opt, sizeof(script_opt), " -S %s ",
1616 script_browse(script_opt);
1621 add_script_opt(struct hist_browser *browser __maybe_unused,
1622 struct popup_action *act, char **optstr,
1623 struct thread *thread, struct symbol *sym)
1626 if (asprintf(optstr, "Run scripts for samples of thread [%s]",
1627 thread__comm_str(thread)) < 0)
1630 if (asprintf(optstr, "Run scripts for samples of symbol [%s]",
1634 if (asprintf(optstr, "Run scripts for all samples") < 0)
1638 act->thread = thread;
1640 act->fn = do_run_script;
1645 do_switch_data(struct hist_browser *browser __maybe_unused,
1646 struct popup_action *act __maybe_unused)
1648 if (switch_data_file()) {
1649 ui__warning("Won't switch the data files due to\n"
1650 "no valid data file get selected!\n");
1654 return K_SWITCH_INPUT_DATA;
1658 add_switch_opt(struct hist_browser *browser,
1659 struct popup_action *act, char **optstr)
1661 if (!is_report_browser(browser->hbt))
1664 if (asprintf(optstr, "Switch to another data file in PWD") < 0)
1667 act->fn = do_switch_data;
1672 do_exit_browser(struct hist_browser *browser __maybe_unused,
1673 struct popup_action *act __maybe_unused)
1679 add_exit_opt(struct hist_browser *browser __maybe_unused,
1680 struct popup_action *act, char **optstr)
1682 if (asprintf(optstr, "Exit") < 0)
1685 act->fn = do_exit_browser;
1690 do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
1692 if (browser->hists->socket_filter > -1) {
1693 pstack__remove(browser->pstack, &browser->hists->socket_filter);
1694 browser->hists->socket_filter = -1;
1695 perf_hpp__set_elide(HISTC_SOCKET, false);
1697 browser->hists->socket_filter = act->socket;
1698 perf_hpp__set_elide(HISTC_SOCKET, true);
1699 pstack__push(browser->pstack, &browser->hists->socket_filter);
1702 hists__filter_by_socket(browser->hists);
1703 hist_browser__reset(browser);
1708 add_socket_opt(struct hist_browser *browser, struct popup_action *act,
1709 char **optstr, int socket_id)
1714 if (asprintf(optstr, "Zoom %s Processor Socket %d",
1715 (browser->hists->socket_filter > -1) ? "out of" : "into",
1719 act->socket = socket_id;
1720 act->fn = do_zoom_socket;
1724 static void hist_browser__update_nr_entries(struct hist_browser *hb)
1727 struct rb_node *nd = rb_first(&hb->hists->entries);
1729 if (hb->min_pcnt == 0) {
1730 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
1734 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
1739 hb->nr_non_filtered_entries = nr_entries;
1742 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
1743 const char *helpline,
1745 struct hist_browser_timer *hbt,
1747 struct perf_env *env)
1749 struct hists *hists = evsel__hists(evsel);
1750 struct hist_browser *browser = hist_browser__new(hists, hbt, env);
1751 struct branch_info *bi;
1752 #define MAX_OPTIONS 16
1753 char *options[MAX_OPTIONS];
1754 struct popup_action actions[MAX_OPTIONS];
1758 int delay_secs = hbt ? hbt->refresh : 0;
1759 struct perf_hpp_fmt *fmt;
1761 #define HIST_BROWSER_HELP_COMMON \
1762 "h/?/F1 Show this window\n" \
1764 "PGDN/SPACE Navigate\n" \
1765 "q/ESC/CTRL+C Exit browser\n\n" \
1766 "For multiple event sessions:\n\n" \
1767 "TAB/UNTAB Switch events\n\n" \
1768 "For symbolic views (--sort has sym):\n\n" \
1769 "ENTER Zoom into DSO/Threads & Annotate current symbol\n" \
1771 "a Annotate current symbol\n" \
1772 "C Collapse all callchains\n" \
1773 "d Zoom into current DSO\n" \
1774 "E Expand all callchains\n" \
1775 "F Toggle percentage of filtered entries\n" \
1776 "H Display column headers\n" \
1777 "m Display context menu\n" \
1778 "S Zoom into current Processor Socket\n" \
1780 /* help messages are sorted by lexical order of the hotkey */
1781 const char report_help[] = HIST_BROWSER_HELP_COMMON
1782 "i Show header information\n"
1783 "P Print histograms to perf.hist.N\n"
1784 "r Run available scripts\n"
1785 "s Switch to another data file in PWD\n"
1786 "t Zoom into current Thread\n"
1787 "V Verbose (DSO names in callchains, etc)\n"
1788 "/ Filter symbol by name";
1789 const char top_help[] = HIST_BROWSER_HELP_COMMON
1790 "P Print histograms to perf.hist.N\n"
1791 "t Zoom into current Thread\n"
1792 "V Verbose (DSO names in callchains, etc)\n"
1793 "z Toggle zeroing of samples\n"
1794 "f Enable/Disable events\n"
1795 "/ Filter symbol by name";
1797 if (browser == NULL)
1800 /* reset abort key so that it can get Ctrl-C as a key */
1802 SLang_init_tty(0, 0, 0);
1805 browser->min_pcnt = min_pcnt;
1806 hist_browser__update_nr_entries(browser);
1809 browser->pstack = pstack__new(3);
1810 if (browser->pstack == NULL)
1813 ui_helpline__push(helpline);
1815 memset(options, 0, sizeof(options));
1816 memset(actions, 0, sizeof(actions));
1818 perf_hpp__for_each_format(fmt) {
1819 perf_hpp__reset_width(fmt, hists);
1821 * This is done just once, and activates the horizontal scrolling
1822 * code in the ui_browser code, it would be better to have a the
1823 * counter in the perf_hpp code, but I couldn't find doing it here
1824 * works, FIXME by setting this in hist_browser__new, for now, be
1827 ++browser->b.columns;
1830 if (symbol_conf.col_width_list_str)
1831 perf_hpp__set_user_width(symbol_conf.col_width_list_str);
1834 struct thread *thread = NULL;
1835 struct map *map = NULL;
1841 key = hist_browser__run(browser, helpline);
1843 if (browser->he_selection != NULL) {
1844 thread = hist_browser__selected_thread(browser);
1845 map = browser->selection->map;
1846 socked_id = browser->he_selection->socket;
1854 * Exit the browser, let hists__browser_tree
1855 * go to the next or previous
1857 goto out_free_stack;
1859 if (!sort__has_sym) {
1860 ui_browser__warning(&browser->b, delay_secs * 2,
1861 "Annotation is only available for symbolic views, "
1862 "include \"sym*\" in --sort to use it.");
1866 if (browser->selection == NULL ||
1867 browser->selection->sym == NULL ||
1868 browser->selection->map->dso->annotate_warned)
1871 actions->ms.map = browser->selection->map;
1872 actions->ms.sym = browser->selection->sym;
1873 do_annotate(browser, actions);
1876 hist_browser__dump(browser);
1879 actions->ms.map = map;
1880 do_zoom_dso(browser, actions);
1883 browser->show_dso = !browser->show_dso;
1886 actions->thread = thread;
1887 do_zoom_thread(browser, actions);
1890 actions->socket = socked_id;
1891 do_zoom_socket(browser, actions);
1894 if (ui_browser__input_window("Symbol to show",
1895 "Please enter the name of symbol you want to see.\n"
1896 "To remove the filter later, press / + ENTER.",
1897 buf, "ENTER: OK, ESC: Cancel",
1898 delay_secs * 2) == K_ENTER) {
1899 hists->symbol_filter_str = *buf ? buf : NULL;
1900 hists__filter_by_symbol(hists);
1901 hist_browser__reset(browser);
1905 if (is_report_browser(hbt)) {
1906 actions->thread = NULL;
1907 actions->ms.sym = NULL;
1908 do_run_script(browser, actions);
1912 if (is_report_browser(hbt)) {
1913 key = do_switch_data(browser, actions);
1914 if (key == K_SWITCH_INPUT_DATA)
1915 goto out_free_stack;
1919 /* env->arch is NULL for live-mode (i.e. perf top) */
1921 tui__header_window(env);
1924 symbol_conf.filter_relative ^= 1;
1927 if (!is_report_browser(hbt)) {
1928 struct perf_top *top = hbt->arg;
1930 top->zero = !top->zero;
1936 ui_browser__help_window(&browser->b,
1937 is_report_browser(hbt) ? report_help : top_help);
1948 if (pstack__empty(browser->pstack)) {
1950 * Go back to the perf_evsel_menu__run or other user
1953 goto out_free_stack;
1956 ui_browser__dialog_yesno(&browser->b,
1957 "Do you really want to exit?"))
1958 goto out_free_stack;
1962 top = pstack__peek(browser->pstack);
1963 if (top == &browser->hists->dso_filter) {
1965 * No need to set actions->dso here since
1966 * it's just to remove the current filter.
1967 * Ditto for thread below.
1969 do_zoom_dso(browser, actions);
1970 } else if (top == &browser->hists->thread_filter) {
1971 do_zoom_thread(browser, actions);
1972 } else if (top == &browser->hists->socket_filter) {
1973 do_zoom_socket(browser, actions);
1979 goto out_free_stack;
1981 if (!is_report_browser(hbt)) {
1982 struct perf_top *top = hbt->arg;
1984 perf_evlist__toggle_enable(top->evlist);
1986 * No need to refresh, resort/decay histogram
1987 * entries if we are not collecting samples:
1989 if (top->evlist->enabled) {
1990 helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
1991 hbt->refresh = delay_secs;
1993 helpline = "Press 'f' again to re-enable the events";
2000 helpline = "Press '?' for help on key bindings";
2005 goto add_exit_option;
2007 if (browser->selection == NULL)
2008 goto skip_annotation;
2010 if (sort__mode == SORT_MODE__BRANCH) {
2011 bi = browser->he_selection->branch_info;
2014 goto skip_annotation;
2016 nr_options += add_annotate_opt(browser,
2017 &actions[nr_options],
2018 &options[nr_options],
2021 if (bi->to.sym != bi->from.sym)
2022 nr_options += add_annotate_opt(browser,
2023 &actions[nr_options],
2024 &options[nr_options],
2028 nr_options += add_annotate_opt(browser,
2029 &actions[nr_options],
2030 &options[nr_options],
2031 browser->selection->map,
2032 browser->selection->sym);
2035 nr_options += add_thread_opt(browser, &actions[nr_options],
2036 &options[nr_options], thread);
2037 nr_options += add_dso_opt(browser, &actions[nr_options],
2038 &options[nr_options], map);
2039 nr_options += add_map_opt(browser, &actions[nr_options],
2040 &options[nr_options],
2041 browser->selection ?
2042 browser->selection->map : NULL);
2043 nr_options += add_socket_opt(browser, &actions[nr_options],
2044 &options[nr_options],
2046 /* perf script support */
2047 if (browser->he_selection) {
2048 nr_options += add_script_opt(browser,
2049 &actions[nr_options],
2050 &options[nr_options],
2053 * Note that browser->selection != NULL
2054 * when browser->he_selection is not NULL,
2055 * so we don't need to check browser->selection
2056 * before fetching browser->selection->sym like what
2057 * we do before fetching browser->selection->map.
2059 * See hist_browser__show_entry.
2061 if (sort__has_sym && browser->selection->sym) {
2062 nr_options += add_script_opt(browser,
2063 &actions[nr_options],
2064 &options[nr_options],
2065 NULL, browser->selection->sym);
2068 nr_options += add_script_opt(browser, &actions[nr_options],
2069 &options[nr_options], NULL, NULL);
2070 nr_options += add_switch_opt(browser, &actions[nr_options],
2071 &options[nr_options]);
2073 nr_options += add_exit_opt(browser, &actions[nr_options],
2074 &options[nr_options]);
2077 struct popup_action *act;
2079 choice = ui__popup_menu(nr_options, options);
2080 if (choice == -1 || choice >= nr_options)
2083 act = &actions[choice];
2084 key = act->fn(browser, act);
2087 if (key == K_SWITCH_INPUT_DATA)
2091 pstack__delete(browser->pstack);
2093 hist_browser__delete(browser);
2094 free_popup_options(options, MAX_OPTIONS);
2098 struct perf_evsel_menu {
2099 struct ui_browser b;
2100 struct perf_evsel *selection;
2101 bool lost_events, lost_events_warned;
2103 struct perf_env *env;
2106 static void perf_evsel_menu__write(struct ui_browser *browser,
2107 void *entry, int row)
2109 struct perf_evsel_menu *menu = container_of(browser,
2110 struct perf_evsel_menu, b);
2111 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
2112 struct hists *hists = evsel__hists(evsel);
2113 bool current_entry = ui_browser__is_current_entry(browser, row);
2114 unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
2115 const char *ev_name = perf_evsel__name(evsel);
2117 const char *warn = " ";
2120 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
2121 HE_COLORSET_NORMAL);
2123 if (perf_evsel__is_group_event(evsel)) {
2124 struct perf_evsel *pos;
2126 ev_name = perf_evsel__group_name(evsel);
2128 for_each_group_member(pos, evsel) {
2129 struct hists *pos_hists = evsel__hists(pos);
2130 nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
2134 nr_events = convert_unit(nr_events, &unit);
2135 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
2136 unit, unit == ' ' ? "" : " ", ev_name);
2137 ui_browser__printf(browser, "%s", bf);
2139 nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
2140 if (nr_events != 0) {
2141 menu->lost_events = true;
2143 ui_browser__set_color(browser, HE_COLORSET_TOP);
2144 nr_events = convert_unit(nr_events, &unit);
2145 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
2146 nr_events, unit, unit == ' ' ? "" : " ");
2150 ui_browser__write_nstring(browser, warn, browser->width - printed);
2153 menu->selection = evsel;
2156 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
2157 int nr_events, const char *help,
2158 struct hist_browser_timer *hbt)
2160 struct perf_evlist *evlist = menu->b.priv;
2161 struct perf_evsel *pos;
2162 const char *title = "Available samples";
2163 int delay_secs = hbt ? hbt->refresh : 0;
2166 if (ui_browser__show(&menu->b, title,
2167 "ESC: exit, ENTER|->: Browse histograms") < 0)
2171 key = ui_browser__run(&menu->b, delay_secs);
2175 hbt->timer(hbt->arg);
2177 if (!menu->lost_events_warned && menu->lost_events) {
2178 ui_browser__warn_lost_events(&menu->b);
2179 menu->lost_events_warned = true;
2184 if (!menu->selection)
2186 pos = menu->selection;
2188 perf_evlist__set_selected(evlist, pos);
2190 * Give the calling tool a chance to populate the non
2191 * default evsel resorted hists tree.
2194 hbt->timer(hbt->arg);
2195 key = perf_evsel__hists_browse(pos, nr_events, help,
2199 ui_browser__show_title(&menu->b, title);
2202 if (pos->node.next == &evlist->entries)
2203 pos = perf_evlist__first(evlist);
2205 pos = perf_evsel__next(pos);
2208 if (pos->node.prev == &evlist->entries)
2209 pos = perf_evlist__last(evlist);
2211 pos = perf_evsel__prev(pos);
2213 case K_SWITCH_INPUT_DATA:
2224 if (!ui_browser__dialog_yesno(&menu->b,
2225 "Do you really want to exit?"))
2237 ui_browser__hide(&menu->b);
2241 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
2244 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
2246 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
2252 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
2253 int nr_entries, const char *help,
2254 struct hist_browser_timer *hbt,
2256 struct perf_env *env)
2258 struct perf_evsel *pos;
2259 struct perf_evsel_menu menu = {
2261 .entries = &evlist->entries,
2262 .refresh = ui_browser__list_head_refresh,
2263 .seek = ui_browser__list_head_seek,
2264 .write = perf_evsel_menu__write,
2265 .filter = filter_group_entries,
2266 .nr_entries = nr_entries,
2269 .min_pcnt = min_pcnt,
2273 ui_helpline__push("Press ESC to exit");
2275 evlist__for_each(evlist, pos) {
2276 const char *ev_name = perf_evsel__name(pos);
2277 size_t line_len = strlen(ev_name) + 7;
2279 if (menu.b.width < line_len)
2280 menu.b.width = line_len;
2283 return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
2286 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
2287 struct hist_browser_timer *hbt,
2289 struct perf_env *env)
2291 int nr_entries = evlist->nr_entries;
2294 if (nr_entries == 1) {
2295 struct perf_evsel *first = perf_evlist__first(evlist);
2297 return perf_evsel__hists_browse(first, nr_entries, help,
2298 false, hbt, min_pcnt,
2302 if (symbol_conf.event_group) {
2303 struct perf_evsel *pos;
2306 evlist__for_each(evlist, pos) {
2307 if (perf_evsel__is_group_leader(pos))
2311 if (nr_entries == 1)
2315 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
2316 hbt, min_pcnt, env);