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);
302 has_children = hist_entry__toggle_fold(he);
304 has_children = callchain_list__toggle_fold(cl);
307 hist_entry__init_have_children(he);
308 browser->b.nr_entries -= he->nr_rows;
309 browser->nr_callchain_rows -= he->nr_rows;
312 he->nr_rows = callchain__count_rows(&he->sorted_chain);
316 browser->b.nr_entries += he->nr_rows;
317 browser->nr_callchain_rows += he->nr_rows;
322 /* If it doesn't have children, no toggling performed */
326 static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
331 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
332 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
333 struct callchain_list *chain;
334 bool has_children = false;
336 list_for_each_entry(chain, &child->val, list) {
338 callchain_list__set_folding(chain, unfold);
339 has_children = chain->has_children;
343 n += callchain_node__set_folding_rb_tree(child, unfold);
349 static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
351 struct callchain_list *chain;
352 bool has_children = false;
355 list_for_each_entry(chain, &node->val, list) {
357 callchain_list__set_folding(chain, unfold);
358 has_children = chain->has_children;
362 n += callchain_node__set_folding_rb_tree(node, unfold);
367 static int callchain__set_folding(struct rb_root *chain, bool unfold)
372 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
373 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
374 n += callchain_node__set_folding(node, unfold);
380 static void hist_entry__set_folding(struct hist_entry *he, bool unfold)
382 hist_entry__init_have_children(he);
383 he->unfolded = unfold ? he->has_children : false;
385 if (he->has_children) {
386 int n = callchain__set_folding(&he->sorted_chain, unfold);
387 he->nr_rows = unfold ? n : 0;
393 __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
396 struct hists *hists = browser->hists;
398 for (nd = rb_first(&hists->entries);
399 (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
401 struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
402 hist_entry__set_folding(he, unfold);
403 browser->nr_callchain_rows += he->nr_rows;
407 static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
409 browser->nr_callchain_rows = 0;
410 __hist_browser__set_folding(browser, unfold);
412 browser->b.nr_entries = hist_browser__nr_entries(browser);
413 /* Go to the start, we may be way after valid entries after a collapse */
414 ui_browser__reset_index(&browser->b);
417 static void ui_browser__warn_lost_events(struct ui_browser *browser)
419 ui_browser__warning(browser, 4,
420 "Events are being lost, check IO/CPU overload!\n\n"
421 "You may want to run 'perf' using a RT scheduler policy:\n\n"
422 " perf top -r 80\n\n"
423 "Or reduce the sampling frequency.");
426 static int hist_browser__run(struct hist_browser *browser, const char *help)
430 struct hist_browser_timer *hbt = browser->hbt;
431 int delay_secs = hbt ? hbt->refresh : 0;
433 browser->b.entries = &browser->hists->entries;
434 browser->b.nr_entries = hist_browser__nr_entries(browser);
436 hists__browser_title(browser->hists, hbt, title, sizeof(title));
438 if (ui_browser__show(&browser->b, title, help) < 0)
442 key = ui_browser__run(&browser->b, delay_secs);
447 hbt->timer(hbt->arg);
449 if (hist_browser__has_filter(browser))
450 hist_browser__update_nr_entries(browser);
452 nr_entries = hist_browser__nr_entries(browser);
453 ui_browser__update_nr_entries(&browser->b, nr_entries);
455 if (browser->hists->stats.nr_lost_warned !=
456 browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
457 browser->hists->stats.nr_lost_warned =
458 browser->hists->stats.nr_events[PERF_RECORD_LOST];
459 ui_browser__warn_lost_events(&browser->b);
462 hists__browser_title(browser->hists,
463 hbt, title, sizeof(title));
464 ui_browser__show_title(&browser->b, title);
467 case 'D': { /* Debug */
469 struct hist_entry *h = rb_entry(browser->b.top,
470 struct hist_entry, rb_node);
472 ui_helpline__fpush("%d: nr_ent=(%d,%d), rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
473 seq++, browser->b.nr_entries,
474 browser->hists->nr_entries,
478 h->row_offset, h->nr_rows);
482 /* Collapse the whole world. */
483 hist_browser__set_folding(browser, false);
486 /* Expand the whole world. */
487 hist_browser__set_folding(browser, true);
490 browser->show_headers = !browser->show_headers;
491 hist_browser__update_rows(browser);
494 if (hist_browser__toggle_fold(browser))
502 ui_browser__hide(&browser->b);
506 struct callchain_print_arg {
507 /* for hists browser */
509 bool is_current_entry;
516 typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
517 struct callchain_list *chain,
518 const char *str, int offset,
520 struct callchain_print_arg *arg);
522 static void hist_browser__show_callchain_entry(struct hist_browser *browser,
523 struct callchain_list *chain,
524 const char *str, int offset,
526 struct callchain_print_arg *arg)
529 char folded_sign = callchain_list__folded(chain);
530 bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
532 color = HE_COLORSET_NORMAL;
533 width = browser->b.width - (offset + 2);
534 if (ui_browser__is_current_entry(&browser->b, row)) {
535 browser->selection = &chain->ms;
536 color = HE_COLORSET_SELECTED;
537 arg->is_current_entry = true;
540 ui_browser__set_color(&browser->b, color);
541 hist_browser__gotorc(browser, row, 0);
542 ui_browser__write_nstring(&browser->b, " ", offset);
543 ui_browser__printf(&browser->b, "%c", folded_sign);
544 ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
545 ui_browser__write_nstring(&browser->b, str, width);
548 static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
549 struct callchain_list *chain,
550 const char *str, int offset,
551 unsigned short row __maybe_unused,
552 struct callchain_print_arg *arg)
554 char folded_sign = callchain_list__folded(chain);
556 arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
560 typedef bool (*check_output_full_fn)(struct hist_browser *browser,
563 static bool hist_browser__check_output_full(struct hist_browser *browser,
566 return browser->b.rows == row;
569 static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
570 unsigned short row __maybe_unused)
575 #define LEVEL_OFFSET_STEP 3
577 static int hist_browser__show_callchain(struct hist_browser *browser,
578 struct rb_root *root, int level,
579 unsigned short row, u64 total,
580 print_callchain_entry_fn print,
581 struct callchain_print_arg *arg,
582 check_output_full_fn is_output_full)
584 struct rb_node *node;
585 int first_row = row, offset = level * LEVEL_OFFSET_STEP;
589 node = rb_first(root);
590 need_percent = node && rb_next(node);
593 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
594 struct rb_node *next = rb_next(node);
595 u64 cumul = callchain_cumul_hits(child);
596 struct callchain_list *chain;
597 char folded_sign = ' ';
599 int extra_offset = 0;
601 list_for_each_entry(chain, &child->val, list) {
602 char bf[1024], *alloc_str;
604 bool was_first = first;
608 else if (need_percent)
609 extra_offset = LEVEL_OFFSET_STEP;
611 folded_sign = callchain_list__folded(chain);
612 if (arg->row_offset != 0) {
618 str = callchain_list__sym_name(chain, bf, sizeof(bf),
621 if (was_first && need_percent) {
622 double percent = cumul * 100.0 / total;
624 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
625 str = "Not enough memory!";
630 print(browser, chain, str, offset + extra_offset, row, arg);
634 if (is_output_full(browser, ++row))
637 if (folded_sign == '+')
641 if (folded_sign == '-') {
642 const int new_level = level + (extra_offset ? 2 : 1);
644 if (callchain_param.mode == CHAIN_GRAPH_REL)
645 new_total = child->children_hit;
649 row += hist_browser__show_callchain(browser, &child->rb_root,
650 new_level, row, new_total,
651 print, arg, is_output_full);
653 if (is_output_full(browser, row))
658 return row - first_row;
662 struct ui_browser *b;
667 static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
669 struct hpp_arg *arg = hpp->ptr;
675 len = va_arg(args, int);
676 percent = va_arg(args, double);
679 ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
681 ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
682 ui_browser__printf(arg->b, "%s", hpp->buf);
684 advance_hpp(hpp, ret);
688 #define __HPP_COLOR_PERCENT_FN(_type, _field) \
689 static u64 __hpp_get_##_field(struct hist_entry *he) \
691 return he->stat._field; \
695 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
696 struct perf_hpp *hpp, \
697 struct hist_entry *he) \
699 return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%", \
700 __hpp__slsmg_color_printf, true); \
703 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
704 static u64 __hpp_get_acc_##_field(struct hist_entry *he) \
706 return he->stat_acc->_field; \
710 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
711 struct perf_hpp *hpp, \
712 struct hist_entry *he) \
714 if (!symbol_conf.cumulate_callchain) { \
715 struct hpp_arg *arg = hpp->ptr; \
716 int len = fmt->user_len ?: fmt->len; \
717 int ret = scnprintf(hpp->buf, hpp->size, \
718 "%*s", len, "N/A"); \
719 ui_browser__printf(arg->b, "%s", hpp->buf); \
723 return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field, \
724 " %*.2f%%", __hpp__slsmg_color_printf, true); \
727 __HPP_COLOR_PERCENT_FN(overhead, period)
728 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
729 __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
730 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
731 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
732 __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
734 #undef __HPP_COLOR_PERCENT_FN
735 #undef __HPP_COLOR_ACC_PERCENT_FN
737 void hist_browser__init_hpp(void)
739 perf_hpp__format[PERF_HPP__OVERHEAD].color =
740 hist_browser__hpp_color_overhead;
741 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
742 hist_browser__hpp_color_overhead_sys;
743 perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
744 hist_browser__hpp_color_overhead_us;
745 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
746 hist_browser__hpp_color_overhead_guest_sys;
747 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
748 hist_browser__hpp_color_overhead_guest_us;
749 perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
750 hist_browser__hpp_color_overhead_acc;
753 static int hist_browser__show_entry(struct hist_browser *browser,
754 struct hist_entry *entry,
759 int width = browser->b.width;
760 char folded_sign = ' ';
761 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
762 off_t row_offset = entry->row_offset;
764 struct perf_hpp_fmt *fmt;
767 browser->he_selection = entry;
768 browser->selection = &entry->ms;
771 if (symbol_conf.use_callchain) {
772 hist_entry__init_have_children(entry);
773 folded_sign = hist_entry__folded(entry);
776 if (row_offset == 0) {
777 struct hpp_arg arg = {
779 .folded_sign = folded_sign,
780 .current_entry = current_entry,
782 struct perf_hpp hpp = {
789 hist_browser__gotorc(browser, row, 0);
791 perf_hpp__for_each_format(fmt) {
792 if (perf_hpp__should_skip(fmt) || column++ < browser->b.horiz_scroll)
795 if (current_entry && browser->b.navkeypressed) {
796 ui_browser__set_color(&browser->b,
797 HE_COLORSET_SELECTED);
799 ui_browser__set_color(&browser->b,
804 if (symbol_conf.use_callchain) {
805 ui_browser__printf(&browser->b, "%c ", folded_sign);
810 ui_browser__printf(&browser->b, " ");
815 width -= fmt->color(fmt, &hpp, entry);
817 width -= fmt->entry(fmt, &hpp, entry);
818 ui_browser__printf(&browser->b, "%s", s);
822 /* The scroll bar isn't being used */
823 if (!browser->b.navkeypressed)
826 ui_browser__write_nstring(&browser->b, "", width);
833 if (folded_sign == '-' && row != browser->b.rows) {
834 u64 total = hists__total_period(entry->hists);
835 struct callchain_print_arg arg = {
836 .row_offset = row_offset,
837 .is_current_entry = current_entry,
840 if (callchain_param.mode == CHAIN_GRAPH_REL) {
841 if (symbol_conf.cumulate_callchain)
842 total = entry->stat_acc->period;
844 total = entry->stat.period;
847 printed += hist_browser__show_callchain(browser,
848 &entry->sorted_chain, 1, row, total,
849 hist_browser__show_callchain_entry, &arg,
850 hist_browser__check_output_full);
852 if (arg.is_current_entry)
853 browser->he_selection = entry;
859 static int advance_hpp_check(struct perf_hpp *hpp, int inc)
861 advance_hpp(hpp, inc);
862 return hpp->size <= 0;
865 static int hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf, size_t size)
867 struct hists *hists = browser->hists;
868 struct perf_hpp dummy_hpp = {
872 struct perf_hpp_fmt *fmt;
876 if (symbol_conf.use_callchain) {
877 ret = scnprintf(buf, size, " ");
878 if (advance_hpp_check(&dummy_hpp, ret))
882 perf_hpp__for_each_format(fmt) {
883 if (perf_hpp__should_skip(fmt) || column++ < browser->b.horiz_scroll)
886 ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists));
887 if (advance_hpp_check(&dummy_hpp, ret))
890 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
891 if (advance_hpp_check(&dummy_hpp, ret))
898 static void hist_browser__show_headers(struct hist_browser *browser)
902 hists_browser__scnprintf_headers(browser, headers, sizeof(headers));
903 ui_browser__gotorc(&browser->b, 0, 0);
904 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
905 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
908 static void ui_browser__hists_init_top(struct ui_browser *browser)
910 if (browser->top == NULL) {
911 struct hist_browser *hb;
913 hb = container_of(browser, struct hist_browser, b);
914 browser->top = rb_first(&hb->hists->entries);
918 static unsigned int hist_browser__refresh(struct ui_browser *browser)
921 u16 header_offset = 0;
923 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
925 if (hb->show_headers) {
926 hist_browser__show_headers(hb);
930 ui_browser__hists_init_top(browser);
932 for (nd = browser->top; nd; nd = rb_next(nd)) {
933 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
939 percent = hist_entry__get_percent_limit(h);
940 if (percent < hb->min_pcnt)
943 row += hist_browser__show_entry(hb, h, row);
944 if (row == browser->rows)
948 return row + header_offset;
951 static struct rb_node *hists__filter_entries(struct rb_node *nd,
955 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
956 float percent = hist_entry__get_percent_limit(h);
958 if (!h->filtered && percent >= min_pcnt)
967 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
971 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
972 float percent = hist_entry__get_percent_limit(h);
974 if (!h->filtered && percent >= min_pcnt)
983 static void ui_browser__hists_seek(struct ui_browser *browser,
984 off_t offset, int whence)
986 struct hist_entry *h;
989 struct hist_browser *hb;
991 hb = container_of(browser, struct hist_browser, b);
993 if (browser->nr_entries == 0)
996 ui_browser__hists_init_top(browser);
1000 nd = hists__filter_entries(rb_first(browser->entries),
1007 nd = hists__filter_prev_entries(rb_last(browser->entries),
1016 * Moves not relative to the first visible entry invalidates its
1019 h = rb_entry(browser->top, struct hist_entry, rb_node);
1023 * Here we have to check if nd is expanded (+), if it is we can't go
1024 * the next top level hist_entry, instead we must compute an offset of
1025 * what _not_ to show and not change the first visible entry.
1027 * This offset increments when we are going from top to bottom and
1028 * decreases when we're going from bottom to top.
1030 * As we don't have backpointers to the top level in the callchains
1031 * structure, we need to always print the whole hist_entry callchain,
1032 * skipping the first ones that are before the first visible entry
1033 * and stop when we printed enough lines to fill the screen.
1038 h = rb_entry(nd, struct hist_entry, rb_node);
1040 u16 remaining = h->nr_rows - h->row_offset;
1041 if (offset > remaining) {
1042 offset -= remaining;
1045 h->row_offset += offset;
1051 nd = hists__filter_entries(rb_next(nd), hb->min_pcnt);
1056 } while (offset != 0);
1057 } else if (offset < 0) {
1059 h = rb_entry(nd, struct hist_entry, rb_node);
1062 if (-offset > h->row_offset) {
1063 offset += h->row_offset;
1066 h->row_offset += offset;
1072 if (-offset > h->nr_rows) {
1073 offset += h->nr_rows;
1076 h->row_offset = h->nr_rows + offset;
1084 nd = hists__filter_prev_entries(rb_prev(nd),
1092 * Last unfiltered hist_entry, check if it is
1093 * unfolded, if it is then we should have
1094 * row_offset at its last entry.
1096 h = rb_entry(nd, struct hist_entry, rb_node);
1098 h->row_offset = h->nr_rows;
1105 h = rb_entry(nd, struct hist_entry, rb_node);
1110 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1111 struct hist_entry *he, FILE *fp)
1113 u64 total = hists__total_period(he->hists);
1114 struct callchain_print_arg arg = {
1118 if (symbol_conf.cumulate_callchain)
1119 total = he->stat_acc->period;
1121 hist_browser__show_callchain(browser, &he->sorted_chain, 1, 0, total,
1122 hist_browser__fprintf_callchain_entry, &arg,
1123 hist_browser__check_dump_full);
1127 static int hist_browser__fprintf_entry(struct hist_browser *browser,
1128 struct hist_entry *he, FILE *fp)
1132 char folded_sign = ' ';
1133 struct perf_hpp hpp = {
1137 struct perf_hpp_fmt *fmt;
1141 if (symbol_conf.use_callchain)
1142 folded_sign = hist_entry__folded(he);
1144 if (symbol_conf.use_callchain)
1145 printed += fprintf(fp, "%c ", folded_sign);
1147 perf_hpp__for_each_format(fmt) {
1148 if (perf_hpp__should_skip(fmt))
1152 ret = scnprintf(hpp.buf, hpp.size, " ");
1153 advance_hpp(&hpp, ret);
1157 ret = fmt->entry(fmt, &hpp, he);
1158 advance_hpp(&hpp, ret);
1160 printed += fprintf(fp, "%s\n", rtrim(s));
1162 if (folded_sign == '-')
1163 printed += hist_browser__fprintf_callchain(browser, he, fp);
1168 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1170 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
1175 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1177 printed += hist_browser__fprintf_entry(browser, h, fp);
1178 nd = hists__filter_entries(rb_next(nd), browser->min_pcnt);
1184 static int hist_browser__dump(struct hist_browser *browser)
1190 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
1191 if (access(filename, F_OK))
1194 * XXX: Just an arbitrary lazy upper limit
1196 if (++browser->print_seq == 8192) {
1197 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1202 fp = fopen(filename, "w");
1205 const char *err = strerror_r(errno, bf, sizeof(bf));
1206 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
1210 ++browser->print_seq;
1211 hist_browser__fprintf(browser, fp);
1213 ui_helpline__fpush("%s written!", filename);
1218 static struct hist_browser *hist_browser__new(struct hists *hists,
1219 struct hist_browser_timer *hbt,
1220 struct perf_env *env)
1222 struct hist_browser *browser = zalloc(sizeof(*browser));
1225 browser->hists = hists;
1226 browser->b.refresh = hist_browser__refresh;
1227 browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
1228 browser->b.seek = ui_browser__hists_seek;
1229 browser->b.use_navkeypressed = true;
1230 browser->show_headers = symbol_conf.show_hist_headers;
1238 static void hist_browser__delete(struct hist_browser *browser)
1243 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
1245 return browser->he_selection;
1248 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
1250 return browser->he_selection->thread;
1253 /* Check whether the browser is for 'top' or 'report' */
1254 static inline bool is_report_browser(void *timer)
1256 return timer == NULL;
1259 static int hists__browser_title(struct hists *hists,
1260 struct hist_browser_timer *hbt,
1261 char *bf, size_t size)
1265 const struct dso *dso = hists->dso_filter;
1266 const struct thread *thread = hists->thread_filter;
1267 int socket_id = hists->socket_filter;
1268 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1269 u64 nr_events = hists->stats.total_period;
1270 struct perf_evsel *evsel = hists_to_evsel(hists);
1271 const char *ev_name = perf_evsel__name(evsel);
1273 size_t buflen = sizeof(buf);
1274 char ref[30] = " show reference callgraph, ";
1275 bool enable_ref = false;
1277 if (symbol_conf.filter_relative) {
1278 nr_samples = hists->stats.nr_non_filtered_samples;
1279 nr_events = hists->stats.total_non_filtered_period;
1282 if (perf_evsel__is_group_event(evsel)) {
1283 struct perf_evsel *pos;
1285 perf_evsel__group_desc(evsel, buf, buflen);
1288 for_each_group_member(pos, evsel) {
1289 struct hists *pos_hists = evsel__hists(pos);
1291 if (symbol_conf.filter_relative) {
1292 nr_samples += pos_hists->stats.nr_non_filtered_samples;
1293 nr_events += pos_hists->stats.total_non_filtered_period;
1295 nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
1296 nr_events += pos_hists->stats.total_period;
1301 if (symbol_conf.show_ref_callgraph &&
1302 strstr(ev_name, "call-graph=no"))
1304 nr_samples = convert_unit(nr_samples, &unit);
1305 printed = scnprintf(bf, size,
1306 "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64,
1307 nr_samples, unit, ev_name, enable_ref ? ref : " ", nr_events);
1310 if (hists->uid_filter_str)
1311 printed += snprintf(bf + printed, size - printed,
1312 ", UID: %s", hists->uid_filter_str);
1314 printed += scnprintf(bf + printed, size - printed,
1316 (thread->comm_set ? thread__comm_str(thread) : ""),
1319 printed += scnprintf(bf + printed, size - printed,
1320 ", DSO: %s", dso->short_name);
1322 printed += scnprintf(bf + printed, size - printed,
1323 ", Processor Socket: %d", socket_id);
1324 if (!is_report_browser(hbt)) {
1325 struct perf_top *top = hbt->arg;
1328 printed += scnprintf(bf + printed, size - printed, " [z]");
1334 static inline void free_popup_options(char **options, int n)
1338 for (i = 0; i < n; ++i)
1343 * Only runtime switching of perf data file will make "input_name" point
1344 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1345 * whether we need to call free() for current "input_name" during the switch.
1347 static bool is_input_name_malloced = false;
1349 static int switch_data_file(void)
1351 char *pwd, *options[32], *abs_path[32], *tmp;
1353 int nr_options = 0, choice = -1, ret = -1;
1354 struct dirent *dent;
1356 pwd = getenv("PWD");
1360 pwd_dir = opendir(pwd);
1364 memset(options, 0, sizeof(options));
1365 memset(options, 0, sizeof(abs_path));
1367 while ((dent = readdir(pwd_dir))) {
1368 char path[PATH_MAX];
1370 char *name = dent->d_name;
1373 if (!(dent->d_type == DT_REG))
1376 snprintf(path, sizeof(path), "%s/%s", pwd, name);
1378 file = fopen(path, "r");
1382 if (fread(&magic, 1, 8, file) < 8)
1383 goto close_file_and_continue;
1385 if (is_perf_magic(magic)) {
1386 options[nr_options] = strdup(name);
1387 if (!options[nr_options])
1388 goto close_file_and_continue;
1390 abs_path[nr_options] = strdup(path);
1391 if (!abs_path[nr_options]) {
1392 zfree(&options[nr_options]);
1393 ui__warning("Can't search all data files due to memory shortage.\n");
1401 close_file_and_continue:
1403 if (nr_options >= 32) {
1404 ui__warning("Too many perf data files in PWD!\n"
1405 "Only the first 32 files will be listed.\n");
1412 choice = ui__popup_menu(nr_options, options);
1413 if (choice < nr_options && choice >= 0) {
1414 tmp = strdup(abs_path[choice]);
1416 if (is_input_name_malloced)
1417 free((void *)input_name);
1419 is_input_name_malloced = true;
1422 ui__warning("Data switch failed due to memory shortage!\n");
1426 free_popup_options(options, nr_options);
1427 free_popup_options(abs_path, nr_options);
1431 struct popup_action {
1432 struct thread *thread;
1434 struct map_symbol ms;
1437 int (*fn)(struct hist_browser *browser, struct popup_action *act);
1441 do_annotate(struct hist_browser *browser, struct popup_action *act)
1443 struct perf_evsel *evsel;
1444 struct annotation *notes;
1445 struct hist_entry *he;
1448 if (!objdump_path && perf_env__lookup_objdump(browser->env))
1451 notes = symbol__annotation(act->ms.sym);
1455 evsel = hists_to_evsel(browser->hists);
1456 err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
1457 he = hist_browser__selected_entry(browser);
1459 * offer option to annotate the other branch source or target
1460 * (if they exists) when returning from annotate
1462 if ((err == 'q' || err == CTRL('c')) && he->branch_info)
1465 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1467 ui_browser__handle_resize(&browser->b);
1472 add_annotate_opt(struct hist_browser *browser __maybe_unused,
1473 struct popup_action *act, char **optstr,
1474 struct map *map, struct symbol *sym)
1476 if (sym == NULL || map->dso->annotate_warned)
1479 if (asprintf(optstr, "Annotate %s", sym->name) < 0)
1484 act->fn = do_annotate;
1489 do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
1491 struct thread *thread = act->thread;
1493 if (browser->hists->thread_filter) {
1494 pstack__remove(browser->pstack, &browser->hists->thread_filter);
1495 perf_hpp__set_elide(HISTC_THREAD, false);
1496 thread__zput(browser->hists->thread_filter);
1499 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
1500 thread->comm_set ? thread__comm_str(thread) : "",
1502 browser->hists->thread_filter = thread__get(thread);
1503 perf_hpp__set_elide(HISTC_THREAD, false);
1504 pstack__push(browser->pstack, &browser->hists->thread_filter);
1507 hists__filter_by_thread(browser->hists);
1508 hist_browser__reset(browser);
1513 add_thread_opt(struct hist_browser *browser, struct popup_action *act,
1514 char **optstr, struct thread *thread)
1519 if (asprintf(optstr, "Zoom %s %s(%d) thread",
1520 browser->hists->thread_filter ? "out of" : "into",
1521 thread->comm_set ? thread__comm_str(thread) : "",
1525 act->thread = thread;
1526 act->fn = do_zoom_thread;
1531 do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
1533 struct map *map = act->ms.map;
1535 if (browser->hists->dso_filter) {
1536 pstack__remove(browser->pstack, &browser->hists->dso_filter);
1537 perf_hpp__set_elide(HISTC_DSO, false);
1538 browser->hists->dso_filter = NULL;
1543 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
1544 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
1545 browser->hists->dso_filter = map->dso;
1546 perf_hpp__set_elide(HISTC_DSO, true);
1547 pstack__push(browser->pstack, &browser->hists->dso_filter);
1550 hists__filter_by_dso(browser->hists);
1551 hist_browser__reset(browser);
1556 add_dso_opt(struct hist_browser *browser, struct popup_action *act,
1557 char **optstr, struct map *map)
1562 if (asprintf(optstr, "Zoom %s %s DSO",
1563 browser->hists->dso_filter ? "out of" : "into",
1564 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
1568 act->dso = map->dso;
1569 act->fn = do_zoom_dso;
1574 do_browse_map(struct hist_browser *browser __maybe_unused,
1575 struct popup_action *act)
1577 map__browse(act->ms.map);
1582 add_map_opt(struct hist_browser *browser __maybe_unused,
1583 struct popup_action *act, char **optstr, struct map *map)
1588 if (asprintf(optstr, "Browse map details") < 0)
1592 act->fn = do_browse_map;
1597 do_run_script(struct hist_browser *browser __maybe_unused,
1598 struct popup_action *act)
1600 char script_opt[64];
1601 memset(script_opt, 0, sizeof(script_opt));
1604 scnprintf(script_opt, sizeof(script_opt), " -c %s ",
1605 thread__comm_str(act->thread));
1606 } else if (act->ms.sym) {
1607 scnprintf(script_opt, sizeof(script_opt), " -S %s ",
1611 script_browse(script_opt);
1616 add_script_opt(struct hist_browser *browser __maybe_unused,
1617 struct popup_action *act, char **optstr,
1618 struct thread *thread, struct symbol *sym)
1621 if (asprintf(optstr, "Run scripts for samples of thread [%s]",
1622 thread__comm_str(thread)) < 0)
1625 if (asprintf(optstr, "Run scripts for samples of symbol [%s]",
1629 if (asprintf(optstr, "Run scripts for all samples") < 0)
1633 act->thread = thread;
1635 act->fn = do_run_script;
1640 do_switch_data(struct hist_browser *browser __maybe_unused,
1641 struct popup_action *act __maybe_unused)
1643 if (switch_data_file()) {
1644 ui__warning("Won't switch the data files due to\n"
1645 "no valid data file get selected!\n");
1649 return K_SWITCH_INPUT_DATA;
1653 add_switch_opt(struct hist_browser *browser,
1654 struct popup_action *act, char **optstr)
1656 if (!is_report_browser(browser->hbt))
1659 if (asprintf(optstr, "Switch to another data file in PWD") < 0)
1662 act->fn = do_switch_data;
1667 do_exit_browser(struct hist_browser *browser __maybe_unused,
1668 struct popup_action *act __maybe_unused)
1674 add_exit_opt(struct hist_browser *browser __maybe_unused,
1675 struct popup_action *act, char **optstr)
1677 if (asprintf(optstr, "Exit") < 0)
1680 act->fn = do_exit_browser;
1685 do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
1687 if (browser->hists->socket_filter > -1) {
1688 pstack__remove(browser->pstack, &browser->hists->socket_filter);
1689 browser->hists->socket_filter = -1;
1690 perf_hpp__set_elide(HISTC_SOCKET, false);
1692 browser->hists->socket_filter = act->socket;
1693 perf_hpp__set_elide(HISTC_SOCKET, true);
1694 pstack__push(browser->pstack, &browser->hists->socket_filter);
1697 hists__filter_by_socket(browser->hists);
1698 hist_browser__reset(browser);
1703 add_socket_opt(struct hist_browser *browser, struct popup_action *act,
1704 char **optstr, int socket_id)
1709 if (asprintf(optstr, "Zoom %s Processor Socket %d",
1710 (browser->hists->socket_filter > -1) ? "out of" : "into",
1714 act->socket = socket_id;
1715 act->fn = do_zoom_socket;
1719 static void hist_browser__update_nr_entries(struct hist_browser *hb)
1722 struct rb_node *nd = rb_first(&hb->hists->entries);
1724 if (hb->min_pcnt == 0) {
1725 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
1729 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
1734 hb->nr_non_filtered_entries = nr_entries;
1737 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
1738 const char *helpline,
1740 struct hist_browser_timer *hbt,
1742 struct perf_env *env)
1744 struct hists *hists = evsel__hists(evsel);
1745 struct hist_browser *browser = hist_browser__new(hists, hbt, env);
1746 struct branch_info *bi;
1747 #define MAX_OPTIONS 16
1748 char *options[MAX_OPTIONS];
1749 struct popup_action actions[MAX_OPTIONS];
1753 int delay_secs = hbt ? hbt->refresh : 0;
1754 struct perf_hpp_fmt *fmt;
1756 #define HIST_BROWSER_HELP_COMMON \
1757 "h/?/F1 Show this window\n" \
1759 "PGDN/SPACE Navigate\n" \
1760 "q/ESC/CTRL+C Exit browser\n\n" \
1761 "For multiple event sessions:\n\n" \
1762 "TAB/UNTAB Switch events\n\n" \
1763 "For symbolic views (--sort has sym):\n\n" \
1764 "ENTER Zoom into DSO/Threads & Annotate current symbol\n" \
1766 "a Annotate current symbol\n" \
1767 "C Collapse all callchains\n" \
1768 "d Zoom into current DSO\n" \
1769 "E Expand all callchains\n" \
1770 "F Toggle percentage of filtered entries\n" \
1771 "H Display column headers\n" \
1772 "m Display context menu\n" \
1773 "S Zoom into current Processor Socket\n" \
1775 /* help messages are sorted by lexical order of the hotkey */
1776 const char report_help[] = HIST_BROWSER_HELP_COMMON
1777 "i Show header information\n"
1778 "P Print histograms to perf.hist.N\n"
1779 "r Run available scripts\n"
1780 "s Switch to another data file in PWD\n"
1781 "t Zoom into current Thread\n"
1782 "V Verbose (DSO names in callchains, etc)\n"
1783 "/ Filter symbol by name";
1784 const char top_help[] = HIST_BROWSER_HELP_COMMON
1785 "P Print histograms to perf.hist.N\n"
1786 "t Zoom into current Thread\n"
1787 "V Verbose (DSO names in callchains, etc)\n"
1788 "z Toggle zeroing of samples\n"
1789 "f Enable/Disable events\n"
1790 "/ Filter symbol by name";
1792 if (browser == NULL)
1795 /* reset abort key so that it can get Ctrl-C as a key */
1797 SLang_init_tty(0, 0, 0);
1800 browser->min_pcnt = min_pcnt;
1801 hist_browser__update_nr_entries(browser);
1804 browser->pstack = pstack__new(3);
1805 if (browser->pstack == NULL)
1808 ui_helpline__push(helpline);
1810 memset(options, 0, sizeof(options));
1811 memset(actions, 0, sizeof(actions));
1813 perf_hpp__for_each_format(fmt) {
1814 perf_hpp__reset_width(fmt, hists);
1816 * This is done just once, and activates the horizontal scrolling
1817 * code in the ui_browser code, it would be better to have a the
1818 * counter in the perf_hpp code, but I couldn't find doing it here
1819 * works, FIXME by setting this in hist_browser__new, for now, be
1822 ++browser->b.columns;
1825 if (symbol_conf.col_width_list_str)
1826 perf_hpp__set_user_width(symbol_conf.col_width_list_str);
1829 struct thread *thread = NULL;
1830 struct dso *dso = NULL;
1831 struct map *map = NULL;
1837 key = hist_browser__run(browser, helpline);
1839 if (browser->he_selection != NULL) {
1840 thread = hist_browser__selected_thread(browser);
1841 map = browser->selection->map;
1844 socked_id = browser->he_selection->socket;
1852 * Exit the browser, let hists__browser_tree
1853 * go to the next or previous
1855 goto out_free_stack;
1857 if (!sort__has_sym) {
1858 ui_browser__warning(&browser->b, delay_secs * 2,
1859 "Annotation is only available for symbolic views, "
1860 "include \"sym*\" in --sort to use it.");
1864 if (browser->selection == NULL ||
1865 browser->selection->sym == NULL ||
1866 browser->selection->map->dso->annotate_warned)
1869 actions->ms.map = browser->selection->map;
1870 actions->ms.sym = browser->selection->sym;
1871 do_annotate(browser, actions);
1874 hist_browser__dump(browser);
1878 do_zoom_dso(browser, actions);
1881 browser->show_dso = !browser->show_dso;
1884 actions->thread = thread;
1885 do_zoom_thread(browser, actions);
1888 actions->socket = socked_id;
1889 do_zoom_socket(browser, actions);
1892 if (ui_browser__input_window("Symbol to show",
1893 "Please enter the name of symbol you want to see.\n"
1894 "To remove the filter later, press / + ENTER.",
1895 buf, "ENTER: OK, ESC: Cancel",
1896 delay_secs * 2) == K_ENTER) {
1897 hists->symbol_filter_str = *buf ? buf : NULL;
1898 hists__filter_by_symbol(hists);
1899 hist_browser__reset(browser);
1903 if (is_report_browser(hbt)) {
1904 actions->thread = NULL;
1905 actions->ms.sym = NULL;
1906 do_run_script(browser, actions);
1910 if (is_report_browser(hbt)) {
1911 key = do_switch_data(browser, actions);
1912 if (key == K_SWITCH_INPUT_DATA)
1913 goto out_free_stack;
1917 /* env->arch is NULL for live-mode (i.e. perf top) */
1919 tui__header_window(env);
1922 symbol_conf.filter_relative ^= 1;
1925 if (!is_report_browser(hbt)) {
1926 struct perf_top *top = hbt->arg;
1928 top->zero = !top->zero;
1934 ui_browser__help_window(&browser->b,
1935 is_report_browser(hbt) ? report_help : top_help);
1946 if (pstack__empty(browser->pstack)) {
1948 * Go back to the perf_evsel_menu__run or other user
1951 goto out_free_stack;
1954 ui_browser__dialog_yesno(&browser->b,
1955 "Do you really want to exit?"))
1956 goto out_free_stack;
1960 top = pstack__peek(browser->pstack);
1961 if (top == &browser->hists->dso_filter) {
1963 * No need to set actions->dso here since
1964 * it's just to remove the current filter.
1965 * Ditto for thread below.
1967 do_zoom_dso(browser, actions);
1968 } else if (top == &browser->hists->thread_filter) {
1969 do_zoom_thread(browser, actions);
1970 } else if (top == &browser->hists->socket_filter) {
1971 do_zoom_socket(browser, actions);
1977 goto out_free_stack;
1979 if (!is_report_browser(hbt)) {
1980 struct perf_top *top = hbt->arg;
1982 perf_evlist__toggle_enable(top->evlist);
1984 * No need to refresh, resort/decay histogram
1985 * entries if we are not collecting samples:
1987 if (top->evlist->enabled) {
1988 helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
1989 hbt->refresh = delay_secs;
1991 helpline = "Press 'f' again to re-enable the events";
1998 helpline = "Press '?' for help on key bindings";
2003 goto add_exit_option;
2005 if (browser->selection == NULL)
2006 goto skip_annotation;
2008 if (sort__mode == SORT_MODE__BRANCH) {
2009 bi = browser->he_selection->branch_info;
2012 goto skip_annotation;
2014 nr_options += add_annotate_opt(browser,
2015 &actions[nr_options],
2016 &options[nr_options],
2019 if (bi->to.sym != bi->from.sym)
2020 nr_options += add_annotate_opt(browser,
2021 &actions[nr_options],
2022 &options[nr_options],
2026 nr_options += add_annotate_opt(browser,
2027 &actions[nr_options],
2028 &options[nr_options],
2029 browser->selection->map,
2030 browser->selection->sym);
2033 nr_options += add_thread_opt(browser, &actions[nr_options],
2034 &options[nr_options], thread);
2035 nr_options += add_dso_opt(browser, &actions[nr_options],
2036 &options[nr_options], map);
2037 nr_options += add_map_opt(browser, &actions[nr_options],
2038 &options[nr_options],
2039 browser->selection ?
2040 browser->selection->map : NULL);
2041 nr_options += add_socket_opt(browser, &actions[nr_options],
2042 &options[nr_options],
2044 /* perf script support */
2045 if (browser->he_selection) {
2046 nr_options += add_script_opt(browser,
2047 &actions[nr_options],
2048 &options[nr_options],
2051 * Note that browser->selection != NULL
2052 * when browser->he_selection is not NULL,
2053 * so we don't need to check browser->selection
2054 * before fetching browser->selection->sym like what
2055 * we do before fetching browser->selection->map.
2057 * See hist_browser__show_entry.
2059 nr_options += add_script_opt(browser,
2060 &actions[nr_options],
2061 &options[nr_options],
2062 NULL, browser->selection->sym);
2064 nr_options += add_script_opt(browser, &actions[nr_options],
2065 &options[nr_options], NULL, NULL);
2066 nr_options += add_switch_opt(browser, &actions[nr_options],
2067 &options[nr_options]);
2069 nr_options += add_exit_opt(browser, &actions[nr_options],
2070 &options[nr_options]);
2073 struct popup_action *act;
2075 choice = ui__popup_menu(nr_options, options);
2076 if (choice == -1 || choice >= nr_options)
2079 act = &actions[choice];
2080 key = act->fn(browser, act);
2083 if (key == K_SWITCH_INPUT_DATA)
2087 pstack__delete(browser->pstack);
2089 hist_browser__delete(browser);
2090 free_popup_options(options, MAX_OPTIONS);
2094 struct perf_evsel_menu {
2095 struct ui_browser b;
2096 struct perf_evsel *selection;
2097 bool lost_events, lost_events_warned;
2099 struct perf_env *env;
2102 static void perf_evsel_menu__write(struct ui_browser *browser,
2103 void *entry, int row)
2105 struct perf_evsel_menu *menu = container_of(browser,
2106 struct perf_evsel_menu, b);
2107 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
2108 struct hists *hists = evsel__hists(evsel);
2109 bool current_entry = ui_browser__is_current_entry(browser, row);
2110 unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
2111 const char *ev_name = perf_evsel__name(evsel);
2113 const char *warn = " ";
2116 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
2117 HE_COLORSET_NORMAL);
2119 if (perf_evsel__is_group_event(evsel)) {
2120 struct perf_evsel *pos;
2122 ev_name = perf_evsel__group_name(evsel);
2124 for_each_group_member(pos, evsel) {
2125 struct hists *pos_hists = evsel__hists(pos);
2126 nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
2130 nr_events = convert_unit(nr_events, &unit);
2131 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
2132 unit, unit == ' ' ? "" : " ", ev_name);
2133 ui_browser__printf(browser, "%s", bf);
2135 nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
2136 if (nr_events != 0) {
2137 menu->lost_events = true;
2139 ui_browser__set_color(browser, HE_COLORSET_TOP);
2140 nr_events = convert_unit(nr_events, &unit);
2141 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
2142 nr_events, unit, unit == ' ' ? "" : " ");
2146 ui_browser__write_nstring(browser, warn, browser->width - printed);
2149 menu->selection = evsel;
2152 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
2153 int nr_events, const char *help,
2154 struct hist_browser_timer *hbt)
2156 struct perf_evlist *evlist = menu->b.priv;
2157 struct perf_evsel *pos;
2158 const char *title = "Available samples";
2159 int delay_secs = hbt ? hbt->refresh : 0;
2162 if (ui_browser__show(&menu->b, title,
2163 "ESC: exit, ENTER|->: Browse histograms") < 0)
2167 key = ui_browser__run(&menu->b, delay_secs);
2171 hbt->timer(hbt->arg);
2173 if (!menu->lost_events_warned && menu->lost_events) {
2174 ui_browser__warn_lost_events(&menu->b);
2175 menu->lost_events_warned = true;
2180 if (!menu->selection)
2182 pos = menu->selection;
2184 perf_evlist__set_selected(evlist, pos);
2186 * Give the calling tool a chance to populate the non
2187 * default evsel resorted hists tree.
2190 hbt->timer(hbt->arg);
2191 key = perf_evsel__hists_browse(pos, nr_events, help,
2195 ui_browser__show_title(&menu->b, title);
2198 if (pos->node.next == &evlist->entries)
2199 pos = perf_evlist__first(evlist);
2201 pos = perf_evsel__next(pos);
2204 if (pos->node.prev == &evlist->entries)
2205 pos = perf_evlist__last(evlist);
2207 pos = perf_evsel__prev(pos);
2209 case K_SWITCH_INPUT_DATA:
2220 if (!ui_browser__dialog_yesno(&menu->b,
2221 "Do you really want to exit?"))
2233 ui_browser__hide(&menu->b);
2237 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
2240 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
2242 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
2248 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
2249 int nr_entries, const char *help,
2250 struct hist_browser_timer *hbt,
2252 struct perf_env *env)
2254 struct perf_evsel *pos;
2255 struct perf_evsel_menu menu = {
2257 .entries = &evlist->entries,
2258 .refresh = ui_browser__list_head_refresh,
2259 .seek = ui_browser__list_head_seek,
2260 .write = perf_evsel_menu__write,
2261 .filter = filter_group_entries,
2262 .nr_entries = nr_entries,
2265 .min_pcnt = min_pcnt,
2269 ui_helpline__push("Press ESC to exit");
2271 evlist__for_each(evlist, pos) {
2272 const char *ev_name = perf_evsel__name(pos);
2273 size_t line_len = strlen(ev_name) + 7;
2275 if (menu.b.width < line_len)
2276 menu.b.width = line_len;
2279 return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
2282 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
2283 struct hist_browser_timer *hbt,
2285 struct perf_env *env)
2287 int nr_entries = evlist->nr_entries;
2290 if (nr_entries == 1) {
2291 struct perf_evsel *first = perf_evlist__first(evlist);
2293 return perf_evsel__hists_browse(first, nr_entries, help,
2294 false, hbt, min_pcnt,
2298 if (symbol_conf.event_group) {
2299 struct perf_evsel *pos;
2302 evlist__for_each(evlist, pos) {
2303 if (perf_evsel__is_group_leader(pos))
2307 if (nr_entries == 1)
2311 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
2312 hbt, min_pcnt, env);