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 "../browsers/hists.h"
16 #include "../helpline.h"
22 extern void hist_browser__init_hpp(void);
24 static int perf_evsel_browser_title(struct hist_browser *browser,
25 char *bf, size_t size);
26 static void hist_browser__update_nr_entries(struct hist_browser *hb);
28 static struct rb_node *hists__filter_entries(struct rb_node *nd,
31 static bool hist_browser__has_filter(struct hist_browser *hb)
33 return hists__has_filter(hb->hists) || hb->min_pcnt || symbol_conf.has_filter;
36 static int hist_browser__get_folding(struct hist_browser *browser)
39 struct hists *hists = browser->hists;
40 int unfolded_rows = 0;
42 for (nd = rb_first(&hists->entries);
43 (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
44 nd = rb_hierarchy_next(nd)) {
45 struct hist_entry *he =
46 rb_entry(nd, struct hist_entry, rb_node);
48 if (he->leaf && he->unfolded)
49 unfolded_rows += he->nr_rows;
54 static u32 hist_browser__nr_entries(struct hist_browser *hb)
58 if (symbol_conf.report_hierarchy)
59 nr_entries = hb->nr_hierarchy_entries;
60 else if (hist_browser__has_filter(hb))
61 nr_entries = hb->nr_non_filtered_entries;
63 nr_entries = hb->hists->nr_entries;
65 hb->nr_callchain_rows = hist_browser__get_folding(hb);
66 return nr_entries + hb->nr_callchain_rows;
69 static void hist_browser__update_rows(struct hist_browser *hb)
71 struct ui_browser *browser = &hb->b;
72 struct hists *hists = hb->hists;
73 struct perf_hpp_list *hpp_list = hists->hpp_list;
74 u16 header_offset, index_row;
76 header_offset = hb->show_headers ? hpp_list->nr_header_lines : 0;
77 browser->rows = browser->height - header_offset;
79 * Verify if we were at the last line and that line isn't
80 * visibe because we now show the header line(s).
82 index_row = browser->index - browser->top_idx;
83 if (index_row >= browser->rows)
84 browser->index -= index_row - browser->rows + 1;
87 static void hist_browser__refresh_dimensions(struct ui_browser *browser)
89 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
91 /* 3 == +/- toggle symbol before actual hist_entry rendering */
92 browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]"));
94 * FIXME: Just keeping existing behaviour, but this really should be
95 * before updating browser->width, as it will invalidate the
96 * calculation above. Fix this and the fallout in another
99 ui_browser__refresh_dimensions(browser);
100 hist_browser__update_rows(hb);
103 static void hist_browser__gotorc(struct hist_browser *browser, int row, int column)
105 struct hists *hists = browser->hists;
106 struct perf_hpp_list *hpp_list = hists->hpp_list;
109 header_offset = browser->show_headers ? hpp_list->nr_header_lines : 0;
110 ui_browser__gotorc(&browser->b, row + header_offset, column);
113 static void hist_browser__reset(struct hist_browser *browser)
116 * The hists__remove_entry_filter() already folds non-filtered
117 * entries so we can assume it has 0 callchain rows.
119 browser->nr_callchain_rows = 0;
121 hist_browser__update_nr_entries(browser);
122 browser->b.nr_entries = hist_browser__nr_entries(browser);
123 hist_browser__refresh_dimensions(&browser->b);
124 ui_browser__reset_index(&browser->b);
127 static char tree__folded_sign(bool unfolded)
129 return unfolded ? '-' : '+';
132 static char hist_entry__folded(const struct hist_entry *he)
134 return he->has_children ? tree__folded_sign(he->unfolded) : ' ';
137 static char callchain_list__folded(const struct callchain_list *cl)
139 return cl->has_children ? tree__folded_sign(cl->unfolded) : ' ';
142 static void callchain_list__set_folding(struct callchain_list *cl, bool unfold)
144 cl->unfolded = unfold ? cl->has_children : false;
147 static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
152 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
153 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
154 struct callchain_list *chain;
155 char folded_sign = ' '; /* No children */
157 list_for_each_entry(chain, &child->val, list) {
159 /* We need this because we may not have children */
160 folded_sign = callchain_list__folded(chain);
161 if (folded_sign == '+')
165 if (folded_sign == '-') /* Have children and they're unfolded */
166 n += callchain_node__count_rows_rb_tree(child);
172 static int callchain_node__count_flat_rows(struct callchain_node *node)
174 struct callchain_list *chain;
175 char folded_sign = 0;
178 list_for_each_entry(chain, &node->parent_val, list) {
180 /* only check first chain list entry */
181 folded_sign = callchain_list__folded(chain);
182 if (folded_sign == '+')
188 list_for_each_entry(chain, &node->val, list) {
190 /* node->parent_val list might be empty */
191 folded_sign = callchain_list__folded(chain);
192 if (folded_sign == '+')
201 static int callchain_node__count_folded_rows(struct callchain_node *node __maybe_unused)
206 static int callchain_node__count_rows(struct callchain_node *node)
208 struct callchain_list *chain;
209 bool unfolded = false;
212 if (callchain_param.mode == CHAIN_FLAT)
213 return callchain_node__count_flat_rows(node);
214 else if (callchain_param.mode == CHAIN_FOLDED)
215 return callchain_node__count_folded_rows(node);
217 list_for_each_entry(chain, &node->val, list) {
219 unfolded = chain->unfolded;
223 n += callchain_node__count_rows_rb_tree(node);
228 static int callchain__count_rows(struct rb_root *chain)
233 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
234 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
235 n += callchain_node__count_rows(node);
241 static int hierarchy_count_rows(struct hist_browser *hb, struct hist_entry *he,
242 bool include_children)
245 struct rb_node *node;
246 struct hist_entry *child;
249 return callchain__count_rows(&he->sorted_chain);
251 if (he->has_no_entry)
254 node = rb_first(&he->hroot_out);
258 child = rb_entry(node, struct hist_entry, rb_node);
259 percent = hist_entry__get_percent_limit(child);
261 if (!child->filtered && percent >= hb->min_pcnt) {
264 if (include_children && child->unfolded)
265 count += hierarchy_count_rows(hb, child, true);
268 node = rb_next(node);
273 static bool hist_entry__toggle_fold(struct hist_entry *he)
278 if (!he->has_children)
281 he->unfolded = !he->unfolded;
285 static bool callchain_list__toggle_fold(struct callchain_list *cl)
290 if (!cl->has_children)
293 cl->unfolded = !cl->unfolded;
297 static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
299 struct rb_node *nd = rb_first(&node->rb_root);
301 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
302 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
303 struct callchain_list *chain;
306 list_for_each_entry(chain, &child->val, list) {
309 chain->has_children = chain->list.next != &child->val ||
310 !RB_EMPTY_ROOT(&child->rb_root);
312 chain->has_children = chain->list.next == &child->val &&
313 !RB_EMPTY_ROOT(&child->rb_root);
316 callchain_node__init_have_children_rb_tree(child);
320 static void callchain_node__init_have_children(struct callchain_node *node,
323 struct callchain_list *chain;
325 chain = list_entry(node->val.next, struct callchain_list, list);
326 chain->has_children = has_sibling;
328 if (!list_empty(&node->val)) {
329 chain = list_entry(node->val.prev, struct callchain_list, list);
330 chain->has_children = !RB_EMPTY_ROOT(&node->rb_root);
333 callchain_node__init_have_children_rb_tree(node);
336 static void callchain__init_have_children(struct rb_root *root)
338 struct rb_node *nd = rb_first(root);
339 bool has_sibling = nd && rb_next(nd);
341 for (nd = rb_first(root); nd; nd = rb_next(nd)) {
342 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
343 callchain_node__init_have_children(node, has_sibling);
344 if (callchain_param.mode == CHAIN_FLAT ||
345 callchain_param.mode == CHAIN_FOLDED)
346 callchain_node__make_parent_list(node);
350 static void hist_entry__init_have_children(struct hist_entry *he)
352 if (he->init_have_children)
356 he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
357 callchain__init_have_children(&he->sorted_chain);
359 he->has_children = !RB_EMPTY_ROOT(&he->hroot_out);
362 he->init_have_children = true;
365 static bool hist_browser__toggle_fold(struct hist_browser *browser)
367 struct hist_entry *he = browser->he_selection;
368 struct map_symbol *ms = browser->selection;
369 struct callchain_list *cl = container_of(ms, struct callchain_list, ms);
376 has_children = hist_entry__toggle_fold(he);
378 has_children = callchain_list__toggle_fold(cl);
383 hist_entry__init_have_children(he);
384 browser->b.nr_entries -= he->nr_rows;
387 browser->nr_callchain_rows -= he->nr_rows;
389 browser->nr_hierarchy_entries -= he->nr_rows;
391 if (symbol_conf.report_hierarchy)
392 child_rows = hierarchy_count_rows(browser, he, true);
396 he->nr_rows = callchain__count_rows(&he->sorted_chain);
398 he->nr_rows = hierarchy_count_rows(browser, he, false);
400 /* account grand children */
401 if (symbol_conf.report_hierarchy)
402 browser->b.nr_entries += child_rows - he->nr_rows;
404 if (!he->leaf && he->nr_rows == 0) {
405 he->has_no_entry = true;
409 if (symbol_conf.report_hierarchy)
410 browser->b.nr_entries -= child_rows - he->nr_rows;
412 if (he->has_no_entry)
413 he->has_no_entry = false;
418 browser->b.nr_entries += he->nr_rows;
421 browser->nr_callchain_rows += he->nr_rows;
423 browser->nr_hierarchy_entries += he->nr_rows;
428 /* If it doesn't have children, no toggling performed */
432 static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
437 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
438 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
439 struct callchain_list *chain;
440 bool has_children = false;
442 list_for_each_entry(chain, &child->val, list) {
444 callchain_list__set_folding(chain, unfold);
445 has_children = chain->has_children;
449 n += callchain_node__set_folding_rb_tree(child, unfold);
455 static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
457 struct callchain_list *chain;
458 bool has_children = false;
461 list_for_each_entry(chain, &node->val, list) {
463 callchain_list__set_folding(chain, unfold);
464 has_children = chain->has_children;
468 n += callchain_node__set_folding_rb_tree(node, unfold);
473 static int callchain__set_folding(struct rb_root *chain, bool unfold)
478 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
479 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
480 n += callchain_node__set_folding(node, unfold);
486 static int hierarchy_set_folding(struct hist_browser *hb, struct hist_entry *he,
487 bool unfold __maybe_unused)
491 struct hist_entry *child;
494 for (nd = rb_first(&he->hroot_out); nd; nd = rb_next(nd)) {
495 child = rb_entry(nd, struct hist_entry, rb_node);
496 percent = hist_entry__get_percent_limit(child);
497 if (!child->filtered && percent >= hb->min_pcnt)
504 static void hist_entry__set_folding(struct hist_entry *he,
505 struct hist_browser *hb, bool unfold)
507 hist_entry__init_have_children(he);
508 he->unfolded = unfold ? he->has_children : false;
510 if (he->has_children) {
514 n = callchain__set_folding(&he->sorted_chain, unfold);
516 n = hierarchy_set_folding(hb, he, unfold);
518 he->nr_rows = unfold ? n : 0;
524 __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
527 struct hist_entry *he;
530 nd = rb_first(&browser->hists->entries);
532 he = rb_entry(nd, struct hist_entry, rb_node);
534 /* set folding state even if it's currently folded */
535 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
537 hist_entry__set_folding(he, browser, unfold);
539 percent = hist_entry__get_percent_limit(he);
540 if (he->filtered || percent < browser->min_pcnt)
543 if (!he->depth || unfold)
544 browser->nr_hierarchy_entries++;
546 browser->nr_callchain_rows += he->nr_rows;
547 else if (unfold && !hist_entry__has_hierarchy_children(he, browser->min_pcnt)) {
548 browser->nr_hierarchy_entries++;
549 he->has_no_entry = true;
552 he->has_no_entry = false;
556 static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
558 browser->nr_hierarchy_entries = 0;
559 browser->nr_callchain_rows = 0;
560 __hist_browser__set_folding(browser, unfold);
562 browser->b.nr_entries = hist_browser__nr_entries(browser);
563 /* Go to the start, we may be way after valid entries after a collapse */
564 ui_browser__reset_index(&browser->b);
567 static void ui_browser__warn_lost_events(struct ui_browser *browser)
569 ui_browser__warning(browser, 4,
570 "Events are being lost, check IO/CPU overload!\n\n"
571 "You may want to run 'perf' using a RT scheduler policy:\n\n"
572 " perf top -r 80\n\n"
573 "Or reduce the sampling frequency.");
576 static int hist_browser__title(struct hist_browser *browser, char *bf, size_t size)
578 return browser->title ? browser->title(browser, bf, size) : 0;
581 int hist_browser__run(struct hist_browser *browser, const char *help)
585 struct hist_browser_timer *hbt = browser->hbt;
586 int delay_secs = hbt ? hbt->refresh : 0;
588 browser->b.entries = &browser->hists->entries;
589 browser->b.nr_entries = hist_browser__nr_entries(browser);
591 hist_browser__title(browser, title, sizeof(title));
593 if (ui_browser__show(&browser->b, title, "%s", help) < 0)
597 key = ui_browser__run(&browser->b, delay_secs);
602 hbt->timer(hbt->arg);
604 if (hist_browser__has_filter(browser) ||
605 symbol_conf.report_hierarchy)
606 hist_browser__update_nr_entries(browser);
608 nr_entries = hist_browser__nr_entries(browser);
609 ui_browser__update_nr_entries(&browser->b, nr_entries);
611 if (browser->hists->stats.nr_lost_warned !=
612 browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
613 browser->hists->stats.nr_lost_warned =
614 browser->hists->stats.nr_events[PERF_RECORD_LOST];
615 ui_browser__warn_lost_events(&browser->b);
618 hist_browser__title(browser, title, sizeof(title));
619 ui_browser__show_title(&browser->b, title);
622 case 'D': { /* Debug */
624 struct hist_entry *h = rb_entry(browser->b.top,
625 struct hist_entry, rb_node);
627 ui_helpline__fpush("%d: nr_ent=(%d,%d), rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
628 seq++, browser->b.nr_entries,
629 browser->hists->nr_entries,
633 h->row_offset, h->nr_rows);
637 /* Collapse the whole world. */
638 hist_browser__set_folding(browser, false);
641 /* Expand the whole world. */
642 hist_browser__set_folding(browser, true);
645 browser->show_headers = !browser->show_headers;
646 hist_browser__update_rows(browser);
649 if (hist_browser__toggle_fold(browser))
657 ui_browser__hide(&browser->b);
661 struct callchain_print_arg {
662 /* for hists browser */
664 bool is_current_entry;
671 typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
672 struct callchain_list *chain,
673 const char *str, int offset,
675 struct callchain_print_arg *arg);
677 static void hist_browser__show_callchain_entry(struct hist_browser *browser,
678 struct callchain_list *chain,
679 const char *str, int offset,
681 struct callchain_print_arg *arg)
684 char folded_sign = callchain_list__folded(chain);
685 bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
687 color = HE_COLORSET_NORMAL;
688 width = browser->b.width - (offset + 2);
689 if (ui_browser__is_current_entry(&browser->b, row)) {
690 browser->selection = &chain->ms;
691 color = HE_COLORSET_SELECTED;
692 arg->is_current_entry = true;
695 ui_browser__set_color(&browser->b, color);
696 hist_browser__gotorc(browser, row, 0);
697 ui_browser__write_nstring(&browser->b, " ", offset);
698 ui_browser__printf(&browser->b, "%c", folded_sign);
699 ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
700 ui_browser__write_nstring(&browser->b, str, width);
703 static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
704 struct callchain_list *chain,
705 const char *str, int offset,
706 unsigned short row __maybe_unused,
707 struct callchain_print_arg *arg)
709 char folded_sign = callchain_list__folded(chain);
711 arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
715 typedef bool (*check_output_full_fn)(struct hist_browser *browser,
718 static bool hist_browser__check_output_full(struct hist_browser *browser,
721 return browser->b.rows == row;
724 static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
725 unsigned short row __maybe_unused)
730 #define LEVEL_OFFSET_STEP 3
732 static int hist_browser__show_callchain_list(struct hist_browser *browser,
733 struct callchain_node *node,
734 struct callchain_list *chain,
735 unsigned short row, u64 total,
736 bool need_percent, int offset,
737 print_callchain_entry_fn print,
738 struct callchain_print_arg *arg)
740 char bf[1024], *alloc_str;
743 if (arg->row_offset != 0) {
749 str = callchain_list__sym_name(chain, bf, sizeof(bf),
755 callchain_node__scnprintf_value(node, buf, sizeof(buf),
758 if (asprintf(&alloc_str, "%s %s", buf, str) < 0)
759 str = "Not enough memory!";
764 print(browser, chain, str, offset, row, arg);
770 static bool check_percent_display(struct rb_node *node, u64 parent_total)
772 struct callchain_node *child;
780 child = rb_entry(node, struct callchain_node, rb_node);
781 return callchain_cumul_hits(child) != parent_total;
784 static int hist_browser__show_callchain_flat(struct hist_browser *browser,
785 struct rb_root *root,
786 unsigned short row, u64 total,
788 print_callchain_entry_fn print,
789 struct callchain_print_arg *arg,
790 check_output_full_fn is_output_full)
792 struct rb_node *node;
793 int first_row = row, offset = LEVEL_OFFSET_STEP;
796 node = rb_first(root);
797 need_percent = check_percent_display(node, parent_total);
800 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
801 struct rb_node *next = rb_next(node);
802 struct callchain_list *chain;
803 char folded_sign = ' ';
805 int extra_offset = 0;
807 list_for_each_entry(chain, &child->parent_val, list) {
808 bool was_first = first;
812 else if (need_percent)
813 extra_offset = LEVEL_OFFSET_STEP;
815 folded_sign = callchain_list__folded(chain);
817 row += hist_browser__show_callchain_list(browser, child,
819 was_first && need_percent,
820 offset + extra_offset,
823 if (is_output_full(browser, row))
826 if (folded_sign == '+')
830 list_for_each_entry(chain, &child->val, list) {
831 bool was_first = first;
835 else if (need_percent)
836 extra_offset = LEVEL_OFFSET_STEP;
838 folded_sign = callchain_list__folded(chain);
840 row += hist_browser__show_callchain_list(browser, child,
842 was_first && need_percent,
843 offset + extra_offset,
846 if (is_output_full(browser, row))
849 if (folded_sign == '+')
854 if (is_output_full(browser, row))
859 return row - first_row;
862 static char *hist_browser__folded_callchain_str(struct hist_browser *browser,
863 struct callchain_list *chain,
864 char *value_str, char *old_str)
870 str = callchain_list__sym_name(chain, bf, sizeof(bf),
873 if (asprintf(&new, "%s%s%s", old_str,
874 symbol_conf.field_sep ?: ";", str) < 0)
878 if (asprintf(&new, "%s %s", value_str, str) < 0)
881 if (asprintf(&new, "%s", str) < 0)
888 static int hist_browser__show_callchain_folded(struct hist_browser *browser,
889 struct rb_root *root,
890 unsigned short row, u64 total,
892 print_callchain_entry_fn print,
893 struct callchain_print_arg *arg,
894 check_output_full_fn is_output_full)
896 struct rb_node *node;
897 int first_row = row, offset = LEVEL_OFFSET_STEP;
900 node = rb_first(root);
901 need_percent = check_percent_display(node, parent_total);
904 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
905 struct rb_node *next = rb_next(node);
906 struct callchain_list *chain, *first_chain = NULL;
908 char *value_str = NULL, *value_str_alloc = NULL;
909 char *chain_str = NULL, *chain_str_alloc = NULL;
911 if (arg->row_offset != 0) {
919 callchain_node__scnprintf_value(child, buf, sizeof(buf), total);
920 if (asprintf(&value_str, "%s", buf) < 0) {
921 value_str = (char *)"<...>";
924 value_str_alloc = value_str;
927 list_for_each_entry(chain, &child->parent_val, list) {
928 chain_str = hist_browser__folded_callchain_str(browser,
929 chain, value_str, chain_str);
935 if (chain_str == NULL) {
936 chain_str = (char *)"Not enough memory!";
940 chain_str_alloc = chain_str;
943 list_for_each_entry(chain, &child->val, list) {
944 chain_str = hist_browser__folded_callchain_str(browser,
945 chain, value_str, chain_str);
951 if (chain_str == NULL) {
952 chain_str = (char *)"Not enough memory!";
956 chain_str_alloc = chain_str;
960 print(browser, first_chain, chain_str, offset, row++, arg);
961 free(value_str_alloc);
962 free(chain_str_alloc);
965 if (is_output_full(browser, row))
970 return row - first_row;
973 static int hist_browser__show_callchain_graph(struct hist_browser *browser,
974 struct rb_root *root, int level,
975 unsigned short row, u64 total,
977 print_callchain_entry_fn print,
978 struct callchain_print_arg *arg,
979 check_output_full_fn is_output_full)
981 struct rb_node *node;
982 int first_row = row, offset = level * LEVEL_OFFSET_STEP;
984 u64 percent_total = total;
986 if (callchain_param.mode == CHAIN_GRAPH_REL)
987 percent_total = parent_total;
989 node = rb_first(root);
990 need_percent = check_percent_display(node, parent_total);
993 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
994 struct rb_node *next = rb_next(node);
995 struct callchain_list *chain;
996 char folded_sign = ' ';
998 int extra_offset = 0;
1000 list_for_each_entry(chain, &child->val, list) {
1001 bool was_first = first;
1005 else if (need_percent)
1006 extra_offset = LEVEL_OFFSET_STEP;
1008 folded_sign = callchain_list__folded(chain);
1010 row += hist_browser__show_callchain_list(browser, child,
1011 chain, row, percent_total,
1012 was_first && need_percent,
1013 offset + extra_offset,
1016 if (is_output_full(browser, row))
1019 if (folded_sign == '+')
1023 if (folded_sign == '-') {
1024 const int new_level = level + (extra_offset ? 2 : 1);
1026 row += hist_browser__show_callchain_graph(browser, &child->rb_root,
1027 new_level, row, total,
1028 child->children_hit,
1029 print, arg, is_output_full);
1031 if (is_output_full(browser, row))
1036 return row - first_row;
1039 static int hist_browser__show_callchain(struct hist_browser *browser,
1040 struct hist_entry *entry, int level,
1042 print_callchain_entry_fn print,
1043 struct callchain_print_arg *arg,
1044 check_output_full_fn is_output_full)
1046 u64 total = hists__total_period(entry->hists);
1050 if (symbol_conf.cumulate_callchain)
1051 parent_total = entry->stat_acc->period;
1053 parent_total = entry->stat.period;
1055 if (callchain_param.mode == CHAIN_FLAT) {
1056 printed = hist_browser__show_callchain_flat(browser,
1057 &entry->sorted_chain, row,
1058 total, parent_total, print, arg,
1060 } else if (callchain_param.mode == CHAIN_FOLDED) {
1061 printed = hist_browser__show_callchain_folded(browser,
1062 &entry->sorted_chain, row,
1063 total, parent_total, print, arg,
1066 printed = hist_browser__show_callchain_graph(browser,
1067 &entry->sorted_chain, level, row,
1068 total, parent_total, print, arg,
1072 if (arg->is_current_entry)
1073 browser->he_selection = entry;
1079 struct ui_browser *b;
1084 int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
1086 struct hpp_arg *arg = hpp->ptr;
1091 va_start(args, fmt);
1092 len = va_arg(args, int);
1093 percent = va_arg(args, double);
1096 ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
1098 ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
1099 ui_browser__printf(arg->b, "%s", hpp->buf);
1104 #define __HPP_COLOR_PERCENT_FN(_type, _field) \
1105 static u64 __hpp_get_##_field(struct hist_entry *he) \
1107 return he->stat._field; \
1111 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
1112 struct perf_hpp *hpp, \
1113 struct hist_entry *he) \
1115 return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%", \
1116 __hpp__slsmg_color_printf, true); \
1119 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
1120 static u64 __hpp_get_acc_##_field(struct hist_entry *he) \
1122 return he->stat_acc->_field; \
1126 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
1127 struct perf_hpp *hpp, \
1128 struct hist_entry *he) \
1130 if (!symbol_conf.cumulate_callchain) { \
1131 struct hpp_arg *arg = hpp->ptr; \
1132 int len = fmt->user_len ?: fmt->len; \
1133 int ret = scnprintf(hpp->buf, hpp->size, \
1134 "%*s", len, "N/A"); \
1135 ui_browser__printf(arg->b, "%s", hpp->buf); \
1139 return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field, \
1140 " %*.2f%%", __hpp__slsmg_color_printf, true); \
1143 __HPP_COLOR_PERCENT_FN(overhead, period)
1144 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
1145 __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
1146 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
1147 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
1148 __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
1150 #undef __HPP_COLOR_PERCENT_FN
1151 #undef __HPP_COLOR_ACC_PERCENT_FN
1153 void hist_browser__init_hpp(void)
1155 perf_hpp__format[PERF_HPP__OVERHEAD].color =
1156 hist_browser__hpp_color_overhead;
1157 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
1158 hist_browser__hpp_color_overhead_sys;
1159 perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
1160 hist_browser__hpp_color_overhead_us;
1161 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
1162 hist_browser__hpp_color_overhead_guest_sys;
1163 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
1164 hist_browser__hpp_color_overhead_guest_us;
1165 perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
1166 hist_browser__hpp_color_overhead_acc;
1169 static int hist_browser__show_entry(struct hist_browser *browser,
1170 struct hist_entry *entry,
1174 int width = browser->b.width;
1175 char folded_sign = ' ';
1176 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1177 off_t row_offset = entry->row_offset;
1179 struct perf_hpp_fmt *fmt;
1181 if (current_entry) {
1182 browser->he_selection = entry;
1183 browser->selection = &entry->ms;
1186 if (symbol_conf.use_callchain) {
1187 hist_entry__init_have_children(entry);
1188 folded_sign = hist_entry__folded(entry);
1191 if (row_offset == 0) {
1192 struct hpp_arg arg = {
1194 .folded_sign = folded_sign,
1195 .current_entry = current_entry,
1199 hist_browser__gotorc(browser, row, 0);
1201 hists__for_each_format(browser->hists, fmt) {
1203 struct perf_hpp hpp = {
1209 if (perf_hpp__should_skip(fmt, entry->hists) ||
1210 column++ < browser->b.horiz_scroll)
1213 if (current_entry && browser->b.navkeypressed) {
1214 ui_browser__set_color(&browser->b,
1215 HE_COLORSET_SELECTED);
1217 ui_browser__set_color(&browser->b,
1218 HE_COLORSET_NORMAL);
1222 if (symbol_conf.use_callchain) {
1223 ui_browser__printf(&browser->b, "%c ", folded_sign);
1228 ui_browser__printf(&browser->b, " ");
1233 int ret = fmt->color(fmt, &hpp, entry);
1234 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1236 * fmt->color() already used ui_browser to
1237 * print the non alignment bits, skip it (+ret):
1239 ui_browser__printf(&browser->b, "%s", s + ret);
1241 hist_entry__snprintf_alignment(entry, &hpp, fmt, fmt->entry(fmt, &hpp, entry));
1242 ui_browser__printf(&browser->b, "%s", s);
1244 width -= hpp.buf - s;
1247 /* The scroll bar isn't being used */
1248 if (!browser->b.navkeypressed)
1251 ui_browser__write_nstring(&browser->b, "", width);
1258 if (folded_sign == '-' && row != browser->b.rows) {
1259 struct callchain_print_arg arg = {
1260 .row_offset = row_offset,
1261 .is_current_entry = current_entry,
1264 printed += hist_browser__show_callchain(browser, entry, 1, row,
1265 hist_browser__show_callchain_entry, &arg,
1266 hist_browser__check_output_full);
1272 static int hist_browser__show_hierarchy_entry(struct hist_browser *browser,
1273 struct hist_entry *entry,
1278 int width = browser->b.width;
1279 char folded_sign = ' ';
1280 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1281 off_t row_offset = entry->row_offset;
1283 struct perf_hpp_fmt *fmt;
1284 struct perf_hpp_list_node *fmt_node;
1285 struct hpp_arg arg = {
1287 .current_entry = current_entry,
1290 int hierarchy_indent = (entry->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
1292 if (current_entry) {
1293 browser->he_selection = entry;
1294 browser->selection = &entry->ms;
1297 hist_entry__init_have_children(entry);
1298 folded_sign = hist_entry__folded(entry);
1299 arg.folded_sign = folded_sign;
1301 if (entry->leaf && row_offset) {
1303 goto show_callchain;
1306 hist_browser__gotorc(browser, row, 0);
1308 if (current_entry && browser->b.navkeypressed)
1309 ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1311 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1313 ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1314 width -= level * HIERARCHY_INDENT;
1316 /* the first hpp_list_node is for overhead columns */
1317 fmt_node = list_first_entry(&entry->hists->hpp_formats,
1318 struct perf_hpp_list_node, list);
1319 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1321 struct perf_hpp hpp = {
1327 if (perf_hpp__should_skip(fmt, entry->hists) ||
1328 column++ < browser->b.horiz_scroll)
1331 if (current_entry && browser->b.navkeypressed) {
1332 ui_browser__set_color(&browser->b,
1333 HE_COLORSET_SELECTED);
1335 ui_browser__set_color(&browser->b,
1336 HE_COLORSET_NORMAL);
1340 ui_browser__printf(&browser->b, "%c", folded_sign);
1344 ui_browser__printf(&browser->b, " ");
1349 int ret = fmt->color(fmt, &hpp, entry);
1350 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1352 * fmt->color() already used ui_browser to
1353 * print the non alignment bits, skip it (+ret):
1355 ui_browser__printf(&browser->b, "%s", s + ret);
1357 int ret = fmt->entry(fmt, &hpp, entry);
1358 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1359 ui_browser__printf(&browser->b, "%s", s);
1361 width -= hpp.buf - s;
1364 ui_browser__write_nstring(&browser->b, "", hierarchy_indent);
1365 width -= hierarchy_indent;
1367 if (column >= browser->b.horiz_scroll) {
1369 struct perf_hpp hpp = {
1375 if (current_entry && browser->b.navkeypressed) {
1376 ui_browser__set_color(&browser->b,
1377 HE_COLORSET_SELECTED);
1379 ui_browser__set_color(&browser->b,
1380 HE_COLORSET_NORMAL);
1383 perf_hpp_list__for_each_format(entry->hpp_list, fmt) {
1384 ui_browser__write_nstring(&browser->b, "", 2);
1388 * No need to call hist_entry__snprintf_alignment()
1389 * since this fmt is always the last column in the
1393 width -= fmt->color(fmt, &hpp, entry);
1397 width -= fmt->entry(fmt, &hpp, entry);
1398 ui_browser__printf(&browser->b, "%s", ltrim(s));
1400 while (isspace(s[i++]))
1406 /* The scroll bar isn't being used */
1407 if (!browser->b.navkeypressed)
1410 ui_browser__write_nstring(&browser->b, "", width);
1416 if (entry->leaf && folded_sign == '-' && row != browser->b.rows) {
1417 struct callchain_print_arg carg = {
1418 .row_offset = row_offset,
1421 printed += hist_browser__show_callchain(browser, entry,
1423 hist_browser__show_callchain_entry, &carg,
1424 hist_browser__check_output_full);
1430 static int hist_browser__show_no_entry(struct hist_browser *browser,
1431 unsigned short row, int level)
1433 int width = browser->b.width;
1434 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1438 struct perf_hpp_fmt *fmt;
1439 struct perf_hpp_list_node *fmt_node;
1440 int indent = browser->hists->nr_hpp_node - 2;
1442 if (current_entry) {
1443 browser->he_selection = NULL;
1444 browser->selection = NULL;
1447 hist_browser__gotorc(browser, row, 0);
1449 if (current_entry && browser->b.navkeypressed)
1450 ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1452 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1454 ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1455 width -= level * HIERARCHY_INDENT;
1457 /* the first hpp_list_node is for overhead columns */
1458 fmt_node = list_first_entry(&browser->hists->hpp_formats,
1459 struct perf_hpp_list_node, list);
1460 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1461 if (perf_hpp__should_skip(fmt, browser->hists) ||
1462 column++ < browser->b.horiz_scroll)
1465 ret = fmt->width(fmt, NULL, browser->hists);
1468 /* for folded sign */
1472 /* space between columns */
1476 ui_browser__write_nstring(&browser->b, "", ret);
1480 ui_browser__write_nstring(&browser->b, "", indent * HIERARCHY_INDENT);
1481 width -= indent * HIERARCHY_INDENT;
1483 if (column >= browser->b.horiz_scroll) {
1486 ret = snprintf(buf, sizeof(buf), "no entry >= %.2f%%", browser->min_pcnt);
1487 ui_browser__printf(&browser->b, " %s", buf);
1491 /* The scroll bar isn't being used */
1492 if (!browser->b.navkeypressed)
1495 ui_browser__write_nstring(&browser->b, "", width);
1499 static int advance_hpp_check(struct perf_hpp *hpp, int inc)
1501 advance_hpp(hpp, inc);
1502 return hpp->size <= 0;
1506 hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf,
1507 size_t size, int line)
1509 struct hists *hists = browser->hists;
1510 struct perf_hpp dummy_hpp = {
1514 struct perf_hpp_fmt *fmt;
1519 if (symbol_conf.use_callchain) {
1520 ret = scnprintf(buf, size, " ");
1521 if (advance_hpp_check(&dummy_hpp, ret))
1525 hists__for_each_format(browser->hists, fmt) {
1526 if (perf_hpp__should_skip(fmt, hists) || column++ < browser->b.horiz_scroll)
1529 ret = fmt->header(fmt, &dummy_hpp, hists, line, &span);
1530 if (advance_hpp_check(&dummy_hpp, ret))
1536 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
1537 if (advance_hpp_check(&dummy_hpp, ret))
1544 static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser, char *buf, size_t size)
1546 struct hists *hists = browser->hists;
1547 struct perf_hpp dummy_hpp = {
1551 struct perf_hpp_fmt *fmt;
1552 struct perf_hpp_list_node *fmt_node;
1555 int indent = hists->nr_hpp_node - 2;
1556 bool first_node, first_col;
1558 ret = scnprintf(buf, size, " ");
1559 if (advance_hpp_check(&dummy_hpp, ret))
1562 /* the first hpp_list_node is for overhead columns */
1563 fmt_node = list_first_entry(&hists->hpp_formats,
1564 struct perf_hpp_list_node, list);
1565 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1566 if (column++ < browser->b.horiz_scroll)
1569 ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1570 if (advance_hpp_check(&dummy_hpp, ret))
1573 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
1574 if (advance_hpp_check(&dummy_hpp, ret))
1578 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "%*s",
1579 indent * HIERARCHY_INDENT, "");
1580 if (advance_hpp_check(&dummy_hpp, ret))
1584 list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
1586 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " / ");
1587 if (advance_hpp_check(&dummy_hpp, ret))
1593 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1596 if (perf_hpp__should_skip(fmt, hists))
1600 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "+");
1601 if (advance_hpp_check(&dummy_hpp, ret))
1606 ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1607 dummy_hpp.buf[ret] = '\0';
1609 start = trim(dummy_hpp.buf);
1610 ret = strlen(start);
1612 if (start != dummy_hpp.buf)
1613 memmove(dummy_hpp.buf, start, ret + 1);
1615 if (advance_hpp_check(&dummy_hpp, ret))
1623 static void hists_browser__hierarchy_headers(struct hist_browser *browser)
1627 hists_browser__scnprintf_hierarchy_headers(browser, headers,
1630 ui_browser__gotorc(&browser->b, 0, 0);
1631 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1632 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1635 static void hists_browser__headers(struct hist_browser *browser)
1637 struct hists *hists = browser->hists;
1638 struct perf_hpp_list *hpp_list = hists->hpp_list;
1642 for (line = 0; line < hpp_list->nr_header_lines; line++) {
1645 hists_browser__scnprintf_headers(browser, headers,
1646 sizeof(headers), line);
1648 ui_browser__gotorc(&browser->b, line, 0);
1649 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1650 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1654 static void hist_browser__show_headers(struct hist_browser *browser)
1656 if (symbol_conf.report_hierarchy)
1657 hists_browser__hierarchy_headers(browser);
1659 hists_browser__headers(browser);
1662 static void ui_browser__hists_init_top(struct ui_browser *browser)
1664 if (browser->top == NULL) {
1665 struct hist_browser *hb;
1667 hb = container_of(browser, struct hist_browser, b);
1668 browser->top = rb_first(&hb->hists->entries);
1672 static unsigned int hist_browser__refresh(struct ui_browser *browser)
1675 u16 header_offset = 0;
1677 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
1678 struct hists *hists = hb->hists;
1680 if (hb->show_headers) {
1681 struct perf_hpp_list *hpp_list = hists->hpp_list;
1683 hist_browser__show_headers(hb);
1684 header_offset = hpp_list->nr_header_lines;
1687 ui_browser__hists_init_top(browser);
1688 hb->he_selection = NULL;
1689 hb->selection = NULL;
1691 for (nd = browser->top; nd; nd = rb_hierarchy_next(nd)) {
1692 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1696 /* let it move to sibling */
1697 h->unfolded = false;
1701 percent = hist_entry__get_percent_limit(h);
1702 if (percent < hb->min_pcnt)
1705 if (symbol_conf.report_hierarchy) {
1706 row += hist_browser__show_hierarchy_entry(hb, h, row,
1708 if (row == browser->rows)
1711 if (h->has_no_entry) {
1712 hist_browser__show_no_entry(hb, row, h->depth + 1);
1716 row += hist_browser__show_entry(hb, h, row);
1719 if (row == browser->rows)
1723 return row + header_offset;
1726 static struct rb_node *hists__filter_entries(struct rb_node *nd,
1729 while (nd != NULL) {
1730 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1731 float percent = hist_entry__get_percent_limit(h);
1733 if (!h->filtered && percent >= min_pcnt)
1737 * If it's filtered, its all children also were filtered.
1738 * So move to sibling node.
1743 nd = rb_hierarchy_next(nd);
1749 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
1752 while (nd != NULL) {
1753 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1754 float percent = hist_entry__get_percent_limit(h);
1756 if (!h->filtered && percent >= min_pcnt)
1759 nd = rb_hierarchy_prev(nd);
1765 static void ui_browser__hists_seek(struct ui_browser *browser,
1766 off_t offset, int whence)
1768 struct hist_entry *h;
1771 struct hist_browser *hb;
1773 hb = container_of(browser, struct hist_browser, b);
1775 if (browser->nr_entries == 0)
1778 ui_browser__hists_init_top(browser);
1782 nd = hists__filter_entries(rb_first(browser->entries),
1789 nd = rb_hierarchy_last(rb_last(browser->entries));
1790 nd = hists__filter_prev_entries(nd, hb->min_pcnt);
1798 * Moves not relative to the first visible entry invalidates its
1801 h = rb_entry(browser->top, struct hist_entry, rb_node);
1805 * Here we have to check if nd is expanded (+), if it is we can't go
1806 * the next top level hist_entry, instead we must compute an offset of
1807 * what _not_ to show and not change the first visible entry.
1809 * This offset increments when we are going from top to bottom and
1810 * decreases when we're going from bottom to top.
1812 * As we don't have backpointers to the top level in the callchains
1813 * structure, we need to always print the whole hist_entry callchain,
1814 * skipping the first ones that are before the first visible entry
1815 * and stop when we printed enough lines to fill the screen.
1823 h = rb_entry(nd, struct hist_entry, rb_node);
1824 if (h->unfolded && h->leaf) {
1825 u16 remaining = h->nr_rows - h->row_offset;
1826 if (offset > remaining) {
1827 offset -= remaining;
1830 h->row_offset += offset;
1836 nd = hists__filter_entries(rb_hierarchy_next(nd),
1842 } while (offset != 0);
1843 } else if (offset < 0) {
1845 h = rb_entry(nd, struct hist_entry, rb_node);
1846 if (h->unfolded && h->leaf) {
1848 if (-offset > h->row_offset) {
1849 offset += h->row_offset;
1852 h->row_offset += offset;
1858 if (-offset > h->nr_rows) {
1859 offset += h->nr_rows;
1862 h->row_offset = h->nr_rows + offset;
1870 nd = hists__filter_prev_entries(rb_hierarchy_prev(nd),
1878 * Last unfiltered hist_entry, check if it is
1879 * unfolded, if it is then we should have
1880 * row_offset at its last entry.
1882 h = rb_entry(nd, struct hist_entry, rb_node);
1883 if (h->unfolded && h->leaf)
1884 h->row_offset = h->nr_rows;
1891 h = rb_entry(nd, struct hist_entry, rb_node);
1896 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1897 struct hist_entry *he, FILE *fp,
1900 struct callchain_print_arg arg = {
1904 hist_browser__show_callchain(browser, he, level, 0,
1905 hist_browser__fprintf_callchain_entry, &arg,
1906 hist_browser__check_dump_full);
1910 static int hist_browser__fprintf_entry(struct hist_browser *browser,
1911 struct hist_entry *he, FILE *fp)
1915 char folded_sign = ' ';
1916 struct perf_hpp hpp = {
1920 struct perf_hpp_fmt *fmt;
1924 if (symbol_conf.use_callchain) {
1925 folded_sign = hist_entry__folded(he);
1926 printed += fprintf(fp, "%c ", folded_sign);
1929 hists__for_each_format(browser->hists, fmt) {
1930 if (perf_hpp__should_skip(fmt, he->hists))
1934 ret = scnprintf(hpp.buf, hpp.size, " ");
1935 advance_hpp(&hpp, ret);
1939 ret = fmt->entry(fmt, &hpp, he);
1940 ret = hist_entry__snprintf_alignment(he, &hpp, fmt, ret);
1941 advance_hpp(&hpp, ret);
1943 printed += fprintf(fp, "%s\n", s);
1945 if (folded_sign == '-')
1946 printed += hist_browser__fprintf_callchain(browser, he, fp, 1);
1952 static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,
1953 struct hist_entry *he,
1954 FILE *fp, int level)
1958 char folded_sign = ' ';
1959 struct perf_hpp hpp = {
1963 struct perf_hpp_fmt *fmt;
1964 struct perf_hpp_list_node *fmt_node;
1967 int hierarchy_indent = (he->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
1969 printed = fprintf(fp, "%*s", level * HIERARCHY_INDENT, "");
1971 folded_sign = hist_entry__folded(he);
1972 printed += fprintf(fp, "%c", folded_sign);
1974 /* the first hpp_list_node is for overhead columns */
1975 fmt_node = list_first_entry(&he->hists->hpp_formats,
1976 struct perf_hpp_list_node, list);
1977 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1979 ret = scnprintf(hpp.buf, hpp.size, " ");
1980 advance_hpp(&hpp, ret);
1984 ret = fmt->entry(fmt, &hpp, he);
1985 advance_hpp(&hpp, ret);
1988 ret = scnprintf(hpp.buf, hpp.size, "%*s", hierarchy_indent, "");
1989 advance_hpp(&hpp, ret);
1991 perf_hpp_list__for_each_format(he->hpp_list, fmt) {
1992 ret = scnprintf(hpp.buf, hpp.size, " ");
1993 advance_hpp(&hpp, ret);
1995 ret = fmt->entry(fmt, &hpp, he);
1996 advance_hpp(&hpp, ret);
1999 printed += fprintf(fp, "%s\n", rtrim(s));
2001 if (he->leaf && folded_sign == '-') {
2002 printed += hist_browser__fprintf_callchain(browser, he, fp,
2009 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
2011 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
2016 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
2018 if (symbol_conf.report_hierarchy) {
2019 printed += hist_browser__fprintf_hierarchy_entry(browser,
2023 printed += hist_browser__fprintf_entry(browser, h, fp);
2026 nd = hists__filter_entries(rb_hierarchy_next(nd),
2033 static int hist_browser__dump(struct hist_browser *browser)
2039 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
2040 if (access(filename, F_OK))
2043 * XXX: Just an arbitrary lazy upper limit
2045 if (++browser->print_seq == 8192) {
2046 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
2051 fp = fopen(filename, "w");
2054 const char *err = str_error_r(errno, bf, sizeof(bf));
2055 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
2059 ++browser->print_seq;
2060 hist_browser__fprintf(browser, fp);
2062 ui_helpline__fpush("%s written!", filename);
2067 void hist_browser__init(struct hist_browser *browser,
2068 struct hists *hists)
2070 struct perf_hpp_fmt *fmt;
2072 browser->hists = hists;
2073 browser->b.refresh = hist_browser__refresh;
2074 browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
2075 browser->b.seek = ui_browser__hists_seek;
2076 browser->b.use_navkeypressed = true;
2077 browser->show_headers = symbol_conf.show_hist_headers;
2079 if (symbol_conf.report_hierarchy) {
2080 struct perf_hpp_list_node *fmt_node;
2082 /* count overhead columns (in the first node) */
2083 fmt_node = list_first_entry(&hists->hpp_formats,
2084 struct perf_hpp_list_node, list);
2085 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt)
2086 ++browser->b.columns;
2088 /* add a single column for whole hierarchy sort keys*/
2089 ++browser->b.columns;
2091 hists__for_each_format(hists, fmt)
2092 ++browser->b.columns;
2095 hists__reset_column_width(hists);
2098 struct hist_browser *hist_browser__new(struct hists *hists)
2100 struct hist_browser *browser = zalloc(sizeof(*browser));
2103 hist_browser__init(browser, hists);
2108 static struct hist_browser *
2109 perf_evsel_browser__new(struct perf_evsel *evsel,
2110 struct hist_browser_timer *hbt,
2111 struct perf_env *env)
2113 struct hist_browser *browser = hist_browser__new(evsel__hists(evsel));
2118 browser->title = perf_evsel_browser_title;
2123 void hist_browser__delete(struct hist_browser *browser)
2128 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
2130 return browser->he_selection;
2133 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
2135 return browser->he_selection->thread;
2138 /* Check whether the browser is for 'top' or 'report' */
2139 static inline bool is_report_browser(void *timer)
2141 return timer == NULL;
2144 static int perf_evsel_browser_title(struct hist_browser *browser,
2145 char *bf, size_t size)
2147 struct hist_browser_timer *hbt = browser->hbt;
2148 struct hists *hists = browser->hists;
2151 const struct dso *dso = hists->dso_filter;
2152 const struct thread *thread = hists->thread_filter;
2153 int socket_id = hists->socket_filter;
2154 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
2155 u64 nr_events = hists->stats.total_period;
2156 struct perf_evsel *evsel = hists_to_evsel(hists);
2157 const char *ev_name = perf_evsel__name(evsel);
2159 size_t buflen = sizeof(buf);
2160 char ref[30] = " show reference callgraph, ";
2161 bool enable_ref = false;
2163 if (symbol_conf.filter_relative) {
2164 nr_samples = hists->stats.nr_non_filtered_samples;
2165 nr_events = hists->stats.total_non_filtered_period;
2168 if (perf_evsel__is_group_event(evsel)) {
2169 struct perf_evsel *pos;
2171 perf_evsel__group_desc(evsel, buf, buflen);
2174 for_each_group_member(pos, evsel) {
2175 struct hists *pos_hists = evsel__hists(pos);
2177 if (symbol_conf.filter_relative) {
2178 nr_samples += pos_hists->stats.nr_non_filtered_samples;
2179 nr_events += pos_hists->stats.total_non_filtered_period;
2181 nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
2182 nr_events += pos_hists->stats.total_period;
2187 if (symbol_conf.show_ref_callgraph &&
2188 strstr(ev_name, "call-graph=no"))
2190 nr_samples = convert_unit(nr_samples, &unit);
2191 printed = scnprintf(bf, size,
2192 "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64,
2193 nr_samples, unit, ev_name, enable_ref ? ref : " ", nr_events);
2196 if (hists->uid_filter_str)
2197 printed += snprintf(bf + printed, size - printed,
2198 ", UID: %s", hists->uid_filter_str);
2200 if (hists__has(hists, thread)) {
2201 printed += scnprintf(bf + printed, size - printed,
2203 (thread->comm_set ? thread__comm_str(thread) : ""),
2206 printed += scnprintf(bf + printed, size - printed,
2208 (thread->comm_set ? thread__comm_str(thread) : ""));
2212 printed += scnprintf(bf + printed, size - printed,
2213 ", DSO: %s", dso->short_name);
2215 printed += scnprintf(bf + printed, size - printed,
2216 ", Processor Socket: %d", socket_id);
2217 if (!is_report_browser(hbt)) {
2218 struct perf_top *top = hbt->arg;
2221 printed += scnprintf(bf + printed, size - printed, " [z]");
2227 static inline void free_popup_options(char **options, int n)
2231 for (i = 0; i < n; ++i)
2236 * Only runtime switching of perf data file will make "input_name" point
2237 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
2238 * whether we need to call free() for current "input_name" during the switch.
2240 static bool is_input_name_malloced = false;
2242 static int switch_data_file(void)
2244 char *pwd, *options[32], *abs_path[32], *tmp;
2246 int nr_options = 0, choice = -1, ret = -1;
2247 struct dirent *dent;
2249 pwd = getenv("PWD");
2253 pwd_dir = opendir(pwd);
2257 memset(options, 0, sizeof(options));
2258 memset(options, 0, sizeof(abs_path));
2260 while ((dent = readdir(pwd_dir))) {
2261 char path[PATH_MAX];
2263 char *name = dent->d_name;
2266 if (!(dent->d_type == DT_REG))
2269 snprintf(path, sizeof(path), "%s/%s", pwd, name);
2271 file = fopen(path, "r");
2275 if (fread(&magic, 1, 8, file) < 8)
2276 goto close_file_and_continue;
2278 if (is_perf_magic(magic)) {
2279 options[nr_options] = strdup(name);
2280 if (!options[nr_options])
2281 goto close_file_and_continue;
2283 abs_path[nr_options] = strdup(path);
2284 if (!abs_path[nr_options]) {
2285 zfree(&options[nr_options]);
2286 ui__warning("Can't search all data files due to memory shortage.\n");
2294 close_file_and_continue:
2296 if (nr_options >= 32) {
2297 ui__warning("Too many perf data files in PWD!\n"
2298 "Only the first 32 files will be listed.\n");
2305 choice = ui__popup_menu(nr_options, options);
2306 if (choice < nr_options && choice >= 0) {
2307 tmp = strdup(abs_path[choice]);
2309 if (is_input_name_malloced)
2310 free((void *)input_name);
2312 is_input_name_malloced = true;
2315 ui__warning("Data switch failed due to memory shortage!\n");
2319 free_popup_options(options, nr_options);
2320 free_popup_options(abs_path, nr_options);
2324 struct popup_action {
2325 struct thread *thread;
2326 struct map_symbol ms;
2329 int (*fn)(struct hist_browser *browser, struct popup_action *act);
2333 do_annotate(struct hist_browser *browser, struct popup_action *act)
2335 struct perf_evsel *evsel;
2336 struct annotation *notes;
2337 struct hist_entry *he;
2340 if (!objdump_path && perf_env__lookup_objdump(browser->env))
2343 notes = symbol__annotation(act->ms.sym);
2347 evsel = hists_to_evsel(browser->hists);
2348 err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
2349 he = hist_browser__selected_entry(browser);
2351 * offer option to annotate the other branch source or target
2352 * (if they exists) when returning from annotate
2354 if ((err == 'q' || err == CTRL('c')) && he->branch_info)
2357 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
2359 ui_browser__handle_resize(&browser->b);
2364 add_annotate_opt(struct hist_browser *browser __maybe_unused,
2365 struct popup_action *act, char **optstr,
2366 struct map *map, struct symbol *sym)
2368 if (sym == NULL || map->dso->annotate_warned)
2371 if (asprintf(optstr, "Annotate %s", sym->name) < 0)
2376 act->fn = do_annotate;
2381 do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
2383 struct thread *thread = act->thread;
2385 if ((!hists__has(browser->hists, thread) &&
2386 !hists__has(browser->hists, comm)) || thread == NULL)
2389 if (browser->hists->thread_filter) {
2390 pstack__remove(browser->pstack, &browser->hists->thread_filter);
2391 perf_hpp__set_elide(HISTC_THREAD, false);
2392 thread__zput(browser->hists->thread_filter);
2395 if (hists__has(browser->hists, thread)) {
2396 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
2397 thread->comm_set ? thread__comm_str(thread) : "",
2400 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
2401 thread->comm_set ? thread__comm_str(thread) : "");
2404 browser->hists->thread_filter = thread__get(thread);
2405 perf_hpp__set_elide(HISTC_THREAD, false);
2406 pstack__push(browser->pstack, &browser->hists->thread_filter);
2409 hists__filter_by_thread(browser->hists);
2410 hist_browser__reset(browser);
2415 add_thread_opt(struct hist_browser *browser, struct popup_action *act,
2416 char **optstr, struct thread *thread)
2420 if ((!hists__has(browser->hists, thread) &&
2421 !hists__has(browser->hists, comm)) || thread == NULL)
2424 if (hists__has(browser->hists, thread)) {
2425 ret = asprintf(optstr, "Zoom %s %s(%d) thread",
2426 browser->hists->thread_filter ? "out of" : "into",
2427 thread->comm_set ? thread__comm_str(thread) : "",
2430 ret = asprintf(optstr, "Zoom %s %s thread",
2431 browser->hists->thread_filter ? "out of" : "into",
2432 thread->comm_set ? thread__comm_str(thread) : "");
2437 act->thread = thread;
2438 act->fn = do_zoom_thread;
2443 do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
2445 struct map *map = act->ms.map;
2447 if (!hists__has(browser->hists, dso) || map == NULL)
2450 if (browser->hists->dso_filter) {
2451 pstack__remove(browser->pstack, &browser->hists->dso_filter);
2452 perf_hpp__set_elide(HISTC_DSO, false);
2453 browser->hists->dso_filter = NULL;
2456 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
2457 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
2458 browser->hists->dso_filter = map->dso;
2459 perf_hpp__set_elide(HISTC_DSO, true);
2460 pstack__push(browser->pstack, &browser->hists->dso_filter);
2463 hists__filter_by_dso(browser->hists);
2464 hist_browser__reset(browser);
2469 add_dso_opt(struct hist_browser *browser, struct popup_action *act,
2470 char **optstr, struct map *map)
2472 if (!hists__has(browser->hists, dso) || map == NULL)
2475 if (asprintf(optstr, "Zoom %s %s DSO",
2476 browser->hists->dso_filter ? "out of" : "into",
2477 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
2481 act->fn = do_zoom_dso;
2486 do_browse_map(struct hist_browser *browser __maybe_unused,
2487 struct popup_action *act)
2489 map__browse(act->ms.map);
2494 add_map_opt(struct hist_browser *browser,
2495 struct popup_action *act, char **optstr, struct map *map)
2497 if (!hists__has(browser->hists, dso) || map == NULL)
2500 if (asprintf(optstr, "Browse map details") < 0)
2504 act->fn = do_browse_map;
2509 do_run_script(struct hist_browser *browser __maybe_unused,
2510 struct popup_action *act)
2512 char script_opt[64];
2513 memset(script_opt, 0, sizeof(script_opt));
2516 scnprintf(script_opt, sizeof(script_opt), " -c %s ",
2517 thread__comm_str(act->thread));
2518 } else if (act->ms.sym) {
2519 scnprintf(script_opt, sizeof(script_opt), " -S %s ",
2523 script_browse(script_opt);
2528 add_script_opt(struct hist_browser *browser __maybe_unused,
2529 struct popup_action *act, char **optstr,
2530 struct thread *thread, struct symbol *sym)
2533 if (asprintf(optstr, "Run scripts for samples of thread [%s]",
2534 thread__comm_str(thread)) < 0)
2537 if (asprintf(optstr, "Run scripts for samples of symbol [%s]",
2541 if (asprintf(optstr, "Run scripts for all samples") < 0)
2545 act->thread = thread;
2547 act->fn = do_run_script;
2552 do_switch_data(struct hist_browser *browser __maybe_unused,
2553 struct popup_action *act __maybe_unused)
2555 if (switch_data_file()) {
2556 ui__warning("Won't switch the data files due to\n"
2557 "no valid data file get selected!\n");
2561 return K_SWITCH_INPUT_DATA;
2565 add_switch_opt(struct hist_browser *browser,
2566 struct popup_action *act, char **optstr)
2568 if (!is_report_browser(browser->hbt))
2571 if (asprintf(optstr, "Switch to another data file in PWD") < 0)
2574 act->fn = do_switch_data;
2579 do_exit_browser(struct hist_browser *browser __maybe_unused,
2580 struct popup_action *act __maybe_unused)
2586 add_exit_opt(struct hist_browser *browser __maybe_unused,
2587 struct popup_action *act, char **optstr)
2589 if (asprintf(optstr, "Exit") < 0)
2592 act->fn = do_exit_browser;
2597 do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
2599 if (!hists__has(browser->hists, socket) || act->socket < 0)
2602 if (browser->hists->socket_filter > -1) {
2603 pstack__remove(browser->pstack, &browser->hists->socket_filter);
2604 browser->hists->socket_filter = -1;
2605 perf_hpp__set_elide(HISTC_SOCKET, false);
2607 browser->hists->socket_filter = act->socket;
2608 perf_hpp__set_elide(HISTC_SOCKET, true);
2609 pstack__push(browser->pstack, &browser->hists->socket_filter);
2612 hists__filter_by_socket(browser->hists);
2613 hist_browser__reset(browser);
2618 add_socket_opt(struct hist_browser *browser, struct popup_action *act,
2619 char **optstr, int socket_id)
2621 if (!hists__has(browser->hists, socket) || socket_id < 0)
2624 if (asprintf(optstr, "Zoom %s Processor Socket %d",
2625 (browser->hists->socket_filter > -1) ? "out of" : "into",
2629 act->socket = socket_id;
2630 act->fn = do_zoom_socket;
2634 static void hist_browser__update_nr_entries(struct hist_browser *hb)
2637 struct rb_node *nd = rb_first(&hb->hists->entries);
2639 if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
2640 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
2644 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2646 nd = rb_hierarchy_next(nd);
2649 hb->nr_non_filtered_entries = nr_entries;
2650 hb->nr_hierarchy_entries = nr_entries;
2653 static void hist_browser__update_percent_limit(struct hist_browser *hb,
2656 struct hist_entry *he;
2657 struct rb_node *nd = rb_first(&hb->hists->entries);
2658 u64 total = hists__total_period(hb->hists);
2659 u64 min_callchain_hits = total * (percent / 100);
2661 hb->min_pcnt = callchain_param.min_percent = percent;
2663 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2664 he = rb_entry(nd, struct hist_entry, rb_node);
2666 if (he->has_no_entry) {
2667 he->has_no_entry = false;
2671 if (!he->leaf || !symbol_conf.use_callchain)
2674 if (callchain_param.mode == CHAIN_GRAPH_REL) {
2675 total = he->stat.period;
2677 if (symbol_conf.cumulate_callchain)
2678 total = he->stat_acc->period;
2680 min_callchain_hits = total * (percent / 100);
2683 callchain_param.sort(&he->sorted_chain, he->callchain,
2684 min_callchain_hits, &callchain_param);
2687 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
2689 /* force to re-evaluate folding state of callchains */
2690 he->init_have_children = false;
2691 hist_entry__set_folding(he, hb, false);
2695 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
2696 const char *helpline,
2698 struct hist_browser_timer *hbt,
2700 struct perf_env *env)
2702 struct hists *hists = evsel__hists(evsel);
2703 struct hist_browser *browser = perf_evsel_browser__new(evsel, hbt, env);
2704 struct branch_info *bi;
2705 #define MAX_OPTIONS 16
2706 char *options[MAX_OPTIONS];
2707 struct popup_action actions[MAX_OPTIONS];
2711 int delay_secs = hbt ? hbt->refresh : 0;
2713 #define HIST_BROWSER_HELP_COMMON \
2714 "h/?/F1 Show this window\n" \
2716 "PGDN/SPACE Navigate\n" \
2717 "q/ESC/CTRL+C Exit browser\n\n" \
2718 "For multiple event sessions:\n\n" \
2719 "TAB/UNTAB Switch events\n\n" \
2720 "For symbolic views (--sort has sym):\n\n" \
2721 "ENTER Zoom into DSO/Threads & Annotate current symbol\n" \
2723 "a Annotate current symbol\n" \
2724 "C Collapse all callchains\n" \
2725 "d Zoom into current DSO\n" \
2726 "E Expand all callchains\n" \
2727 "F Toggle percentage of filtered entries\n" \
2728 "H Display column headers\n" \
2729 "L Change percent limit\n" \
2730 "m Display context menu\n" \
2731 "S Zoom into current Processor Socket\n" \
2733 /* help messages are sorted by lexical order of the hotkey */
2734 const char report_help[] = HIST_BROWSER_HELP_COMMON
2735 "i Show header information\n"
2736 "P Print histograms to perf.hist.N\n"
2737 "r Run available scripts\n"
2738 "s Switch to another data file in PWD\n"
2739 "t Zoom into current Thread\n"
2740 "V Verbose (DSO names in callchains, etc)\n"
2741 "/ Filter symbol by name";
2742 const char top_help[] = HIST_BROWSER_HELP_COMMON
2743 "P Print histograms to perf.hist.N\n"
2744 "t Zoom into current Thread\n"
2745 "V Verbose (DSO names in callchains, etc)\n"
2746 "z Toggle zeroing of samples\n"
2747 "f Enable/Disable events\n"
2748 "/ Filter symbol by name";
2750 if (browser == NULL)
2753 /* reset abort key so that it can get Ctrl-C as a key */
2755 SLang_init_tty(0, 0, 0);
2758 browser->min_pcnt = min_pcnt;
2759 hist_browser__update_nr_entries(browser);
2761 browser->pstack = pstack__new(3);
2762 if (browser->pstack == NULL)
2765 ui_helpline__push(helpline);
2767 memset(options, 0, sizeof(options));
2768 memset(actions, 0, sizeof(actions));
2770 if (symbol_conf.col_width_list_str)
2771 perf_hpp__set_user_width(symbol_conf.col_width_list_str);
2774 struct thread *thread = NULL;
2775 struct map *map = NULL;
2781 key = hist_browser__run(browser, helpline);
2783 if (browser->he_selection != NULL) {
2784 thread = hist_browser__selected_thread(browser);
2785 map = browser->selection->map;
2786 socked_id = browser->he_selection->socket;
2794 * Exit the browser, let hists__browser_tree
2795 * go to the next or previous
2797 goto out_free_stack;
2799 if (!hists__has(hists, sym)) {
2800 ui_browser__warning(&browser->b, delay_secs * 2,
2801 "Annotation is only available for symbolic views, "
2802 "include \"sym*\" in --sort to use it.");
2806 if (browser->selection == NULL ||
2807 browser->selection->sym == NULL ||
2808 browser->selection->map->dso->annotate_warned)
2811 actions->ms.map = browser->selection->map;
2812 actions->ms.sym = browser->selection->sym;
2813 do_annotate(browser, actions);
2816 hist_browser__dump(browser);
2819 actions->ms.map = map;
2820 do_zoom_dso(browser, actions);
2823 browser->show_dso = !browser->show_dso;
2826 actions->thread = thread;
2827 do_zoom_thread(browser, actions);
2830 actions->socket = socked_id;
2831 do_zoom_socket(browser, actions);
2834 if (ui_browser__input_window("Symbol to show",
2835 "Please enter the name of symbol you want to see.\n"
2836 "To remove the filter later, press / + ENTER.",
2837 buf, "ENTER: OK, ESC: Cancel",
2838 delay_secs * 2) == K_ENTER) {
2839 hists->symbol_filter_str = *buf ? buf : NULL;
2840 hists__filter_by_symbol(hists);
2841 hist_browser__reset(browser);
2845 if (is_report_browser(hbt)) {
2846 actions->thread = NULL;
2847 actions->ms.sym = NULL;
2848 do_run_script(browser, actions);
2852 if (is_report_browser(hbt)) {
2853 key = do_switch_data(browser, actions);
2854 if (key == K_SWITCH_INPUT_DATA)
2855 goto out_free_stack;
2859 /* env->arch is NULL for live-mode (i.e. perf top) */
2861 tui__header_window(env);
2864 symbol_conf.filter_relative ^= 1;
2867 if (!is_report_browser(hbt)) {
2868 struct perf_top *top = hbt->arg;
2870 top->zero = !top->zero;
2874 if (ui_browser__input_window("Percent Limit",
2875 "Please enter the value you want to hide entries under that percent.",
2876 buf, "ENTER: OK, ESC: Cancel",
2877 delay_secs * 2) == K_ENTER) {
2879 double new_percent = strtod(buf, &end);
2881 if (new_percent < 0 || new_percent > 100) {
2882 ui_browser__warning(&browser->b, delay_secs * 2,
2883 "Invalid percent: %.2f", new_percent);
2887 hist_browser__update_percent_limit(browser, new_percent);
2888 hist_browser__reset(browser);
2894 ui_browser__help_window(&browser->b,
2895 is_report_browser(hbt) ? report_help : top_help);
2906 if (pstack__empty(browser->pstack)) {
2908 * Go back to the perf_evsel_menu__run or other user
2911 goto out_free_stack;
2914 ui_browser__dialog_yesno(&browser->b,
2915 "Do you really want to exit?"))
2916 goto out_free_stack;
2920 top = pstack__peek(browser->pstack);
2921 if (top == &browser->hists->dso_filter) {
2923 * No need to set actions->dso here since
2924 * it's just to remove the current filter.
2925 * Ditto for thread below.
2927 do_zoom_dso(browser, actions);
2928 } else if (top == &browser->hists->thread_filter) {
2929 do_zoom_thread(browser, actions);
2930 } else if (top == &browser->hists->socket_filter) {
2931 do_zoom_socket(browser, actions);
2937 goto out_free_stack;
2939 if (!is_report_browser(hbt)) {
2940 struct perf_top *top = hbt->arg;
2942 perf_evlist__toggle_enable(top->evlist);
2944 * No need to refresh, resort/decay histogram
2945 * entries if we are not collecting samples:
2947 if (top->evlist->enabled) {
2948 helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
2949 hbt->refresh = delay_secs;
2951 helpline = "Press 'f' again to re-enable the events";
2958 helpline = "Press '?' for help on key bindings";
2962 if (!hists__has(hists, sym) || browser->selection == NULL)
2963 goto skip_annotation;
2965 if (sort__mode == SORT_MODE__BRANCH) {
2966 bi = browser->he_selection->branch_info;
2969 goto skip_annotation;
2971 nr_options += add_annotate_opt(browser,
2972 &actions[nr_options],
2973 &options[nr_options],
2976 if (bi->to.sym != bi->from.sym)
2977 nr_options += add_annotate_opt(browser,
2978 &actions[nr_options],
2979 &options[nr_options],
2983 nr_options += add_annotate_opt(browser,
2984 &actions[nr_options],
2985 &options[nr_options],
2986 browser->selection->map,
2987 browser->selection->sym);
2990 nr_options += add_thread_opt(browser, &actions[nr_options],
2991 &options[nr_options], thread);
2992 nr_options += add_dso_opt(browser, &actions[nr_options],
2993 &options[nr_options], map);
2994 nr_options += add_map_opt(browser, &actions[nr_options],
2995 &options[nr_options],
2996 browser->selection ?
2997 browser->selection->map : NULL);
2998 nr_options += add_socket_opt(browser, &actions[nr_options],
2999 &options[nr_options],
3001 /* perf script support */
3002 if (!is_report_browser(hbt))
3003 goto skip_scripting;
3005 if (browser->he_selection) {
3006 if (hists__has(hists, thread) && thread) {
3007 nr_options += add_script_opt(browser,
3008 &actions[nr_options],
3009 &options[nr_options],
3013 * Note that browser->selection != NULL
3014 * when browser->he_selection is not NULL,
3015 * so we don't need to check browser->selection
3016 * before fetching browser->selection->sym like what
3017 * we do before fetching browser->selection->map.
3019 * See hist_browser__show_entry.
3021 if (hists__has(hists, sym) && browser->selection->sym) {
3022 nr_options += add_script_opt(browser,
3023 &actions[nr_options],
3024 &options[nr_options],
3025 NULL, browser->selection->sym);
3028 nr_options += add_script_opt(browser, &actions[nr_options],
3029 &options[nr_options], NULL, NULL);
3030 nr_options += add_switch_opt(browser, &actions[nr_options],
3031 &options[nr_options]);
3033 nr_options += add_exit_opt(browser, &actions[nr_options],
3034 &options[nr_options]);
3037 struct popup_action *act;
3039 choice = ui__popup_menu(nr_options, options);
3040 if (choice == -1 || choice >= nr_options)
3043 act = &actions[choice];
3044 key = act->fn(browser, act);
3047 if (key == K_SWITCH_INPUT_DATA)
3051 pstack__delete(browser->pstack);
3053 hist_browser__delete(browser);
3054 free_popup_options(options, MAX_OPTIONS);
3058 struct perf_evsel_menu {
3059 struct ui_browser b;
3060 struct perf_evsel *selection;
3061 bool lost_events, lost_events_warned;
3063 struct perf_env *env;
3066 static void perf_evsel_menu__write(struct ui_browser *browser,
3067 void *entry, int row)
3069 struct perf_evsel_menu *menu = container_of(browser,
3070 struct perf_evsel_menu, b);
3071 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
3072 struct hists *hists = evsel__hists(evsel);
3073 bool current_entry = ui_browser__is_current_entry(browser, row);
3074 unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
3075 const char *ev_name = perf_evsel__name(evsel);
3077 const char *warn = " ";
3080 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
3081 HE_COLORSET_NORMAL);
3083 if (perf_evsel__is_group_event(evsel)) {
3084 struct perf_evsel *pos;
3086 ev_name = perf_evsel__group_name(evsel);
3088 for_each_group_member(pos, evsel) {
3089 struct hists *pos_hists = evsel__hists(pos);
3090 nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
3094 nr_events = convert_unit(nr_events, &unit);
3095 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
3096 unit, unit == ' ' ? "" : " ", ev_name);
3097 ui_browser__printf(browser, "%s", bf);
3099 nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
3100 if (nr_events != 0) {
3101 menu->lost_events = true;
3103 ui_browser__set_color(browser, HE_COLORSET_TOP);
3104 nr_events = convert_unit(nr_events, &unit);
3105 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
3106 nr_events, unit, unit == ' ' ? "" : " ");
3110 ui_browser__write_nstring(browser, warn, browser->width - printed);
3113 menu->selection = evsel;
3116 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
3117 int nr_events, const char *help,
3118 struct hist_browser_timer *hbt)
3120 struct perf_evlist *evlist = menu->b.priv;
3121 struct perf_evsel *pos;
3122 const char *title = "Available samples";
3123 int delay_secs = hbt ? hbt->refresh : 0;
3126 if (ui_browser__show(&menu->b, title,
3127 "ESC: exit, ENTER|->: Browse histograms") < 0)
3131 key = ui_browser__run(&menu->b, delay_secs);
3135 hbt->timer(hbt->arg);
3137 if (!menu->lost_events_warned && menu->lost_events) {
3138 ui_browser__warn_lost_events(&menu->b);
3139 menu->lost_events_warned = true;
3144 if (!menu->selection)
3146 pos = menu->selection;
3148 perf_evlist__set_selected(evlist, pos);
3150 * Give the calling tool a chance to populate the non
3151 * default evsel resorted hists tree.
3154 hbt->timer(hbt->arg);
3155 key = perf_evsel__hists_browse(pos, nr_events, help,
3159 ui_browser__show_title(&menu->b, title);
3162 if (pos->node.next == &evlist->entries)
3163 pos = perf_evlist__first(evlist);
3165 pos = perf_evsel__next(pos);
3168 if (pos->node.prev == &evlist->entries)
3169 pos = perf_evlist__last(evlist);
3171 pos = perf_evsel__prev(pos);
3173 case K_SWITCH_INPUT_DATA:
3184 if (!ui_browser__dialog_yesno(&menu->b,
3185 "Do you really want to exit?"))
3197 ui_browser__hide(&menu->b);
3201 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
3204 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
3206 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
3212 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
3213 int nr_entries, const char *help,
3214 struct hist_browser_timer *hbt,
3216 struct perf_env *env)
3218 struct perf_evsel *pos;
3219 struct perf_evsel_menu menu = {
3221 .entries = &evlist->entries,
3222 .refresh = ui_browser__list_head_refresh,
3223 .seek = ui_browser__list_head_seek,
3224 .write = perf_evsel_menu__write,
3225 .filter = filter_group_entries,
3226 .nr_entries = nr_entries,
3229 .min_pcnt = min_pcnt,
3233 ui_helpline__push("Press ESC to exit");
3235 evlist__for_each_entry(evlist, pos) {
3236 const char *ev_name = perf_evsel__name(pos);
3237 size_t line_len = strlen(ev_name) + 7;
3239 if (menu.b.width < line_len)
3240 menu.b.width = line_len;
3243 return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
3246 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
3247 struct hist_browser_timer *hbt,
3249 struct perf_env *env)
3251 int nr_entries = evlist->nr_entries;
3254 if (nr_entries == 1) {
3255 struct perf_evsel *first = perf_evlist__first(evlist);
3257 return perf_evsel__hists_browse(first, nr_entries, help,
3258 false, hbt, min_pcnt,
3262 if (symbol_conf.event_group) {
3263 struct perf_evsel *pos;
3266 evlist__for_each_entry(evlist, pos) {
3267 if (perf_evsel__is_group_leader(pos))
3271 if (nr_entries == 1)
3275 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
3276 hbt, min_pcnt, env);