6 #include <linux/rbtree.h>
7 #include <sys/ttydefaults.h>
9 #include "../../util/evsel.h"
10 #include "../../util/evlist.h"
11 #include "../../util/hist.h"
12 #include "../../util/pstack.h"
13 #include "../../util/sort.h"
14 #include "../../util/util.h"
15 #include "../../util/top.h"
16 #include "../../arch/common.h"
18 #include "../browsers/hists.h"
19 #include "../helpline.h"
27 #include "sane_ctype.h"
29 extern void hist_browser__init_hpp(void);
31 static int perf_evsel_browser_title(struct hist_browser *browser,
32 char *bf, size_t size);
33 static void hist_browser__update_nr_entries(struct hist_browser *hb);
35 static struct rb_node *hists__filter_entries(struct rb_node *nd,
38 static bool hist_browser__has_filter(struct hist_browser *hb)
40 return hists__has_filter(hb->hists) || hb->min_pcnt || symbol_conf.has_filter || hb->c2c_filter;
43 static int hist_browser__get_folding(struct hist_browser *browser)
46 struct hists *hists = browser->hists;
47 int unfolded_rows = 0;
49 for (nd = rb_first(&hists->entries);
50 (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
51 nd = rb_hierarchy_next(nd)) {
52 struct hist_entry *he =
53 rb_entry(nd, struct hist_entry, rb_node);
55 if (he->leaf && he->unfolded)
56 unfolded_rows += he->nr_rows;
61 static u32 hist_browser__nr_entries(struct hist_browser *hb)
65 if (symbol_conf.report_hierarchy)
66 nr_entries = hb->nr_hierarchy_entries;
67 else if (hist_browser__has_filter(hb))
68 nr_entries = hb->nr_non_filtered_entries;
70 nr_entries = hb->hists->nr_entries;
72 hb->nr_callchain_rows = hist_browser__get_folding(hb);
73 return nr_entries + hb->nr_callchain_rows;
76 static void hist_browser__update_rows(struct hist_browser *hb)
78 struct ui_browser *browser = &hb->b;
79 struct hists *hists = hb->hists;
80 struct perf_hpp_list *hpp_list = hists->hpp_list;
81 u16 header_offset, index_row;
83 header_offset = hb->show_headers ? hpp_list->nr_header_lines : 0;
84 browser->rows = browser->height - header_offset;
86 * Verify if we were at the last line and that line isn't
87 * visibe because we now show the header line(s).
89 index_row = browser->index - browser->top_idx;
90 if (index_row >= browser->rows)
91 browser->index -= index_row - browser->rows + 1;
94 static void hist_browser__refresh_dimensions(struct ui_browser *browser)
96 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
98 /* 3 == +/- toggle symbol before actual hist_entry rendering */
99 browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]"));
101 * FIXME: Just keeping existing behaviour, but this really should be
102 * before updating browser->width, as it will invalidate the
103 * calculation above. Fix this and the fallout in another
106 ui_browser__refresh_dimensions(browser);
107 hist_browser__update_rows(hb);
110 static void hist_browser__gotorc(struct hist_browser *browser, int row, int column)
112 struct hists *hists = browser->hists;
113 struct perf_hpp_list *hpp_list = hists->hpp_list;
116 header_offset = browser->show_headers ? hpp_list->nr_header_lines : 0;
117 ui_browser__gotorc(&browser->b, row + header_offset, column);
120 static void hist_browser__reset(struct hist_browser *browser)
123 * The hists__remove_entry_filter() already folds non-filtered
124 * entries so we can assume it has 0 callchain rows.
126 browser->nr_callchain_rows = 0;
128 hist_browser__update_nr_entries(browser);
129 browser->b.nr_entries = hist_browser__nr_entries(browser);
130 hist_browser__refresh_dimensions(&browser->b);
131 ui_browser__reset_index(&browser->b);
134 static char tree__folded_sign(bool unfolded)
136 return unfolded ? '-' : '+';
139 static char hist_entry__folded(const struct hist_entry *he)
141 return he->has_children ? tree__folded_sign(he->unfolded) : ' ';
144 static char callchain_list__folded(const struct callchain_list *cl)
146 return cl->has_children ? tree__folded_sign(cl->unfolded) : ' ';
149 static void callchain_list__set_folding(struct callchain_list *cl, bool unfold)
151 cl->unfolded = unfold ? cl->has_children : false;
154 static struct inline_node *inline_node__create(struct map *map, u64 ip)
157 struct inline_node *node;
166 if (dso->kernel != DSO_TYPE_USER)
169 node = dso__parse_addr_inlines(dso,
170 map__rip_2objdump(map, ip));
175 static int inline__count_rows(struct inline_node *node)
177 struct inline_list *ilist;
183 list_for_each_entry(ilist, &node->val, list) {
184 if ((ilist->filename != NULL) || (ilist->funcname != NULL))
191 static int callchain_list__inline_rows(struct callchain_list *chain)
193 struct inline_node *node;
196 node = inline_node__create(chain->ms.map, chain->ip);
200 rows = inline__count_rows(node);
201 inline_node__delete(node);
205 static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
207 int n = 0, inline_rows;
210 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
211 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
212 struct callchain_list *chain;
213 char folded_sign = ' '; /* No children */
215 list_for_each_entry(chain, &child->val, list) {
218 if (symbol_conf.inline_name) {
220 callchain_list__inline_rows(chain);
224 /* We need this because we may not have children */
225 folded_sign = callchain_list__folded(chain);
226 if (folded_sign == '+')
230 if (folded_sign == '-') /* Have children and they're unfolded */
231 n += callchain_node__count_rows_rb_tree(child);
237 static int callchain_node__count_flat_rows(struct callchain_node *node)
239 struct callchain_list *chain;
240 char folded_sign = 0;
243 list_for_each_entry(chain, &node->parent_val, list) {
245 /* only check first chain list entry */
246 folded_sign = callchain_list__folded(chain);
247 if (folded_sign == '+')
253 list_for_each_entry(chain, &node->val, list) {
255 /* node->parent_val list might be empty */
256 folded_sign = callchain_list__folded(chain);
257 if (folded_sign == '+')
266 static int callchain_node__count_folded_rows(struct callchain_node *node __maybe_unused)
271 static int callchain_node__count_rows(struct callchain_node *node)
273 struct callchain_list *chain;
274 bool unfolded = false;
275 int n = 0, inline_rows;
277 if (callchain_param.mode == CHAIN_FLAT)
278 return callchain_node__count_flat_rows(node);
279 else if (callchain_param.mode == CHAIN_FOLDED)
280 return callchain_node__count_folded_rows(node);
282 list_for_each_entry(chain, &node->val, list) {
284 if (symbol_conf.inline_name) {
285 inline_rows = callchain_list__inline_rows(chain);
289 unfolded = chain->unfolded;
293 n += callchain_node__count_rows_rb_tree(node);
298 static int callchain__count_rows(struct rb_root *chain)
303 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
304 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
305 n += callchain_node__count_rows(node);
311 static int hierarchy_count_rows(struct hist_browser *hb, struct hist_entry *he,
312 bool include_children)
315 struct rb_node *node;
316 struct hist_entry *child;
319 return callchain__count_rows(&he->sorted_chain);
321 if (he->has_no_entry)
324 node = rb_first(&he->hroot_out);
328 child = rb_entry(node, struct hist_entry, rb_node);
329 percent = hist_entry__get_percent_limit(child);
331 if (!child->filtered && percent >= hb->min_pcnt) {
334 if (include_children && child->unfolded)
335 count += hierarchy_count_rows(hb, child, true);
338 node = rb_next(node);
343 static bool hist_entry__toggle_fold(struct hist_entry *he)
348 if (!he->has_children)
351 he->unfolded = !he->unfolded;
355 static bool callchain_list__toggle_fold(struct callchain_list *cl)
360 if (!cl->has_children)
363 cl->unfolded = !cl->unfolded;
367 static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
369 struct rb_node *nd = rb_first(&node->rb_root);
371 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
372 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
373 struct callchain_list *chain;
376 list_for_each_entry(chain, &child->val, list) {
379 chain->has_children = chain->list.next != &child->val ||
380 !RB_EMPTY_ROOT(&child->rb_root);
382 chain->has_children = chain->list.next == &child->val &&
383 !RB_EMPTY_ROOT(&child->rb_root);
386 callchain_node__init_have_children_rb_tree(child);
390 static void callchain_node__init_have_children(struct callchain_node *node,
393 struct callchain_list *chain;
395 chain = list_entry(node->val.next, struct callchain_list, list);
396 chain->has_children = has_sibling;
398 if (!list_empty(&node->val)) {
399 chain = list_entry(node->val.prev, struct callchain_list, list);
400 chain->has_children = !RB_EMPTY_ROOT(&node->rb_root);
403 callchain_node__init_have_children_rb_tree(node);
406 static void callchain__init_have_children(struct rb_root *root)
408 struct rb_node *nd = rb_first(root);
409 bool has_sibling = nd && rb_next(nd);
411 for (nd = rb_first(root); nd; nd = rb_next(nd)) {
412 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
413 callchain_node__init_have_children(node, has_sibling);
414 if (callchain_param.mode == CHAIN_FLAT ||
415 callchain_param.mode == CHAIN_FOLDED)
416 callchain_node__make_parent_list(node);
420 static void hist_entry__init_have_children(struct hist_entry *he)
422 if (he->init_have_children)
426 he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
427 callchain__init_have_children(&he->sorted_chain);
429 he->has_children = !RB_EMPTY_ROOT(&he->hroot_out);
432 he->init_have_children = true;
435 static void hist_entry_init_inline_node(struct hist_entry *he)
440 he->inline_node = inline_node__create(he->ms.map, he->ip);
442 if (he->inline_node == NULL)
445 he->has_children = true;
448 static bool hist_browser__toggle_fold(struct hist_browser *browser)
450 struct hist_entry *he = browser->he_selection;
451 struct map_symbol *ms = browser->selection;
452 struct callchain_list *cl = container_of(ms, struct callchain_list, ms);
459 has_children = hist_entry__toggle_fold(he);
461 has_children = callchain_list__toggle_fold(cl);
466 hist_entry__init_have_children(he);
467 browser->b.nr_entries -= he->nr_rows;
470 browser->nr_callchain_rows -= he->nr_rows;
472 browser->nr_hierarchy_entries -= he->nr_rows;
474 if (symbol_conf.report_hierarchy)
475 child_rows = hierarchy_count_rows(browser, he, true);
480 he->nr_rows = inline__count_rows(
483 he->nr_rows = callchain__count_rows(
486 he->nr_rows = hierarchy_count_rows(browser, he, false);
488 /* account grand children */
489 if (symbol_conf.report_hierarchy)
490 browser->b.nr_entries += child_rows - he->nr_rows;
492 if (!he->leaf && he->nr_rows == 0) {
493 he->has_no_entry = true;
497 if (symbol_conf.report_hierarchy)
498 browser->b.nr_entries -= child_rows - he->nr_rows;
500 if (he->has_no_entry)
501 he->has_no_entry = false;
506 browser->b.nr_entries += he->nr_rows;
509 browser->nr_callchain_rows += he->nr_rows;
511 browser->nr_hierarchy_entries += he->nr_rows;
516 /* If it doesn't have children, no toggling performed */
520 static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
525 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
526 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
527 struct callchain_list *chain;
528 bool has_children = false;
530 list_for_each_entry(chain, &child->val, list) {
532 callchain_list__set_folding(chain, unfold);
533 has_children = chain->has_children;
537 n += callchain_node__set_folding_rb_tree(child, unfold);
543 static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
545 struct callchain_list *chain;
546 bool has_children = false;
549 list_for_each_entry(chain, &node->val, list) {
551 callchain_list__set_folding(chain, unfold);
552 has_children = chain->has_children;
556 n += callchain_node__set_folding_rb_tree(node, unfold);
561 static int callchain__set_folding(struct rb_root *chain, bool unfold)
566 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
567 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
568 n += callchain_node__set_folding(node, unfold);
574 static int hierarchy_set_folding(struct hist_browser *hb, struct hist_entry *he,
575 bool unfold __maybe_unused)
579 struct hist_entry *child;
582 for (nd = rb_first(&he->hroot_out); nd; nd = rb_next(nd)) {
583 child = rb_entry(nd, struct hist_entry, rb_node);
584 percent = hist_entry__get_percent_limit(child);
585 if (!child->filtered && percent >= hb->min_pcnt)
592 static void __hist_entry__set_folding(struct hist_entry *he,
593 struct hist_browser *hb, bool unfold)
595 hist_entry__init_have_children(he);
596 he->unfolded = unfold ? he->has_children : false;
598 if (he->has_children) {
602 n = callchain__set_folding(&he->sorted_chain, unfold);
604 n = hierarchy_set_folding(hb, he, unfold);
606 he->nr_rows = unfold ? n : 0;
611 static void hist_entry__set_folding(struct hist_entry *he,
612 struct hist_browser *browser, bool unfold)
616 percent = hist_entry__get_percent_limit(he);
617 if (he->filtered || percent < browser->min_pcnt)
620 __hist_entry__set_folding(he, browser, unfold);
622 if (!he->depth || unfold)
623 browser->nr_hierarchy_entries++;
625 browser->nr_callchain_rows += he->nr_rows;
626 else if (unfold && !hist_entry__has_hierarchy_children(he, browser->min_pcnt)) {
627 browser->nr_hierarchy_entries++;
628 he->has_no_entry = true;
631 he->has_no_entry = false;
635 __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
638 struct hist_entry *he;
640 nd = rb_first(&browser->hists->entries);
642 he = rb_entry(nd, struct hist_entry, rb_node);
644 /* set folding state even if it's currently folded */
645 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
647 hist_entry__set_folding(he, browser, unfold);
651 static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
653 browser->nr_hierarchy_entries = 0;
654 browser->nr_callchain_rows = 0;
655 __hist_browser__set_folding(browser, unfold);
657 browser->b.nr_entries = hist_browser__nr_entries(browser);
658 /* Go to the start, we may be way after valid entries after a collapse */
659 ui_browser__reset_index(&browser->b);
662 static void hist_browser__set_folding_selected(struct hist_browser *browser, bool unfold)
664 if (!browser->he_selection)
667 hist_entry__set_folding(browser->he_selection, browser, unfold);
668 browser->b.nr_entries = hist_browser__nr_entries(browser);
671 static void ui_browser__warn_lost_events(struct ui_browser *browser)
673 ui_browser__warning(browser, 4,
674 "Events are being lost, check IO/CPU overload!\n\n"
675 "You may want to run 'perf' using a RT scheduler policy:\n\n"
676 " perf top -r 80\n\n"
677 "Or reduce the sampling frequency.");
680 static int hist_browser__title(struct hist_browser *browser, char *bf, size_t size)
682 return browser->title ? browser->title(browser, bf, size) : 0;
685 int hist_browser__run(struct hist_browser *browser, const char *help)
689 struct hist_browser_timer *hbt = browser->hbt;
690 int delay_secs = hbt ? hbt->refresh : 0;
692 browser->b.entries = &browser->hists->entries;
693 browser->b.nr_entries = hist_browser__nr_entries(browser);
695 hist_browser__title(browser, title, sizeof(title));
697 if (ui_browser__show(&browser->b, title, "%s", help) < 0)
701 key = ui_browser__run(&browser->b, delay_secs);
706 hbt->timer(hbt->arg);
708 if (hist_browser__has_filter(browser) ||
709 symbol_conf.report_hierarchy)
710 hist_browser__update_nr_entries(browser);
712 nr_entries = hist_browser__nr_entries(browser);
713 ui_browser__update_nr_entries(&browser->b, nr_entries);
715 if (browser->hists->stats.nr_lost_warned !=
716 browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
717 browser->hists->stats.nr_lost_warned =
718 browser->hists->stats.nr_events[PERF_RECORD_LOST];
719 ui_browser__warn_lost_events(&browser->b);
722 hist_browser__title(browser, title, sizeof(title));
723 ui_browser__show_title(&browser->b, title);
726 case 'D': { /* Debug */
728 struct hist_entry *h = rb_entry(browser->b.top,
729 struct hist_entry, rb_node);
731 ui_helpline__fpush("%d: nr_ent=(%d,%d), rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
732 seq++, browser->b.nr_entries,
733 browser->hists->nr_entries,
737 h->row_offset, h->nr_rows);
741 /* Collapse the whole world. */
742 hist_browser__set_folding(browser, false);
745 /* Collapse the selected entry. */
746 hist_browser__set_folding_selected(browser, false);
749 /* Expand the whole world. */
750 hist_browser__set_folding(browser, true);
753 /* Expand the selected entry. */
754 hist_browser__set_folding_selected(browser, true);
757 browser->show_headers = !browser->show_headers;
758 hist_browser__update_rows(browser);
761 if (hist_browser__toggle_fold(browser))
769 ui_browser__hide(&browser->b);
773 struct callchain_print_arg {
774 /* for hists browser */
776 bool is_current_entry;
783 typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
784 struct callchain_list *chain,
785 const char *str, int offset,
787 struct callchain_print_arg *arg);
789 static void hist_browser__show_callchain_entry(struct hist_browser *browser,
790 struct callchain_list *chain,
791 const char *str, int offset,
793 struct callchain_print_arg *arg)
796 char folded_sign = callchain_list__folded(chain);
797 bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
799 color = HE_COLORSET_NORMAL;
800 width = browser->b.width - (offset + 2);
801 if (ui_browser__is_current_entry(&browser->b, row)) {
802 browser->selection = &chain->ms;
803 color = HE_COLORSET_SELECTED;
804 arg->is_current_entry = true;
807 ui_browser__set_color(&browser->b, color);
808 hist_browser__gotorc(browser, row, 0);
809 ui_browser__write_nstring(&browser->b, " ", offset);
810 ui_browser__printf(&browser->b, "%c", folded_sign);
811 ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
812 ui_browser__write_nstring(&browser->b, str, width);
815 static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
816 struct callchain_list *chain,
817 const char *str, int offset,
818 unsigned short row __maybe_unused,
819 struct callchain_print_arg *arg)
821 char folded_sign = callchain_list__folded(chain);
823 arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
827 typedef bool (*check_output_full_fn)(struct hist_browser *browser,
830 static bool hist_browser__check_output_full(struct hist_browser *browser,
833 return browser->b.rows == row;
836 static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
837 unsigned short row __maybe_unused)
842 #define LEVEL_OFFSET_STEP 3
844 static int hist_browser__show_inline(struct hist_browser *browser,
845 struct inline_node *node,
849 struct inline_list *ilist;
851 int color, width, first_row;
854 width = browser->b.width - (LEVEL_OFFSET_STEP + 2);
855 list_for_each_entry(ilist, &node->val, list) {
856 if ((ilist->filename != NULL) || (ilist->funcname != NULL)) {
857 color = HE_COLORSET_NORMAL;
858 if (ui_browser__is_current_entry(&browser->b, row))
859 color = HE_COLORSET_SELECTED;
861 if (callchain_param.key == CCKEY_ADDRESS ||
862 callchain_param.key == CCKEY_SRCLINE) {
863 if (ilist->filename != NULL)
864 scnprintf(buf, sizeof(buf),
869 scnprintf(buf, sizeof(buf), "??");
870 } else if (ilist->funcname != NULL)
871 scnprintf(buf, sizeof(buf), "%s (inline)",
873 else if (ilist->filename != NULL)
874 scnprintf(buf, sizeof(buf),
879 scnprintf(buf, sizeof(buf), "??");
881 ui_browser__set_color(&browser->b, color);
882 hist_browser__gotorc(browser, row, 0);
883 ui_browser__write_nstring(&browser->b, " ",
884 LEVEL_OFFSET_STEP + offset);
885 ui_browser__write_nstring(&browser->b, buf, width);
890 return row - first_row;
893 static size_t show_inline_list(struct hist_browser *browser, struct map *map,
894 u64 ip, int row, int offset)
896 struct inline_node *node;
899 node = inline_node__create(map, ip);
903 ret = hist_browser__show_inline(browser, node, row, offset);
905 inline_node__delete(node);
909 static int hist_browser__show_callchain_list(struct hist_browser *browser,
910 struct callchain_node *node,
911 struct callchain_list *chain,
912 unsigned short row, u64 total,
913 bool need_percent, int offset,
914 print_callchain_entry_fn print,
915 struct callchain_print_arg *arg)
917 char bf[1024], *alloc_str;
918 char buf[64], *alloc_str2;
920 int inline_rows = 0, ret = 1;
922 if (arg->row_offset != 0) {
930 str = callchain_list__sym_name(chain, bf, sizeof(bf),
933 if (symbol_conf.show_branchflag_count) {
935 callchain_list_counts__printf_value(node, chain, NULL,
938 callchain_list_counts__printf_value(NULL, chain, NULL,
941 if (asprintf(&alloc_str2, "%s%s", str, buf) < 0)
942 str = "Not enough memory!";
948 callchain_node__scnprintf_value(node, buf, sizeof(buf),
951 if (asprintf(&alloc_str, "%s %s", buf, str) < 0)
952 str = "Not enough memory!";
957 print(browser, chain, str, offset, row, arg);
961 if (symbol_conf.inline_name) {
962 inline_rows = show_inline_list(browser, chain->ms.map,
963 chain->ip, row + 1, offset);
966 return ret + inline_rows;
969 static bool check_percent_display(struct rb_node *node, u64 parent_total)
971 struct callchain_node *child;
979 child = rb_entry(node, struct callchain_node, rb_node);
980 return callchain_cumul_hits(child) != parent_total;
983 static int hist_browser__show_callchain_flat(struct hist_browser *browser,
984 struct rb_root *root,
985 unsigned short row, u64 total,
987 print_callchain_entry_fn print,
988 struct callchain_print_arg *arg,
989 check_output_full_fn is_output_full)
991 struct rb_node *node;
992 int first_row = row, offset = LEVEL_OFFSET_STEP;
995 node = rb_first(root);
996 need_percent = check_percent_display(node, parent_total);
999 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1000 struct rb_node *next = rb_next(node);
1001 struct callchain_list *chain;
1002 char folded_sign = ' ';
1004 int extra_offset = 0;
1006 list_for_each_entry(chain, &child->parent_val, list) {
1007 bool was_first = first;
1011 else if (need_percent)
1012 extra_offset = LEVEL_OFFSET_STEP;
1014 folded_sign = callchain_list__folded(chain);
1016 row += hist_browser__show_callchain_list(browser, child,
1018 was_first && need_percent,
1019 offset + extra_offset,
1022 if (is_output_full(browser, row))
1025 if (folded_sign == '+')
1029 list_for_each_entry(chain, &child->val, list) {
1030 bool was_first = first;
1034 else if (need_percent)
1035 extra_offset = LEVEL_OFFSET_STEP;
1037 folded_sign = callchain_list__folded(chain);
1039 row += hist_browser__show_callchain_list(browser, child,
1041 was_first && need_percent,
1042 offset + extra_offset,
1045 if (is_output_full(browser, row))
1048 if (folded_sign == '+')
1053 if (is_output_full(browser, row))
1058 return row - first_row;
1061 static char *hist_browser__folded_callchain_str(struct hist_browser *browser,
1062 struct callchain_list *chain,
1063 char *value_str, char *old_str)
1069 str = callchain_list__sym_name(chain, bf, sizeof(bf),
1072 if (asprintf(&new, "%s%s%s", old_str,
1073 symbol_conf.field_sep ?: ";", str) < 0)
1077 if (asprintf(&new, "%s %s", value_str, str) < 0)
1080 if (asprintf(&new, "%s", str) < 0)
1087 static int hist_browser__show_callchain_folded(struct hist_browser *browser,
1088 struct rb_root *root,
1089 unsigned short row, u64 total,
1091 print_callchain_entry_fn print,
1092 struct callchain_print_arg *arg,
1093 check_output_full_fn is_output_full)
1095 struct rb_node *node;
1096 int first_row = row, offset = LEVEL_OFFSET_STEP;
1099 node = rb_first(root);
1100 need_percent = check_percent_display(node, parent_total);
1103 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1104 struct rb_node *next = rb_next(node);
1105 struct callchain_list *chain, *first_chain = NULL;
1107 char *value_str = NULL, *value_str_alloc = NULL;
1108 char *chain_str = NULL, *chain_str_alloc = NULL;
1110 if (arg->row_offset != 0) {
1118 callchain_node__scnprintf_value(child, buf, sizeof(buf), total);
1119 if (asprintf(&value_str, "%s", buf) < 0) {
1120 value_str = (char *)"<...>";
1123 value_str_alloc = value_str;
1126 list_for_each_entry(chain, &child->parent_val, list) {
1127 chain_str = hist_browser__folded_callchain_str(browser,
1128 chain, value_str, chain_str);
1131 first_chain = chain;
1134 if (chain_str == NULL) {
1135 chain_str = (char *)"Not enough memory!";
1139 chain_str_alloc = chain_str;
1142 list_for_each_entry(chain, &child->val, list) {
1143 chain_str = hist_browser__folded_callchain_str(browser,
1144 chain, value_str, chain_str);
1147 first_chain = chain;
1150 if (chain_str == NULL) {
1151 chain_str = (char *)"Not enough memory!";
1155 chain_str_alloc = chain_str;
1159 print(browser, first_chain, chain_str, offset, row++, arg);
1160 free(value_str_alloc);
1161 free(chain_str_alloc);
1164 if (is_output_full(browser, row))
1169 return row - first_row;
1172 static int hist_browser__show_callchain_graph(struct hist_browser *browser,
1173 struct rb_root *root, int level,
1174 unsigned short row, u64 total,
1176 print_callchain_entry_fn print,
1177 struct callchain_print_arg *arg,
1178 check_output_full_fn is_output_full)
1180 struct rb_node *node;
1181 int first_row = row, offset = level * LEVEL_OFFSET_STEP;
1183 u64 percent_total = total;
1185 if (callchain_param.mode == CHAIN_GRAPH_REL)
1186 percent_total = parent_total;
1188 node = rb_first(root);
1189 need_percent = check_percent_display(node, parent_total);
1192 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1193 struct rb_node *next = rb_next(node);
1194 struct callchain_list *chain;
1195 char folded_sign = ' ';
1197 int extra_offset = 0;
1199 list_for_each_entry(chain, &child->val, list) {
1200 bool was_first = first;
1204 else if (need_percent)
1205 extra_offset = LEVEL_OFFSET_STEP;
1207 folded_sign = callchain_list__folded(chain);
1209 row += hist_browser__show_callchain_list(browser, child,
1210 chain, row, percent_total,
1211 was_first && need_percent,
1212 offset + extra_offset,
1215 if (is_output_full(browser, row))
1218 if (folded_sign == '+')
1222 if (folded_sign == '-') {
1223 const int new_level = level + (extra_offset ? 2 : 1);
1225 row += hist_browser__show_callchain_graph(browser, &child->rb_root,
1226 new_level, row, total,
1227 child->children_hit,
1228 print, arg, is_output_full);
1230 if (is_output_full(browser, row))
1235 return row - first_row;
1238 static int hist_browser__show_callchain(struct hist_browser *browser,
1239 struct hist_entry *entry, int level,
1241 print_callchain_entry_fn print,
1242 struct callchain_print_arg *arg,
1243 check_output_full_fn is_output_full)
1245 u64 total = hists__total_period(entry->hists);
1249 if (symbol_conf.cumulate_callchain)
1250 parent_total = entry->stat_acc->period;
1252 parent_total = entry->stat.period;
1254 if (callchain_param.mode == CHAIN_FLAT) {
1255 printed = hist_browser__show_callchain_flat(browser,
1256 &entry->sorted_chain, row,
1257 total, parent_total, print, arg,
1259 } else if (callchain_param.mode == CHAIN_FOLDED) {
1260 printed = hist_browser__show_callchain_folded(browser,
1261 &entry->sorted_chain, row,
1262 total, parent_total, print, arg,
1265 printed = hist_browser__show_callchain_graph(browser,
1266 &entry->sorted_chain, level, row,
1267 total, parent_total, print, arg,
1271 if (arg->is_current_entry)
1272 browser->he_selection = entry;
1278 struct ui_browser *b;
1283 int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
1285 struct hpp_arg *arg = hpp->ptr;
1290 va_start(args, fmt);
1291 len = va_arg(args, int);
1292 percent = va_arg(args, double);
1295 ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
1297 ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
1298 ui_browser__printf(arg->b, "%s", hpp->buf);
1303 #define __HPP_COLOR_PERCENT_FN(_type, _field) \
1304 static u64 __hpp_get_##_field(struct hist_entry *he) \
1306 return he->stat._field; \
1310 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
1311 struct perf_hpp *hpp, \
1312 struct hist_entry *he) \
1314 return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%", \
1315 __hpp__slsmg_color_printf, true); \
1318 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
1319 static u64 __hpp_get_acc_##_field(struct hist_entry *he) \
1321 return he->stat_acc->_field; \
1325 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
1326 struct perf_hpp *hpp, \
1327 struct hist_entry *he) \
1329 if (!symbol_conf.cumulate_callchain) { \
1330 struct hpp_arg *arg = hpp->ptr; \
1331 int len = fmt->user_len ?: fmt->len; \
1332 int ret = scnprintf(hpp->buf, hpp->size, \
1333 "%*s", len, "N/A"); \
1334 ui_browser__printf(arg->b, "%s", hpp->buf); \
1338 return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field, \
1339 " %*.2f%%", __hpp__slsmg_color_printf, true); \
1342 __HPP_COLOR_PERCENT_FN(overhead, period)
1343 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
1344 __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
1345 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
1346 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
1347 __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
1349 #undef __HPP_COLOR_PERCENT_FN
1350 #undef __HPP_COLOR_ACC_PERCENT_FN
1352 void hist_browser__init_hpp(void)
1354 perf_hpp__format[PERF_HPP__OVERHEAD].color =
1355 hist_browser__hpp_color_overhead;
1356 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
1357 hist_browser__hpp_color_overhead_sys;
1358 perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
1359 hist_browser__hpp_color_overhead_us;
1360 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
1361 hist_browser__hpp_color_overhead_guest_sys;
1362 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
1363 hist_browser__hpp_color_overhead_guest_us;
1364 perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
1365 hist_browser__hpp_color_overhead_acc;
1368 static int hist_browser__show_entry(struct hist_browser *browser,
1369 struct hist_entry *entry,
1373 int width = browser->b.width;
1374 char folded_sign = ' ';
1375 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1376 off_t row_offset = entry->row_offset;
1378 struct perf_hpp_fmt *fmt;
1380 if (current_entry) {
1381 browser->he_selection = entry;
1382 browser->selection = &entry->ms;
1385 if (symbol_conf.use_callchain) {
1386 hist_entry__init_have_children(entry);
1387 folded_sign = hist_entry__folded(entry);
1390 if (symbol_conf.inline_name &&
1391 (!entry->has_children)) {
1392 hist_entry_init_inline_node(entry);
1393 folded_sign = hist_entry__folded(entry);
1396 if (row_offset == 0) {
1397 struct hpp_arg arg = {
1399 .folded_sign = folded_sign,
1400 .current_entry = current_entry,
1404 hist_browser__gotorc(browser, row, 0);
1406 hists__for_each_format(browser->hists, fmt) {
1408 struct perf_hpp hpp = {
1414 if (perf_hpp__should_skip(fmt, entry->hists) ||
1415 column++ < browser->b.horiz_scroll)
1418 if (current_entry && browser->b.navkeypressed) {
1419 ui_browser__set_color(&browser->b,
1420 HE_COLORSET_SELECTED);
1422 ui_browser__set_color(&browser->b,
1423 HE_COLORSET_NORMAL);
1427 if (symbol_conf.use_callchain ||
1428 symbol_conf.inline_name) {
1429 ui_browser__printf(&browser->b, "%c ", folded_sign);
1434 ui_browser__printf(&browser->b, " ");
1439 int ret = fmt->color(fmt, &hpp, entry);
1440 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1442 * fmt->color() already used ui_browser to
1443 * print the non alignment bits, skip it (+ret):
1445 ui_browser__printf(&browser->b, "%s", s + ret);
1447 hist_entry__snprintf_alignment(entry, &hpp, fmt, fmt->entry(fmt, &hpp, entry));
1448 ui_browser__printf(&browser->b, "%s", s);
1450 width -= hpp.buf - s;
1453 /* The scroll bar isn't being used */
1454 if (!browser->b.navkeypressed)
1457 ui_browser__write_nstring(&browser->b, "", width);
1464 if (folded_sign == '-' && row != browser->b.rows) {
1465 struct callchain_print_arg arg = {
1466 .row_offset = row_offset,
1467 .is_current_entry = current_entry,
1470 if (entry->inline_node)
1471 printed += hist_browser__show_inline(browser,
1472 entry->inline_node, row, 0);
1474 printed += hist_browser__show_callchain(browser,
1476 hist_browser__show_callchain_entry,
1478 hist_browser__check_output_full);
1484 static int hist_browser__show_hierarchy_entry(struct hist_browser *browser,
1485 struct hist_entry *entry,
1490 int width = browser->b.width;
1491 char folded_sign = ' ';
1492 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1493 off_t row_offset = entry->row_offset;
1495 struct perf_hpp_fmt *fmt;
1496 struct perf_hpp_list_node *fmt_node;
1497 struct hpp_arg arg = {
1499 .current_entry = current_entry,
1502 int hierarchy_indent = (entry->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
1504 if (current_entry) {
1505 browser->he_selection = entry;
1506 browser->selection = &entry->ms;
1509 hist_entry__init_have_children(entry);
1510 folded_sign = hist_entry__folded(entry);
1511 arg.folded_sign = folded_sign;
1513 if (entry->leaf && row_offset) {
1515 goto show_callchain;
1518 hist_browser__gotorc(browser, row, 0);
1520 if (current_entry && browser->b.navkeypressed)
1521 ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1523 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1525 ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1526 width -= level * HIERARCHY_INDENT;
1528 /* the first hpp_list_node is for overhead columns */
1529 fmt_node = list_first_entry(&entry->hists->hpp_formats,
1530 struct perf_hpp_list_node, list);
1531 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1533 struct perf_hpp hpp = {
1539 if (perf_hpp__should_skip(fmt, entry->hists) ||
1540 column++ < browser->b.horiz_scroll)
1543 if (current_entry && browser->b.navkeypressed) {
1544 ui_browser__set_color(&browser->b,
1545 HE_COLORSET_SELECTED);
1547 ui_browser__set_color(&browser->b,
1548 HE_COLORSET_NORMAL);
1552 ui_browser__printf(&browser->b, "%c ", folded_sign);
1556 ui_browser__printf(&browser->b, " ");
1561 int ret = fmt->color(fmt, &hpp, entry);
1562 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1564 * fmt->color() already used ui_browser to
1565 * print the non alignment bits, skip it (+ret):
1567 ui_browser__printf(&browser->b, "%s", s + ret);
1569 int ret = fmt->entry(fmt, &hpp, entry);
1570 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1571 ui_browser__printf(&browser->b, "%s", s);
1573 width -= hpp.buf - s;
1577 ui_browser__write_nstring(&browser->b, "", hierarchy_indent);
1578 width -= hierarchy_indent;
1581 if (column >= browser->b.horiz_scroll) {
1583 struct perf_hpp hpp = {
1589 if (current_entry && browser->b.navkeypressed) {
1590 ui_browser__set_color(&browser->b,
1591 HE_COLORSET_SELECTED);
1593 ui_browser__set_color(&browser->b,
1594 HE_COLORSET_NORMAL);
1597 perf_hpp_list__for_each_format(entry->hpp_list, fmt) {
1599 ui_browser__printf(&browser->b, "%c ", folded_sign);
1602 ui_browser__write_nstring(&browser->b, "", 2);
1608 * No need to call hist_entry__snprintf_alignment()
1609 * since this fmt is always the last column in the
1613 width -= fmt->color(fmt, &hpp, entry);
1617 width -= fmt->entry(fmt, &hpp, entry);
1618 ui_browser__printf(&browser->b, "%s", ltrim(s));
1620 while (isspace(s[i++]))
1626 /* The scroll bar isn't being used */
1627 if (!browser->b.navkeypressed)
1630 ui_browser__write_nstring(&browser->b, "", width);
1636 if (entry->leaf && folded_sign == '-' && row != browser->b.rows) {
1637 struct callchain_print_arg carg = {
1638 .row_offset = row_offset,
1641 printed += hist_browser__show_callchain(browser, entry,
1643 hist_browser__show_callchain_entry, &carg,
1644 hist_browser__check_output_full);
1650 static int hist_browser__show_no_entry(struct hist_browser *browser,
1651 unsigned short row, int level)
1653 int width = browser->b.width;
1654 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1658 struct perf_hpp_fmt *fmt;
1659 struct perf_hpp_list_node *fmt_node;
1660 int indent = browser->hists->nr_hpp_node - 2;
1662 if (current_entry) {
1663 browser->he_selection = NULL;
1664 browser->selection = NULL;
1667 hist_browser__gotorc(browser, row, 0);
1669 if (current_entry && browser->b.navkeypressed)
1670 ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1672 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1674 ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1675 width -= level * HIERARCHY_INDENT;
1677 /* the first hpp_list_node is for overhead columns */
1678 fmt_node = list_first_entry(&browser->hists->hpp_formats,
1679 struct perf_hpp_list_node, list);
1680 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1681 if (perf_hpp__should_skip(fmt, browser->hists) ||
1682 column++ < browser->b.horiz_scroll)
1685 ret = fmt->width(fmt, NULL, browser->hists);
1688 /* for folded sign */
1692 /* space between columns */
1696 ui_browser__write_nstring(&browser->b, "", ret);
1700 ui_browser__write_nstring(&browser->b, "", indent * HIERARCHY_INDENT);
1701 width -= indent * HIERARCHY_INDENT;
1703 if (column >= browser->b.horiz_scroll) {
1706 ret = snprintf(buf, sizeof(buf), "no entry >= %.2f%%", browser->min_pcnt);
1707 ui_browser__printf(&browser->b, " %s", buf);
1711 /* The scroll bar isn't being used */
1712 if (!browser->b.navkeypressed)
1715 ui_browser__write_nstring(&browser->b, "", width);
1719 static int advance_hpp_check(struct perf_hpp *hpp, int inc)
1721 advance_hpp(hpp, inc);
1722 return hpp->size <= 0;
1726 hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf,
1727 size_t size, int line)
1729 struct hists *hists = browser->hists;
1730 struct perf_hpp dummy_hpp = {
1734 struct perf_hpp_fmt *fmt;
1739 if (symbol_conf.use_callchain) {
1740 ret = scnprintf(buf, size, " ");
1741 if (advance_hpp_check(&dummy_hpp, ret))
1745 hists__for_each_format(browser->hists, fmt) {
1746 if (perf_hpp__should_skip(fmt, hists) || column++ < browser->b.horiz_scroll)
1749 ret = fmt->header(fmt, &dummy_hpp, hists, line, &span);
1750 if (advance_hpp_check(&dummy_hpp, ret))
1756 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
1757 if (advance_hpp_check(&dummy_hpp, ret))
1764 static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser, char *buf, size_t size)
1766 struct hists *hists = browser->hists;
1767 struct perf_hpp dummy_hpp = {
1771 struct perf_hpp_fmt *fmt;
1772 struct perf_hpp_list_node *fmt_node;
1775 int indent = hists->nr_hpp_node - 2;
1776 bool first_node, first_col;
1778 ret = scnprintf(buf, size, " ");
1779 if (advance_hpp_check(&dummy_hpp, ret))
1783 /* the first hpp_list_node is for overhead columns */
1784 fmt_node = list_first_entry(&hists->hpp_formats,
1785 struct perf_hpp_list_node, list);
1786 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1787 if (column++ < browser->b.horiz_scroll)
1790 ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1791 if (advance_hpp_check(&dummy_hpp, ret))
1794 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
1795 if (advance_hpp_check(&dummy_hpp, ret))
1802 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "%*s",
1803 indent * HIERARCHY_INDENT, "");
1804 if (advance_hpp_check(&dummy_hpp, ret))
1809 list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
1811 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " / ");
1812 if (advance_hpp_check(&dummy_hpp, ret))
1818 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1821 if (perf_hpp__should_skip(fmt, hists))
1825 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "+");
1826 if (advance_hpp_check(&dummy_hpp, ret))
1831 ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1832 dummy_hpp.buf[ret] = '\0';
1834 start = trim(dummy_hpp.buf);
1835 ret = strlen(start);
1837 if (start != dummy_hpp.buf)
1838 memmove(dummy_hpp.buf, start, ret + 1);
1840 if (advance_hpp_check(&dummy_hpp, ret))
1848 static void hists_browser__hierarchy_headers(struct hist_browser *browser)
1852 hists_browser__scnprintf_hierarchy_headers(browser, headers,
1855 ui_browser__gotorc(&browser->b, 0, 0);
1856 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1857 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1860 static void hists_browser__headers(struct hist_browser *browser)
1862 struct hists *hists = browser->hists;
1863 struct perf_hpp_list *hpp_list = hists->hpp_list;
1867 for (line = 0; line < hpp_list->nr_header_lines; line++) {
1870 hists_browser__scnprintf_headers(browser, headers,
1871 sizeof(headers), line);
1873 ui_browser__gotorc(&browser->b, line, 0);
1874 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1875 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1879 static void hist_browser__show_headers(struct hist_browser *browser)
1881 if (symbol_conf.report_hierarchy)
1882 hists_browser__hierarchy_headers(browser);
1884 hists_browser__headers(browser);
1887 static void ui_browser__hists_init_top(struct ui_browser *browser)
1889 if (browser->top == NULL) {
1890 struct hist_browser *hb;
1892 hb = container_of(browser, struct hist_browser, b);
1893 browser->top = rb_first(&hb->hists->entries);
1897 static unsigned int hist_browser__refresh(struct ui_browser *browser)
1900 u16 header_offset = 0;
1902 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
1903 struct hists *hists = hb->hists;
1905 if (hb->show_headers) {
1906 struct perf_hpp_list *hpp_list = hists->hpp_list;
1908 hist_browser__show_headers(hb);
1909 header_offset = hpp_list->nr_header_lines;
1912 ui_browser__hists_init_top(browser);
1913 hb->he_selection = NULL;
1914 hb->selection = NULL;
1916 for (nd = browser->top; nd; nd = rb_hierarchy_next(nd)) {
1917 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1921 /* let it move to sibling */
1922 h->unfolded = false;
1926 percent = hist_entry__get_percent_limit(h);
1927 if (percent < hb->min_pcnt)
1930 if (symbol_conf.report_hierarchy) {
1931 row += hist_browser__show_hierarchy_entry(hb, h, row,
1933 if (row == browser->rows)
1936 if (h->has_no_entry) {
1937 hist_browser__show_no_entry(hb, row, h->depth + 1);
1941 row += hist_browser__show_entry(hb, h, row);
1944 if (row == browser->rows)
1948 return row + header_offset;
1951 static struct rb_node *hists__filter_entries(struct rb_node *nd,
1954 while (nd != NULL) {
1955 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1956 float percent = hist_entry__get_percent_limit(h);
1958 if (!h->filtered && percent >= min_pcnt)
1962 * If it's filtered, its all children also were filtered.
1963 * So move to sibling node.
1968 nd = rb_hierarchy_next(nd);
1974 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
1977 while (nd != NULL) {
1978 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1979 float percent = hist_entry__get_percent_limit(h);
1981 if (!h->filtered && percent >= min_pcnt)
1984 nd = rb_hierarchy_prev(nd);
1990 static void ui_browser__hists_seek(struct ui_browser *browser,
1991 off_t offset, int whence)
1993 struct hist_entry *h;
1996 struct hist_browser *hb;
1998 hb = container_of(browser, struct hist_browser, b);
2000 if (browser->nr_entries == 0)
2003 ui_browser__hists_init_top(browser);
2007 nd = hists__filter_entries(rb_first(browser->entries),
2014 nd = rb_hierarchy_last(rb_last(browser->entries));
2015 nd = hists__filter_prev_entries(nd, hb->min_pcnt);
2023 * Moves not relative to the first visible entry invalidates its
2026 h = rb_entry(browser->top, struct hist_entry, rb_node);
2030 * Here we have to check if nd is expanded (+), if it is we can't go
2031 * the next top level hist_entry, instead we must compute an offset of
2032 * what _not_ to show and not change the first visible entry.
2034 * This offset increments when we are going from top to bottom and
2035 * decreases when we're going from bottom to top.
2037 * As we don't have backpointers to the top level in the callchains
2038 * structure, we need to always print the whole hist_entry callchain,
2039 * skipping the first ones that are before the first visible entry
2040 * and stop when we printed enough lines to fill the screen.
2048 h = rb_entry(nd, struct hist_entry, rb_node);
2049 if (h->unfolded && h->leaf) {
2050 u16 remaining = h->nr_rows - h->row_offset;
2051 if (offset > remaining) {
2052 offset -= remaining;
2055 h->row_offset += offset;
2061 nd = hists__filter_entries(rb_hierarchy_next(nd),
2067 } while (offset != 0);
2068 } else if (offset < 0) {
2070 h = rb_entry(nd, struct hist_entry, rb_node);
2071 if (h->unfolded && h->leaf) {
2073 if (-offset > h->row_offset) {
2074 offset += h->row_offset;
2077 h->row_offset += offset;
2083 if (-offset > h->nr_rows) {
2084 offset += h->nr_rows;
2087 h->row_offset = h->nr_rows + offset;
2095 nd = hists__filter_prev_entries(rb_hierarchy_prev(nd),
2103 * Last unfiltered hist_entry, check if it is
2104 * unfolded, if it is then we should have
2105 * row_offset at its last entry.
2107 h = rb_entry(nd, struct hist_entry, rb_node);
2108 if (h->unfolded && h->leaf)
2109 h->row_offset = h->nr_rows;
2116 h = rb_entry(nd, struct hist_entry, rb_node);
2121 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
2122 struct hist_entry *he, FILE *fp,
2125 struct callchain_print_arg arg = {
2129 hist_browser__show_callchain(browser, he, level, 0,
2130 hist_browser__fprintf_callchain_entry, &arg,
2131 hist_browser__check_dump_full);
2135 static int hist_browser__fprintf_entry(struct hist_browser *browser,
2136 struct hist_entry *he, FILE *fp)
2140 char folded_sign = ' ';
2141 struct perf_hpp hpp = {
2145 struct perf_hpp_fmt *fmt;
2149 if (symbol_conf.use_callchain) {
2150 folded_sign = hist_entry__folded(he);
2151 printed += fprintf(fp, "%c ", folded_sign);
2154 hists__for_each_format(browser->hists, fmt) {
2155 if (perf_hpp__should_skip(fmt, he->hists))
2159 ret = scnprintf(hpp.buf, hpp.size, " ");
2160 advance_hpp(&hpp, ret);
2164 ret = fmt->entry(fmt, &hpp, he);
2165 ret = hist_entry__snprintf_alignment(he, &hpp, fmt, ret);
2166 advance_hpp(&hpp, ret);
2168 printed += fprintf(fp, "%s\n", s);
2170 if (folded_sign == '-')
2171 printed += hist_browser__fprintf_callchain(browser, he, fp, 1);
2177 static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,
2178 struct hist_entry *he,
2179 FILE *fp, int level)
2183 char folded_sign = ' ';
2184 struct perf_hpp hpp = {
2188 struct perf_hpp_fmt *fmt;
2189 struct perf_hpp_list_node *fmt_node;
2192 int hierarchy_indent = (he->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
2194 printed = fprintf(fp, "%*s", level * HIERARCHY_INDENT, "");
2196 folded_sign = hist_entry__folded(he);
2197 printed += fprintf(fp, "%c", folded_sign);
2199 /* the first hpp_list_node is for overhead columns */
2200 fmt_node = list_first_entry(&he->hists->hpp_formats,
2201 struct perf_hpp_list_node, list);
2202 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
2204 ret = scnprintf(hpp.buf, hpp.size, " ");
2205 advance_hpp(&hpp, ret);
2209 ret = fmt->entry(fmt, &hpp, he);
2210 advance_hpp(&hpp, ret);
2213 ret = scnprintf(hpp.buf, hpp.size, "%*s", hierarchy_indent, "");
2214 advance_hpp(&hpp, ret);
2216 perf_hpp_list__for_each_format(he->hpp_list, fmt) {
2217 ret = scnprintf(hpp.buf, hpp.size, " ");
2218 advance_hpp(&hpp, ret);
2220 ret = fmt->entry(fmt, &hpp, he);
2221 advance_hpp(&hpp, ret);
2224 printed += fprintf(fp, "%s\n", rtrim(s));
2226 if (he->leaf && folded_sign == '-') {
2227 printed += hist_browser__fprintf_callchain(browser, he, fp,
2234 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
2236 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
2241 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
2243 if (symbol_conf.report_hierarchy) {
2244 printed += hist_browser__fprintf_hierarchy_entry(browser,
2248 printed += hist_browser__fprintf_entry(browser, h, fp);
2251 nd = hists__filter_entries(rb_hierarchy_next(nd),
2258 static int hist_browser__dump(struct hist_browser *browser)
2264 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
2265 if (access(filename, F_OK))
2268 * XXX: Just an arbitrary lazy upper limit
2270 if (++browser->print_seq == 8192) {
2271 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
2276 fp = fopen(filename, "w");
2279 const char *err = str_error_r(errno, bf, sizeof(bf));
2280 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
2284 ++browser->print_seq;
2285 hist_browser__fprintf(browser, fp);
2287 ui_helpline__fpush("%s written!", filename);
2292 void hist_browser__init(struct hist_browser *browser,
2293 struct hists *hists)
2295 struct perf_hpp_fmt *fmt;
2297 browser->hists = hists;
2298 browser->b.refresh = hist_browser__refresh;
2299 browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
2300 browser->b.seek = ui_browser__hists_seek;
2301 browser->b.use_navkeypressed = true;
2302 browser->show_headers = symbol_conf.show_hist_headers;
2304 if (symbol_conf.report_hierarchy) {
2305 struct perf_hpp_list_node *fmt_node;
2307 /* count overhead columns (in the first node) */
2308 fmt_node = list_first_entry(&hists->hpp_formats,
2309 struct perf_hpp_list_node, list);
2310 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt)
2311 ++browser->b.columns;
2313 /* add a single column for whole hierarchy sort keys*/
2314 ++browser->b.columns;
2316 hists__for_each_format(hists, fmt)
2317 ++browser->b.columns;
2320 hists__reset_column_width(hists);
2323 struct hist_browser *hist_browser__new(struct hists *hists)
2325 struct hist_browser *browser = zalloc(sizeof(*browser));
2328 hist_browser__init(browser, hists);
2333 static struct hist_browser *
2334 perf_evsel_browser__new(struct perf_evsel *evsel,
2335 struct hist_browser_timer *hbt,
2336 struct perf_env *env)
2338 struct hist_browser *browser = hist_browser__new(evsel__hists(evsel));
2343 browser->title = perf_evsel_browser_title;
2348 void hist_browser__delete(struct hist_browser *browser)
2353 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
2355 return browser->he_selection;
2358 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
2360 return browser->he_selection->thread;
2363 /* Check whether the browser is for 'top' or 'report' */
2364 static inline bool is_report_browser(void *timer)
2366 return timer == NULL;
2369 static int perf_evsel_browser_title(struct hist_browser *browser,
2370 char *bf, size_t size)
2372 struct hist_browser_timer *hbt = browser->hbt;
2373 struct hists *hists = browser->hists;
2376 const struct dso *dso = hists->dso_filter;
2377 const struct thread *thread = hists->thread_filter;
2378 int socket_id = hists->socket_filter;
2379 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
2380 u64 nr_events = hists->stats.total_period;
2381 struct perf_evsel *evsel = hists_to_evsel(hists);
2382 const char *ev_name = perf_evsel__name(evsel);
2384 size_t buflen = sizeof(buf);
2385 char ref[30] = " show reference callgraph, ";
2386 bool enable_ref = false;
2388 if (symbol_conf.filter_relative) {
2389 nr_samples = hists->stats.nr_non_filtered_samples;
2390 nr_events = hists->stats.total_non_filtered_period;
2393 if (perf_evsel__is_group_event(evsel)) {
2394 struct perf_evsel *pos;
2396 perf_evsel__group_desc(evsel, buf, buflen);
2399 for_each_group_member(pos, evsel) {
2400 struct hists *pos_hists = evsel__hists(pos);
2402 if (symbol_conf.filter_relative) {
2403 nr_samples += pos_hists->stats.nr_non_filtered_samples;
2404 nr_events += pos_hists->stats.total_non_filtered_period;
2406 nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
2407 nr_events += pos_hists->stats.total_period;
2412 if (symbol_conf.show_ref_callgraph &&
2413 strstr(ev_name, "call-graph=no"))
2415 nr_samples = convert_unit(nr_samples, &unit);
2416 printed = scnprintf(bf, size,
2417 "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64,
2418 nr_samples, unit, ev_name, enable_ref ? ref : " ", nr_events);
2421 if (hists->uid_filter_str)
2422 printed += snprintf(bf + printed, size - printed,
2423 ", UID: %s", hists->uid_filter_str);
2425 if (hists__has(hists, thread)) {
2426 printed += scnprintf(bf + printed, size - printed,
2428 (thread->comm_set ? thread__comm_str(thread) : ""),
2431 printed += scnprintf(bf + printed, size - printed,
2433 (thread->comm_set ? thread__comm_str(thread) : ""));
2437 printed += scnprintf(bf + printed, size - printed,
2438 ", DSO: %s", dso->short_name);
2440 printed += scnprintf(bf + printed, size - printed,
2441 ", Processor Socket: %d", socket_id);
2442 if (!is_report_browser(hbt)) {
2443 struct perf_top *top = hbt->arg;
2446 printed += scnprintf(bf + printed, size - printed, " [z]");
2452 static inline void free_popup_options(char **options, int n)
2456 for (i = 0; i < n; ++i)
2461 * Only runtime switching of perf data file will make "input_name" point
2462 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
2463 * whether we need to call free() for current "input_name" during the switch.
2465 static bool is_input_name_malloced = false;
2467 static int switch_data_file(void)
2469 char *pwd, *options[32], *abs_path[32], *tmp;
2471 int nr_options = 0, choice = -1, ret = -1;
2472 struct dirent *dent;
2474 pwd = getenv("PWD");
2478 pwd_dir = opendir(pwd);
2482 memset(options, 0, sizeof(options));
2483 memset(abs_path, 0, sizeof(abs_path));
2485 while ((dent = readdir(pwd_dir))) {
2486 char path[PATH_MAX];
2488 char *name = dent->d_name;
2491 if (!(dent->d_type == DT_REG))
2494 snprintf(path, sizeof(path), "%s/%s", pwd, name);
2496 file = fopen(path, "r");
2500 if (fread(&magic, 1, 8, file) < 8)
2501 goto close_file_and_continue;
2503 if (is_perf_magic(magic)) {
2504 options[nr_options] = strdup(name);
2505 if (!options[nr_options])
2506 goto close_file_and_continue;
2508 abs_path[nr_options] = strdup(path);
2509 if (!abs_path[nr_options]) {
2510 zfree(&options[nr_options]);
2511 ui__warning("Can't search all data files due to memory shortage.\n");
2519 close_file_and_continue:
2521 if (nr_options >= 32) {
2522 ui__warning("Too many perf data files in PWD!\n"
2523 "Only the first 32 files will be listed.\n");
2530 choice = ui__popup_menu(nr_options, options);
2531 if (choice < nr_options && choice >= 0) {
2532 tmp = strdup(abs_path[choice]);
2534 if (is_input_name_malloced)
2535 free((void *)input_name);
2537 is_input_name_malloced = true;
2540 ui__warning("Data switch failed due to memory shortage!\n");
2544 free_popup_options(options, nr_options);
2545 free_popup_options(abs_path, nr_options);
2549 struct popup_action {
2550 struct thread *thread;
2551 struct map_symbol ms;
2554 int (*fn)(struct hist_browser *browser, struct popup_action *act);
2558 do_annotate(struct hist_browser *browser, struct popup_action *act)
2560 struct perf_evsel *evsel;
2561 struct annotation *notes;
2562 struct hist_entry *he;
2565 if (!objdump_path && perf_env__lookup_objdump(browser->env))
2568 notes = symbol__annotation(act->ms.sym);
2572 evsel = hists_to_evsel(browser->hists);
2573 err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
2574 he = hist_browser__selected_entry(browser);
2576 * offer option to annotate the other branch source or target
2577 * (if they exists) when returning from annotate
2579 if ((err == 'q' || err == CTRL('c')) && he->branch_info)
2582 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
2584 ui_browser__handle_resize(&browser->b);
2589 add_annotate_opt(struct hist_browser *browser __maybe_unused,
2590 struct popup_action *act, char **optstr,
2591 struct map *map, struct symbol *sym)
2593 if (sym == NULL || map->dso->annotate_warned)
2596 if (asprintf(optstr, "Annotate %s", sym->name) < 0)
2601 act->fn = do_annotate;
2606 do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
2608 struct thread *thread = act->thread;
2610 if ((!hists__has(browser->hists, thread) &&
2611 !hists__has(browser->hists, comm)) || thread == NULL)
2614 if (browser->hists->thread_filter) {
2615 pstack__remove(browser->pstack, &browser->hists->thread_filter);
2616 perf_hpp__set_elide(HISTC_THREAD, false);
2617 thread__zput(browser->hists->thread_filter);
2620 if (hists__has(browser->hists, thread)) {
2621 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
2622 thread->comm_set ? thread__comm_str(thread) : "",
2625 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
2626 thread->comm_set ? thread__comm_str(thread) : "");
2629 browser->hists->thread_filter = thread__get(thread);
2630 perf_hpp__set_elide(HISTC_THREAD, false);
2631 pstack__push(browser->pstack, &browser->hists->thread_filter);
2634 hists__filter_by_thread(browser->hists);
2635 hist_browser__reset(browser);
2640 add_thread_opt(struct hist_browser *browser, struct popup_action *act,
2641 char **optstr, struct thread *thread)
2645 if ((!hists__has(browser->hists, thread) &&
2646 !hists__has(browser->hists, comm)) || thread == NULL)
2649 if (hists__has(browser->hists, thread)) {
2650 ret = asprintf(optstr, "Zoom %s %s(%d) thread",
2651 browser->hists->thread_filter ? "out of" : "into",
2652 thread->comm_set ? thread__comm_str(thread) : "",
2655 ret = asprintf(optstr, "Zoom %s %s thread",
2656 browser->hists->thread_filter ? "out of" : "into",
2657 thread->comm_set ? thread__comm_str(thread) : "");
2662 act->thread = thread;
2663 act->fn = do_zoom_thread;
2668 do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
2670 struct map *map = act->ms.map;
2672 if (!hists__has(browser->hists, dso) || map == NULL)
2675 if (browser->hists->dso_filter) {
2676 pstack__remove(browser->pstack, &browser->hists->dso_filter);
2677 perf_hpp__set_elide(HISTC_DSO, false);
2678 browser->hists->dso_filter = NULL;
2681 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
2682 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
2683 browser->hists->dso_filter = map->dso;
2684 perf_hpp__set_elide(HISTC_DSO, true);
2685 pstack__push(browser->pstack, &browser->hists->dso_filter);
2688 hists__filter_by_dso(browser->hists);
2689 hist_browser__reset(browser);
2694 add_dso_opt(struct hist_browser *browser, struct popup_action *act,
2695 char **optstr, struct map *map)
2697 if (!hists__has(browser->hists, dso) || map == NULL)
2700 if (asprintf(optstr, "Zoom %s %s DSO",
2701 browser->hists->dso_filter ? "out of" : "into",
2702 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
2706 act->fn = do_zoom_dso;
2711 do_browse_map(struct hist_browser *browser __maybe_unused,
2712 struct popup_action *act)
2714 map__browse(act->ms.map);
2719 add_map_opt(struct hist_browser *browser,
2720 struct popup_action *act, char **optstr, struct map *map)
2722 if (!hists__has(browser->hists, dso) || map == NULL)
2725 if (asprintf(optstr, "Browse map details") < 0)
2729 act->fn = do_browse_map;
2734 do_run_script(struct hist_browser *browser __maybe_unused,
2735 struct popup_action *act)
2737 char script_opt[64];
2738 memset(script_opt, 0, sizeof(script_opt));
2741 scnprintf(script_opt, sizeof(script_opt), " -c %s ",
2742 thread__comm_str(act->thread));
2743 } else if (act->ms.sym) {
2744 scnprintf(script_opt, sizeof(script_opt), " -S %s ",
2748 script_browse(script_opt);
2753 add_script_opt(struct hist_browser *browser __maybe_unused,
2754 struct popup_action *act, char **optstr,
2755 struct thread *thread, struct symbol *sym)
2758 if (asprintf(optstr, "Run scripts for samples of thread [%s]",
2759 thread__comm_str(thread)) < 0)
2762 if (asprintf(optstr, "Run scripts for samples of symbol [%s]",
2766 if (asprintf(optstr, "Run scripts for all samples") < 0)
2770 act->thread = thread;
2772 act->fn = do_run_script;
2777 do_switch_data(struct hist_browser *browser __maybe_unused,
2778 struct popup_action *act __maybe_unused)
2780 if (switch_data_file()) {
2781 ui__warning("Won't switch the data files due to\n"
2782 "no valid data file get selected!\n");
2786 return K_SWITCH_INPUT_DATA;
2790 add_switch_opt(struct hist_browser *browser,
2791 struct popup_action *act, char **optstr)
2793 if (!is_report_browser(browser->hbt))
2796 if (asprintf(optstr, "Switch to another data file in PWD") < 0)
2799 act->fn = do_switch_data;
2804 do_exit_browser(struct hist_browser *browser __maybe_unused,
2805 struct popup_action *act __maybe_unused)
2811 add_exit_opt(struct hist_browser *browser __maybe_unused,
2812 struct popup_action *act, char **optstr)
2814 if (asprintf(optstr, "Exit") < 0)
2817 act->fn = do_exit_browser;
2822 do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
2824 if (!hists__has(browser->hists, socket) || act->socket < 0)
2827 if (browser->hists->socket_filter > -1) {
2828 pstack__remove(browser->pstack, &browser->hists->socket_filter);
2829 browser->hists->socket_filter = -1;
2830 perf_hpp__set_elide(HISTC_SOCKET, false);
2832 browser->hists->socket_filter = act->socket;
2833 perf_hpp__set_elide(HISTC_SOCKET, true);
2834 pstack__push(browser->pstack, &browser->hists->socket_filter);
2837 hists__filter_by_socket(browser->hists);
2838 hist_browser__reset(browser);
2843 add_socket_opt(struct hist_browser *browser, struct popup_action *act,
2844 char **optstr, int socket_id)
2846 if (!hists__has(browser->hists, socket) || socket_id < 0)
2849 if (asprintf(optstr, "Zoom %s Processor Socket %d",
2850 (browser->hists->socket_filter > -1) ? "out of" : "into",
2854 act->socket = socket_id;
2855 act->fn = do_zoom_socket;
2859 static void hist_browser__update_nr_entries(struct hist_browser *hb)
2862 struct rb_node *nd = rb_first(&hb->hists->entries);
2864 if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
2865 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
2869 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2871 nd = rb_hierarchy_next(nd);
2874 hb->nr_non_filtered_entries = nr_entries;
2875 hb->nr_hierarchy_entries = nr_entries;
2878 static void hist_browser__update_percent_limit(struct hist_browser *hb,
2881 struct hist_entry *he;
2882 struct rb_node *nd = rb_first(&hb->hists->entries);
2883 u64 total = hists__total_period(hb->hists);
2884 u64 min_callchain_hits = total * (percent / 100);
2886 hb->min_pcnt = callchain_param.min_percent = percent;
2888 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2889 he = rb_entry(nd, struct hist_entry, rb_node);
2891 if (he->has_no_entry) {
2892 he->has_no_entry = false;
2896 if (!he->leaf || !symbol_conf.use_callchain)
2899 if (callchain_param.mode == CHAIN_GRAPH_REL) {
2900 total = he->stat.period;
2902 if (symbol_conf.cumulate_callchain)
2903 total = he->stat_acc->period;
2905 min_callchain_hits = total * (percent / 100);
2908 callchain_param.sort(&he->sorted_chain, he->callchain,
2909 min_callchain_hits, &callchain_param);
2912 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
2914 /* force to re-evaluate folding state of callchains */
2915 he->init_have_children = false;
2916 hist_entry__set_folding(he, hb, false);
2920 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
2921 const char *helpline,
2923 struct hist_browser_timer *hbt,
2925 struct perf_env *env)
2927 struct hists *hists = evsel__hists(evsel);
2928 struct hist_browser *browser = perf_evsel_browser__new(evsel, hbt, env);
2929 struct branch_info *bi;
2930 #define MAX_OPTIONS 16
2931 char *options[MAX_OPTIONS];
2932 struct popup_action actions[MAX_OPTIONS];
2936 int delay_secs = hbt ? hbt->refresh : 0;
2938 #define HIST_BROWSER_HELP_COMMON \
2939 "h/?/F1 Show this window\n" \
2941 "PGDN/SPACE Navigate\n" \
2942 "q/ESC/CTRL+C Exit browser\n\n" \
2943 "For multiple event sessions:\n\n" \
2944 "TAB/UNTAB Switch events\n\n" \
2945 "For symbolic views (--sort has sym):\n\n" \
2946 "ENTER Zoom into DSO/Threads & Annotate current symbol\n" \
2948 "a Annotate current symbol\n" \
2949 "C Collapse all callchains\n" \
2950 "d Zoom into current DSO\n" \
2951 "E Expand all callchains\n" \
2952 "F Toggle percentage of filtered entries\n" \
2953 "H Display column headers\n" \
2954 "L Change percent limit\n" \
2955 "m Display context menu\n" \
2956 "S Zoom into current Processor Socket\n" \
2958 /* help messages are sorted by lexical order of the hotkey */
2959 const char report_help[] = HIST_BROWSER_HELP_COMMON
2960 "i Show header information\n"
2961 "P Print histograms to perf.hist.N\n"
2962 "r Run available scripts\n"
2963 "s Switch to another data file in PWD\n"
2964 "t Zoom into current Thread\n"
2965 "V Verbose (DSO names in callchains, etc)\n"
2966 "/ Filter symbol by name";
2967 const char top_help[] = HIST_BROWSER_HELP_COMMON
2968 "P Print histograms to perf.hist.N\n"
2969 "t Zoom into current Thread\n"
2970 "V Verbose (DSO names in callchains, etc)\n"
2971 "z Toggle zeroing of samples\n"
2972 "f Enable/Disable events\n"
2973 "/ Filter symbol by name";
2975 if (browser == NULL)
2978 /* reset abort key so that it can get Ctrl-C as a key */
2980 SLang_init_tty(0, 0, 0);
2983 browser->min_pcnt = min_pcnt;
2984 hist_browser__update_nr_entries(browser);
2986 browser->pstack = pstack__new(3);
2987 if (browser->pstack == NULL)
2990 ui_helpline__push(helpline);
2992 memset(options, 0, sizeof(options));
2993 memset(actions, 0, sizeof(actions));
2995 if (symbol_conf.col_width_list_str)
2996 perf_hpp__set_user_width(symbol_conf.col_width_list_str);
2999 struct thread *thread = NULL;
3000 struct map *map = NULL;
3006 key = hist_browser__run(browser, helpline);
3008 if (browser->he_selection != NULL) {
3009 thread = hist_browser__selected_thread(browser);
3010 map = browser->selection->map;
3011 socked_id = browser->he_selection->socket;
3019 * Exit the browser, let hists__browser_tree
3020 * go to the next or previous
3022 goto out_free_stack;
3024 if (!hists__has(hists, sym)) {
3025 ui_browser__warning(&browser->b, delay_secs * 2,
3026 "Annotation is only available for symbolic views, "
3027 "include \"sym*\" in --sort to use it.");
3031 if (browser->selection == NULL ||
3032 browser->selection->sym == NULL ||
3033 browser->selection->map->dso->annotate_warned)
3036 actions->ms.map = browser->selection->map;
3037 actions->ms.sym = browser->selection->sym;
3038 do_annotate(browser, actions);
3041 hist_browser__dump(browser);
3044 actions->ms.map = map;
3045 do_zoom_dso(browser, actions);
3048 verbose = (verbose + 1) % 4;
3049 browser->show_dso = verbose > 0;
3050 ui_helpline__fpush("Verbosity level set to %d\n",
3054 actions->thread = thread;
3055 do_zoom_thread(browser, actions);
3058 actions->socket = socked_id;
3059 do_zoom_socket(browser, actions);
3062 if (ui_browser__input_window("Symbol to show",
3063 "Please enter the name of symbol you want to see.\n"
3064 "To remove the filter later, press / + ENTER.",
3065 buf, "ENTER: OK, ESC: Cancel",
3066 delay_secs * 2) == K_ENTER) {
3067 hists->symbol_filter_str = *buf ? buf : NULL;
3068 hists__filter_by_symbol(hists);
3069 hist_browser__reset(browser);
3073 if (is_report_browser(hbt)) {
3074 actions->thread = NULL;
3075 actions->ms.sym = NULL;
3076 do_run_script(browser, actions);
3080 if (is_report_browser(hbt)) {
3081 key = do_switch_data(browser, actions);
3082 if (key == K_SWITCH_INPUT_DATA)
3083 goto out_free_stack;
3087 /* env->arch is NULL for live-mode (i.e. perf top) */
3089 tui__header_window(env);
3092 symbol_conf.filter_relative ^= 1;
3095 if (!is_report_browser(hbt)) {
3096 struct perf_top *top = hbt->arg;
3098 top->zero = !top->zero;
3102 if (ui_browser__input_window("Percent Limit",
3103 "Please enter the value you want to hide entries under that percent.",
3104 buf, "ENTER: OK, ESC: Cancel",
3105 delay_secs * 2) == K_ENTER) {
3107 double new_percent = strtod(buf, &end);
3109 if (new_percent < 0 || new_percent > 100) {
3110 ui_browser__warning(&browser->b, delay_secs * 2,
3111 "Invalid percent: %.2f", new_percent);
3115 hist_browser__update_percent_limit(browser, new_percent);
3116 hist_browser__reset(browser);
3122 ui_browser__help_window(&browser->b,
3123 is_report_browser(hbt) ? report_help : top_help);
3134 if (pstack__empty(browser->pstack)) {
3136 * Go back to the perf_evsel_menu__run or other user
3139 goto out_free_stack;
3142 ui_browser__dialog_yesno(&browser->b,
3143 "Do you really want to exit?"))
3144 goto out_free_stack;
3148 top = pstack__peek(browser->pstack);
3149 if (top == &browser->hists->dso_filter) {
3151 * No need to set actions->dso here since
3152 * it's just to remove the current filter.
3153 * Ditto for thread below.
3155 do_zoom_dso(browser, actions);
3156 } else if (top == &browser->hists->thread_filter) {
3157 do_zoom_thread(browser, actions);
3158 } else if (top == &browser->hists->socket_filter) {
3159 do_zoom_socket(browser, actions);
3165 goto out_free_stack;
3167 if (!is_report_browser(hbt)) {
3168 struct perf_top *top = hbt->arg;
3170 perf_evlist__toggle_enable(top->evlist);
3172 * No need to refresh, resort/decay histogram
3173 * entries if we are not collecting samples:
3175 if (top->evlist->enabled) {
3176 helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
3177 hbt->refresh = delay_secs;
3179 helpline = "Press 'f' again to re-enable the events";
3186 helpline = "Press '?' for help on key bindings";
3190 if (!hists__has(hists, sym) || browser->selection == NULL)
3191 goto skip_annotation;
3193 if (sort__mode == SORT_MODE__BRANCH) {
3194 bi = browser->he_selection->branch_info;
3197 goto skip_annotation;
3199 nr_options += add_annotate_opt(browser,
3200 &actions[nr_options],
3201 &options[nr_options],
3204 if (bi->to.sym != bi->from.sym)
3205 nr_options += add_annotate_opt(browser,
3206 &actions[nr_options],
3207 &options[nr_options],
3211 nr_options += add_annotate_opt(browser,
3212 &actions[nr_options],
3213 &options[nr_options],
3214 browser->selection->map,
3215 browser->selection->sym);
3218 nr_options += add_thread_opt(browser, &actions[nr_options],
3219 &options[nr_options], thread);
3220 nr_options += add_dso_opt(browser, &actions[nr_options],
3221 &options[nr_options], map);
3222 nr_options += add_map_opt(browser, &actions[nr_options],
3223 &options[nr_options],
3224 browser->selection ?
3225 browser->selection->map : NULL);
3226 nr_options += add_socket_opt(browser, &actions[nr_options],
3227 &options[nr_options],
3229 /* perf script support */
3230 if (!is_report_browser(hbt))
3231 goto skip_scripting;
3233 if (browser->he_selection) {
3234 if (hists__has(hists, thread) && thread) {
3235 nr_options += add_script_opt(browser,
3236 &actions[nr_options],
3237 &options[nr_options],
3241 * Note that browser->selection != NULL
3242 * when browser->he_selection is not NULL,
3243 * so we don't need to check browser->selection
3244 * before fetching browser->selection->sym like what
3245 * we do before fetching browser->selection->map.
3247 * See hist_browser__show_entry.
3249 if (hists__has(hists, sym) && browser->selection->sym) {
3250 nr_options += add_script_opt(browser,
3251 &actions[nr_options],
3252 &options[nr_options],
3253 NULL, browser->selection->sym);
3256 nr_options += add_script_opt(browser, &actions[nr_options],
3257 &options[nr_options], NULL, NULL);
3258 nr_options += add_switch_opt(browser, &actions[nr_options],
3259 &options[nr_options]);
3261 nr_options += add_exit_opt(browser, &actions[nr_options],
3262 &options[nr_options]);
3265 struct popup_action *act;
3267 choice = ui__popup_menu(nr_options, options);
3268 if (choice == -1 || choice >= nr_options)
3271 act = &actions[choice];
3272 key = act->fn(browser, act);
3275 if (key == K_SWITCH_INPUT_DATA)
3279 pstack__delete(browser->pstack);
3281 hist_browser__delete(browser);
3282 free_popup_options(options, MAX_OPTIONS);
3286 struct perf_evsel_menu {
3287 struct ui_browser b;
3288 struct perf_evsel *selection;
3289 bool lost_events, lost_events_warned;
3291 struct perf_env *env;
3294 static void perf_evsel_menu__write(struct ui_browser *browser,
3295 void *entry, int row)
3297 struct perf_evsel_menu *menu = container_of(browser,
3298 struct perf_evsel_menu, b);
3299 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
3300 struct hists *hists = evsel__hists(evsel);
3301 bool current_entry = ui_browser__is_current_entry(browser, row);
3302 unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
3303 const char *ev_name = perf_evsel__name(evsel);
3305 const char *warn = " ";
3308 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
3309 HE_COLORSET_NORMAL);
3311 if (perf_evsel__is_group_event(evsel)) {
3312 struct perf_evsel *pos;
3314 ev_name = perf_evsel__group_name(evsel);
3316 for_each_group_member(pos, evsel) {
3317 struct hists *pos_hists = evsel__hists(pos);
3318 nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
3322 nr_events = convert_unit(nr_events, &unit);
3323 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
3324 unit, unit == ' ' ? "" : " ", ev_name);
3325 ui_browser__printf(browser, "%s", bf);
3327 nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
3328 if (nr_events != 0) {
3329 menu->lost_events = true;
3331 ui_browser__set_color(browser, HE_COLORSET_TOP);
3332 nr_events = convert_unit(nr_events, &unit);
3333 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
3334 nr_events, unit, unit == ' ' ? "" : " ");
3338 ui_browser__write_nstring(browser, warn, browser->width - printed);
3341 menu->selection = evsel;
3344 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
3345 int nr_events, const char *help,
3346 struct hist_browser_timer *hbt)
3348 struct perf_evlist *evlist = menu->b.priv;
3349 struct perf_evsel *pos;
3350 const char *title = "Available samples";
3351 int delay_secs = hbt ? hbt->refresh : 0;
3354 if (ui_browser__show(&menu->b, title,
3355 "ESC: exit, ENTER|->: Browse histograms") < 0)
3359 key = ui_browser__run(&menu->b, delay_secs);
3363 hbt->timer(hbt->arg);
3365 if (!menu->lost_events_warned && menu->lost_events) {
3366 ui_browser__warn_lost_events(&menu->b);
3367 menu->lost_events_warned = true;
3372 if (!menu->selection)
3374 pos = menu->selection;
3376 perf_evlist__set_selected(evlist, pos);
3378 * Give the calling tool a chance to populate the non
3379 * default evsel resorted hists tree.
3382 hbt->timer(hbt->arg);
3383 key = perf_evsel__hists_browse(pos, nr_events, help,
3387 ui_browser__show_title(&menu->b, title);
3390 if (pos->node.next == &evlist->entries)
3391 pos = perf_evlist__first(evlist);
3393 pos = perf_evsel__next(pos);
3396 if (pos->node.prev == &evlist->entries)
3397 pos = perf_evlist__last(evlist);
3399 pos = perf_evsel__prev(pos);
3401 case K_SWITCH_INPUT_DATA:
3412 if (!ui_browser__dialog_yesno(&menu->b,
3413 "Do you really want to exit?"))
3425 ui_browser__hide(&menu->b);
3429 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
3432 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
3434 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
3440 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
3441 int nr_entries, const char *help,
3442 struct hist_browser_timer *hbt,
3444 struct perf_env *env)
3446 struct perf_evsel *pos;
3447 struct perf_evsel_menu menu = {
3449 .entries = &evlist->entries,
3450 .refresh = ui_browser__list_head_refresh,
3451 .seek = ui_browser__list_head_seek,
3452 .write = perf_evsel_menu__write,
3453 .filter = filter_group_entries,
3454 .nr_entries = nr_entries,
3457 .min_pcnt = min_pcnt,
3461 ui_helpline__push("Press ESC to exit");
3463 evlist__for_each_entry(evlist, pos) {
3464 const char *ev_name = perf_evsel__name(pos);
3465 size_t line_len = strlen(ev_name) + 7;
3467 if (menu.b.width < line_len)
3468 menu.b.width = line_len;
3471 return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
3474 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
3475 struct hist_browser_timer *hbt,
3477 struct perf_env *env)
3479 int nr_entries = evlist->nr_entries;
3482 if (nr_entries == 1) {
3483 struct perf_evsel *first = perf_evlist__first(evlist);
3485 return perf_evsel__hists_browse(first, nr_entries, help,
3486 false, hbt, min_pcnt,
3490 if (symbol_conf.event_group) {
3491 struct perf_evsel *pos;
3494 evlist__for_each_entry(evlist, pos) {
3495 if (perf_evsel__is_group_leader(pos))
3499 if (nr_entries == 1)
3503 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
3504 hbt, min_pcnt, env);