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_flat_rows(struct callchain_node *node)
183 struct callchain_list *chain;
184 char folded_sign = 0;
187 list_for_each_entry(chain, &node->parent_val, list) {
189 /* only check first chain list entry */
190 folded_sign = callchain_list__folded(chain);
191 if (folded_sign == '+')
197 list_for_each_entry(chain, &node->val, list) {
199 /* node->parent_val list might be empty */
200 folded_sign = callchain_list__folded(chain);
201 if (folded_sign == '+')
210 static int callchain_node__count_folded_rows(struct callchain_node *node __maybe_unused)
215 static int callchain_node__count_rows(struct callchain_node *node)
217 struct callchain_list *chain;
218 bool unfolded = false;
221 if (callchain_param.mode == CHAIN_FLAT)
222 return callchain_node__count_flat_rows(node);
223 else if (callchain_param.mode == CHAIN_FOLDED)
224 return callchain_node__count_folded_rows(node);
226 list_for_each_entry(chain, &node->val, list) {
228 unfolded = chain->unfolded;
232 n += callchain_node__count_rows_rb_tree(node);
237 static int callchain__count_rows(struct rb_root *chain)
242 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
243 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
244 n += callchain_node__count_rows(node);
250 static bool hist_entry__toggle_fold(struct hist_entry *he)
255 if (!he->has_children)
258 he->unfolded = !he->unfolded;
262 static bool callchain_list__toggle_fold(struct callchain_list *cl)
267 if (!cl->has_children)
270 cl->unfolded = !cl->unfolded;
274 static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
276 struct rb_node *nd = rb_first(&node->rb_root);
278 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
279 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
280 struct callchain_list *chain;
283 list_for_each_entry(chain, &child->val, list) {
286 chain->has_children = chain->list.next != &child->val ||
287 !RB_EMPTY_ROOT(&child->rb_root);
289 chain->has_children = chain->list.next == &child->val &&
290 !RB_EMPTY_ROOT(&child->rb_root);
293 callchain_node__init_have_children_rb_tree(child);
297 static void callchain_node__init_have_children(struct callchain_node *node,
300 struct callchain_list *chain;
302 chain = list_entry(node->val.next, struct callchain_list, list);
303 chain->has_children = has_sibling;
305 if (node->val.next != node->val.prev) {
306 chain = list_entry(node->val.prev, struct callchain_list, list);
307 chain->has_children = !RB_EMPTY_ROOT(&node->rb_root);
310 callchain_node__init_have_children_rb_tree(node);
313 static void callchain__init_have_children(struct rb_root *root)
315 struct rb_node *nd = rb_first(root);
316 bool has_sibling = nd && rb_next(nd);
318 for (nd = rb_first(root); nd; nd = rb_next(nd)) {
319 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
320 callchain_node__init_have_children(node, has_sibling);
321 if (callchain_param.mode == CHAIN_FLAT ||
322 callchain_param.mode == CHAIN_FOLDED)
323 callchain_node__make_parent_list(node);
327 static void hist_entry__init_have_children(struct hist_entry *he)
329 if (!he->init_have_children) {
330 he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
331 callchain__init_have_children(&he->sorted_chain);
332 he->init_have_children = true;
336 static bool hist_browser__toggle_fold(struct hist_browser *browser)
338 struct hist_entry *he = browser->he_selection;
339 struct map_symbol *ms = browser->selection;
340 struct callchain_list *cl = container_of(ms, struct callchain_list, ms);
347 has_children = hist_entry__toggle_fold(he);
349 has_children = callchain_list__toggle_fold(cl);
352 hist_entry__init_have_children(he);
353 browser->b.nr_entries -= he->nr_rows;
354 browser->nr_callchain_rows -= he->nr_rows;
357 he->nr_rows = callchain__count_rows(&he->sorted_chain);
361 browser->b.nr_entries += he->nr_rows;
362 browser->nr_callchain_rows += he->nr_rows;
367 /* If it doesn't have children, no toggling performed */
371 static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
376 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
377 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
378 struct callchain_list *chain;
379 bool has_children = false;
381 list_for_each_entry(chain, &child->val, list) {
383 callchain_list__set_folding(chain, unfold);
384 has_children = chain->has_children;
388 n += callchain_node__set_folding_rb_tree(child, unfold);
394 static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
396 struct callchain_list *chain;
397 bool has_children = false;
400 list_for_each_entry(chain, &node->val, list) {
402 callchain_list__set_folding(chain, unfold);
403 has_children = chain->has_children;
407 n += callchain_node__set_folding_rb_tree(node, unfold);
412 static int callchain__set_folding(struct rb_root *chain, bool unfold)
417 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
418 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
419 n += callchain_node__set_folding(node, unfold);
425 static void hist_entry__set_folding(struct hist_entry *he, bool unfold)
427 hist_entry__init_have_children(he);
428 he->unfolded = unfold ? he->has_children : false;
430 if (he->has_children) {
431 int n = callchain__set_folding(&he->sorted_chain, unfold);
432 he->nr_rows = unfold ? n : 0;
438 __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
441 struct hists *hists = browser->hists;
443 for (nd = rb_first(&hists->entries);
444 (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
446 struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
447 hist_entry__set_folding(he, unfold);
448 browser->nr_callchain_rows += he->nr_rows;
452 static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
454 browser->nr_callchain_rows = 0;
455 __hist_browser__set_folding(browser, unfold);
457 browser->b.nr_entries = hist_browser__nr_entries(browser);
458 /* Go to the start, we may be way after valid entries after a collapse */
459 ui_browser__reset_index(&browser->b);
462 static void ui_browser__warn_lost_events(struct ui_browser *browser)
464 ui_browser__warning(browser, 4,
465 "Events are being lost, check IO/CPU overload!\n\n"
466 "You may want to run 'perf' using a RT scheduler policy:\n\n"
467 " perf top -r 80\n\n"
468 "Or reduce the sampling frequency.");
471 static int hist_browser__run(struct hist_browser *browser, const char *help)
475 struct hist_browser_timer *hbt = browser->hbt;
476 int delay_secs = hbt ? hbt->refresh : 0;
478 browser->b.entries = &browser->hists->entries;
479 browser->b.nr_entries = hist_browser__nr_entries(browser);
481 hists__browser_title(browser->hists, hbt, title, sizeof(title));
483 if (ui_browser__show(&browser->b, title, "%s", help) < 0)
487 key = ui_browser__run(&browser->b, delay_secs);
492 hbt->timer(hbt->arg);
494 if (hist_browser__has_filter(browser))
495 hist_browser__update_nr_entries(browser);
497 nr_entries = hist_browser__nr_entries(browser);
498 ui_browser__update_nr_entries(&browser->b, nr_entries);
500 if (browser->hists->stats.nr_lost_warned !=
501 browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
502 browser->hists->stats.nr_lost_warned =
503 browser->hists->stats.nr_events[PERF_RECORD_LOST];
504 ui_browser__warn_lost_events(&browser->b);
507 hists__browser_title(browser->hists,
508 hbt, title, sizeof(title));
509 ui_browser__show_title(&browser->b, title);
512 case 'D': { /* Debug */
514 struct hist_entry *h = rb_entry(browser->b.top,
515 struct hist_entry, rb_node);
517 ui_helpline__fpush("%d: nr_ent=(%d,%d), rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
518 seq++, browser->b.nr_entries,
519 browser->hists->nr_entries,
523 h->row_offset, h->nr_rows);
527 /* Collapse the whole world. */
528 hist_browser__set_folding(browser, false);
531 /* Expand the whole world. */
532 hist_browser__set_folding(browser, true);
535 browser->show_headers = !browser->show_headers;
536 hist_browser__update_rows(browser);
539 if (hist_browser__toggle_fold(browser))
547 ui_browser__hide(&browser->b);
551 struct callchain_print_arg {
552 /* for hists browser */
554 bool is_current_entry;
561 typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
562 struct callchain_list *chain,
563 const char *str, int offset,
565 struct callchain_print_arg *arg);
567 static void hist_browser__show_callchain_entry(struct hist_browser *browser,
568 struct callchain_list *chain,
569 const char *str, int offset,
571 struct callchain_print_arg *arg)
574 char folded_sign = callchain_list__folded(chain);
575 bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
577 color = HE_COLORSET_NORMAL;
578 width = browser->b.width - (offset + 2);
579 if (ui_browser__is_current_entry(&browser->b, row)) {
580 browser->selection = &chain->ms;
581 color = HE_COLORSET_SELECTED;
582 arg->is_current_entry = true;
585 ui_browser__set_color(&browser->b, color);
586 hist_browser__gotorc(browser, row, 0);
587 ui_browser__write_nstring(&browser->b, " ", offset);
588 ui_browser__printf(&browser->b, "%c", folded_sign);
589 ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
590 ui_browser__write_nstring(&browser->b, str, width);
593 static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
594 struct callchain_list *chain,
595 const char *str, int offset,
596 unsigned short row __maybe_unused,
597 struct callchain_print_arg *arg)
599 char folded_sign = callchain_list__folded(chain);
601 arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
605 typedef bool (*check_output_full_fn)(struct hist_browser *browser,
608 static bool hist_browser__check_output_full(struct hist_browser *browser,
611 return browser->b.rows == row;
614 static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
615 unsigned short row __maybe_unused)
620 #define LEVEL_OFFSET_STEP 3
622 static int hist_browser__show_callchain_list(struct hist_browser *browser,
623 struct callchain_node *node,
624 struct callchain_list *chain,
625 unsigned short row, u64 total,
626 bool need_percent, int offset,
627 print_callchain_entry_fn print,
628 struct callchain_print_arg *arg)
630 char bf[1024], *alloc_str;
633 if (arg->row_offset != 0) {
639 str = callchain_list__sym_name(chain, bf, sizeof(bf),
645 callchain_node__scnprintf_value(node, buf, sizeof(buf),
648 if (asprintf(&alloc_str, "%s %s", buf, str) < 0)
649 str = "Not enough memory!";
654 print(browser, chain, str, offset, row, arg);
660 static int hist_browser__show_callchain_flat(struct hist_browser *browser,
661 struct rb_root *root,
662 unsigned short row, u64 total,
663 print_callchain_entry_fn print,
664 struct callchain_print_arg *arg,
665 check_output_full_fn is_output_full)
667 struct rb_node *node;
668 int first_row = row, offset = LEVEL_OFFSET_STEP;
671 node = rb_first(root);
672 need_percent = node && rb_next(node);
675 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
676 struct rb_node *next = rb_next(node);
677 struct callchain_list *chain;
678 char folded_sign = ' ';
680 int extra_offset = 0;
682 list_for_each_entry(chain, &child->parent_val, list) {
683 bool was_first = first;
687 else if (need_percent)
688 extra_offset = LEVEL_OFFSET_STEP;
690 folded_sign = callchain_list__folded(chain);
692 row += hist_browser__show_callchain_list(browser, child,
694 was_first && need_percent,
695 offset + extra_offset,
698 if (is_output_full(browser, row))
701 if (folded_sign == '+')
705 list_for_each_entry(chain, &child->val, list) {
706 bool was_first = first;
710 else if (need_percent)
711 extra_offset = LEVEL_OFFSET_STEP;
713 folded_sign = callchain_list__folded(chain);
715 row += hist_browser__show_callchain_list(browser, child,
717 was_first && need_percent,
718 offset + extra_offset,
721 if (is_output_full(browser, row))
724 if (folded_sign == '+')
729 if (is_output_full(browser, row))
734 return row - first_row;
737 static char *hist_browser__folded_callchain_str(struct hist_browser *browser,
738 struct callchain_list *chain,
739 char *value_str, char *old_str)
745 str = callchain_list__sym_name(chain, bf, sizeof(bf),
748 if (asprintf(&new, "%s%s%s", old_str,
749 symbol_conf.field_sep ?: ";", str) < 0)
753 if (asprintf(&new, "%s %s", value_str, str) < 0)
756 if (asprintf(&new, "%s", str) < 0)
763 static int hist_browser__show_callchain_folded(struct hist_browser *browser,
764 struct rb_root *root,
765 unsigned short row, u64 total,
766 print_callchain_entry_fn print,
767 struct callchain_print_arg *arg,
768 check_output_full_fn is_output_full)
770 struct rb_node *node;
771 int first_row = row, offset = LEVEL_OFFSET_STEP;
774 node = rb_first(root);
775 need_percent = node && rb_next(node);
778 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
779 struct rb_node *next = rb_next(node);
780 struct callchain_list *chain, *first_chain = NULL;
782 char *value_str = NULL, *value_str_alloc = NULL;
783 char *chain_str = NULL, *chain_str_alloc = NULL;
785 if (arg->row_offset != 0) {
793 callchain_node__scnprintf_value(child, buf, sizeof(buf), total);
794 if (asprintf(&value_str, "%s", buf) < 0) {
795 value_str = (char *)"<...>";
798 value_str_alloc = value_str;
801 list_for_each_entry(chain, &child->parent_val, list) {
802 chain_str = hist_browser__folded_callchain_str(browser,
803 chain, value_str, chain_str);
809 if (chain_str == NULL) {
810 chain_str = (char *)"Not enough memory!";
814 chain_str_alloc = chain_str;
817 list_for_each_entry(chain, &child->val, list) {
818 chain_str = hist_browser__folded_callchain_str(browser,
819 chain, value_str, chain_str);
825 if (chain_str == NULL) {
826 chain_str = (char *)"Not enough memory!";
830 chain_str_alloc = chain_str;
834 print(browser, first_chain, chain_str, offset, row++, arg);
835 free(value_str_alloc);
836 free(chain_str_alloc);
839 if (is_output_full(browser, row))
844 return row - first_row;
847 static int hist_browser__show_callchain_graph(struct hist_browser *browser,
848 struct rb_root *root, int level,
849 unsigned short row, u64 total,
850 print_callchain_entry_fn print,
851 struct callchain_print_arg *arg,
852 check_output_full_fn is_output_full)
854 struct rb_node *node;
855 int first_row = row, offset = level * LEVEL_OFFSET_STEP;
859 node = rb_first(root);
860 need_percent = node && rb_next(node);
863 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
864 struct rb_node *next = rb_next(node);
865 struct callchain_list *chain;
866 char folded_sign = ' ';
868 int extra_offset = 0;
870 list_for_each_entry(chain, &child->val, list) {
871 bool was_first = first;
875 else if (need_percent)
876 extra_offset = LEVEL_OFFSET_STEP;
878 folded_sign = callchain_list__folded(chain);
880 row += hist_browser__show_callchain_list(browser, child,
882 was_first && need_percent,
883 offset + extra_offset,
886 if (is_output_full(browser, row))
889 if (folded_sign == '+')
893 if (folded_sign == '-') {
894 const int new_level = level + (extra_offset ? 2 : 1);
896 if (callchain_param.mode == CHAIN_GRAPH_REL)
897 new_total = child->children_hit;
901 row += hist_browser__show_callchain_graph(browser, &child->rb_root,
902 new_level, row, new_total,
903 print, arg, is_output_full);
905 if (is_output_full(browser, row))
910 return row - first_row;
913 static int hist_browser__show_callchain(struct hist_browser *browser,
914 struct hist_entry *entry, int level,
916 print_callchain_entry_fn print,
917 struct callchain_print_arg *arg,
918 check_output_full_fn is_output_full)
920 u64 total = hists__total_period(entry->hists);
923 if (callchain_param.mode == CHAIN_GRAPH_REL) {
924 if (symbol_conf.cumulate_callchain)
925 total = entry->stat_acc->period;
927 total = entry->stat.period;
930 if (callchain_param.mode == CHAIN_FLAT) {
931 printed = hist_browser__show_callchain_flat(browser,
932 &entry->sorted_chain, row, total,
933 print, arg, is_output_full);
934 } else if (callchain_param.mode == CHAIN_FOLDED) {
935 printed = hist_browser__show_callchain_folded(browser,
936 &entry->sorted_chain, row, total,
937 print, arg, is_output_full);
939 printed = hist_browser__show_callchain_graph(browser,
940 &entry->sorted_chain, level, row, total,
941 print, arg, is_output_full);
944 if (arg->is_current_entry)
945 browser->he_selection = entry;
951 struct ui_browser *b;
956 static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
958 struct hpp_arg *arg = hpp->ptr;
964 len = va_arg(args, int);
965 percent = va_arg(args, double);
968 ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
970 ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
971 ui_browser__printf(arg->b, "%s", hpp->buf);
973 advance_hpp(hpp, ret);
977 #define __HPP_COLOR_PERCENT_FN(_type, _field) \
978 static u64 __hpp_get_##_field(struct hist_entry *he) \
980 return he->stat._field; \
984 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
985 struct perf_hpp *hpp, \
986 struct hist_entry *he) \
988 return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%", \
989 __hpp__slsmg_color_printf, true); \
992 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
993 static u64 __hpp_get_acc_##_field(struct hist_entry *he) \
995 return he->stat_acc->_field; \
999 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
1000 struct perf_hpp *hpp, \
1001 struct hist_entry *he) \
1003 if (!symbol_conf.cumulate_callchain) { \
1004 struct hpp_arg *arg = hpp->ptr; \
1005 int len = fmt->user_len ?: fmt->len; \
1006 int ret = scnprintf(hpp->buf, hpp->size, \
1007 "%*s", len, "N/A"); \
1008 ui_browser__printf(arg->b, "%s", hpp->buf); \
1012 return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field, \
1013 " %*.2f%%", __hpp__slsmg_color_printf, true); \
1016 __HPP_COLOR_PERCENT_FN(overhead, period)
1017 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
1018 __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
1019 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
1020 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
1021 __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
1023 #undef __HPP_COLOR_PERCENT_FN
1024 #undef __HPP_COLOR_ACC_PERCENT_FN
1026 void hist_browser__init_hpp(void)
1028 perf_hpp__format[PERF_HPP__OVERHEAD].color =
1029 hist_browser__hpp_color_overhead;
1030 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
1031 hist_browser__hpp_color_overhead_sys;
1032 perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
1033 hist_browser__hpp_color_overhead_us;
1034 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
1035 hist_browser__hpp_color_overhead_guest_sys;
1036 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
1037 hist_browser__hpp_color_overhead_guest_us;
1038 perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
1039 hist_browser__hpp_color_overhead_acc;
1042 static int hist_browser__show_entry(struct hist_browser *browser,
1043 struct hist_entry *entry,
1048 int width = browser->b.width;
1049 char folded_sign = ' ';
1050 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1051 off_t row_offset = entry->row_offset;
1053 struct perf_hpp_fmt *fmt;
1055 if (current_entry) {
1056 browser->he_selection = entry;
1057 browser->selection = &entry->ms;
1060 if (symbol_conf.use_callchain) {
1061 hist_entry__init_have_children(entry);
1062 folded_sign = hist_entry__folded(entry);
1065 if (row_offset == 0) {
1066 struct hpp_arg arg = {
1068 .folded_sign = folded_sign,
1069 .current_entry = current_entry,
1071 struct perf_hpp hpp = {
1078 hist_browser__gotorc(browser, row, 0);
1080 perf_hpp__for_each_format(fmt) {
1081 if (perf_hpp__should_skip(fmt, entry->hists) ||
1082 column++ < browser->b.horiz_scroll)
1085 if (current_entry && browser->b.navkeypressed) {
1086 ui_browser__set_color(&browser->b,
1087 HE_COLORSET_SELECTED);
1089 ui_browser__set_color(&browser->b,
1090 HE_COLORSET_NORMAL);
1094 if (symbol_conf.use_callchain) {
1095 ui_browser__printf(&browser->b, "%c ", folded_sign);
1100 ui_browser__printf(&browser->b, " ");
1105 width -= fmt->color(fmt, &hpp, entry);
1107 width -= fmt->entry(fmt, &hpp, entry);
1108 ui_browser__printf(&browser->b, "%s", s);
1112 /* The scroll bar isn't being used */
1113 if (!browser->b.navkeypressed)
1116 ui_browser__write_nstring(&browser->b, "", width);
1123 if (folded_sign == '-' && row != browser->b.rows) {
1124 struct callchain_print_arg arg = {
1125 .row_offset = row_offset,
1126 .is_current_entry = current_entry,
1129 printed += hist_browser__show_callchain(browser, entry, 1, row,
1130 hist_browser__show_callchain_entry, &arg,
1131 hist_browser__check_output_full);
1137 static int advance_hpp_check(struct perf_hpp *hpp, int inc)
1139 advance_hpp(hpp, inc);
1140 return hpp->size <= 0;
1143 static int hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf, size_t size)
1145 struct hists *hists = browser->hists;
1146 struct perf_hpp dummy_hpp = {
1150 struct perf_hpp_fmt *fmt;
1154 if (symbol_conf.use_callchain) {
1155 ret = scnprintf(buf, size, " ");
1156 if (advance_hpp_check(&dummy_hpp, ret))
1160 perf_hpp__for_each_format(fmt) {
1161 if (perf_hpp__should_skip(fmt, hists) || column++ < browser->b.horiz_scroll)
1164 ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists));
1165 if (advance_hpp_check(&dummy_hpp, ret))
1168 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
1169 if (advance_hpp_check(&dummy_hpp, ret))
1176 static void hist_browser__show_headers(struct hist_browser *browser)
1180 hists_browser__scnprintf_headers(browser, headers, sizeof(headers));
1181 ui_browser__gotorc(&browser->b, 0, 0);
1182 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1183 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1186 static void ui_browser__hists_init_top(struct ui_browser *browser)
1188 if (browser->top == NULL) {
1189 struct hist_browser *hb;
1191 hb = container_of(browser, struct hist_browser, b);
1192 browser->top = rb_first(&hb->hists->entries);
1196 static unsigned int hist_browser__refresh(struct ui_browser *browser)
1199 u16 header_offset = 0;
1201 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
1203 if (hb->show_headers) {
1204 hist_browser__show_headers(hb);
1208 ui_browser__hists_init_top(browser);
1209 hb->he_selection = NULL;
1210 hb->selection = NULL;
1212 for (nd = browser->top; nd; nd = rb_next(nd)) {
1213 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1219 percent = hist_entry__get_percent_limit(h);
1220 if (percent < hb->min_pcnt)
1223 row += hist_browser__show_entry(hb, h, row);
1224 if (row == browser->rows)
1228 return row + header_offset;
1231 static struct rb_node *hists__filter_entries(struct rb_node *nd,
1234 while (nd != NULL) {
1235 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1236 float percent = hist_entry__get_percent_limit(h);
1238 if (!h->filtered && percent >= min_pcnt)
1247 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
1250 while (nd != NULL) {
1251 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1252 float percent = hist_entry__get_percent_limit(h);
1254 if (!h->filtered && percent >= min_pcnt)
1263 static void ui_browser__hists_seek(struct ui_browser *browser,
1264 off_t offset, int whence)
1266 struct hist_entry *h;
1269 struct hist_browser *hb;
1271 hb = container_of(browser, struct hist_browser, b);
1273 if (browser->nr_entries == 0)
1276 ui_browser__hists_init_top(browser);
1280 nd = hists__filter_entries(rb_first(browser->entries),
1287 nd = hists__filter_prev_entries(rb_last(browser->entries),
1296 * Moves not relative to the first visible entry invalidates its
1299 h = rb_entry(browser->top, struct hist_entry, rb_node);
1303 * Here we have to check if nd is expanded (+), if it is we can't go
1304 * the next top level hist_entry, instead we must compute an offset of
1305 * what _not_ to show and not change the first visible entry.
1307 * This offset increments when we are going from top to bottom and
1308 * decreases when we're going from bottom to top.
1310 * As we don't have backpointers to the top level in the callchains
1311 * structure, we need to always print the whole hist_entry callchain,
1312 * skipping the first ones that are before the first visible entry
1313 * and stop when we printed enough lines to fill the screen.
1321 h = rb_entry(nd, struct hist_entry, rb_node);
1323 u16 remaining = h->nr_rows - h->row_offset;
1324 if (offset > remaining) {
1325 offset -= remaining;
1328 h->row_offset += offset;
1334 nd = hists__filter_entries(rb_next(nd), hb->min_pcnt);
1339 } while (offset != 0);
1340 } else if (offset < 0) {
1342 h = rb_entry(nd, struct hist_entry, rb_node);
1345 if (-offset > h->row_offset) {
1346 offset += h->row_offset;
1349 h->row_offset += offset;
1355 if (-offset > h->nr_rows) {
1356 offset += h->nr_rows;
1359 h->row_offset = h->nr_rows + offset;
1367 nd = hists__filter_prev_entries(rb_prev(nd),
1375 * Last unfiltered hist_entry, check if it is
1376 * unfolded, if it is then we should have
1377 * row_offset at its last entry.
1379 h = rb_entry(nd, struct hist_entry, rb_node);
1381 h->row_offset = h->nr_rows;
1388 h = rb_entry(nd, struct hist_entry, rb_node);
1393 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1394 struct hist_entry *he, FILE *fp)
1396 struct callchain_print_arg arg = {
1400 hist_browser__show_callchain(browser, he, 1, 0,
1401 hist_browser__fprintf_callchain_entry, &arg,
1402 hist_browser__check_dump_full);
1406 static int hist_browser__fprintf_entry(struct hist_browser *browser,
1407 struct hist_entry *he, FILE *fp)
1411 char folded_sign = ' ';
1412 struct perf_hpp hpp = {
1416 struct perf_hpp_fmt *fmt;
1420 if (symbol_conf.use_callchain)
1421 folded_sign = hist_entry__folded(he);
1423 if (symbol_conf.use_callchain)
1424 printed += fprintf(fp, "%c ", folded_sign);
1426 perf_hpp__for_each_format(fmt) {
1427 if (perf_hpp__should_skip(fmt, he->hists))
1431 ret = scnprintf(hpp.buf, hpp.size, " ");
1432 advance_hpp(&hpp, ret);
1436 ret = fmt->entry(fmt, &hpp, he);
1437 advance_hpp(&hpp, ret);
1439 printed += fprintf(fp, "%s\n", rtrim(s));
1441 if (folded_sign == '-')
1442 printed += hist_browser__fprintf_callchain(browser, he, fp);
1447 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1449 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
1454 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1456 printed += hist_browser__fprintf_entry(browser, h, fp);
1457 nd = hists__filter_entries(rb_next(nd), browser->min_pcnt);
1463 static int hist_browser__dump(struct hist_browser *browser)
1469 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
1470 if (access(filename, F_OK))
1473 * XXX: Just an arbitrary lazy upper limit
1475 if (++browser->print_seq == 8192) {
1476 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1481 fp = fopen(filename, "w");
1484 const char *err = strerror_r(errno, bf, sizeof(bf));
1485 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
1489 ++browser->print_seq;
1490 hist_browser__fprintf(browser, fp);
1492 ui_helpline__fpush("%s written!", filename);
1497 static struct hist_browser *hist_browser__new(struct hists *hists,
1498 struct hist_browser_timer *hbt,
1499 struct perf_env *env)
1501 struct hist_browser *browser = zalloc(sizeof(*browser));
1504 browser->hists = hists;
1505 browser->b.refresh = hist_browser__refresh;
1506 browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
1507 browser->b.seek = ui_browser__hists_seek;
1508 browser->b.use_navkeypressed = true;
1509 browser->show_headers = symbol_conf.show_hist_headers;
1517 static void hist_browser__delete(struct hist_browser *browser)
1522 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
1524 return browser->he_selection;
1527 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
1529 return browser->he_selection->thread;
1532 /* Check whether the browser is for 'top' or 'report' */
1533 static inline bool is_report_browser(void *timer)
1535 return timer == NULL;
1538 static int hists__browser_title(struct hists *hists,
1539 struct hist_browser_timer *hbt,
1540 char *bf, size_t size)
1544 const struct dso *dso = hists->dso_filter;
1545 const struct thread *thread = hists->thread_filter;
1546 int socket_id = hists->socket_filter;
1547 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1548 u64 nr_events = hists->stats.total_period;
1549 struct perf_evsel *evsel = hists_to_evsel(hists);
1550 const char *ev_name = perf_evsel__name(evsel);
1552 size_t buflen = sizeof(buf);
1553 char ref[30] = " show reference callgraph, ";
1554 bool enable_ref = false;
1556 if (symbol_conf.filter_relative) {
1557 nr_samples = hists->stats.nr_non_filtered_samples;
1558 nr_events = hists->stats.total_non_filtered_period;
1561 if (perf_evsel__is_group_event(evsel)) {
1562 struct perf_evsel *pos;
1564 perf_evsel__group_desc(evsel, buf, buflen);
1567 for_each_group_member(pos, evsel) {
1568 struct hists *pos_hists = evsel__hists(pos);
1570 if (symbol_conf.filter_relative) {
1571 nr_samples += pos_hists->stats.nr_non_filtered_samples;
1572 nr_events += pos_hists->stats.total_non_filtered_period;
1574 nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
1575 nr_events += pos_hists->stats.total_period;
1580 if (symbol_conf.show_ref_callgraph &&
1581 strstr(ev_name, "call-graph=no"))
1583 nr_samples = convert_unit(nr_samples, &unit);
1584 printed = scnprintf(bf, size,
1585 "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64,
1586 nr_samples, unit, ev_name, enable_ref ? ref : " ", nr_events);
1589 if (hists->uid_filter_str)
1590 printed += snprintf(bf + printed, size - printed,
1591 ", UID: %s", hists->uid_filter_str);
1593 printed += scnprintf(bf + printed, size - printed,
1595 (thread->comm_set ? thread__comm_str(thread) : ""),
1598 printed += scnprintf(bf + printed, size - printed,
1599 ", DSO: %s", dso->short_name);
1601 printed += scnprintf(bf + printed, size - printed,
1602 ", Processor Socket: %d", socket_id);
1603 if (!is_report_browser(hbt)) {
1604 struct perf_top *top = hbt->arg;
1607 printed += scnprintf(bf + printed, size - printed, " [z]");
1613 static inline void free_popup_options(char **options, int n)
1617 for (i = 0; i < n; ++i)
1622 * Only runtime switching of perf data file will make "input_name" point
1623 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1624 * whether we need to call free() for current "input_name" during the switch.
1626 static bool is_input_name_malloced = false;
1628 static int switch_data_file(void)
1630 char *pwd, *options[32], *abs_path[32], *tmp;
1632 int nr_options = 0, choice = -1, ret = -1;
1633 struct dirent *dent;
1635 pwd = getenv("PWD");
1639 pwd_dir = opendir(pwd);
1643 memset(options, 0, sizeof(options));
1644 memset(options, 0, sizeof(abs_path));
1646 while ((dent = readdir(pwd_dir))) {
1647 char path[PATH_MAX];
1649 char *name = dent->d_name;
1652 if (!(dent->d_type == DT_REG))
1655 snprintf(path, sizeof(path), "%s/%s", pwd, name);
1657 file = fopen(path, "r");
1661 if (fread(&magic, 1, 8, file) < 8)
1662 goto close_file_and_continue;
1664 if (is_perf_magic(magic)) {
1665 options[nr_options] = strdup(name);
1666 if (!options[nr_options])
1667 goto close_file_and_continue;
1669 abs_path[nr_options] = strdup(path);
1670 if (!abs_path[nr_options]) {
1671 zfree(&options[nr_options]);
1672 ui__warning("Can't search all data files due to memory shortage.\n");
1680 close_file_and_continue:
1682 if (nr_options >= 32) {
1683 ui__warning("Too many perf data files in PWD!\n"
1684 "Only the first 32 files will be listed.\n");
1691 choice = ui__popup_menu(nr_options, options);
1692 if (choice < nr_options && choice >= 0) {
1693 tmp = strdup(abs_path[choice]);
1695 if (is_input_name_malloced)
1696 free((void *)input_name);
1698 is_input_name_malloced = true;
1701 ui__warning("Data switch failed due to memory shortage!\n");
1705 free_popup_options(options, nr_options);
1706 free_popup_options(abs_path, nr_options);
1710 struct popup_action {
1711 struct thread *thread;
1712 struct map_symbol ms;
1715 int (*fn)(struct hist_browser *browser, struct popup_action *act);
1719 do_annotate(struct hist_browser *browser, struct popup_action *act)
1721 struct perf_evsel *evsel;
1722 struct annotation *notes;
1723 struct hist_entry *he;
1726 if (!objdump_path && perf_env__lookup_objdump(browser->env))
1729 notes = symbol__annotation(act->ms.sym);
1733 evsel = hists_to_evsel(browser->hists);
1734 err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
1735 he = hist_browser__selected_entry(browser);
1737 * offer option to annotate the other branch source or target
1738 * (if they exists) when returning from annotate
1740 if ((err == 'q' || err == CTRL('c')) && he->branch_info)
1743 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1745 ui_browser__handle_resize(&browser->b);
1750 add_annotate_opt(struct hist_browser *browser __maybe_unused,
1751 struct popup_action *act, char **optstr,
1752 struct map *map, struct symbol *sym)
1754 if (sym == NULL || map->dso->annotate_warned)
1757 if (asprintf(optstr, "Annotate %s", sym->name) < 0)
1762 act->fn = do_annotate;
1767 do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
1769 struct thread *thread = act->thread;
1771 if (browser->hists->thread_filter) {
1772 pstack__remove(browser->pstack, &browser->hists->thread_filter);
1773 perf_hpp__set_elide(HISTC_THREAD, false);
1774 thread__zput(browser->hists->thread_filter);
1777 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
1778 thread->comm_set ? thread__comm_str(thread) : "",
1780 browser->hists->thread_filter = thread__get(thread);
1781 perf_hpp__set_elide(HISTC_THREAD, false);
1782 pstack__push(browser->pstack, &browser->hists->thread_filter);
1785 hists__filter_by_thread(browser->hists);
1786 hist_browser__reset(browser);
1791 add_thread_opt(struct hist_browser *browser, struct popup_action *act,
1792 char **optstr, struct thread *thread)
1794 if (!sort__has_thread || thread == NULL)
1797 if (asprintf(optstr, "Zoom %s %s(%d) thread",
1798 browser->hists->thread_filter ? "out of" : "into",
1799 thread->comm_set ? thread__comm_str(thread) : "",
1803 act->thread = thread;
1804 act->fn = do_zoom_thread;
1809 do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
1811 struct map *map = act->ms.map;
1813 if (browser->hists->dso_filter) {
1814 pstack__remove(browser->pstack, &browser->hists->dso_filter);
1815 perf_hpp__set_elide(HISTC_DSO, false);
1816 browser->hists->dso_filter = NULL;
1821 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
1822 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
1823 browser->hists->dso_filter = map->dso;
1824 perf_hpp__set_elide(HISTC_DSO, true);
1825 pstack__push(browser->pstack, &browser->hists->dso_filter);
1828 hists__filter_by_dso(browser->hists);
1829 hist_browser__reset(browser);
1834 add_dso_opt(struct hist_browser *browser, struct popup_action *act,
1835 char **optstr, struct map *map)
1837 if (!sort__has_dso || map == NULL)
1840 if (asprintf(optstr, "Zoom %s %s DSO",
1841 browser->hists->dso_filter ? "out of" : "into",
1842 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
1846 act->fn = do_zoom_dso;
1851 do_browse_map(struct hist_browser *browser __maybe_unused,
1852 struct popup_action *act)
1854 map__browse(act->ms.map);
1859 add_map_opt(struct hist_browser *browser __maybe_unused,
1860 struct popup_action *act, char **optstr, struct map *map)
1862 if (!sort__has_dso || map == NULL)
1865 if (asprintf(optstr, "Browse map details") < 0)
1869 act->fn = do_browse_map;
1874 do_run_script(struct hist_browser *browser __maybe_unused,
1875 struct popup_action *act)
1877 char script_opt[64];
1878 memset(script_opt, 0, sizeof(script_opt));
1881 scnprintf(script_opt, sizeof(script_opt), " -c %s ",
1882 thread__comm_str(act->thread));
1883 } else if (act->ms.sym) {
1884 scnprintf(script_opt, sizeof(script_opt), " -S %s ",
1888 script_browse(script_opt);
1893 add_script_opt(struct hist_browser *browser __maybe_unused,
1894 struct popup_action *act, char **optstr,
1895 struct thread *thread, struct symbol *sym)
1898 if (asprintf(optstr, "Run scripts for samples of thread [%s]",
1899 thread__comm_str(thread)) < 0)
1902 if (asprintf(optstr, "Run scripts for samples of symbol [%s]",
1906 if (asprintf(optstr, "Run scripts for all samples") < 0)
1910 act->thread = thread;
1912 act->fn = do_run_script;
1917 do_switch_data(struct hist_browser *browser __maybe_unused,
1918 struct popup_action *act __maybe_unused)
1920 if (switch_data_file()) {
1921 ui__warning("Won't switch the data files due to\n"
1922 "no valid data file get selected!\n");
1926 return K_SWITCH_INPUT_DATA;
1930 add_switch_opt(struct hist_browser *browser,
1931 struct popup_action *act, char **optstr)
1933 if (!is_report_browser(browser->hbt))
1936 if (asprintf(optstr, "Switch to another data file in PWD") < 0)
1939 act->fn = do_switch_data;
1944 do_exit_browser(struct hist_browser *browser __maybe_unused,
1945 struct popup_action *act __maybe_unused)
1951 add_exit_opt(struct hist_browser *browser __maybe_unused,
1952 struct popup_action *act, char **optstr)
1954 if (asprintf(optstr, "Exit") < 0)
1957 act->fn = do_exit_browser;
1962 do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
1964 if (browser->hists->socket_filter > -1) {
1965 pstack__remove(browser->pstack, &browser->hists->socket_filter);
1966 browser->hists->socket_filter = -1;
1967 perf_hpp__set_elide(HISTC_SOCKET, false);
1969 browser->hists->socket_filter = act->socket;
1970 perf_hpp__set_elide(HISTC_SOCKET, true);
1971 pstack__push(browser->pstack, &browser->hists->socket_filter);
1974 hists__filter_by_socket(browser->hists);
1975 hist_browser__reset(browser);
1980 add_socket_opt(struct hist_browser *browser, struct popup_action *act,
1981 char **optstr, int socket_id)
1983 if (!sort__has_socket || socket_id < 0)
1986 if (asprintf(optstr, "Zoom %s Processor Socket %d",
1987 (browser->hists->socket_filter > -1) ? "out of" : "into",
1991 act->socket = socket_id;
1992 act->fn = do_zoom_socket;
1996 static void hist_browser__update_nr_entries(struct hist_browser *hb)
1999 struct rb_node *nd = rb_first(&hb->hists->entries);
2001 if (hb->min_pcnt == 0) {
2002 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
2006 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2011 hb->nr_non_filtered_entries = nr_entries;
2014 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
2015 const char *helpline,
2017 struct hist_browser_timer *hbt,
2019 struct perf_env *env)
2021 struct hists *hists = evsel__hists(evsel);
2022 struct hist_browser *browser = hist_browser__new(hists, hbt, env);
2023 struct branch_info *bi;
2024 #define MAX_OPTIONS 16
2025 char *options[MAX_OPTIONS];
2026 struct popup_action actions[MAX_OPTIONS];
2030 int delay_secs = hbt ? hbt->refresh : 0;
2031 struct perf_hpp_fmt *fmt;
2033 #define HIST_BROWSER_HELP_COMMON \
2034 "h/?/F1 Show this window\n" \
2036 "PGDN/SPACE Navigate\n" \
2037 "q/ESC/CTRL+C Exit browser\n\n" \
2038 "For multiple event sessions:\n\n" \
2039 "TAB/UNTAB Switch events\n\n" \
2040 "For symbolic views (--sort has sym):\n\n" \
2041 "ENTER Zoom into DSO/Threads & Annotate current symbol\n" \
2043 "a Annotate current symbol\n" \
2044 "C Collapse all callchains\n" \
2045 "d Zoom into current DSO\n" \
2046 "E Expand all callchains\n" \
2047 "F Toggle percentage of filtered entries\n" \
2048 "H Display column headers\n" \
2049 "m Display context menu\n" \
2050 "S Zoom into current Processor Socket\n" \
2052 /* help messages are sorted by lexical order of the hotkey */
2053 const char report_help[] = HIST_BROWSER_HELP_COMMON
2054 "i Show header information\n"
2055 "P Print histograms to perf.hist.N\n"
2056 "r Run available scripts\n"
2057 "s Switch to another data file in PWD\n"
2058 "t Zoom into current Thread\n"
2059 "V Verbose (DSO names in callchains, etc)\n"
2060 "/ Filter symbol by name";
2061 const char top_help[] = HIST_BROWSER_HELP_COMMON
2062 "P Print histograms to perf.hist.N\n"
2063 "t Zoom into current Thread\n"
2064 "V Verbose (DSO names in callchains, etc)\n"
2065 "z Toggle zeroing of samples\n"
2066 "f Enable/Disable events\n"
2067 "/ Filter symbol by name";
2069 if (browser == NULL)
2072 /* reset abort key so that it can get Ctrl-C as a key */
2074 SLang_init_tty(0, 0, 0);
2077 browser->min_pcnt = min_pcnt;
2078 hist_browser__update_nr_entries(browser);
2080 browser->pstack = pstack__new(3);
2081 if (browser->pstack == NULL)
2084 ui_helpline__push(helpline);
2086 memset(options, 0, sizeof(options));
2087 memset(actions, 0, sizeof(actions));
2089 perf_hpp__for_each_format(fmt) {
2090 perf_hpp__reset_width(fmt, hists);
2092 * This is done just once, and activates the horizontal scrolling
2093 * code in the ui_browser code, it would be better to have a the
2094 * counter in the perf_hpp code, but I couldn't find doing it here
2095 * works, FIXME by setting this in hist_browser__new, for now, be
2098 ++browser->b.columns;
2101 if (symbol_conf.col_width_list_str)
2102 perf_hpp__set_user_width(symbol_conf.col_width_list_str);
2105 struct thread *thread = NULL;
2106 struct map *map = NULL;
2112 key = hist_browser__run(browser, helpline);
2114 if (browser->he_selection != NULL) {
2115 thread = hist_browser__selected_thread(browser);
2116 map = browser->selection->map;
2117 socked_id = browser->he_selection->socket;
2125 * Exit the browser, let hists__browser_tree
2126 * go to the next or previous
2128 goto out_free_stack;
2130 if (!sort__has_sym) {
2131 ui_browser__warning(&browser->b, delay_secs * 2,
2132 "Annotation is only available for symbolic views, "
2133 "include \"sym*\" in --sort to use it.");
2137 if (browser->selection == NULL ||
2138 browser->selection->sym == NULL ||
2139 browser->selection->map->dso->annotate_warned)
2142 actions->ms.map = browser->selection->map;
2143 actions->ms.sym = browser->selection->sym;
2144 do_annotate(browser, actions);
2147 hist_browser__dump(browser);
2150 actions->ms.map = map;
2151 do_zoom_dso(browser, actions);
2154 browser->show_dso = !browser->show_dso;
2157 actions->thread = thread;
2158 do_zoom_thread(browser, actions);
2161 actions->socket = socked_id;
2162 do_zoom_socket(browser, actions);
2165 if (ui_browser__input_window("Symbol to show",
2166 "Please enter the name of symbol you want to see.\n"
2167 "To remove the filter later, press / + ENTER.",
2168 buf, "ENTER: OK, ESC: Cancel",
2169 delay_secs * 2) == K_ENTER) {
2170 hists->symbol_filter_str = *buf ? buf : NULL;
2171 hists__filter_by_symbol(hists);
2172 hist_browser__reset(browser);
2176 if (is_report_browser(hbt)) {
2177 actions->thread = NULL;
2178 actions->ms.sym = NULL;
2179 do_run_script(browser, actions);
2183 if (is_report_browser(hbt)) {
2184 key = do_switch_data(browser, actions);
2185 if (key == K_SWITCH_INPUT_DATA)
2186 goto out_free_stack;
2190 /* env->arch is NULL for live-mode (i.e. perf top) */
2192 tui__header_window(env);
2195 symbol_conf.filter_relative ^= 1;
2198 if (!is_report_browser(hbt)) {
2199 struct perf_top *top = hbt->arg;
2201 top->zero = !top->zero;
2207 ui_browser__help_window(&browser->b,
2208 is_report_browser(hbt) ? report_help : top_help);
2219 if (pstack__empty(browser->pstack)) {
2221 * Go back to the perf_evsel_menu__run or other user
2224 goto out_free_stack;
2227 ui_browser__dialog_yesno(&browser->b,
2228 "Do you really want to exit?"))
2229 goto out_free_stack;
2233 top = pstack__peek(browser->pstack);
2234 if (top == &browser->hists->dso_filter) {
2236 * No need to set actions->dso here since
2237 * it's just to remove the current filter.
2238 * Ditto for thread below.
2240 do_zoom_dso(browser, actions);
2241 } else if (top == &browser->hists->thread_filter) {
2242 do_zoom_thread(browser, actions);
2243 } else if (top == &browser->hists->socket_filter) {
2244 do_zoom_socket(browser, actions);
2250 goto out_free_stack;
2252 if (!is_report_browser(hbt)) {
2253 struct perf_top *top = hbt->arg;
2255 perf_evlist__toggle_enable(top->evlist);
2257 * No need to refresh, resort/decay histogram
2258 * entries if we are not collecting samples:
2260 if (top->evlist->enabled) {
2261 helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
2262 hbt->refresh = delay_secs;
2264 helpline = "Press 'f' again to re-enable the events";
2271 helpline = "Press '?' for help on key bindings";
2275 if (!sort__has_sym || browser->selection == NULL)
2276 goto skip_annotation;
2278 if (sort__mode == SORT_MODE__BRANCH) {
2279 bi = browser->he_selection->branch_info;
2282 goto skip_annotation;
2284 nr_options += add_annotate_opt(browser,
2285 &actions[nr_options],
2286 &options[nr_options],
2289 if (bi->to.sym != bi->from.sym)
2290 nr_options += add_annotate_opt(browser,
2291 &actions[nr_options],
2292 &options[nr_options],
2296 nr_options += add_annotate_opt(browser,
2297 &actions[nr_options],
2298 &options[nr_options],
2299 browser->selection->map,
2300 browser->selection->sym);
2303 nr_options += add_thread_opt(browser, &actions[nr_options],
2304 &options[nr_options], thread);
2305 nr_options += add_dso_opt(browser, &actions[nr_options],
2306 &options[nr_options], map);
2307 nr_options += add_map_opt(browser, &actions[nr_options],
2308 &options[nr_options],
2309 browser->selection ?
2310 browser->selection->map : NULL);
2311 nr_options += add_socket_opt(browser, &actions[nr_options],
2312 &options[nr_options],
2314 /* perf script support */
2315 if (!is_report_browser(hbt))
2316 goto skip_scripting;
2318 if (browser->he_selection) {
2319 if (sort__has_thread && thread) {
2320 nr_options += add_script_opt(browser,
2321 &actions[nr_options],
2322 &options[nr_options],
2326 * Note that browser->selection != NULL
2327 * when browser->he_selection is not NULL,
2328 * so we don't need to check browser->selection
2329 * before fetching browser->selection->sym like what
2330 * we do before fetching browser->selection->map.
2332 * See hist_browser__show_entry.
2334 if (sort__has_sym && browser->selection->sym) {
2335 nr_options += add_script_opt(browser,
2336 &actions[nr_options],
2337 &options[nr_options],
2338 NULL, browser->selection->sym);
2341 nr_options += add_script_opt(browser, &actions[nr_options],
2342 &options[nr_options], NULL, NULL);
2343 nr_options += add_switch_opt(browser, &actions[nr_options],
2344 &options[nr_options]);
2346 nr_options += add_exit_opt(browser, &actions[nr_options],
2347 &options[nr_options]);
2350 struct popup_action *act;
2352 choice = ui__popup_menu(nr_options, options);
2353 if (choice == -1 || choice >= nr_options)
2356 act = &actions[choice];
2357 key = act->fn(browser, act);
2360 if (key == K_SWITCH_INPUT_DATA)
2364 pstack__delete(browser->pstack);
2366 hist_browser__delete(browser);
2367 free_popup_options(options, MAX_OPTIONS);
2371 struct perf_evsel_menu {
2372 struct ui_browser b;
2373 struct perf_evsel *selection;
2374 bool lost_events, lost_events_warned;
2376 struct perf_env *env;
2379 static void perf_evsel_menu__write(struct ui_browser *browser,
2380 void *entry, int row)
2382 struct perf_evsel_menu *menu = container_of(browser,
2383 struct perf_evsel_menu, b);
2384 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
2385 struct hists *hists = evsel__hists(evsel);
2386 bool current_entry = ui_browser__is_current_entry(browser, row);
2387 unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
2388 const char *ev_name = perf_evsel__name(evsel);
2390 const char *warn = " ";
2393 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
2394 HE_COLORSET_NORMAL);
2396 if (perf_evsel__is_group_event(evsel)) {
2397 struct perf_evsel *pos;
2399 ev_name = perf_evsel__group_name(evsel);
2401 for_each_group_member(pos, evsel) {
2402 struct hists *pos_hists = evsel__hists(pos);
2403 nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
2407 nr_events = convert_unit(nr_events, &unit);
2408 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
2409 unit, unit == ' ' ? "" : " ", ev_name);
2410 ui_browser__printf(browser, "%s", bf);
2412 nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
2413 if (nr_events != 0) {
2414 menu->lost_events = true;
2416 ui_browser__set_color(browser, HE_COLORSET_TOP);
2417 nr_events = convert_unit(nr_events, &unit);
2418 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
2419 nr_events, unit, unit == ' ' ? "" : " ");
2423 ui_browser__write_nstring(browser, warn, browser->width - printed);
2426 menu->selection = evsel;
2429 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
2430 int nr_events, const char *help,
2431 struct hist_browser_timer *hbt)
2433 struct perf_evlist *evlist = menu->b.priv;
2434 struct perf_evsel *pos;
2435 const char *title = "Available samples";
2436 int delay_secs = hbt ? hbt->refresh : 0;
2439 if (ui_browser__show(&menu->b, title,
2440 "ESC: exit, ENTER|->: Browse histograms") < 0)
2444 key = ui_browser__run(&menu->b, delay_secs);
2448 hbt->timer(hbt->arg);
2450 if (!menu->lost_events_warned && menu->lost_events) {
2451 ui_browser__warn_lost_events(&menu->b);
2452 menu->lost_events_warned = true;
2457 if (!menu->selection)
2459 pos = menu->selection;
2461 perf_evlist__set_selected(evlist, pos);
2463 * Give the calling tool a chance to populate the non
2464 * default evsel resorted hists tree.
2467 hbt->timer(hbt->arg);
2468 key = perf_evsel__hists_browse(pos, nr_events, help,
2472 ui_browser__show_title(&menu->b, title);
2475 if (pos->node.next == &evlist->entries)
2476 pos = perf_evlist__first(evlist);
2478 pos = perf_evsel__next(pos);
2481 if (pos->node.prev == &evlist->entries)
2482 pos = perf_evlist__last(evlist);
2484 pos = perf_evsel__prev(pos);
2486 case K_SWITCH_INPUT_DATA:
2497 if (!ui_browser__dialog_yesno(&menu->b,
2498 "Do you really want to exit?"))
2510 ui_browser__hide(&menu->b);
2514 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
2517 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
2519 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
2525 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
2526 int nr_entries, const char *help,
2527 struct hist_browser_timer *hbt,
2529 struct perf_env *env)
2531 struct perf_evsel *pos;
2532 struct perf_evsel_menu menu = {
2534 .entries = &evlist->entries,
2535 .refresh = ui_browser__list_head_refresh,
2536 .seek = ui_browser__list_head_seek,
2537 .write = perf_evsel_menu__write,
2538 .filter = filter_group_entries,
2539 .nr_entries = nr_entries,
2542 .min_pcnt = min_pcnt,
2546 ui_helpline__push("Press ESC to exit");
2548 evlist__for_each(evlist, pos) {
2549 const char *ev_name = perf_evsel__name(pos);
2550 size_t line_len = strlen(ev_name) + 7;
2552 if (menu.b.width < line_len)
2553 menu.b.width = line_len;
2556 return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
2559 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
2560 struct hist_browser_timer *hbt,
2562 struct perf_env *env)
2564 int nr_entries = evlist->nr_entries;
2567 if (nr_entries == 1) {
2568 struct perf_evsel *first = perf_evlist__first(evlist);
2570 return perf_evsel__hists_browse(first, nr_entries, help,
2571 false, hbt, min_pcnt,
2575 if (symbol_conf.event_group) {
2576 struct perf_evsel *pos;
2579 evlist__for_each(evlist, pos) {
2580 if (perf_evsel__is_group_leader(pos))
2584 if (nr_entries == 1)
2588 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
2589 hbt, min_pcnt, env);