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 int socket_id = hists->socket_filter;
1265 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1266 u64 nr_events = hists->stats.total_period;
1267 struct perf_evsel *evsel = hists_to_evsel(hists);
1268 const char *ev_name = perf_evsel__name(evsel);
1270 size_t buflen = sizeof(buf);
1271 char ref[30] = " show reference callgraph, ";
1272 bool enable_ref = false;
1274 if (symbol_conf.filter_relative) {
1275 nr_samples = hists->stats.nr_non_filtered_samples;
1276 nr_events = hists->stats.total_non_filtered_period;
1279 if (perf_evsel__is_group_event(evsel)) {
1280 struct perf_evsel *pos;
1282 perf_evsel__group_desc(evsel, buf, buflen);
1285 for_each_group_member(pos, evsel) {
1286 struct hists *pos_hists = evsel__hists(pos);
1288 if (symbol_conf.filter_relative) {
1289 nr_samples += pos_hists->stats.nr_non_filtered_samples;
1290 nr_events += pos_hists->stats.total_non_filtered_period;
1292 nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
1293 nr_events += pos_hists->stats.total_period;
1298 if (symbol_conf.show_ref_callgraph &&
1299 strstr(ev_name, "call-graph=no"))
1301 nr_samples = convert_unit(nr_samples, &unit);
1302 printed = scnprintf(bf, size,
1303 "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64,
1304 nr_samples, unit, ev_name, enable_ref ? ref : " ", nr_events);
1307 if (hists->uid_filter_str)
1308 printed += snprintf(bf + printed, size - printed,
1309 ", UID: %s", hists->uid_filter_str);
1311 printed += scnprintf(bf + printed, size - printed,
1313 (thread->comm_set ? thread__comm_str(thread) : ""),
1316 printed += scnprintf(bf + printed, size - printed,
1317 ", DSO: %s", dso->short_name);
1319 printed += scnprintf(bf + printed, size - printed,
1320 ", Processor Socket: %d", socket_id);
1321 if (!is_report_browser(hbt)) {
1322 struct perf_top *top = hbt->arg;
1325 printed += scnprintf(bf + printed, size - printed, " [z]");
1331 static inline void free_popup_options(char **options, int n)
1335 for (i = 0; i < n; ++i)
1340 * Only runtime switching of perf data file will make "input_name" point
1341 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1342 * whether we need to call free() for current "input_name" during the switch.
1344 static bool is_input_name_malloced = false;
1346 static int switch_data_file(void)
1348 char *pwd, *options[32], *abs_path[32], *tmp;
1350 int nr_options = 0, choice = -1, ret = -1;
1351 struct dirent *dent;
1353 pwd = getenv("PWD");
1357 pwd_dir = opendir(pwd);
1361 memset(options, 0, sizeof(options));
1362 memset(options, 0, sizeof(abs_path));
1364 while ((dent = readdir(pwd_dir))) {
1365 char path[PATH_MAX];
1367 char *name = dent->d_name;
1370 if (!(dent->d_type == DT_REG))
1373 snprintf(path, sizeof(path), "%s/%s", pwd, name);
1375 file = fopen(path, "r");
1379 if (fread(&magic, 1, 8, file) < 8)
1380 goto close_file_and_continue;
1382 if (is_perf_magic(magic)) {
1383 options[nr_options] = strdup(name);
1384 if (!options[nr_options])
1385 goto close_file_and_continue;
1387 abs_path[nr_options] = strdup(path);
1388 if (!abs_path[nr_options]) {
1389 zfree(&options[nr_options]);
1390 ui__warning("Can't search all data files due to memory shortage.\n");
1398 close_file_and_continue:
1400 if (nr_options >= 32) {
1401 ui__warning("Too many perf data files in PWD!\n"
1402 "Only the first 32 files will be listed.\n");
1409 choice = ui__popup_menu(nr_options, options);
1410 if (choice < nr_options && choice >= 0) {
1411 tmp = strdup(abs_path[choice]);
1413 if (is_input_name_malloced)
1414 free((void *)input_name);
1416 is_input_name_malloced = true;
1419 ui__warning("Data switch failed due to memory shortage!\n");
1423 free_popup_options(options, nr_options);
1424 free_popup_options(abs_path, nr_options);
1428 struct popup_action {
1429 struct thread *thread;
1431 struct map_symbol ms;
1434 int (*fn)(struct hist_browser *browser, struct popup_action *act);
1438 do_annotate(struct hist_browser *browser, struct popup_action *act)
1440 struct perf_evsel *evsel;
1441 struct annotation *notes;
1442 struct hist_entry *he;
1445 if (!objdump_path && perf_env__lookup_objdump(browser->env))
1448 notes = symbol__annotation(act->ms.sym);
1452 evsel = hists_to_evsel(browser->hists);
1453 err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
1454 he = hist_browser__selected_entry(browser);
1456 * offer option to annotate the other branch source or target
1457 * (if they exists) when returning from annotate
1459 if ((err == 'q' || err == CTRL('c')) && he->branch_info)
1462 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1464 ui_browser__handle_resize(&browser->b);
1469 add_annotate_opt(struct hist_browser *browser __maybe_unused,
1470 struct popup_action *act, char **optstr,
1471 struct map *map, struct symbol *sym)
1473 if (sym == NULL || map->dso->annotate_warned)
1476 if (asprintf(optstr, "Annotate %s", sym->name) < 0)
1481 act->fn = do_annotate;
1486 do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
1488 struct thread *thread = act->thread;
1490 if (browser->hists->thread_filter) {
1491 pstack__remove(browser->pstack, &browser->hists->thread_filter);
1492 perf_hpp__set_elide(HISTC_THREAD, false);
1493 thread__zput(browser->hists->thread_filter);
1496 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1497 thread->comm_set ? thread__comm_str(thread) : "",
1499 browser->hists->thread_filter = thread__get(thread);
1500 perf_hpp__set_elide(HISTC_THREAD, false);
1501 pstack__push(browser->pstack, &browser->hists->thread_filter);
1504 hists__filter_by_thread(browser->hists);
1505 hist_browser__reset(browser);
1510 add_thread_opt(struct hist_browser *browser, struct popup_action *act,
1511 char **optstr, struct thread *thread)
1516 if (asprintf(optstr, "Zoom %s %s(%d) thread",
1517 browser->hists->thread_filter ? "out of" : "into",
1518 thread->comm_set ? thread__comm_str(thread) : "",
1522 act->thread = thread;
1523 act->fn = do_zoom_thread;
1528 do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
1530 struct dso *dso = act->dso;
1532 if (browser->hists->dso_filter) {
1533 pstack__remove(browser->pstack, &browser->hists->dso_filter);
1534 perf_hpp__set_elide(HISTC_DSO, false);
1535 browser->hists->dso_filter = NULL;
1540 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1541 dso->kernel ? "the Kernel" : dso->short_name);
1542 browser->hists->dso_filter = dso;
1543 perf_hpp__set_elide(HISTC_DSO, true);
1544 pstack__push(browser->pstack, &browser->hists->dso_filter);
1547 hists__filter_by_dso(browser->hists);
1548 hist_browser__reset(browser);
1553 add_dso_opt(struct hist_browser *browser, struct popup_action *act,
1554 char **optstr, struct dso *dso)
1559 if (asprintf(optstr, "Zoom %s %s DSO",
1560 browser->hists->dso_filter ? "out of" : "into",
1561 dso->kernel ? "the Kernel" : dso->short_name) < 0)
1565 act->fn = do_zoom_dso;
1570 do_browse_map(struct hist_browser *browser __maybe_unused,
1571 struct popup_action *act)
1573 map__browse(act->ms.map);
1578 add_map_opt(struct hist_browser *browser __maybe_unused,
1579 struct popup_action *act, char **optstr, struct map *map)
1584 if (asprintf(optstr, "Browse map details") < 0)
1588 act->fn = do_browse_map;
1593 do_run_script(struct hist_browser *browser __maybe_unused,
1594 struct popup_action *act)
1596 char script_opt[64];
1597 memset(script_opt, 0, sizeof(script_opt));
1600 scnprintf(script_opt, sizeof(script_opt), " -c %s ",
1601 thread__comm_str(act->thread));
1602 } else if (act->ms.sym) {
1603 scnprintf(script_opt, sizeof(script_opt), " -S %s ",
1607 script_browse(script_opt);
1612 add_script_opt(struct hist_browser *browser __maybe_unused,
1613 struct popup_action *act, char **optstr,
1614 struct thread *thread, struct symbol *sym)
1617 if (asprintf(optstr, "Run scripts for samples of thread [%s]",
1618 thread__comm_str(thread)) < 0)
1621 if (asprintf(optstr, "Run scripts for samples of symbol [%s]",
1625 if (asprintf(optstr, "Run scripts for all samples") < 0)
1629 act->thread = thread;
1631 act->fn = do_run_script;
1636 do_switch_data(struct hist_browser *browser __maybe_unused,
1637 struct popup_action *act __maybe_unused)
1639 if (switch_data_file()) {
1640 ui__warning("Won't switch the data files due to\n"
1641 "no valid data file get selected!\n");
1645 return K_SWITCH_INPUT_DATA;
1649 add_switch_opt(struct hist_browser *browser,
1650 struct popup_action *act, char **optstr)
1652 if (!is_report_browser(browser->hbt))
1655 if (asprintf(optstr, "Switch to another data file in PWD") < 0)
1658 act->fn = do_switch_data;
1663 do_exit_browser(struct hist_browser *browser __maybe_unused,
1664 struct popup_action *act __maybe_unused)
1670 add_exit_opt(struct hist_browser *browser __maybe_unused,
1671 struct popup_action *act, char **optstr)
1673 if (asprintf(optstr, "Exit") < 0)
1676 act->fn = do_exit_browser;
1681 do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
1683 if (browser->hists->socket_filter > -1) {
1684 pstack__remove(browser->pstack, &browser->hists->socket_filter);
1685 browser->hists->socket_filter = -1;
1686 perf_hpp__set_elide(HISTC_SOCKET, false);
1688 browser->hists->socket_filter = act->socket;
1689 perf_hpp__set_elide(HISTC_SOCKET, true);
1690 pstack__push(browser->pstack, &browser->hists->socket_filter);
1693 hists__filter_by_socket(browser->hists);
1694 hist_browser__reset(browser);
1699 add_socket_opt(struct hist_browser *browser, struct popup_action *act,
1700 char **optstr, int socket_id)
1705 if (asprintf(optstr, "Zoom %s Processor Socket %d",
1706 (browser->hists->socket_filter > -1) ? "out of" : "into",
1710 act->socket = socket_id;
1711 act->fn = do_zoom_socket;
1715 static void hist_browser__update_nr_entries(struct hist_browser *hb)
1718 struct rb_node *nd = rb_first(&hb->hists->entries);
1720 if (hb->min_pcnt == 0) {
1721 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
1725 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
1730 hb->nr_non_filtered_entries = nr_entries;
1733 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
1734 const char *helpline,
1736 struct hist_browser_timer *hbt,
1738 struct perf_env *env)
1740 struct hists *hists = evsel__hists(evsel);
1741 struct hist_browser *browser = hist_browser__new(hists, hbt, env);
1742 struct branch_info *bi;
1743 #define MAX_OPTIONS 16
1744 char *options[MAX_OPTIONS];
1745 struct popup_action actions[MAX_OPTIONS];
1749 int delay_secs = hbt ? hbt->refresh : 0;
1750 struct perf_hpp_fmt *fmt;
1752 #define HIST_BROWSER_HELP_COMMON \
1753 "h/?/F1 Show this window\n" \
1755 "PGDN/SPACE Navigate\n" \
1756 "q/ESC/CTRL+C Exit browser\n\n" \
1757 "For multiple event sessions:\n\n" \
1758 "TAB/UNTAB Switch events\n\n" \
1759 "For symbolic views (--sort has sym):\n\n" \
1760 "-> Zoom into DSO/Threads & Annotate current symbol\n" \
1762 "a Annotate current symbol\n" \
1763 "C Collapse all callchains\n" \
1764 "d Zoom into current DSO\n" \
1765 "E Expand all callchains\n" \
1766 "F Toggle percentage of filtered entries\n" \
1767 "H Display column headers\n" \
1768 "S Zoom into current Processor Socket\n" \
1770 /* help messages are sorted by lexical order of the hotkey */
1771 const char report_help[] = HIST_BROWSER_HELP_COMMON
1772 "i Show header information\n"
1773 "P Print histograms to perf.hist.N\n"
1774 "r Run available scripts\n"
1775 "s Switch to another data file in PWD\n"
1776 "t Zoom into current Thread\n"
1777 "V Verbose (DSO names in callchains, etc)\n"
1778 "/ Filter symbol by name";
1779 const char top_help[] = HIST_BROWSER_HELP_COMMON
1780 "P Print histograms to perf.hist.N\n"
1781 "t Zoom into current Thread\n"
1782 "V Verbose (DSO names in callchains, etc)\n"
1783 "z Toggle zeroing of samples\n"
1784 "f Enable/Disable events\n"
1785 "/ Filter symbol by name";
1787 if (browser == NULL)
1790 /* reset abort key so that it can get Ctrl-C as a key */
1792 SLang_init_tty(0, 0, 0);
1795 browser->min_pcnt = min_pcnt;
1796 hist_browser__update_nr_entries(browser);
1799 browser->pstack = pstack__new(3);
1800 if (browser->pstack == NULL)
1803 ui_helpline__push(helpline);
1805 memset(options, 0, sizeof(options));
1806 memset(actions, 0, sizeof(actions));
1808 perf_hpp__for_each_format(fmt)
1809 perf_hpp__reset_width(fmt, hists);
1811 if (symbol_conf.col_width_list_str)
1812 perf_hpp__set_user_width(symbol_conf.col_width_list_str);
1815 struct thread *thread = NULL;
1816 struct dso *dso = NULL;
1822 key = hist_browser__run(browser, helpline);
1824 if (browser->he_selection != NULL) {
1825 thread = hist_browser__selected_thread(browser);
1826 dso = browser->selection->map ? browser->selection->map->dso : NULL;
1827 socked_id = browser->he_selection->socket;
1835 * Exit the browser, let hists__browser_tree
1836 * go to the next or previous
1838 goto out_free_stack;
1840 if (!sort__has_sym) {
1841 ui_browser__warning(&browser->b, delay_secs * 2,
1842 "Annotation is only available for symbolic views, "
1843 "include \"sym*\" in --sort to use it.");
1847 if (browser->selection == NULL ||
1848 browser->selection->sym == NULL ||
1849 browser->selection->map->dso->annotate_warned)
1852 actions->ms.map = browser->selection->map;
1853 actions->ms.sym = browser->selection->sym;
1854 do_annotate(browser, actions);
1857 hist_browser__dump(browser);
1861 do_zoom_dso(browser, actions);
1864 browser->show_dso = !browser->show_dso;
1867 actions->thread = thread;
1868 do_zoom_thread(browser, actions);
1871 actions->socket = socked_id;
1872 do_zoom_socket(browser, actions);
1875 if (ui_browser__input_window("Symbol to show",
1876 "Please enter the name of symbol you want to see",
1877 buf, "ENTER: OK, ESC: Cancel",
1878 delay_secs * 2) == K_ENTER) {
1879 hists->symbol_filter_str = *buf ? buf : NULL;
1880 hists__filter_by_symbol(hists);
1881 hist_browser__reset(browser);
1885 if (is_report_browser(hbt)) {
1886 actions->thread = NULL;
1887 actions->ms.sym = NULL;
1888 do_run_script(browser, actions);
1892 if (is_report_browser(hbt)) {
1893 key = do_switch_data(browser, actions);
1894 if (key == K_SWITCH_INPUT_DATA)
1895 goto out_free_stack;
1899 /* env->arch is NULL for live-mode (i.e. perf top) */
1901 tui__header_window(env);
1904 symbol_conf.filter_relative ^= 1;
1907 if (!is_report_browser(hbt)) {
1908 struct perf_top *top = hbt->arg;
1910 top->zero = !top->zero;
1916 ui_browser__help_window(&browser->b,
1917 is_report_browser(hbt) ? report_help : top_help);
1927 if (pstack__empty(browser->pstack)) {
1929 * Go back to the perf_evsel_menu__run or other user
1932 goto out_free_stack;
1935 ui_browser__dialog_yesno(&browser->b,
1936 "Do you really want to exit?"))
1937 goto out_free_stack;
1941 top = pstack__peek(browser->pstack);
1942 if (top == &browser->hists->dso_filter) {
1944 * No need to set actions->dso here since
1945 * it's just to remove the current filter.
1946 * Ditto for thread below.
1948 do_zoom_dso(browser, actions);
1949 } else if (top == &browser->hists->thread_filter) {
1950 do_zoom_thread(browser, actions);
1951 } else if (top == &browser->hists->socket_filter) {
1952 do_zoom_socket(browser, actions);
1958 goto out_free_stack;
1960 if (!is_report_browser(hbt)) {
1961 struct perf_top *top = hbt->arg;
1963 perf_evlist__toggle_enable(top->evlist);
1965 * No need to refresh, resort/decay histogram
1966 * entries if we are not collecting samples:
1968 if (top->evlist->enabled) {
1969 helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
1970 hbt->refresh = delay_secs;
1972 helpline = "Press 'f' again to re-enable the events";
1979 helpline = "Press '?' for help on key bindings";
1984 goto add_exit_option;
1986 if (browser->selection == NULL)
1987 goto skip_annotation;
1989 if (sort__mode == SORT_MODE__BRANCH) {
1990 bi = browser->he_selection->branch_info;
1993 goto skip_annotation;
1995 nr_options += add_annotate_opt(browser,
1996 &actions[nr_options],
1997 &options[nr_options],
2000 if (bi->to.sym != bi->from.sym)
2001 nr_options += add_annotate_opt(browser,
2002 &actions[nr_options],
2003 &options[nr_options],
2007 nr_options += add_annotate_opt(browser,
2008 &actions[nr_options],
2009 &options[nr_options],
2010 browser->selection->map,
2011 browser->selection->sym);
2014 nr_options += add_thread_opt(browser, &actions[nr_options],
2015 &options[nr_options], thread);
2016 nr_options += add_dso_opt(browser, &actions[nr_options],
2017 &options[nr_options], dso);
2018 nr_options += add_map_opt(browser, &actions[nr_options],
2019 &options[nr_options],
2020 browser->selection->map);
2021 nr_options += add_socket_opt(browser, &actions[nr_options],
2022 &options[nr_options],
2024 /* perf script support */
2025 if (browser->he_selection) {
2026 nr_options += add_script_opt(browser,
2027 &actions[nr_options],
2028 &options[nr_options],
2030 nr_options += add_script_opt(browser,
2031 &actions[nr_options],
2032 &options[nr_options],
2033 NULL, browser->selection->sym);
2035 nr_options += add_script_opt(browser, &actions[nr_options],
2036 &options[nr_options], NULL, NULL);
2037 nr_options += add_switch_opt(browser, &actions[nr_options],
2038 &options[nr_options]);
2040 nr_options += add_exit_opt(browser, &actions[nr_options],
2041 &options[nr_options]);
2044 struct popup_action *act;
2046 choice = ui__popup_menu(nr_options, options);
2047 if (choice == -1 || choice >= nr_options)
2050 act = &actions[choice];
2051 key = act->fn(browser, act);
2054 if (key == K_SWITCH_INPUT_DATA)
2058 pstack__delete(browser->pstack);
2060 hist_browser__delete(browser);
2061 free_popup_options(options, MAX_OPTIONS);
2065 struct perf_evsel_menu {
2066 struct ui_browser b;
2067 struct perf_evsel *selection;
2068 bool lost_events, lost_events_warned;
2070 struct perf_env *env;
2073 static void perf_evsel_menu__write(struct ui_browser *browser,
2074 void *entry, int row)
2076 struct perf_evsel_menu *menu = container_of(browser,
2077 struct perf_evsel_menu, b);
2078 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
2079 struct hists *hists = evsel__hists(evsel);
2080 bool current_entry = ui_browser__is_current_entry(browser, row);
2081 unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
2082 const char *ev_name = perf_evsel__name(evsel);
2084 const char *warn = " ";
2087 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
2088 HE_COLORSET_NORMAL);
2090 if (perf_evsel__is_group_event(evsel)) {
2091 struct perf_evsel *pos;
2093 ev_name = perf_evsel__group_name(evsel);
2095 for_each_group_member(pos, evsel) {
2096 struct hists *pos_hists = evsel__hists(pos);
2097 nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
2101 nr_events = convert_unit(nr_events, &unit);
2102 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
2103 unit, unit == ' ' ? "" : " ", ev_name);
2104 ui_browser__printf(browser, "%s", bf);
2106 nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
2107 if (nr_events != 0) {
2108 menu->lost_events = true;
2110 ui_browser__set_color(browser, HE_COLORSET_TOP);
2111 nr_events = convert_unit(nr_events, &unit);
2112 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
2113 nr_events, unit, unit == ' ' ? "" : " ");
2117 ui_browser__write_nstring(browser, warn, browser->width - printed);
2120 menu->selection = evsel;
2123 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
2124 int nr_events, const char *help,
2125 struct hist_browser_timer *hbt)
2127 struct perf_evlist *evlist = menu->b.priv;
2128 struct perf_evsel *pos;
2129 const char *title = "Available samples";
2130 int delay_secs = hbt ? hbt->refresh : 0;
2133 if (ui_browser__show(&menu->b, title,
2134 "ESC: exit, ENTER|->: Browse histograms") < 0)
2138 key = ui_browser__run(&menu->b, delay_secs);
2142 hbt->timer(hbt->arg);
2144 if (!menu->lost_events_warned && menu->lost_events) {
2145 ui_browser__warn_lost_events(&menu->b);
2146 menu->lost_events_warned = true;
2151 if (!menu->selection)
2153 pos = menu->selection;
2155 perf_evlist__set_selected(evlist, pos);
2157 * Give the calling tool a chance to populate the non
2158 * default evsel resorted hists tree.
2161 hbt->timer(hbt->arg);
2162 key = perf_evsel__hists_browse(pos, nr_events, help,
2166 ui_browser__show_title(&menu->b, title);
2169 if (pos->node.next == &evlist->entries)
2170 pos = perf_evlist__first(evlist);
2172 pos = perf_evsel__next(pos);
2175 if (pos->node.prev == &evlist->entries)
2176 pos = perf_evlist__last(evlist);
2178 pos = perf_evsel__prev(pos);
2180 case K_SWITCH_INPUT_DATA:
2191 if (!ui_browser__dialog_yesno(&menu->b,
2192 "Do you really want to exit?"))
2204 ui_browser__hide(&menu->b);
2208 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
2211 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
2213 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
2219 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
2220 int nr_entries, const char *help,
2221 struct hist_browser_timer *hbt,
2223 struct perf_env *env)
2225 struct perf_evsel *pos;
2226 struct perf_evsel_menu menu = {
2228 .entries = &evlist->entries,
2229 .refresh = ui_browser__list_head_refresh,
2230 .seek = ui_browser__list_head_seek,
2231 .write = perf_evsel_menu__write,
2232 .filter = filter_group_entries,
2233 .nr_entries = nr_entries,
2236 .min_pcnt = min_pcnt,
2240 ui_helpline__push("Press ESC to exit");
2242 evlist__for_each(evlist, pos) {
2243 const char *ev_name = perf_evsel__name(pos);
2244 size_t line_len = strlen(ev_name) + 7;
2246 if (menu.b.width < line_len)
2247 menu.b.width = line_len;
2250 return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
2253 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
2254 struct hist_browser_timer *hbt,
2256 struct perf_env *env)
2258 int nr_entries = evlist->nr_entries;
2261 if (nr_entries == 1) {
2262 struct perf_evsel *first = perf_evlist__first(evlist);
2264 return perf_evsel__hists_browse(first, nr_entries, help,
2265 false, hbt, min_pcnt,
2269 if (symbol_conf.event_group) {
2270 struct perf_evsel *pos;
2273 evlist__for_each(evlist, pos) {
2274 if (perf_evsel__is_group_leader(pos))
2278 if (nr_entries == 1)
2282 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
2283 hbt, min_pcnt, env);