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 = {
788 hist_browser__gotorc(browser, row, 0);
790 perf_hpp__for_each_format(fmt) {
791 if (perf_hpp__should_skip(fmt))
794 if (current_entry && browser->b.navkeypressed) {
795 ui_browser__set_color(&browser->b,
796 HE_COLORSET_SELECTED);
798 ui_browser__set_color(&browser->b,
803 if (symbol_conf.use_callchain) {
804 ui_browser__printf(&browser->b, "%c ", folded_sign);
809 ui_browser__printf(&browser->b, " ");
814 width -= fmt->color(fmt, &hpp, entry);
816 width -= fmt->entry(fmt, &hpp, entry);
817 ui_browser__printf(&browser->b, "%s", s);
821 /* The scroll bar isn't being used */
822 if (!browser->b.navkeypressed)
825 ui_browser__write_nstring(&browser->b, "", width);
832 if (folded_sign == '-' && row != browser->b.rows) {
833 u64 total = hists__total_period(entry->hists);
834 struct callchain_print_arg arg = {
835 .row_offset = row_offset,
836 .is_current_entry = current_entry,
839 if (callchain_param.mode == CHAIN_GRAPH_REL) {
840 if (symbol_conf.cumulate_callchain)
841 total = entry->stat_acc->period;
843 total = entry->stat.period;
846 printed += hist_browser__show_callchain(browser,
847 &entry->sorted_chain, 1, row, total,
848 hist_browser__show_callchain_entry, &arg,
849 hist_browser__check_output_full);
851 if (arg.is_current_entry)
852 browser->he_selection = entry;
858 static int advance_hpp_check(struct perf_hpp *hpp, int inc)
860 advance_hpp(hpp, inc);
861 return hpp->size <= 0;
864 static int hists__scnprintf_headers(char *buf, size_t size, struct hists *hists)
866 struct perf_hpp dummy_hpp = {
870 struct perf_hpp_fmt *fmt;
873 if (symbol_conf.use_callchain) {
874 ret = scnprintf(buf, size, " ");
875 if (advance_hpp_check(&dummy_hpp, ret))
879 perf_hpp__for_each_format(fmt) {
880 if (perf_hpp__should_skip(fmt))
883 ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists));
884 if (advance_hpp_check(&dummy_hpp, ret))
887 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
888 if (advance_hpp_check(&dummy_hpp, ret))
895 static void hist_browser__show_headers(struct hist_browser *browser)
899 hists__scnprintf_headers(headers, sizeof(headers), browser->hists);
900 ui_browser__gotorc(&browser->b, 0, 0);
901 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
902 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
905 static void ui_browser__hists_init_top(struct ui_browser *browser)
907 if (browser->top == NULL) {
908 struct hist_browser *hb;
910 hb = container_of(browser, struct hist_browser, b);
911 browser->top = rb_first(&hb->hists->entries);
915 static unsigned int hist_browser__refresh(struct ui_browser *browser)
918 u16 header_offset = 0;
920 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
922 if (hb->show_headers) {
923 hist_browser__show_headers(hb);
927 ui_browser__hists_init_top(browser);
929 for (nd = browser->top; nd; nd = rb_next(nd)) {
930 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
936 percent = hist_entry__get_percent_limit(h);
937 if (percent < hb->min_pcnt)
940 row += hist_browser__show_entry(hb, h, row);
941 if (row == browser->rows)
945 return row + header_offset;
948 static struct rb_node *hists__filter_entries(struct rb_node *nd,
952 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
953 float percent = hist_entry__get_percent_limit(h);
955 if (!h->filtered && percent >= min_pcnt)
964 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
968 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
969 float percent = hist_entry__get_percent_limit(h);
971 if (!h->filtered && percent >= min_pcnt)
980 static void ui_browser__hists_seek(struct ui_browser *browser,
981 off_t offset, int whence)
983 struct hist_entry *h;
986 struct hist_browser *hb;
988 hb = container_of(browser, struct hist_browser, b);
990 if (browser->nr_entries == 0)
993 ui_browser__hists_init_top(browser);
997 nd = hists__filter_entries(rb_first(browser->entries),
1004 nd = hists__filter_prev_entries(rb_last(browser->entries),
1013 * Moves not relative to the first visible entry invalidates its
1016 h = rb_entry(browser->top, struct hist_entry, rb_node);
1020 * Here we have to check if nd is expanded (+), if it is we can't go
1021 * the next top level hist_entry, instead we must compute an offset of
1022 * what _not_ to show and not change the first visible entry.
1024 * This offset increments when we are going from top to bottom and
1025 * decreases when we're going from bottom to top.
1027 * As we don't have backpointers to the top level in the callchains
1028 * structure, we need to always print the whole hist_entry callchain,
1029 * skipping the first ones that are before the first visible entry
1030 * and stop when we printed enough lines to fill the screen.
1035 h = rb_entry(nd, struct hist_entry, rb_node);
1037 u16 remaining = h->nr_rows - h->row_offset;
1038 if (offset > remaining) {
1039 offset -= remaining;
1042 h->row_offset += offset;
1048 nd = hists__filter_entries(rb_next(nd), hb->min_pcnt);
1053 } while (offset != 0);
1054 } else if (offset < 0) {
1056 h = rb_entry(nd, struct hist_entry, rb_node);
1059 if (-offset > h->row_offset) {
1060 offset += h->row_offset;
1063 h->row_offset += offset;
1069 if (-offset > h->nr_rows) {
1070 offset += h->nr_rows;
1073 h->row_offset = h->nr_rows + offset;
1081 nd = hists__filter_prev_entries(rb_prev(nd),
1089 * Last unfiltered hist_entry, check if it is
1090 * unfolded, if it is then we should have
1091 * row_offset at its last entry.
1093 h = rb_entry(nd, struct hist_entry, rb_node);
1095 h->row_offset = h->nr_rows;
1102 h = rb_entry(nd, struct hist_entry, rb_node);
1107 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1108 struct hist_entry *he, FILE *fp)
1110 u64 total = hists__total_period(he->hists);
1111 struct callchain_print_arg arg = {
1115 if (symbol_conf.cumulate_callchain)
1116 total = he->stat_acc->period;
1118 hist_browser__show_callchain(browser, &he->sorted_chain, 1, 0, total,
1119 hist_browser__fprintf_callchain_entry, &arg,
1120 hist_browser__check_dump_full);
1124 static int hist_browser__fprintf_entry(struct hist_browser *browser,
1125 struct hist_entry *he, FILE *fp)
1129 char folded_sign = ' ';
1130 struct perf_hpp hpp = {
1134 struct perf_hpp_fmt *fmt;
1138 if (symbol_conf.use_callchain)
1139 folded_sign = hist_entry__folded(he);
1141 if (symbol_conf.use_callchain)
1142 printed += fprintf(fp, "%c ", folded_sign);
1144 perf_hpp__for_each_format(fmt) {
1145 if (perf_hpp__should_skip(fmt))
1149 ret = scnprintf(hpp.buf, hpp.size, " ");
1150 advance_hpp(&hpp, ret);
1154 ret = fmt->entry(fmt, &hpp, he);
1155 advance_hpp(&hpp, ret);
1157 printed += fprintf(fp, "%s\n", rtrim(s));
1159 if (folded_sign == '-')
1160 printed += hist_browser__fprintf_callchain(browser, he, fp);
1165 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1167 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
1172 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1174 printed += hist_browser__fprintf_entry(browser, h, fp);
1175 nd = hists__filter_entries(rb_next(nd), browser->min_pcnt);
1181 static int hist_browser__dump(struct hist_browser *browser)
1187 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
1188 if (access(filename, F_OK))
1191 * XXX: Just an arbitrary lazy upper limit
1193 if (++browser->print_seq == 8192) {
1194 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1199 fp = fopen(filename, "w");
1202 const char *err = strerror_r(errno, bf, sizeof(bf));
1203 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
1207 ++browser->print_seq;
1208 hist_browser__fprintf(browser, fp);
1210 ui_helpline__fpush("%s written!", filename);
1215 static struct hist_browser *hist_browser__new(struct hists *hists,
1216 struct hist_browser_timer *hbt,
1217 struct perf_env *env)
1219 struct hist_browser *browser = zalloc(sizeof(*browser));
1222 browser->hists = hists;
1223 browser->b.refresh = hist_browser__refresh;
1224 browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
1225 browser->b.seek = ui_browser__hists_seek;
1226 browser->b.use_navkeypressed = true;
1227 browser->show_headers = symbol_conf.show_hist_headers;
1235 static void hist_browser__delete(struct hist_browser *browser)
1240 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
1242 return browser->he_selection;
1245 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
1247 return browser->he_selection->thread;
1250 /* Check whether the browser is for 'top' or 'report' */
1251 static inline bool is_report_browser(void *timer)
1253 return timer == NULL;
1256 static int hists__browser_title(struct hists *hists,
1257 struct hist_browser_timer *hbt,
1258 char *bf, size_t size)
1262 const struct dso *dso = hists->dso_filter;
1263 const struct thread *thread = hists->thread_filter;
1264 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1265 u64 nr_events = hists->stats.total_period;
1266 struct perf_evsel *evsel = hists_to_evsel(hists);
1267 const char *ev_name = perf_evsel__name(evsel);
1269 size_t buflen = sizeof(buf);
1270 char ref[30] = " show reference callgraph, ";
1271 bool enable_ref = false;
1273 if (symbol_conf.filter_relative) {
1274 nr_samples = hists->stats.nr_non_filtered_samples;
1275 nr_events = hists->stats.total_non_filtered_period;
1278 if (perf_evsel__is_group_event(evsel)) {
1279 struct perf_evsel *pos;
1281 perf_evsel__group_desc(evsel, buf, buflen);
1284 for_each_group_member(pos, evsel) {
1285 struct hists *pos_hists = evsel__hists(pos);
1287 if (symbol_conf.filter_relative) {
1288 nr_samples += pos_hists->stats.nr_non_filtered_samples;
1289 nr_events += pos_hists->stats.total_non_filtered_period;
1291 nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
1292 nr_events += pos_hists->stats.total_period;
1297 if (symbol_conf.show_ref_callgraph &&
1298 strstr(ev_name, "call-graph=no"))
1300 nr_samples = convert_unit(nr_samples, &unit);
1301 printed = scnprintf(bf, size,
1302 "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64,
1303 nr_samples, unit, ev_name, enable_ref ? ref : " ", nr_events);
1306 if (hists->uid_filter_str)
1307 printed += snprintf(bf + printed, size - printed,
1308 ", UID: %s", hists->uid_filter_str);
1310 printed += scnprintf(bf + printed, size - printed,
1312 (thread->comm_set ? thread__comm_str(thread) : ""),
1315 printed += scnprintf(bf + printed, size - printed,
1316 ", DSO: %s", dso->short_name);
1317 if (!is_report_browser(hbt)) {
1318 struct perf_top *top = hbt->arg;
1321 printed += scnprintf(bf + printed, size - printed, " [z]");
1327 static inline void free_popup_options(char **options, int n)
1331 for (i = 0; i < n; ++i)
1336 * Only runtime switching of perf data file will make "input_name" point
1337 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1338 * whether we need to call free() for current "input_name" during the switch.
1340 static bool is_input_name_malloced = false;
1342 static int switch_data_file(void)
1344 char *pwd, *options[32], *abs_path[32], *tmp;
1346 int nr_options = 0, choice = -1, ret = -1;
1347 struct dirent *dent;
1349 pwd = getenv("PWD");
1353 pwd_dir = opendir(pwd);
1357 memset(options, 0, sizeof(options));
1358 memset(options, 0, sizeof(abs_path));
1360 while ((dent = readdir(pwd_dir))) {
1361 char path[PATH_MAX];
1363 char *name = dent->d_name;
1366 if (!(dent->d_type == DT_REG))
1369 snprintf(path, sizeof(path), "%s/%s", pwd, name);
1371 file = fopen(path, "r");
1375 if (fread(&magic, 1, 8, file) < 8)
1376 goto close_file_and_continue;
1378 if (is_perf_magic(magic)) {
1379 options[nr_options] = strdup(name);
1380 if (!options[nr_options])
1381 goto close_file_and_continue;
1383 abs_path[nr_options] = strdup(path);
1384 if (!abs_path[nr_options]) {
1385 zfree(&options[nr_options]);
1386 ui__warning("Can't search all data files due to memory shortage.\n");
1394 close_file_and_continue:
1396 if (nr_options >= 32) {
1397 ui__warning("Too many perf data files in PWD!\n"
1398 "Only the first 32 files will be listed.\n");
1405 choice = ui__popup_menu(nr_options, options);
1406 if (choice < nr_options && choice >= 0) {
1407 tmp = strdup(abs_path[choice]);
1409 if (is_input_name_malloced)
1410 free((void *)input_name);
1412 is_input_name_malloced = true;
1415 ui__warning("Data switch failed due to memory shortage!\n");
1419 free_popup_options(options, nr_options);
1420 free_popup_options(abs_path, nr_options);
1424 struct popup_action {
1425 struct thread *thread;
1427 struct map_symbol ms;
1429 int (*fn)(struct hist_browser *browser, struct popup_action *act);
1433 do_annotate(struct hist_browser *browser, struct popup_action *act)
1435 struct perf_evsel *evsel;
1436 struct annotation *notes;
1437 struct hist_entry *he;
1440 if (!objdump_path && perf_session_env__lookup_objdump(browser->env))
1443 notes = symbol__annotation(act->ms.sym);
1447 evsel = hists_to_evsel(browser->hists);
1448 err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
1449 he = hist_browser__selected_entry(browser);
1451 * offer option to annotate the other branch source or target
1452 * (if they exists) when returning from annotate
1454 if ((err == 'q' || err == CTRL('c')) && he->branch_info)
1457 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1459 ui_browser__handle_resize(&browser->b);
1464 add_annotate_opt(struct hist_browser *browser __maybe_unused,
1465 struct popup_action *act, char **optstr,
1466 struct map *map, struct symbol *sym)
1468 if (sym == NULL || map->dso->annotate_warned)
1471 if (asprintf(optstr, "Annotate %s", sym->name) < 0)
1476 act->fn = do_annotate;
1481 do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
1483 struct thread *thread = act->thread;
1485 if (browser->hists->thread_filter) {
1486 pstack__remove(browser->pstack, &browser->hists->thread_filter);
1487 perf_hpp__set_elide(HISTC_THREAD, false);
1488 thread__zput(browser->hists->thread_filter);
1491 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1492 thread->comm_set ? thread__comm_str(thread) : "",
1494 browser->hists->thread_filter = thread__get(thread);
1495 perf_hpp__set_elide(HISTC_THREAD, false);
1496 pstack__push(browser->pstack, &browser->hists->thread_filter);
1499 hists__filter_by_thread(browser->hists);
1500 hist_browser__reset(browser);
1505 add_thread_opt(struct hist_browser *browser, struct popup_action *act,
1506 char **optstr, struct thread *thread)
1511 if (asprintf(optstr, "Zoom %s %s(%d) thread",
1512 browser->hists->thread_filter ? "out of" : "into",
1513 thread->comm_set ? thread__comm_str(thread) : "",
1517 act->thread = thread;
1518 act->fn = do_zoom_thread;
1523 do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
1525 struct dso *dso = act->dso;
1527 if (browser->hists->dso_filter) {
1528 pstack__remove(browser->pstack, &browser->hists->dso_filter);
1529 perf_hpp__set_elide(HISTC_DSO, false);
1530 browser->hists->dso_filter = NULL;
1535 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1536 dso->kernel ? "the Kernel" : dso->short_name);
1537 browser->hists->dso_filter = dso;
1538 perf_hpp__set_elide(HISTC_DSO, true);
1539 pstack__push(browser->pstack, &browser->hists->dso_filter);
1542 hists__filter_by_dso(browser->hists);
1543 hist_browser__reset(browser);
1548 add_dso_opt(struct hist_browser *browser, struct popup_action *act,
1549 char **optstr, struct dso *dso)
1554 if (asprintf(optstr, "Zoom %s %s DSO",
1555 browser->hists->dso_filter ? "out of" : "into",
1556 dso->kernel ? "the Kernel" : dso->short_name) < 0)
1560 act->fn = do_zoom_dso;
1565 do_browse_map(struct hist_browser *browser __maybe_unused,
1566 struct popup_action *act)
1568 map__browse(act->ms.map);
1573 add_map_opt(struct hist_browser *browser __maybe_unused,
1574 struct popup_action *act, char **optstr, struct map *map)
1579 if (asprintf(optstr, "Browse map details") < 0)
1583 act->fn = do_browse_map;
1588 do_run_script(struct hist_browser *browser __maybe_unused,
1589 struct popup_action *act)
1591 char script_opt[64];
1592 memset(script_opt, 0, sizeof(script_opt));
1595 scnprintf(script_opt, sizeof(script_opt), " -c %s ",
1596 thread__comm_str(act->thread));
1597 } else if (act->ms.sym) {
1598 scnprintf(script_opt, sizeof(script_opt), " -S %s ",
1602 script_browse(script_opt);
1607 add_script_opt(struct hist_browser *browser __maybe_unused,
1608 struct popup_action *act, char **optstr,
1609 struct thread *thread, struct symbol *sym)
1612 if (asprintf(optstr, "Run scripts for samples of thread [%s]",
1613 thread__comm_str(thread)) < 0)
1616 if (asprintf(optstr, "Run scripts for samples of symbol [%s]",
1620 if (asprintf(optstr, "Run scripts for all samples") < 0)
1624 act->thread = thread;
1626 act->fn = do_run_script;
1631 do_switch_data(struct hist_browser *browser __maybe_unused,
1632 struct popup_action *act __maybe_unused)
1634 if (switch_data_file()) {
1635 ui__warning("Won't switch the data files due to\n"
1636 "no valid data file get selected!\n");
1640 return K_SWITCH_INPUT_DATA;
1644 add_switch_opt(struct hist_browser *browser,
1645 struct popup_action *act, char **optstr)
1647 if (!is_report_browser(browser->hbt))
1650 if (asprintf(optstr, "Switch to another data file in PWD") < 0)
1653 act->fn = do_switch_data;
1658 do_exit_browser(struct hist_browser *browser __maybe_unused,
1659 struct popup_action *act __maybe_unused)
1665 add_exit_opt(struct hist_browser *browser __maybe_unused,
1666 struct popup_action *act, char **optstr)
1668 if (asprintf(optstr, "Exit") < 0)
1671 act->fn = do_exit_browser;
1675 static void hist_browser__update_nr_entries(struct hist_browser *hb)
1678 struct rb_node *nd = rb_first(&hb->hists->entries);
1680 if (hb->min_pcnt == 0) {
1681 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
1685 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
1690 hb->nr_non_filtered_entries = nr_entries;
1693 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
1694 const char *helpline,
1696 struct hist_browser_timer *hbt,
1698 struct perf_env *env)
1700 struct hists *hists = evsel__hists(evsel);
1701 struct hist_browser *browser = hist_browser__new(hists, hbt, env);
1702 struct branch_info *bi;
1703 #define MAX_OPTIONS 16
1704 char *options[MAX_OPTIONS];
1705 struct popup_action actions[MAX_OPTIONS];
1709 int delay_secs = hbt ? hbt->refresh : 0;
1710 struct perf_hpp_fmt *fmt;
1712 #define HIST_BROWSER_HELP_COMMON \
1713 "h/?/F1 Show this window\n" \
1715 "PGDN/SPACE Navigate\n" \
1716 "q/ESC/CTRL+C Exit browser\n\n" \
1717 "For multiple event sessions:\n\n" \
1718 "TAB/UNTAB Switch events\n\n" \
1719 "For symbolic views (--sort has sym):\n\n" \
1720 "-> Zoom into DSO/Threads & Annotate current symbol\n" \
1722 "a Annotate current symbol\n" \
1723 "C Collapse all callchains\n" \
1724 "d Zoom into current DSO\n" \
1725 "E Expand all callchains\n" \
1726 "F Toggle percentage of filtered entries\n" \
1727 "H Display column headers\n" \
1729 /* help messages are sorted by lexical order of the hotkey */
1730 const char report_help[] = HIST_BROWSER_HELP_COMMON
1731 "i Show header information\n"
1732 "P Print histograms to perf.hist.N\n"
1733 "r Run available scripts\n"
1734 "s Switch to another data file in PWD\n"
1735 "t Zoom into current Thread\n"
1736 "V Verbose (DSO names in callchains, etc)\n"
1737 "/ Filter symbol by name";
1738 const char top_help[] = HIST_BROWSER_HELP_COMMON
1739 "P Print histograms to perf.hist.N\n"
1740 "t Zoom into current Thread\n"
1741 "V Verbose (DSO names in callchains, etc)\n"
1742 "z Toggle zeroing of samples\n"
1743 "f Enable/Disable events\n"
1744 "/ Filter symbol by name";
1746 if (browser == NULL)
1749 /* reset abort key so that it can get Ctrl-C as a key */
1751 SLang_init_tty(0, 0, 0);
1754 browser->min_pcnt = min_pcnt;
1755 hist_browser__update_nr_entries(browser);
1758 browser->pstack = pstack__new(2);
1759 if (browser->pstack == NULL)
1762 ui_helpline__push(helpline);
1764 memset(options, 0, sizeof(options));
1765 memset(actions, 0, sizeof(actions));
1767 perf_hpp__for_each_format(fmt)
1768 perf_hpp__reset_width(fmt, hists);
1770 if (symbol_conf.col_width_list_str)
1771 perf_hpp__set_user_width(symbol_conf.col_width_list_str);
1774 struct thread *thread = NULL;
1775 struct dso *dso = NULL;
1780 key = hist_browser__run(browser, helpline);
1782 if (browser->he_selection != NULL) {
1783 thread = hist_browser__selected_thread(browser);
1784 dso = browser->selection->map ? browser->selection->map->dso : NULL;
1792 * Exit the browser, let hists__browser_tree
1793 * go to the next or previous
1795 goto out_free_stack;
1797 if (!sort__has_sym) {
1798 ui_browser__warning(&browser->b, delay_secs * 2,
1799 "Annotation is only available for symbolic views, "
1800 "include \"sym*\" in --sort to use it.");
1804 if (browser->selection == NULL ||
1805 browser->selection->sym == NULL ||
1806 browser->selection->map->dso->annotate_warned)
1809 actions->ms.map = browser->selection->map;
1810 actions->ms.sym = browser->selection->sym;
1811 do_annotate(browser, actions);
1814 hist_browser__dump(browser);
1818 do_zoom_dso(browser, actions);
1821 browser->show_dso = !browser->show_dso;
1824 actions->thread = thread;
1825 do_zoom_thread(browser, actions);
1828 if (ui_browser__input_window("Symbol to show",
1829 "Please enter the name of symbol you want to see",
1830 buf, "ENTER: OK, ESC: Cancel",
1831 delay_secs * 2) == K_ENTER) {
1832 hists->symbol_filter_str = *buf ? buf : NULL;
1833 hists__filter_by_symbol(hists);
1834 hist_browser__reset(browser);
1838 if (is_report_browser(hbt)) {
1839 actions->thread = NULL;
1840 actions->ms.sym = NULL;
1841 do_run_script(browser, actions);
1845 if (is_report_browser(hbt)) {
1846 key = do_switch_data(browser, actions);
1847 if (key == K_SWITCH_INPUT_DATA)
1848 goto out_free_stack;
1852 /* env->arch is NULL for live-mode (i.e. perf top) */
1854 tui__header_window(env);
1857 symbol_conf.filter_relative ^= 1;
1860 if (!is_report_browser(hbt)) {
1861 struct perf_top *top = hbt->arg;
1863 top->zero = !top->zero;
1869 ui_browser__help_window(&browser->b,
1870 is_report_browser(hbt) ? report_help : top_help);
1880 if (pstack__empty(browser->pstack)) {
1882 * Go back to the perf_evsel_menu__run or other user
1885 goto out_free_stack;
1888 ui_browser__dialog_yesno(&browser->b,
1889 "Do you really want to exit?"))
1890 goto out_free_stack;
1894 top = pstack__peek(browser->pstack);
1895 if (top == &browser->hists->dso_filter) {
1897 * No need to set actions->dso here since
1898 * it's just to remove the current filter.
1899 * Ditto for thread below.
1901 do_zoom_dso(browser, actions);
1903 if (top == &browser->hists->thread_filter)
1904 do_zoom_thread(browser, actions);
1909 goto out_free_stack;
1911 if (!is_report_browser(hbt)) {
1912 struct perf_top *top = hbt->arg;
1914 perf_evlist__toggle_enable(top->evlist);
1916 * No need to refresh, resort/decay histogram
1917 * entries if we are not collecting samples:
1919 if (top->evlist->enabled) {
1920 helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
1921 hbt->refresh = delay_secs;
1923 helpline = "Press 'f' again to re-enable the events";
1930 helpline = "Press '?' for help on key bindings";
1935 goto add_exit_option;
1937 if (browser->selection == NULL)
1938 goto skip_annotation;
1940 if (sort__mode == SORT_MODE__BRANCH) {
1941 bi = browser->he_selection->branch_info;
1944 goto skip_annotation;
1946 nr_options += add_annotate_opt(browser,
1947 &actions[nr_options],
1948 &options[nr_options],
1951 if (bi->to.sym != bi->from.sym)
1952 nr_options += add_annotate_opt(browser,
1953 &actions[nr_options],
1954 &options[nr_options],
1958 nr_options += add_annotate_opt(browser,
1959 &actions[nr_options],
1960 &options[nr_options],
1961 browser->selection->map,
1962 browser->selection->sym);
1965 nr_options += add_thread_opt(browser, &actions[nr_options],
1966 &options[nr_options], thread);
1967 nr_options += add_dso_opt(browser, &actions[nr_options],
1968 &options[nr_options], dso);
1969 nr_options += add_map_opt(browser, &actions[nr_options],
1970 &options[nr_options],
1971 browser->selection->map);
1973 /* perf script support */
1974 if (browser->he_selection) {
1975 nr_options += add_script_opt(browser,
1976 &actions[nr_options],
1977 &options[nr_options],
1979 nr_options += add_script_opt(browser,
1980 &actions[nr_options],
1981 &options[nr_options],
1982 NULL, browser->selection->sym);
1984 nr_options += add_script_opt(browser, &actions[nr_options],
1985 &options[nr_options], NULL, NULL);
1986 nr_options += add_switch_opt(browser, &actions[nr_options],
1987 &options[nr_options]);
1989 nr_options += add_exit_opt(browser, &actions[nr_options],
1990 &options[nr_options]);
1993 struct popup_action *act;
1995 choice = ui__popup_menu(nr_options, options);
1996 if (choice == -1 || choice >= nr_options)
1999 act = &actions[choice];
2000 key = act->fn(browser, act);
2003 if (key == K_SWITCH_INPUT_DATA)
2007 pstack__delete(browser->pstack);
2009 hist_browser__delete(browser);
2010 free_popup_options(options, MAX_OPTIONS);
2014 struct perf_evsel_menu {
2015 struct ui_browser b;
2016 struct perf_evsel *selection;
2017 bool lost_events, lost_events_warned;
2019 struct perf_env *env;
2022 static void perf_evsel_menu__write(struct ui_browser *browser,
2023 void *entry, int row)
2025 struct perf_evsel_menu *menu = container_of(browser,
2026 struct perf_evsel_menu, b);
2027 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
2028 struct hists *hists = evsel__hists(evsel);
2029 bool current_entry = ui_browser__is_current_entry(browser, row);
2030 unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
2031 const char *ev_name = perf_evsel__name(evsel);
2033 const char *warn = " ";
2036 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
2037 HE_COLORSET_NORMAL);
2039 if (perf_evsel__is_group_event(evsel)) {
2040 struct perf_evsel *pos;
2042 ev_name = perf_evsel__group_name(evsel);
2044 for_each_group_member(pos, evsel) {
2045 struct hists *pos_hists = evsel__hists(pos);
2046 nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
2050 nr_events = convert_unit(nr_events, &unit);
2051 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
2052 unit, unit == ' ' ? "" : " ", ev_name);
2053 ui_browser__printf(browser, "%s", bf);
2055 nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
2056 if (nr_events != 0) {
2057 menu->lost_events = true;
2059 ui_browser__set_color(browser, HE_COLORSET_TOP);
2060 nr_events = convert_unit(nr_events, &unit);
2061 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
2062 nr_events, unit, unit == ' ' ? "" : " ");
2066 ui_browser__write_nstring(browser, warn, browser->width - printed);
2069 menu->selection = evsel;
2072 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
2073 int nr_events, const char *help,
2074 struct hist_browser_timer *hbt)
2076 struct perf_evlist *evlist = menu->b.priv;
2077 struct perf_evsel *pos;
2078 const char *title = "Available samples";
2079 int delay_secs = hbt ? hbt->refresh : 0;
2082 if (ui_browser__show(&menu->b, title,
2083 "ESC: exit, ENTER|->: Browse histograms") < 0)
2087 key = ui_browser__run(&menu->b, delay_secs);
2091 hbt->timer(hbt->arg);
2093 if (!menu->lost_events_warned && menu->lost_events) {
2094 ui_browser__warn_lost_events(&menu->b);
2095 menu->lost_events_warned = true;
2100 if (!menu->selection)
2102 pos = menu->selection;
2104 perf_evlist__set_selected(evlist, pos);
2106 * Give the calling tool a chance to populate the non
2107 * default evsel resorted hists tree.
2110 hbt->timer(hbt->arg);
2111 key = perf_evsel__hists_browse(pos, nr_events, help,
2115 ui_browser__show_title(&menu->b, title);
2118 if (pos->node.next == &evlist->entries)
2119 pos = perf_evlist__first(evlist);
2121 pos = perf_evsel__next(pos);
2124 if (pos->node.prev == &evlist->entries)
2125 pos = perf_evlist__last(evlist);
2127 pos = perf_evsel__prev(pos);
2129 case K_SWITCH_INPUT_DATA:
2140 if (!ui_browser__dialog_yesno(&menu->b,
2141 "Do you really want to exit?"))
2153 ui_browser__hide(&menu->b);
2157 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
2160 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
2162 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
2168 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
2169 int nr_entries, const char *help,
2170 struct hist_browser_timer *hbt,
2172 struct perf_env *env)
2174 struct perf_evsel *pos;
2175 struct perf_evsel_menu menu = {
2177 .entries = &evlist->entries,
2178 .refresh = ui_browser__list_head_refresh,
2179 .seek = ui_browser__list_head_seek,
2180 .write = perf_evsel_menu__write,
2181 .filter = filter_group_entries,
2182 .nr_entries = nr_entries,
2185 .min_pcnt = min_pcnt,
2189 ui_helpline__push("Press ESC to exit");
2191 evlist__for_each(evlist, pos) {
2192 const char *ev_name = perf_evsel__name(pos);
2193 size_t line_len = strlen(ev_name) + 7;
2195 if (menu.b.width < line_len)
2196 menu.b.width = line_len;
2199 return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
2202 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
2203 struct hist_browser_timer *hbt,
2205 struct perf_env *env)
2207 int nr_entries = evlist->nr_entries;
2210 if (nr_entries == 1) {
2211 struct perf_evsel *first = perf_evlist__first(evlist);
2213 return perf_evsel__hists_browse(first, nr_entries, help,
2214 false, hbt, min_pcnt,
2218 if (symbol_conf.event_group) {
2219 struct perf_evsel *pos;
2222 evlist__for_each(evlist, pos) {
2223 if (perf_evsel__is_group_leader(pos))
2227 if (nr_entries == 1)
2231 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
2232 hbt, min_pcnt, env);