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 || hb->c2c_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 struct inline_node *inline_node__create(struct map *map, u64 ip)
150 struct inline_node *node;
159 if (dso->kernel != DSO_TYPE_USER)
162 node = dso__parse_addr_inlines(dso,
163 map__rip_2objdump(map, ip));
168 static int inline__count_rows(struct inline_node *node)
170 struct inline_list *ilist;
176 list_for_each_entry(ilist, &node->val, list) {
177 if ((ilist->filename != NULL) || (ilist->funcname != NULL))
184 static int callchain_list__inline_rows(struct callchain_list *chain)
186 struct inline_node *node;
189 node = inline_node__create(chain->ms.map, chain->ip);
193 rows = inline__count_rows(node);
194 inline_node__delete(node);
198 static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
200 int n = 0, inline_rows;
203 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
204 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
205 struct callchain_list *chain;
206 char folded_sign = ' '; /* No children */
208 list_for_each_entry(chain, &child->val, list) {
211 if (symbol_conf.inline_name) {
213 callchain_list__inline_rows(chain);
217 /* We need this because we may not have children */
218 folded_sign = callchain_list__folded(chain);
219 if (folded_sign == '+')
223 if (folded_sign == '-') /* Have children and they're unfolded */
224 n += callchain_node__count_rows_rb_tree(child);
230 static int callchain_node__count_flat_rows(struct callchain_node *node)
232 struct callchain_list *chain;
233 char folded_sign = 0;
236 list_for_each_entry(chain, &node->parent_val, list) {
238 /* only check first chain list entry */
239 folded_sign = callchain_list__folded(chain);
240 if (folded_sign == '+')
246 list_for_each_entry(chain, &node->val, list) {
248 /* node->parent_val list might be empty */
249 folded_sign = callchain_list__folded(chain);
250 if (folded_sign == '+')
259 static int callchain_node__count_folded_rows(struct callchain_node *node __maybe_unused)
264 static int callchain_node__count_rows(struct callchain_node *node)
266 struct callchain_list *chain;
267 bool unfolded = false;
268 int n = 0, inline_rows;
270 if (callchain_param.mode == CHAIN_FLAT)
271 return callchain_node__count_flat_rows(node);
272 else if (callchain_param.mode == CHAIN_FOLDED)
273 return callchain_node__count_folded_rows(node);
275 list_for_each_entry(chain, &node->val, list) {
277 if (symbol_conf.inline_name) {
278 inline_rows = callchain_list__inline_rows(chain);
282 unfolded = chain->unfolded;
286 n += callchain_node__count_rows_rb_tree(node);
291 static int callchain__count_rows(struct rb_root *chain)
296 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
297 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
298 n += callchain_node__count_rows(node);
304 static int hierarchy_count_rows(struct hist_browser *hb, struct hist_entry *he,
305 bool include_children)
308 struct rb_node *node;
309 struct hist_entry *child;
312 return callchain__count_rows(&he->sorted_chain);
314 if (he->has_no_entry)
317 node = rb_first(&he->hroot_out);
321 child = rb_entry(node, struct hist_entry, rb_node);
322 percent = hist_entry__get_percent_limit(child);
324 if (!child->filtered && percent >= hb->min_pcnt) {
327 if (include_children && child->unfolded)
328 count += hierarchy_count_rows(hb, child, true);
331 node = rb_next(node);
336 static bool hist_entry__toggle_fold(struct hist_entry *he)
341 if (!he->has_children)
344 he->unfolded = !he->unfolded;
348 static bool callchain_list__toggle_fold(struct callchain_list *cl)
353 if (!cl->has_children)
356 cl->unfolded = !cl->unfolded;
360 static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
362 struct rb_node *nd = rb_first(&node->rb_root);
364 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
365 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
366 struct callchain_list *chain;
369 list_for_each_entry(chain, &child->val, list) {
372 chain->has_children = chain->list.next != &child->val ||
373 !RB_EMPTY_ROOT(&child->rb_root);
375 chain->has_children = chain->list.next == &child->val &&
376 !RB_EMPTY_ROOT(&child->rb_root);
379 callchain_node__init_have_children_rb_tree(child);
383 static void callchain_node__init_have_children(struct callchain_node *node,
386 struct callchain_list *chain;
388 chain = list_entry(node->val.next, struct callchain_list, list);
389 chain->has_children = has_sibling;
391 if (!list_empty(&node->val)) {
392 chain = list_entry(node->val.prev, struct callchain_list, list);
393 chain->has_children = !RB_EMPTY_ROOT(&node->rb_root);
396 callchain_node__init_have_children_rb_tree(node);
399 static void callchain__init_have_children(struct rb_root *root)
401 struct rb_node *nd = rb_first(root);
402 bool has_sibling = nd && rb_next(nd);
404 for (nd = rb_first(root); nd; nd = rb_next(nd)) {
405 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
406 callchain_node__init_have_children(node, has_sibling);
407 if (callchain_param.mode == CHAIN_FLAT ||
408 callchain_param.mode == CHAIN_FOLDED)
409 callchain_node__make_parent_list(node);
413 static void hist_entry__init_have_children(struct hist_entry *he)
415 if (he->init_have_children)
419 he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
420 callchain__init_have_children(&he->sorted_chain);
422 he->has_children = !RB_EMPTY_ROOT(&he->hroot_out);
425 he->init_have_children = true;
428 static void hist_entry_init_inline_node(struct hist_entry *he)
433 he->inline_node = inline_node__create(he->ms.map, he->ip);
435 if (he->inline_node == NULL)
438 he->has_children = true;
441 static bool hist_browser__toggle_fold(struct hist_browser *browser)
443 struct hist_entry *he = browser->he_selection;
444 struct map_symbol *ms = browser->selection;
445 struct callchain_list *cl = container_of(ms, struct callchain_list, ms);
452 has_children = hist_entry__toggle_fold(he);
454 has_children = callchain_list__toggle_fold(cl);
459 hist_entry__init_have_children(he);
460 browser->b.nr_entries -= he->nr_rows;
463 browser->nr_callchain_rows -= he->nr_rows;
465 browser->nr_hierarchy_entries -= he->nr_rows;
467 if (symbol_conf.report_hierarchy)
468 child_rows = hierarchy_count_rows(browser, he, true);
473 he->nr_rows = inline__count_rows(
476 he->nr_rows = callchain__count_rows(
479 he->nr_rows = hierarchy_count_rows(browser, he, false);
481 /* account grand children */
482 if (symbol_conf.report_hierarchy)
483 browser->b.nr_entries += child_rows - he->nr_rows;
485 if (!he->leaf && he->nr_rows == 0) {
486 he->has_no_entry = true;
490 if (symbol_conf.report_hierarchy)
491 browser->b.nr_entries -= child_rows - he->nr_rows;
493 if (he->has_no_entry)
494 he->has_no_entry = false;
499 browser->b.nr_entries += he->nr_rows;
502 browser->nr_callchain_rows += he->nr_rows;
504 browser->nr_hierarchy_entries += he->nr_rows;
509 /* If it doesn't have children, no toggling performed */
513 static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
518 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
519 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
520 struct callchain_list *chain;
521 bool has_children = false;
523 list_for_each_entry(chain, &child->val, list) {
525 callchain_list__set_folding(chain, unfold);
526 has_children = chain->has_children;
530 n += callchain_node__set_folding_rb_tree(child, unfold);
536 static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
538 struct callchain_list *chain;
539 bool has_children = false;
542 list_for_each_entry(chain, &node->val, list) {
544 callchain_list__set_folding(chain, unfold);
545 has_children = chain->has_children;
549 n += callchain_node__set_folding_rb_tree(node, unfold);
554 static int callchain__set_folding(struct rb_root *chain, bool unfold)
559 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
560 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
561 n += callchain_node__set_folding(node, unfold);
567 static int hierarchy_set_folding(struct hist_browser *hb, struct hist_entry *he,
568 bool unfold __maybe_unused)
572 struct hist_entry *child;
575 for (nd = rb_first(&he->hroot_out); nd; nd = rb_next(nd)) {
576 child = rb_entry(nd, struct hist_entry, rb_node);
577 percent = hist_entry__get_percent_limit(child);
578 if (!child->filtered && percent >= hb->min_pcnt)
585 static void __hist_entry__set_folding(struct hist_entry *he,
586 struct hist_browser *hb, bool unfold)
588 hist_entry__init_have_children(he);
589 he->unfolded = unfold ? he->has_children : false;
591 if (he->has_children) {
595 n = callchain__set_folding(&he->sorted_chain, unfold);
597 n = hierarchy_set_folding(hb, he, unfold);
599 he->nr_rows = unfold ? n : 0;
604 static void hist_entry__set_folding(struct hist_entry *he,
605 struct hist_browser *browser, bool unfold)
609 percent = hist_entry__get_percent_limit(he);
610 if (he->filtered || percent < browser->min_pcnt)
613 __hist_entry__set_folding(he, browser, unfold);
615 if (!he->depth || unfold)
616 browser->nr_hierarchy_entries++;
618 browser->nr_callchain_rows += he->nr_rows;
619 else if (unfold && !hist_entry__has_hierarchy_children(he, browser->min_pcnt)) {
620 browser->nr_hierarchy_entries++;
621 he->has_no_entry = true;
624 he->has_no_entry = false;
628 __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
631 struct hist_entry *he;
633 nd = rb_first(&browser->hists->entries);
635 he = rb_entry(nd, struct hist_entry, rb_node);
637 /* set folding state even if it's currently folded */
638 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
640 hist_entry__set_folding(he, browser, unfold);
644 static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
646 browser->nr_hierarchy_entries = 0;
647 browser->nr_callchain_rows = 0;
648 __hist_browser__set_folding(browser, unfold);
650 browser->b.nr_entries = hist_browser__nr_entries(browser);
651 /* Go to the start, we may be way after valid entries after a collapse */
652 ui_browser__reset_index(&browser->b);
655 static void hist_browser__set_folding_selected(struct hist_browser *browser, bool unfold)
657 if (!browser->he_selection)
660 hist_entry__set_folding(browser->he_selection, browser, unfold);
661 browser->b.nr_entries = hist_browser__nr_entries(browser);
664 static void ui_browser__warn_lost_events(struct ui_browser *browser)
666 ui_browser__warning(browser, 4,
667 "Events are being lost, check IO/CPU overload!\n\n"
668 "You may want to run 'perf' using a RT scheduler policy:\n\n"
669 " perf top -r 80\n\n"
670 "Or reduce the sampling frequency.");
673 static int hist_browser__title(struct hist_browser *browser, char *bf, size_t size)
675 return browser->title ? browser->title(browser, bf, size) : 0;
678 int hist_browser__run(struct hist_browser *browser, const char *help)
682 struct hist_browser_timer *hbt = browser->hbt;
683 int delay_secs = hbt ? hbt->refresh : 0;
685 browser->b.entries = &browser->hists->entries;
686 browser->b.nr_entries = hist_browser__nr_entries(browser);
688 hist_browser__title(browser, title, sizeof(title));
690 if (ui_browser__show(&browser->b, title, "%s", help) < 0)
694 key = ui_browser__run(&browser->b, delay_secs);
699 hbt->timer(hbt->arg);
701 if (hist_browser__has_filter(browser) ||
702 symbol_conf.report_hierarchy)
703 hist_browser__update_nr_entries(browser);
705 nr_entries = hist_browser__nr_entries(browser);
706 ui_browser__update_nr_entries(&browser->b, nr_entries);
708 if (browser->hists->stats.nr_lost_warned !=
709 browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
710 browser->hists->stats.nr_lost_warned =
711 browser->hists->stats.nr_events[PERF_RECORD_LOST];
712 ui_browser__warn_lost_events(&browser->b);
715 hist_browser__title(browser, title, sizeof(title));
716 ui_browser__show_title(&browser->b, title);
719 case 'D': { /* Debug */
721 struct hist_entry *h = rb_entry(browser->b.top,
722 struct hist_entry, rb_node);
724 ui_helpline__fpush("%d: nr_ent=(%d,%d), rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
725 seq++, browser->b.nr_entries,
726 browser->hists->nr_entries,
730 h->row_offset, h->nr_rows);
734 /* Collapse the whole world. */
735 hist_browser__set_folding(browser, false);
738 /* Collapse the selected entry. */
739 hist_browser__set_folding_selected(browser, false);
742 /* Expand the whole world. */
743 hist_browser__set_folding(browser, true);
746 /* Expand the selected entry. */
747 hist_browser__set_folding_selected(browser, true);
750 browser->show_headers = !browser->show_headers;
751 hist_browser__update_rows(browser);
754 if (hist_browser__toggle_fold(browser))
762 ui_browser__hide(&browser->b);
766 struct callchain_print_arg {
767 /* for hists browser */
769 bool is_current_entry;
776 typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
777 struct callchain_list *chain,
778 const char *str, int offset,
780 struct callchain_print_arg *arg);
782 static void hist_browser__show_callchain_entry(struct hist_browser *browser,
783 struct callchain_list *chain,
784 const char *str, int offset,
786 struct callchain_print_arg *arg)
789 char folded_sign = callchain_list__folded(chain);
790 bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
792 color = HE_COLORSET_NORMAL;
793 width = browser->b.width - (offset + 2);
794 if (ui_browser__is_current_entry(&browser->b, row)) {
795 browser->selection = &chain->ms;
796 color = HE_COLORSET_SELECTED;
797 arg->is_current_entry = true;
800 ui_browser__set_color(&browser->b, color);
801 hist_browser__gotorc(browser, row, 0);
802 ui_browser__write_nstring(&browser->b, " ", offset);
803 ui_browser__printf(&browser->b, "%c", folded_sign);
804 ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
805 ui_browser__write_nstring(&browser->b, str, width);
808 static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
809 struct callchain_list *chain,
810 const char *str, int offset,
811 unsigned short row __maybe_unused,
812 struct callchain_print_arg *arg)
814 char folded_sign = callchain_list__folded(chain);
816 arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
820 typedef bool (*check_output_full_fn)(struct hist_browser *browser,
823 static bool hist_browser__check_output_full(struct hist_browser *browser,
826 return browser->b.rows == row;
829 static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
830 unsigned short row __maybe_unused)
835 #define LEVEL_OFFSET_STEP 3
837 static int hist_browser__show_inline(struct hist_browser *browser,
838 struct inline_node *node,
842 struct inline_list *ilist;
844 int color, width, first_row;
847 width = browser->b.width - (LEVEL_OFFSET_STEP + 2);
848 list_for_each_entry(ilist, &node->val, list) {
849 if ((ilist->filename != NULL) || (ilist->funcname != NULL)) {
850 color = HE_COLORSET_NORMAL;
851 if (ui_browser__is_current_entry(&browser->b, row))
852 color = HE_COLORSET_SELECTED;
854 if (callchain_param.key == CCKEY_ADDRESS ||
855 callchain_param.key == CCKEY_SRCLINE) {
856 if (ilist->filename != NULL)
857 scnprintf(buf, sizeof(buf),
862 scnprintf(buf, sizeof(buf), "??");
863 } else if (ilist->funcname != NULL)
864 scnprintf(buf, sizeof(buf), "%s (inline)",
866 else if (ilist->filename != NULL)
867 scnprintf(buf, sizeof(buf),
872 scnprintf(buf, sizeof(buf), "??");
874 ui_browser__set_color(&browser->b, color);
875 hist_browser__gotorc(browser, row, 0);
876 ui_browser__write_nstring(&browser->b, " ",
877 LEVEL_OFFSET_STEP + offset);
878 ui_browser__write_nstring(&browser->b, buf, width);
883 return row - first_row;
886 static size_t show_inline_list(struct hist_browser *browser, struct map *map,
887 u64 ip, int row, int offset)
889 struct inline_node *node;
892 node = inline_node__create(map, ip);
896 ret = hist_browser__show_inline(browser, node, row, offset);
898 inline_node__delete(node);
902 static int hist_browser__show_callchain_list(struct hist_browser *browser,
903 struct callchain_node *node,
904 struct callchain_list *chain,
905 unsigned short row, u64 total,
906 bool need_percent, int offset,
907 print_callchain_entry_fn print,
908 struct callchain_print_arg *arg)
910 char bf[1024], *alloc_str;
911 char buf[64], *alloc_str2;
913 int inline_rows = 0, ret = 1;
915 if (arg->row_offset != 0) {
923 str = callchain_list__sym_name(chain, bf, sizeof(bf),
926 if (symbol_conf.show_branchflag_count) {
928 callchain_list_counts__printf_value(node, chain, NULL,
931 callchain_list_counts__printf_value(NULL, chain, NULL,
934 if (asprintf(&alloc_str2, "%s%s", str, buf) < 0)
935 str = "Not enough memory!";
941 callchain_node__scnprintf_value(node, buf, sizeof(buf),
944 if (asprintf(&alloc_str, "%s %s", buf, str) < 0)
945 str = "Not enough memory!";
950 print(browser, chain, str, offset, row, arg);
954 if (symbol_conf.inline_name) {
955 inline_rows = show_inline_list(browser, chain->ms.map,
956 chain->ip, row + 1, offset);
959 return ret + inline_rows;
962 static bool check_percent_display(struct rb_node *node, u64 parent_total)
964 struct callchain_node *child;
972 child = rb_entry(node, struct callchain_node, rb_node);
973 return callchain_cumul_hits(child) != parent_total;
976 static int hist_browser__show_callchain_flat(struct hist_browser *browser,
977 struct rb_root *root,
978 unsigned short row, u64 total,
980 print_callchain_entry_fn print,
981 struct callchain_print_arg *arg,
982 check_output_full_fn is_output_full)
984 struct rb_node *node;
985 int first_row = row, offset = LEVEL_OFFSET_STEP;
988 node = rb_first(root);
989 need_percent = check_percent_display(node, parent_total);
992 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
993 struct rb_node *next = rb_next(node);
994 struct callchain_list *chain;
995 char folded_sign = ' ';
997 int extra_offset = 0;
999 list_for_each_entry(chain, &child->parent_val, list) {
1000 bool was_first = first;
1004 else if (need_percent)
1005 extra_offset = LEVEL_OFFSET_STEP;
1007 folded_sign = callchain_list__folded(chain);
1009 row += hist_browser__show_callchain_list(browser, child,
1011 was_first && need_percent,
1012 offset + extra_offset,
1015 if (is_output_full(browser, row))
1018 if (folded_sign == '+')
1022 list_for_each_entry(chain, &child->val, list) {
1023 bool was_first = first;
1027 else if (need_percent)
1028 extra_offset = LEVEL_OFFSET_STEP;
1030 folded_sign = callchain_list__folded(chain);
1032 row += hist_browser__show_callchain_list(browser, child,
1034 was_first && need_percent,
1035 offset + extra_offset,
1038 if (is_output_full(browser, row))
1041 if (folded_sign == '+')
1046 if (is_output_full(browser, row))
1051 return row - first_row;
1054 static char *hist_browser__folded_callchain_str(struct hist_browser *browser,
1055 struct callchain_list *chain,
1056 char *value_str, char *old_str)
1062 str = callchain_list__sym_name(chain, bf, sizeof(bf),
1065 if (asprintf(&new, "%s%s%s", old_str,
1066 symbol_conf.field_sep ?: ";", str) < 0)
1070 if (asprintf(&new, "%s %s", value_str, str) < 0)
1073 if (asprintf(&new, "%s", str) < 0)
1080 static int hist_browser__show_callchain_folded(struct hist_browser *browser,
1081 struct rb_root *root,
1082 unsigned short row, u64 total,
1084 print_callchain_entry_fn print,
1085 struct callchain_print_arg *arg,
1086 check_output_full_fn is_output_full)
1088 struct rb_node *node;
1089 int first_row = row, offset = LEVEL_OFFSET_STEP;
1092 node = rb_first(root);
1093 need_percent = check_percent_display(node, parent_total);
1096 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1097 struct rb_node *next = rb_next(node);
1098 struct callchain_list *chain, *first_chain = NULL;
1100 char *value_str = NULL, *value_str_alloc = NULL;
1101 char *chain_str = NULL, *chain_str_alloc = NULL;
1103 if (arg->row_offset != 0) {
1111 callchain_node__scnprintf_value(child, buf, sizeof(buf), total);
1112 if (asprintf(&value_str, "%s", buf) < 0) {
1113 value_str = (char *)"<...>";
1116 value_str_alloc = value_str;
1119 list_for_each_entry(chain, &child->parent_val, list) {
1120 chain_str = hist_browser__folded_callchain_str(browser,
1121 chain, value_str, chain_str);
1124 first_chain = chain;
1127 if (chain_str == NULL) {
1128 chain_str = (char *)"Not enough memory!";
1132 chain_str_alloc = chain_str;
1135 list_for_each_entry(chain, &child->val, list) {
1136 chain_str = hist_browser__folded_callchain_str(browser,
1137 chain, value_str, chain_str);
1140 first_chain = chain;
1143 if (chain_str == NULL) {
1144 chain_str = (char *)"Not enough memory!";
1148 chain_str_alloc = chain_str;
1152 print(browser, first_chain, chain_str, offset, row++, arg);
1153 free(value_str_alloc);
1154 free(chain_str_alloc);
1157 if (is_output_full(browser, row))
1162 return row - first_row;
1165 static int hist_browser__show_callchain_graph(struct hist_browser *browser,
1166 struct rb_root *root, int level,
1167 unsigned short row, u64 total,
1169 print_callchain_entry_fn print,
1170 struct callchain_print_arg *arg,
1171 check_output_full_fn is_output_full)
1173 struct rb_node *node;
1174 int first_row = row, offset = level * LEVEL_OFFSET_STEP;
1176 u64 percent_total = total;
1178 if (callchain_param.mode == CHAIN_GRAPH_REL)
1179 percent_total = parent_total;
1181 node = rb_first(root);
1182 need_percent = check_percent_display(node, parent_total);
1185 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1186 struct rb_node *next = rb_next(node);
1187 struct callchain_list *chain;
1188 char folded_sign = ' ';
1190 int extra_offset = 0;
1192 list_for_each_entry(chain, &child->val, list) {
1193 bool was_first = first;
1197 else if (need_percent)
1198 extra_offset = LEVEL_OFFSET_STEP;
1200 folded_sign = callchain_list__folded(chain);
1202 row += hist_browser__show_callchain_list(browser, child,
1203 chain, row, percent_total,
1204 was_first && need_percent,
1205 offset + extra_offset,
1208 if (is_output_full(browser, row))
1211 if (folded_sign == '+')
1215 if (folded_sign == '-') {
1216 const int new_level = level + (extra_offset ? 2 : 1);
1218 row += hist_browser__show_callchain_graph(browser, &child->rb_root,
1219 new_level, row, total,
1220 child->children_hit,
1221 print, arg, is_output_full);
1223 if (is_output_full(browser, row))
1228 return row - first_row;
1231 static int hist_browser__show_callchain(struct hist_browser *browser,
1232 struct hist_entry *entry, int level,
1234 print_callchain_entry_fn print,
1235 struct callchain_print_arg *arg,
1236 check_output_full_fn is_output_full)
1238 u64 total = hists__total_period(entry->hists);
1242 if (symbol_conf.cumulate_callchain)
1243 parent_total = entry->stat_acc->period;
1245 parent_total = entry->stat.period;
1247 if (callchain_param.mode == CHAIN_FLAT) {
1248 printed = hist_browser__show_callchain_flat(browser,
1249 &entry->sorted_chain, row,
1250 total, parent_total, print, arg,
1252 } else if (callchain_param.mode == CHAIN_FOLDED) {
1253 printed = hist_browser__show_callchain_folded(browser,
1254 &entry->sorted_chain, row,
1255 total, parent_total, print, arg,
1258 printed = hist_browser__show_callchain_graph(browser,
1259 &entry->sorted_chain, level, row,
1260 total, parent_total, print, arg,
1264 if (arg->is_current_entry)
1265 browser->he_selection = entry;
1271 struct ui_browser *b;
1276 int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
1278 struct hpp_arg *arg = hpp->ptr;
1283 va_start(args, fmt);
1284 len = va_arg(args, int);
1285 percent = va_arg(args, double);
1288 ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
1290 ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
1291 ui_browser__printf(arg->b, "%s", hpp->buf);
1296 #define __HPP_COLOR_PERCENT_FN(_type, _field) \
1297 static u64 __hpp_get_##_field(struct hist_entry *he) \
1299 return he->stat._field; \
1303 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
1304 struct perf_hpp *hpp, \
1305 struct hist_entry *he) \
1307 return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%", \
1308 __hpp__slsmg_color_printf, true); \
1311 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
1312 static u64 __hpp_get_acc_##_field(struct hist_entry *he) \
1314 return he->stat_acc->_field; \
1318 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
1319 struct perf_hpp *hpp, \
1320 struct hist_entry *he) \
1322 if (!symbol_conf.cumulate_callchain) { \
1323 struct hpp_arg *arg = hpp->ptr; \
1324 int len = fmt->user_len ?: fmt->len; \
1325 int ret = scnprintf(hpp->buf, hpp->size, \
1326 "%*s", len, "N/A"); \
1327 ui_browser__printf(arg->b, "%s", hpp->buf); \
1331 return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field, \
1332 " %*.2f%%", __hpp__slsmg_color_printf, true); \
1335 __HPP_COLOR_PERCENT_FN(overhead, period)
1336 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
1337 __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
1338 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
1339 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
1340 __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
1342 #undef __HPP_COLOR_PERCENT_FN
1343 #undef __HPP_COLOR_ACC_PERCENT_FN
1345 void hist_browser__init_hpp(void)
1347 perf_hpp__format[PERF_HPP__OVERHEAD].color =
1348 hist_browser__hpp_color_overhead;
1349 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
1350 hist_browser__hpp_color_overhead_sys;
1351 perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
1352 hist_browser__hpp_color_overhead_us;
1353 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
1354 hist_browser__hpp_color_overhead_guest_sys;
1355 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
1356 hist_browser__hpp_color_overhead_guest_us;
1357 perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
1358 hist_browser__hpp_color_overhead_acc;
1361 static int hist_browser__show_entry(struct hist_browser *browser,
1362 struct hist_entry *entry,
1366 int width = browser->b.width;
1367 char folded_sign = ' ';
1368 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1369 off_t row_offset = entry->row_offset;
1371 struct perf_hpp_fmt *fmt;
1373 if (current_entry) {
1374 browser->he_selection = entry;
1375 browser->selection = &entry->ms;
1378 if (symbol_conf.use_callchain) {
1379 hist_entry__init_have_children(entry);
1380 folded_sign = hist_entry__folded(entry);
1383 if (symbol_conf.inline_name &&
1384 (!entry->has_children)) {
1385 hist_entry_init_inline_node(entry);
1386 folded_sign = hist_entry__folded(entry);
1389 if (row_offset == 0) {
1390 struct hpp_arg arg = {
1392 .folded_sign = folded_sign,
1393 .current_entry = current_entry,
1397 hist_browser__gotorc(browser, row, 0);
1399 hists__for_each_format(browser->hists, fmt) {
1401 struct perf_hpp hpp = {
1407 if (perf_hpp__should_skip(fmt, entry->hists) ||
1408 column++ < browser->b.horiz_scroll)
1411 if (current_entry && browser->b.navkeypressed) {
1412 ui_browser__set_color(&browser->b,
1413 HE_COLORSET_SELECTED);
1415 ui_browser__set_color(&browser->b,
1416 HE_COLORSET_NORMAL);
1420 if (symbol_conf.use_callchain ||
1421 symbol_conf.inline_name) {
1422 ui_browser__printf(&browser->b, "%c ", folded_sign);
1427 ui_browser__printf(&browser->b, " ");
1432 int ret = fmt->color(fmt, &hpp, entry);
1433 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1435 * fmt->color() already used ui_browser to
1436 * print the non alignment bits, skip it (+ret):
1438 ui_browser__printf(&browser->b, "%s", s + ret);
1440 hist_entry__snprintf_alignment(entry, &hpp, fmt, fmt->entry(fmt, &hpp, entry));
1441 ui_browser__printf(&browser->b, "%s", s);
1443 width -= hpp.buf - s;
1446 /* The scroll bar isn't being used */
1447 if (!browser->b.navkeypressed)
1450 ui_browser__write_nstring(&browser->b, "", width);
1457 if (folded_sign == '-' && row != browser->b.rows) {
1458 struct callchain_print_arg arg = {
1459 .row_offset = row_offset,
1460 .is_current_entry = current_entry,
1463 if (entry->inline_node)
1464 printed += hist_browser__show_inline(browser,
1465 entry->inline_node, row, 0);
1467 printed += hist_browser__show_callchain(browser,
1469 hist_browser__show_callchain_entry,
1471 hist_browser__check_output_full);
1477 static int hist_browser__show_hierarchy_entry(struct hist_browser *browser,
1478 struct hist_entry *entry,
1483 int width = browser->b.width;
1484 char folded_sign = ' ';
1485 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1486 off_t row_offset = entry->row_offset;
1488 struct perf_hpp_fmt *fmt;
1489 struct perf_hpp_list_node *fmt_node;
1490 struct hpp_arg arg = {
1492 .current_entry = current_entry,
1495 int hierarchy_indent = (entry->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
1497 if (current_entry) {
1498 browser->he_selection = entry;
1499 browser->selection = &entry->ms;
1502 hist_entry__init_have_children(entry);
1503 folded_sign = hist_entry__folded(entry);
1504 arg.folded_sign = folded_sign;
1506 if (entry->leaf && row_offset) {
1508 goto show_callchain;
1511 hist_browser__gotorc(browser, row, 0);
1513 if (current_entry && browser->b.navkeypressed)
1514 ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1516 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1518 ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1519 width -= level * HIERARCHY_INDENT;
1521 /* the first hpp_list_node is for overhead columns */
1522 fmt_node = list_first_entry(&entry->hists->hpp_formats,
1523 struct perf_hpp_list_node, list);
1524 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1526 struct perf_hpp hpp = {
1532 if (perf_hpp__should_skip(fmt, entry->hists) ||
1533 column++ < browser->b.horiz_scroll)
1536 if (current_entry && browser->b.navkeypressed) {
1537 ui_browser__set_color(&browser->b,
1538 HE_COLORSET_SELECTED);
1540 ui_browser__set_color(&browser->b,
1541 HE_COLORSET_NORMAL);
1545 ui_browser__printf(&browser->b, "%c ", folded_sign);
1549 ui_browser__printf(&browser->b, " ");
1554 int ret = fmt->color(fmt, &hpp, entry);
1555 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1557 * fmt->color() already used ui_browser to
1558 * print the non alignment bits, skip it (+ret):
1560 ui_browser__printf(&browser->b, "%s", s + ret);
1562 int ret = fmt->entry(fmt, &hpp, entry);
1563 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1564 ui_browser__printf(&browser->b, "%s", s);
1566 width -= hpp.buf - s;
1570 ui_browser__write_nstring(&browser->b, "", hierarchy_indent);
1571 width -= hierarchy_indent;
1574 if (column >= browser->b.horiz_scroll) {
1576 struct perf_hpp hpp = {
1582 if (current_entry && browser->b.navkeypressed) {
1583 ui_browser__set_color(&browser->b,
1584 HE_COLORSET_SELECTED);
1586 ui_browser__set_color(&browser->b,
1587 HE_COLORSET_NORMAL);
1590 perf_hpp_list__for_each_format(entry->hpp_list, fmt) {
1592 ui_browser__printf(&browser->b, "%c ", folded_sign);
1595 ui_browser__write_nstring(&browser->b, "", 2);
1601 * No need to call hist_entry__snprintf_alignment()
1602 * since this fmt is always the last column in the
1606 width -= fmt->color(fmt, &hpp, entry);
1610 width -= fmt->entry(fmt, &hpp, entry);
1611 ui_browser__printf(&browser->b, "%s", ltrim(s));
1613 while (isspace(s[i++]))
1619 /* The scroll bar isn't being used */
1620 if (!browser->b.navkeypressed)
1623 ui_browser__write_nstring(&browser->b, "", width);
1629 if (entry->leaf && folded_sign == '-' && row != browser->b.rows) {
1630 struct callchain_print_arg carg = {
1631 .row_offset = row_offset,
1634 printed += hist_browser__show_callchain(browser, entry,
1636 hist_browser__show_callchain_entry, &carg,
1637 hist_browser__check_output_full);
1643 static int hist_browser__show_no_entry(struct hist_browser *browser,
1644 unsigned short row, int level)
1646 int width = browser->b.width;
1647 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1651 struct perf_hpp_fmt *fmt;
1652 struct perf_hpp_list_node *fmt_node;
1653 int indent = browser->hists->nr_hpp_node - 2;
1655 if (current_entry) {
1656 browser->he_selection = NULL;
1657 browser->selection = NULL;
1660 hist_browser__gotorc(browser, row, 0);
1662 if (current_entry && browser->b.navkeypressed)
1663 ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1665 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1667 ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1668 width -= level * HIERARCHY_INDENT;
1670 /* the first hpp_list_node is for overhead columns */
1671 fmt_node = list_first_entry(&browser->hists->hpp_formats,
1672 struct perf_hpp_list_node, list);
1673 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1674 if (perf_hpp__should_skip(fmt, browser->hists) ||
1675 column++ < browser->b.horiz_scroll)
1678 ret = fmt->width(fmt, NULL, browser->hists);
1681 /* for folded sign */
1685 /* space between columns */
1689 ui_browser__write_nstring(&browser->b, "", ret);
1693 ui_browser__write_nstring(&browser->b, "", indent * HIERARCHY_INDENT);
1694 width -= indent * HIERARCHY_INDENT;
1696 if (column >= browser->b.horiz_scroll) {
1699 ret = snprintf(buf, sizeof(buf), "no entry >= %.2f%%", browser->min_pcnt);
1700 ui_browser__printf(&browser->b, " %s", buf);
1704 /* The scroll bar isn't being used */
1705 if (!browser->b.navkeypressed)
1708 ui_browser__write_nstring(&browser->b, "", width);
1712 static int advance_hpp_check(struct perf_hpp *hpp, int inc)
1714 advance_hpp(hpp, inc);
1715 return hpp->size <= 0;
1719 hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf,
1720 size_t size, int line)
1722 struct hists *hists = browser->hists;
1723 struct perf_hpp dummy_hpp = {
1727 struct perf_hpp_fmt *fmt;
1732 if (symbol_conf.use_callchain) {
1733 ret = scnprintf(buf, size, " ");
1734 if (advance_hpp_check(&dummy_hpp, ret))
1738 hists__for_each_format(browser->hists, fmt) {
1739 if (perf_hpp__should_skip(fmt, hists) || column++ < browser->b.horiz_scroll)
1742 ret = fmt->header(fmt, &dummy_hpp, hists, line, &span);
1743 if (advance_hpp_check(&dummy_hpp, ret))
1749 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
1750 if (advance_hpp_check(&dummy_hpp, ret))
1757 static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser, char *buf, size_t size)
1759 struct hists *hists = browser->hists;
1760 struct perf_hpp dummy_hpp = {
1764 struct perf_hpp_fmt *fmt;
1765 struct perf_hpp_list_node *fmt_node;
1768 int indent = hists->nr_hpp_node - 2;
1769 bool first_node, first_col;
1771 ret = scnprintf(buf, size, " ");
1772 if (advance_hpp_check(&dummy_hpp, ret))
1776 /* the first hpp_list_node is for overhead columns */
1777 fmt_node = list_first_entry(&hists->hpp_formats,
1778 struct perf_hpp_list_node, list);
1779 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1780 if (column++ < browser->b.horiz_scroll)
1783 ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1784 if (advance_hpp_check(&dummy_hpp, ret))
1787 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
1788 if (advance_hpp_check(&dummy_hpp, ret))
1795 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "%*s",
1796 indent * HIERARCHY_INDENT, "");
1797 if (advance_hpp_check(&dummy_hpp, ret))
1802 list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
1804 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " / ");
1805 if (advance_hpp_check(&dummy_hpp, ret))
1811 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1814 if (perf_hpp__should_skip(fmt, hists))
1818 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "+");
1819 if (advance_hpp_check(&dummy_hpp, ret))
1824 ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1825 dummy_hpp.buf[ret] = '\0';
1827 start = trim(dummy_hpp.buf);
1828 ret = strlen(start);
1830 if (start != dummy_hpp.buf)
1831 memmove(dummy_hpp.buf, start, ret + 1);
1833 if (advance_hpp_check(&dummy_hpp, ret))
1841 static void hists_browser__hierarchy_headers(struct hist_browser *browser)
1845 hists_browser__scnprintf_hierarchy_headers(browser, headers,
1848 ui_browser__gotorc(&browser->b, 0, 0);
1849 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1850 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1853 static void hists_browser__headers(struct hist_browser *browser)
1855 struct hists *hists = browser->hists;
1856 struct perf_hpp_list *hpp_list = hists->hpp_list;
1860 for (line = 0; line < hpp_list->nr_header_lines; line++) {
1863 hists_browser__scnprintf_headers(browser, headers,
1864 sizeof(headers), line);
1866 ui_browser__gotorc(&browser->b, line, 0);
1867 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1868 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1872 static void hist_browser__show_headers(struct hist_browser *browser)
1874 if (symbol_conf.report_hierarchy)
1875 hists_browser__hierarchy_headers(browser);
1877 hists_browser__headers(browser);
1880 static void ui_browser__hists_init_top(struct ui_browser *browser)
1882 if (browser->top == NULL) {
1883 struct hist_browser *hb;
1885 hb = container_of(browser, struct hist_browser, b);
1886 browser->top = rb_first(&hb->hists->entries);
1890 static unsigned int hist_browser__refresh(struct ui_browser *browser)
1893 u16 header_offset = 0;
1895 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
1896 struct hists *hists = hb->hists;
1898 if (hb->show_headers) {
1899 struct perf_hpp_list *hpp_list = hists->hpp_list;
1901 hist_browser__show_headers(hb);
1902 header_offset = hpp_list->nr_header_lines;
1905 ui_browser__hists_init_top(browser);
1906 hb->he_selection = NULL;
1907 hb->selection = NULL;
1909 for (nd = browser->top; nd; nd = rb_hierarchy_next(nd)) {
1910 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1914 /* let it move to sibling */
1915 h->unfolded = false;
1919 percent = hist_entry__get_percent_limit(h);
1920 if (percent < hb->min_pcnt)
1923 if (symbol_conf.report_hierarchy) {
1924 row += hist_browser__show_hierarchy_entry(hb, h, row,
1926 if (row == browser->rows)
1929 if (h->has_no_entry) {
1930 hist_browser__show_no_entry(hb, row, h->depth + 1);
1934 row += hist_browser__show_entry(hb, h, row);
1937 if (row == browser->rows)
1941 return row + header_offset;
1944 static struct rb_node *hists__filter_entries(struct rb_node *nd,
1947 while (nd != NULL) {
1948 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1949 float percent = hist_entry__get_percent_limit(h);
1951 if (!h->filtered && percent >= min_pcnt)
1955 * If it's filtered, its all children also were filtered.
1956 * So move to sibling node.
1961 nd = rb_hierarchy_next(nd);
1967 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
1970 while (nd != NULL) {
1971 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1972 float percent = hist_entry__get_percent_limit(h);
1974 if (!h->filtered && percent >= min_pcnt)
1977 nd = rb_hierarchy_prev(nd);
1983 static void ui_browser__hists_seek(struct ui_browser *browser,
1984 off_t offset, int whence)
1986 struct hist_entry *h;
1989 struct hist_browser *hb;
1991 hb = container_of(browser, struct hist_browser, b);
1993 if (browser->nr_entries == 0)
1996 ui_browser__hists_init_top(browser);
2000 nd = hists__filter_entries(rb_first(browser->entries),
2007 nd = rb_hierarchy_last(rb_last(browser->entries));
2008 nd = hists__filter_prev_entries(nd, hb->min_pcnt);
2016 * Moves not relative to the first visible entry invalidates its
2019 h = rb_entry(browser->top, struct hist_entry, rb_node);
2023 * Here we have to check if nd is expanded (+), if it is we can't go
2024 * the next top level hist_entry, instead we must compute an offset of
2025 * what _not_ to show and not change the first visible entry.
2027 * This offset increments when we are going from top to bottom and
2028 * decreases when we're going from bottom to top.
2030 * As we don't have backpointers to the top level in the callchains
2031 * structure, we need to always print the whole hist_entry callchain,
2032 * skipping the first ones that are before the first visible entry
2033 * and stop when we printed enough lines to fill the screen.
2041 h = rb_entry(nd, struct hist_entry, rb_node);
2042 if (h->unfolded && h->leaf) {
2043 u16 remaining = h->nr_rows - h->row_offset;
2044 if (offset > remaining) {
2045 offset -= remaining;
2048 h->row_offset += offset;
2054 nd = hists__filter_entries(rb_hierarchy_next(nd),
2060 } while (offset != 0);
2061 } else if (offset < 0) {
2063 h = rb_entry(nd, struct hist_entry, rb_node);
2064 if (h->unfolded && h->leaf) {
2066 if (-offset > h->row_offset) {
2067 offset += h->row_offset;
2070 h->row_offset += offset;
2076 if (-offset > h->nr_rows) {
2077 offset += h->nr_rows;
2080 h->row_offset = h->nr_rows + offset;
2088 nd = hists__filter_prev_entries(rb_hierarchy_prev(nd),
2096 * Last unfiltered hist_entry, check if it is
2097 * unfolded, if it is then we should have
2098 * row_offset at its last entry.
2100 h = rb_entry(nd, struct hist_entry, rb_node);
2101 if (h->unfolded && h->leaf)
2102 h->row_offset = h->nr_rows;
2109 h = rb_entry(nd, struct hist_entry, rb_node);
2114 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
2115 struct hist_entry *he, FILE *fp,
2118 struct callchain_print_arg arg = {
2122 hist_browser__show_callchain(browser, he, level, 0,
2123 hist_browser__fprintf_callchain_entry, &arg,
2124 hist_browser__check_dump_full);
2128 static int hist_browser__fprintf_entry(struct hist_browser *browser,
2129 struct hist_entry *he, FILE *fp)
2133 char folded_sign = ' ';
2134 struct perf_hpp hpp = {
2138 struct perf_hpp_fmt *fmt;
2142 if (symbol_conf.use_callchain) {
2143 folded_sign = hist_entry__folded(he);
2144 printed += fprintf(fp, "%c ", folded_sign);
2147 hists__for_each_format(browser->hists, fmt) {
2148 if (perf_hpp__should_skip(fmt, he->hists))
2152 ret = scnprintf(hpp.buf, hpp.size, " ");
2153 advance_hpp(&hpp, ret);
2157 ret = fmt->entry(fmt, &hpp, he);
2158 ret = hist_entry__snprintf_alignment(he, &hpp, fmt, ret);
2159 advance_hpp(&hpp, ret);
2161 printed += fprintf(fp, "%s\n", s);
2163 if (folded_sign == '-')
2164 printed += hist_browser__fprintf_callchain(browser, he, fp, 1);
2170 static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,
2171 struct hist_entry *he,
2172 FILE *fp, int level)
2176 char folded_sign = ' ';
2177 struct perf_hpp hpp = {
2181 struct perf_hpp_fmt *fmt;
2182 struct perf_hpp_list_node *fmt_node;
2185 int hierarchy_indent = (he->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
2187 printed = fprintf(fp, "%*s", level * HIERARCHY_INDENT, "");
2189 folded_sign = hist_entry__folded(he);
2190 printed += fprintf(fp, "%c", folded_sign);
2192 /* the first hpp_list_node is for overhead columns */
2193 fmt_node = list_first_entry(&he->hists->hpp_formats,
2194 struct perf_hpp_list_node, list);
2195 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
2197 ret = scnprintf(hpp.buf, hpp.size, " ");
2198 advance_hpp(&hpp, ret);
2202 ret = fmt->entry(fmt, &hpp, he);
2203 advance_hpp(&hpp, ret);
2206 ret = scnprintf(hpp.buf, hpp.size, "%*s", hierarchy_indent, "");
2207 advance_hpp(&hpp, ret);
2209 perf_hpp_list__for_each_format(he->hpp_list, fmt) {
2210 ret = scnprintf(hpp.buf, hpp.size, " ");
2211 advance_hpp(&hpp, ret);
2213 ret = fmt->entry(fmt, &hpp, he);
2214 advance_hpp(&hpp, ret);
2217 printed += fprintf(fp, "%s\n", rtrim(s));
2219 if (he->leaf && folded_sign == '-') {
2220 printed += hist_browser__fprintf_callchain(browser, he, fp,
2227 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
2229 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
2234 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
2236 if (symbol_conf.report_hierarchy) {
2237 printed += hist_browser__fprintf_hierarchy_entry(browser,
2241 printed += hist_browser__fprintf_entry(browser, h, fp);
2244 nd = hists__filter_entries(rb_hierarchy_next(nd),
2251 static int hist_browser__dump(struct hist_browser *browser)
2257 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
2258 if (access(filename, F_OK))
2261 * XXX: Just an arbitrary lazy upper limit
2263 if (++browser->print_seq == 8192) {
2264 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
2269 fp = fopen(filename, "w");
2272 const char *err = str_error_r(errno, bf, sizeof(bf));
2273 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
2277 ++browser->print_seq;
2278 hist_browser__fprintf(browser, fp);
2280 ui_helpline__fpush("%s written!", filename);
2285 void hist_browser__init(struct hist_browser *browser,
2286 struct hists *hists)
2288 struct perf_hpp_fmt *fmt;
2290 browser->hists = hists;
2291 browser->b.refresh = hist_browser__refresh;
2292 browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
2293 browser->b.seek = ui_browser__hists_seek;
2294 browser->b.use_navkeypressed = true;
2295 browser->show_headers = symbol_conf.show_hist_headers;
2297 if (symbol_conf.report_hierarchy) {
2298 struct perf_hpp_list_node *fmt_node;
2300 /* count overhead columns (in the first node) */
2301 fmt_node = list_first_entry(&hists->hpp_formats,
2302 struct perf_hpp_list_node, list);
2303 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt)
2304 ++browser->b.columns;
2306 /* add a single column for whole hierarchy sort keys*/
2307 ++browser->b.columns;
2309 hists__for_each_format(hists, fmt)
2310 ++browser->b.columns;
2313 hists__reset_column_width(hists);
2316 struct hist_browser *hist_browser__new(struct hists *hists)
2318 struct hist_browser *browser = zalloc(sizeof(*browser));
2321 hist_browser__init(browser, hists);
2326 static struct hist_browser *
2327 perf_evsel_browser__new(struct perf_evsel *evsel,
2328 struct hist_browser_timer *hbt,
2329 struct perf_env *env)
2331 struct hist_browser *browser = hist_browser__new(evsel__hists(evsel));
2336 browser->title = perf_evsel_browser_title;
2341 void hist_browser__delete(struct hist_browser *browser)
2346 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
2348 return browser->he_selection;
2351 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
2353 return browser->he_selection->thread;
2356 /* Check whether the browser is for 'top' or 'report' */
2357 static inline bool is_report_browser(void *timer)
2359 return timer == NULL;
2362 static int perf_evsel_browser_title(struct hist_browser *browser,
2363 char *bf, size_t size)
2365 struct hist_browser_timer *hbt = browser->hbt;
2366 struct hists *hists = browser->hists;
2369 const struct dso *dso = hists->dso_filter;
2370 const struct thread *thread = hists->thread_filter;
2371 int socket_id = hists->socket_filter;
2372 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
2373 u64 nr_events = hists->stats.total_period;
2374 struct perf_evsel *evsel = hists_to_evsel(hists);
2375 const char *ev_name = perf_evsel__name(evsel);
2377 size_t buflen = sizeof(buf);
2378 char ref[30] = " show reference callgraph, ";
2379 bool enable_ref = false;
2381 if (symbol_conf.filter_relative) {
2382 nr_samples = hists->stats.nr_non_filtered_samples;
2383 nr_events = hists->stats.total_non_filtered_period;
2386 if (perf_evsel__is_group_event(evsel)) {
2387 struct perf_evsel *pos;
2389 perf_evsel__group_desc(evsel, buf, buflen);
2392 for_each_group_member(pos, evsel) {
2393 struct hists *pos_hists = evsel__hists(pos);
2395 if (symbol_conf.filter_relative) {
2396 nr_samples += pos_hists->stats.nr_non_filtered_samples;
2397 nr_events += pos_hists->stats.total_non_filtered_period;
2399 nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
2400 nr_events += pos_hists->stats.total_period;
2405 if (symbol_conf.show_ref_callgraph &&
2406 strstr(ev_name, "call-graph=no"))
2408 nr_samples = convert_unit(nr_samples, &unit);
2409 printed = scnprintf(bf, size,
2410 "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64,
2411 nr_samples, unit, ev_name, enable_ref ? ref : " ", nr_events);
2414 if (hists->uid_filter_str)
2415 printed += snprintf(bf + printed, size - printed,
2416 ", UID: %s", hists->uid_filter_str);
2418 if (hists__has(hists, thread)) {
2419 printed += scnprintf(bf + printed, size - printed,
2421 (thread->comm_set ? thread__comm_str(thread) : ""),
2424 printed += scnprintf(bf + printed, size - printed,
2426 (thread->comm_set ? thread__comm_str(thread) : ""));
2430 printed += scnprintf(bf + printed, size - printed,
2431 ", DSO: %s", dso->short_name);
2433 printed += scnprintf(bf + printed, size - printed,
2434 ", Processor Socket: %d", socket_id);
2435 if (!is_report_browser(hbt)) {
2436 struct perf_top *top = hbt->arg;
2439 printed += scnprintf(bf + printed, size - printed, " [z]");
2445 static inline void free_popup_options(char **options, int n)
2449 for (i = 0; i < n; ++i)
2454 * Only runtime switching of perf data file will make "input_name" point
2455 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
2456 * whether we need to call free() for current "input_name" during the switch.
2458 static bool is_input_name_malloced = false;
2460 static int switch_data_file(void)
2462 char *pwd, *options[32], *abs_path[32], *tmp;
2464 int nr_options = 0, choice = -1, ret = -1;
2465 struct dirent *dent;
2467 pwd = getenv("PWD");
2471 pwd_dir = opendir(pwd);
2475 memset(options, 0, sizeof(options));
2476 memset(abs_path, 0, sizeof(abs_path));
2478 while ((dent = readdir(pwd_dir))) {
2479 char path[PATH_MAX];
2481 char *name = dent->d_name;
2484 if (!(dent->d_type == DT_REG))
2487 snprintf(path, sizeof(path), "%s/%s", pwd, name);
2489 file = fopen(path, "r");
2493 if (fread(&magic, 1, 8, file) < 8)
2494 goto close_file_and_continue;
2496 if (is_perf_magic(magic)) {
2497 options[nr_options] = strdup(name);
2498 if (!options[nr_options])
2499 goto close_file_and_continue;
2501 abs_path[nr_options] = strdup(path);
2502 if (!abs_path[nr_options]) {
2503 zfree(&options[nr_options]);
2504 ui__warning("Can't search all data files due to memory shortage.\n");
2512 close_file_and_continue:
2514 if (nr_options >= 32) {
2515 ui__warning("Too many perf data files in PWD!\n"
2516 "Only the first 32 files will be listed.\n");
2523 choice = ui__popup_menu(nr_options, options);
2524 if (choice < nr_options && choice >= 0) {
2525 tmp = strdup(abs_path[choice]);
2527 if (is_input_name_malloced)
2528 free((void *)input_name);
2530 is_input_name_malloced = true;
2533 ui__warning("Data switch failed due to memory shortage!\n");
2537 free_popup_options(options, nr_options);
2538 free_popup_options(abs_path, nr_options);
2542 struct popup_action {
2543 struct thread *thread;
2544 struct map_symbol ms;
2547 int (*fn)(struct hist_browser *browser, struct popup_action *act);
2551 do_annotate(struct hist_browser *browser, struct popup_action *act)
2553 struct perf_evsel *evsel;
2554 struct annotation *notes;
2555 struct hist_entry *he;
2558 if (!objdump_path && perf_env__lookup_objdump(browser->env))
2561 notes = symbol__annotation(act->ms.sym);
2565 evsel = hists_to_evsel(browser->hists);
2566 err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
2567 he = hist_browser__selected_entry(browser);
2569 * offer option to annotate the other branch source or target
2570 * (if they exists) when returning from annotate
2572 if ((err == 'q' || err == CTRL('c')) && he->branch_info)
2575 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
2577 ui_browser__handle_resize(&browser->b);
2582 add_annotate_opt(struct hist_browser *browser __maybe_unused,
2583 struct popup_action *act, char **optstr,
2584 struct map *map, struct symbol *sym)
2586 if (sym == NULL || map->dso->annotate_warned)
2589 if (asprintf(optstr, "Annotate %s", sym->name) < 0)
2594 act->fn = do_annotate;
2599 do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
2601 struct thread *thread = act->thread;
2603 if ((!hists__has(browser->hists, thread) &&
2604 !hists__has(browser->hists, comm)) || thread == NULL)
2607 if (browser->hists->thread_filter) {
2608 pstack__remove(browser->pstack, &browser->hists->thread_filter);
2609 perf_hpp__set_elide(HISTC_THREAD, false);
2610 thread__zput(browser->hists->thread_filter);
2613 if (hists__has(browser->hists, thread)) {
2614 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
2615 thread->comm_set ? thread__comm_str(thread) : "",
2618 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
2619 thread->comm_set ? thread__comm_str(thread) : "");
2622 browser->hists->thread_filter = thread__get(thread);
2623 perf_hpp__set_elide(HISTC_THREAD, false);
2624 pstack__push(browser->pstack, &browser->hists->thread_filter);
2627 hists__filter_by_thread(browser->hists);
2628 hist_browser__reset(browser);
2633 add_thread_opt(struct hist_browser *browser, struct popup_action *act,
2634 char **optstr, struct thread *thread)
2638 if ((!hists__has(browser->hists, thread) &&
2639 !hists__has(browser->hists, comm)) || thread == NULL)
2642 if (hists__has(browser->hists, thread)) {
2643 ret = asprintf(optstr, "Zoom %s %s(%d) thread",
2644 browser->hists->thread_filter ? "out of" : "into",
2645 thread->comm_set ? thread__comm_str(thread) : "",
2648 ret = asprintf(optstr, "Zoom %s %s thread",
2649 browser->hists->thread_filter ? "out of" : "into",
2650 thread->comm_set ? thread__comm_str(thread) : "");
2655 act->thread = thread;
2656 act->fn = do_zoom_thread;
2661 do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
2663 struct map *map = act->ms.map;
2665 if (!hists__has(browser->hists, dso) || map == NULL)
2668 if (browser->hists->dso_filter) {
2669 pstack__remove(browser->pstack, &browser->hists->dso_filter);
2670 perf_hpp__set_elide(HISTC_DSO, false);
2671 browser->hists->dso_filter = NULL;
2674 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
2675 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
2676 browser->hists->dso_filter = map->dso;
2677 perf_hpp__set_elide(HISTC_DSO, true);
2678 pstack__push(browser->pstack, &browser->hists->dso_filter);
2681 hists__filter_by_dso(browser->hists);
2682 hist_browser__reset(browser);
2687 add_dso_opt(struct hist_browser *browser, struct popup_action *act,
2688 char **optstr, struct map *map)
2690 if (!hists__has(browser->hists, dso) || map == NULL)
2693 if (asprintf(optstr, "Zoom %s %s DSO",
2694 browser->hists->dso_filter ? "out of" : "into",
2695 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
2699 act->fn = do_zoom_dso;
2704 do_browse_map(struct hist_browser *browser __maybe_unused,
2705 struct popup_action *act)
2707 map__browse(act->ms.map);
2712 add_map_opt(struct hist_browser *browser,
2713 struct popup_action *act, char **optstr, struct map *map)
2715 if (!hists__has(browser->hists, dso) || map == NULL)
2718 if (asprintf(optstr, "Browse map details") < 0)
2722 act->fn = do_browse_map;
2727 do_run_script(struct hist_browser *browser __maybe_unused,
2728 struct popup_action *act)
2730 char script_opt[64];
2731 memset(script_opt, 0, sizeof(script_opt));
2734 scnprintf(script_opt, sizeof(script_opt), " -c %s ",
2735 thread__comm_str(act->thread));
2736 } else if (act->ms.sym) {
2737 scnprintf(script_opt, sizeof(script_opt), " -S %s ",
2741 script_browse(script_opt);
2746 add_script_opt(struct hist_browser *browser __maybe_unused,
2747 struct popup_action *act, char **optstr,
2748 struct thread *thread, struct symbol *sym)
2751 if (asprintf(optstr, "Run scripts for samples of thread [%s]",
2752 thread__comm_str(thread)) < 0)
2755 if (asprintf(optstr, "Run scripts for samples of symbol [%s]",
2759 if (asprintf(optstr, "Run scripts for all samples") < 0)
2763 act->thread = thread;
2765 act->fn = do_run_script;
2770 do_switch_data(struct hist_browser *browser __maybe_unused,
2771 struct popup_action *act __maybe_unused)
2773 if (switch_data_file()) {
2774 ui__warning("Won't switch the data files due to\n"
2775 "no valid data file get selected!\n");
2779 return K_SWITCH_INPUT_DATA;
2783 add_switch_opt(struct hist_browser *browser,
2784 struct popup_action *act, char **optstr)
2786 if (!is_report_browser(browser->hbt))
2789 if (asprintf(optstr, "Switch to another data file in PWD") < 0)
2792 act->fn = do_switch_data;
2797 do_exit_browser(struct hist_browser *browser __maybe_unused,
2798 struct popup_action *act __maybe_unused)
2804 add_exit_opt(struct hist_browser *browser __maybe_unused,
2805 struct popup_action *act, char **optstr)
2807 if (asprintf(optstr, "Exit") < 0)
2810 act->fn = do_exit_browser;
2815 do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
2817 if (!hists__has(browser->hists, socket) || act->socket < 0)
2820 if (browser->hists->socket_filter > -1) {
2821 pstack__remove(browser->pstack, &browser->hists->socket_filter);
2822 browser->hists->socket_filter = -1;
2823 perf_hpp__set_elide(HISTC_SOCKET, false);
2825 browser->hists->socket_filter = act->socket;
2826 perf_hpp__set_elide(HISTC_SOCKET, true);
2827 pstack__push(browser->pstack, &browser->hists->socket_filter);
2830 hists__filter_by_socket(browser->hists);
2831 hist_browser__reset(browser);
2836 add_socket_opt(struct hist_browser *browser, struct popup_action *act,
2837 char **optstr, int socket_id)
2839 if (!hists__has(browser->hists, socket) || socket_id < 0)
2842 if (asprintf(optstr, "Zoom %s Processor Socket %d",
2843 (browser->hists->socket_filter > -1) ? "out of" : "into",
2847 act->socket = socket_id;
2848 act->fn = do_zoom_socket;
2852 static void hist_browser__update_nr_entries(struct hist_browser *hb)
2855 struct rb_node *nd = rb_first(&hb->hists->entries);
2857 if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
2858 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
2862 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2864 nd = rb_hierarchy_next(nd);
2867 hb->nr_non_filtered_entries = nr_entries;
2868 hb->nr_hierarchy_entries = nr_entries;
2871 static void hist_browser__update_percent_limit(struct hist_browser *hb,
2874 struct hist_entry *he;
2875 struct rb_node *nd = rb_first(&hb->hists->entries);
2876 u64 total = hists__total_period(hb->hists);
2877 u64 min_callchain_hits = total * (percent / 100);
2879 hb->min_pcnt = callchain_param.min_percent = percent;
2881 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2882 he = rb_entry(nd, struct hist_entry, rb_node);
2884 if (he->has_no_entry) {
2885 he->has_no_entry = false;
2889 if (!he->leaf || !symbol_conf.use_callchain)
2892 if (callchain_param.mode == CHAIN_GRAPH_REL) {
2893 total = he->stat.period;
2895 if (symbol_conf.cumulate_callchain)
2896 total = he->stat_acc->period;
2898 min_callchain_hits = total * (percent / 100);
2901 callchain_param.sort(&he->sorted_chain, he->callchain,
2902 min_callchain_hits, &callchain_param);
2905 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
2907 /* force to re-evaluate folding state of callchains */
2908 he->init_have_children = false;
2909 hist_entry__set_folding(he, hb, false);
2913 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
2914 const char *helpline,
2916 struct hist_browser_timer *hbt,
2918 struct perf_env *env)
2920 struct hists *hists = evsel__hists(evsel);
2921 struct hist_browser *browser = perf_evsel_browser__new(evsel, hbt, env);
2922 struct branch_info *bi;
2923 #define MAX_OPTIONS 16
2924 char *options[MAX_OPTIONS];
2925 struct popup_action actions[MAX_OPTIONS];
2929 int delay_secs = hbt ? hbt->refresh : 0;
2931 #define HIST_BROWSER_HELP_COMMON \
2932 "h/?/F1 Show this window\n" \
2934 "PGDN/SPACE Navigate\n" \
2935 "q/ESC/CTRL+C Exit browser\n\n" \
2936 "For multiple event sessions:\n\n" \
2937 "TAB/UNTAB Switch events\n\n" \
2938 "For symbolic views (--sort has sym):\n\n" \
2939 "ENTER Zoom into DSO/Threads & Annotate current symbol\n" \
2941 "a Annotate current symbol\n" \
2942 "C Collapse all callchains\n" \
2943 "d Zoom into current DSO\n" \
2944 "E Expand all callchains\n" \
2945 "F Toggle percentage of filtered entries\n" \
2946 "H Display column headers\n" \
2947 "L Change percent limit\n" \
2948 "m Display context menu\n" \
2949 "S Zoom into current Processor Socket\n" \
2951 /* help messages are sorted by lexical order of the hotkey */
2952 const char report_help[] = HIST_BROWSER_HELP_COMMON
2953 "i Show header information\n"
2954 "P Print histograms to perf.hist.N\n"
2955 "r Run available scripts\n"
2956 "s Switch to another data file in PWD\n"
2957 "t Zoom into current Thread\n"
2958 "V Verbose (DSO names in callchains, etc)\n"
2959 "/ Filter symbol by name";
2960 const char top_help[] = HIST_BROWSER_HELP_COMMON
2961 "P Print histograms to perf.hist.N\n"
2962 "t Zoom into current Thread\n"
2963 "V Verbose (DSO names in callchains, etc)\n"
2964 "z Toggle zeroing of samples\n"
2965 "f Enable/Disable events\n"
2966 "/ Filter symbol by name";
2968 if (browser == NULL)
2971 /* reset abort key so that it can get Ctrl-C as a key */
2973 SLang_init_tty(0, 0, 0);
2976 browser->min_pcnt = min_pcnt;
2977 hist_browser__update_nr_entries(browser);
2979 browser->pstack = pstack__new(3);
2980 if (browser->pstack == NULL)
2983 ui_helpline__push(helpline);
2985 memset(options, 0, sizeof(options));
2986 memset(actions, 0, sizeof(actions));
2988 if (symbol_conf.col_width_list_str)
2989 perf_hpp__set_user_width(symbol_conf.col_width_list_str);
2992 struct thread *thread = NULL;
2993 struct map *map = NULL;
2999 key = hist_browser__run(browser, helpline);
3001 if (browser->he_selection != NULL) {
3002 thread = hist_browser__selected_thread(browser);
3003 map = browser->selection->map;
3004 socked_id = browser->he_selection->socket;
3012 * Exit the browser, let hists__browser_tree
3013 * go to the next or previous
3015 goto out_free_stack;
3017 if (!hists__has(hists, sym)) {
3018 ui_browser__warning(&browser->b, delay_secs * 2,
3019 "Annotation is only available for symbolic views, "
3020 "include \"sym*\" in --sort to use it.");
3024 if (browser->selection == NULL ||
3025 browser->selection->sym == NULL ||
3026 browser->selection->map->dso->annotate_warned)
3029 actions->ms.map = browser->selection->map;
3030 actions->ms.sym = browser->selection->sym;
3031 do_annotate(browser, actions);
3034 hist_browser__dump(browser);
3037 actions->ms.map = map;
3038 do_zoom_dso(browser, actions);
3041 verbose = (verbose + 1) % 4;
3042 browser->show_dso = verbose > 0;
3043 ui_helpline__fpush("Verbosity level set to %d\n",
3047 actions->thread = thread;
3048 do_zoom_thread(browser, actions);
3051 actions->socket = socked_id;
3052 do_zoom_socket(browser, actions);
3055 if (ui_browser__input_window("Symbol to show",
3056 "Please enter the name of symbol you want to see.\n"
3057 "To remove the filter later, press / + ENTER.",
3058 buf, "ENTER: OK, ESC: Cancel",
3059 delay_secs * 2) == K_ENTER) {
3060 hists->symbol_filter_str = *buf ? buf : NULL;
3061 hists__filter_by_symbol(hists);
3062 hist_browser__reset(browser);
3066 if (is_report_browser(hbt)) {
3067 actions->thread = NULL;
3068 actions->ms.sym = NULL;
3069 do_run_script(browser, actions);
3073 if (is_report_browser(hbt)) {
3074 key = do_switch_data(browser, actions);
3075 if (key == K_SWITCH_INPUT_DATA)
3076 goto out_free_stack;
3080 /* env->arch is NULL for live-mode (i.e. perf top) */
3082 tui__header_window(env);
3085 symbol_conf.filter_relative ^= 1;
3088 if (!is_report_browser(hbt)) {
3089 struct perf_top *top = hbt->arg;
3091 top->zero = !top->zero;
3095 if (ui_browser__input_window("Percent Limit",
3096 "Please enter the value you want to hide entries under that percent.",
3097 buf, "ENTER: OK, ESC: Cancel",
3098 delay_secs * 2) == K_ENTER) {
3100 double new_percent = strtod(buf, &end);
3102 if (new_percent < 0 || new_percent > 100) {
3103 ui_browser__warning(&browser->b, delay_secs * 2,
3104 "Invalid percent: %.2f", new_percent);
3108 hist_browser__update_percent_limit(browser, new_percent);
3109 hist_browser__reset(browser);
3115 ui_browser__help_window(&browser->b,
3116 is_report_browser(hbt) ? report_help : top_help);
3127 if (pstack__empty(browser->pstack)) {
3129 * Go back to the perf_evsel_menu__run or other user
3132 goto out_free_stack;
3135 ui_browser__dialog_yesno(&browser->b,
3136 "Do you really want to exit?"))
3137 goto out_free_stack;
3141 top = pstack__peek(browser->pstack);
3142 if (top == &browser->hists->dso_filter) {
3144 * No need to set actions->dso here since
3145 * it's just to remove the current filter.
3146 * Ditto for thread below.
3148 do_zoom_dso(browser, actions);
3149 } else if (top == &browser->hists->thread_filter) {
3150 do_zoom_thread(browser, actions);
3151 } else if (top == &browser->hists->socket_filter) {
3152 do_zoom_socket(browser, actions);
3158 goto out_free_stack;
3160 if (!is_report_browser(hbt)) {
3161 struct perf_top *top = hbt->arg;
3163 perf_evlist__toggle_enable(top->evlist);
3165 * No need to refresh, resort/decay histogram
3166 * entries if we are not collecting samples:
3168 if (top->evlist->enabled) {
3169 helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
3170 hbt->refresh = delay_secs;
3172 helpline = "Press 'f' again to re-enable the events";
3179 helpline = "Press '?' for help on key bindings";
3183 if (!hists__has(hists, sym) || browser->selection == NULL)
3184 goto skip_annotation;
3186 if (sort__mode == SORT_MODE__BRANCH) {
3187 bi = browser->he_selection->branch_info;
3190 goto skip_annotation;
3192 nr_options += add_annotate_opt(browser,
3193 &actions[nr_options],
3194 &options[nr_options],
3197 if (bi->to.sym != bi->from.sym)
3198 nr_options += add_annotate_opt(browser,
3199 &actions[nr_options],
3200 &options[nr_options],
3204 nr_options += add_annotate_opt(browser,
3205 &actions[nr_options],
3206 &options[nr_options],
3207 browser->selection->map,
3208 browser->selection->sym);
3211 nr_options += add_thread_opt(browser, &actions[nr_options],
3212 &options[nr_options], thread);
3213 nr_options += add_dso_opt(browser, &actions[nr_options],
3214 &options[nr_options], map);
3215 nr_options += add_map_opt(browser, &actions[nr_options],
3216 &options[nr_options],
3217 browser->selection ?
3218 browser->selection->map : NULL);
3219 nr_options += add_socket_opt(browser, &actions[nr_options],
3220 &options[nr_options],
3222 /* perf script support */
3223 if (!is_report_browser(hbt))
3224 goto skip_scripting;
3226 if (browser->he_selection) {
3227 if (hists__has(hists, thread) && thread) {
3228 nr_options += add_script_opt(browser,
3229 &actions[nr_options],
3230 &options[nr_options],
3234 * Note that browser->selection != NULL
3235 * when browser->he_selection is not NULL,
3236 * so we don't need to check browser->selection
3237 * before fetching browser->selection->sym like what
3238 * we do before fetching browser->selection->map.
3240 * See hist_browser__show_entry.
3242 if (hists__has(hists, sym) && browser->selection->sym) {
3243 nr_options += add_script_opt(browser,
3244 &actions[nr_options],
3245 &options[nr_options],
3246 NULL, browser->selection->sym);
3249 nr_options += add_script_opt(browser, &actions[nr_options],
3250 &options[nr_options], NULL, NULL);
3251 nr_options += add_switch_opt(browser, &actions[nr_options],
3252 &options[nr_options]);
3254 nr_options += add_exit_opt(browser, &actions[nr_options],
3255 &options[nr_options]);
3258 struct popup_action *act;
3260 choice = ui__popup_menu(nr_options, options);
3261 if (choice == -1 || choice >= nr_options)
3264 act = &actions[choice];
3265 key = act->fn(browser, act);
3268 if (key == K_SWITCH_INPUT_DATA)
3272 pstack__delete(browser->pstack);
3274 hist_browser__delete(browser);
3275 free_popup_options(options, MAX_OPTIONS);
3279 struct perf_evsel_menu {
3280 struct ui_browser b;
3281 struct perf_evsel *selection;
3282 bool lost_events, lost_events_warned;
3284 struct perf_env *env;
3287 static void perf_evsel_menu__write(struct ui_browser *browser,
3288 void *entry, int row)
3290 struct perf_evsel_menu *menu = container_of(browser,
3291 struct perf_evsel_menu, b);
3292 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
3293 struct hists *hists = evsel__hists(evsel);
3294 bool current_entry = ui_browser__is_current_entry(browser, row);
3295 unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
3296 const char *ev_name = perf_evsel__name(evsel);
3298 const char *warn = " ";
3301 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
3302 HE_COLORSET_NORMAL);
3304 if (perf_evsel__is_group_event(evsel)) {
3305 struct perf_evsel *pos;
3307 ev_name = perf_evsel__group_name(evsel);
3309 for_each_group_member(pos, evsel) {
3310 struct hists *pos_hists = evsel__hists(pos);
3311 nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
3315 nr_events = convert_unit(nr_events, &unit);
3316 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
3317 unit, unit == ' ' ? "" : " ", ev_name);
3318 ui_browser__printf(browser, "%s", bf);
3320 nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
3321 if (nr_events != 0) {
3322 menu->lost_events = true;
3324 ui_browser__set_color(browser, HE_COLORSET_TOP);
3325 nr_events = convert_unit(nr_events, &unit);
3326 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
3327 nr_events, unit, unit == ' ' ? "" : " ");
3331 ui_browser__write_nstring(browser, warn, browser->width - printed);
3334 menu->selection = evsel;
3337 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
3338 int nr_events, const char *help,
3339 struct hist_browser_timer *hbt)
3341 struct perf_evlist *evlist = menu->b.priv;
3342 struct perf_evsel *pos;
3343 const char *title = "Available samples";
3344 int delay_secs = hbt ? hbt->refresh : 0;
3347 if (ui_browser__show(&menu->b, title,
3348 "ESC: exit, ENTER|->: Browse histograms") < 0)
3352 key = ui_browser__run(&menu->b, delay_secs);
3356 hbt->timer(hbt->arg);
3358 if (!menu->lost_events_warned && menu->lost_events) {
3359 ui_browser__warn_lost_events(&menu->b);
3360 menu->lost_events_warned = true;
3365 if (!menu->selection)
3367 pos = menu->selection;
3369 perf_evlist__set_selected(evlist, pos);
3371 * Give the calling tool a chance to populate the non
3372 * default evsel resorted hists tree.
3375 hbt->timer(hbt->arg);
3376 key = perf_evsel__hists_browse(pos, nr_events, help,
3380 ui_browser__show_title(&menu->b, title);
3383 if (pos->node.next == &evlist->entries)
3384 pos = perf_evlist__first(evlist);
3386 pos = perf_evsel__next(pos);
3389 if (pos->node.prev == &evlist->entries)
3390 pos = perf_evlist__last(evlist);
3392 pos = perf_evsel__prev(pos);
3394 case K_SWITCH_INPUT_DATA:
3405 if (!ui_browser__dialog_yesno(&menu->b,
3406 "Do you really want to exit?"))
3418 ui_browser__hide(&menu->b);
3422 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
3425 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
3427 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
3433 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
3434 int nr_entries, const char *help,
3435 struct hist_browser_timer *hbt,
3437 struct perf_env *env)
3439 struct perf_evsel *pos;
3440 struct perf_evsel_menu menu = {
3442 .entries = &evlist->entries,
3443 .refresh = ui_browser__list_head_refresh,
3444 .seek = ui_browser__list_head_seek,
3445 .write = perf_evsel_menu__write,
3446 .filter = filter_group_entries,
3447 .nr_entries = nr_entries,
3450 .min_pcnt = min_pcnt,
3454 ui_helpline__push("Press ESC to exit");
3456 evlist__for_each_entry(evlist, pos) {
3457 const char *ev_name = perf_evsel__name(pos);
3458 size_t line_len = strlen(ev_name) + 7;
3460 if (menu.b.width < line_len)
3461 menu.b.width = line_len;
3464 return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
3467 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
3468 struct hist_browser_timer *hbt,
3470 struct perf_env *env)
3472 int nr_entries = evlist->nr_entries;
3475 if (nr_entries == 1) {
3476 struct perf_evsel *first = perf_evlist__first(evlist);
3478 return perf_evsel__hists_browse(first, nr_entries, help,
3479 false, hbt, min_pcnt,
3483 if (symbol_conf.event_group) {
3484 struct perf_evsel *pos;
3487 evlist__for_each_entry(evlist, pos) {
3488 if (perf_evsel__is_group_leader(pos))
3492 if (nr_entries == 1)
3496 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
3497 hbt, min_pcnt, env);