5 #include <linux/rbtree.h>
7 #include "../../util/evsel.h"
8 #include "../../util/evlist.h"
9 #include "../../util/hist.h"
10 #include "../../util/pstack.h"
11 #include "../../util/sort.h"
12 #include "../../util/util.h"
13 #include "../../util/top.h"
14 #include "../../arch/common.h"
16 #include "../browsers/hists.h"
17 #include "../helpline.h"
23 extern void hist_browser__init_hpp(void);
25 static int perf_evsel_browser_title(struct hist_browser *browser,
26 char *bf, size_t size);
27 static void hist_browser__update_nr_entries(struct hist_browser *hb);
29 static struct rb_node *hists__filter_entries(struct rb_node *nd,
32 static bool hist_browser__has_filter(struct hist_browser *hb)
34 return hists__has_filter(hb->hists) || hb->min_pcnt || symbol_conf.has_filter || hb->c2c_filter;
37 static int hist_browser__get_folding(struct hist_browser *browser)
40 struct hists *hists = browser->hists;
41 int unfolded_rows = 0;
43 for (nd = rb_first(&hists->entries);
44 (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
45 nd = rb_hierarchy_next(nd)) {
46 struct hist_entry *he =
47 rb_entry(nd, struct hist_entry, rb_node);
49 if (he->leaf && he->unfolded)
50 unfolded_rows += he->nr_rows;
55 static u32 hist_browser__nr_entries(struct hist_browser *hb)
59 if (symbol_conf.report_hierarchy)
60 nr_entries = hb->nr_hierarchy_entries;
61 else if (hist_browser__has_filter(hb))
62 nr_entries = hb->nr_non_filtered_entries;
64 nr_entries = hb->hists->nr_entries;
66 hb->nr_callchain_rows = hist_browser__get_folding(hb);
67 return nr_entries + hb->nr_callchain_rows;
70 static void hist_browser__update_rows(struct hist_browser *hb)
72 struct ui_browser *browser = &hb->b;
73 struct hists *hists = hb->hists;
74 struct perf_hpp_list *hpp_list = hists->hpp_list;
75 u16 header_offset, index_row;
77 header_offset = hb->show_headers ? hpp_list->nr_header_lines : 0;
78 browser->rows = browser->height - header_offset;
80 * Verify if we were at the last line and that line isn't
81 * visibe because we now show the header line(s).
83 index_row = browser->index - browser->top_idx;
84 if (index_row >= browser->rows)
85 browser->index -= index_row - browser->rows + 1;
88 static void hist_browser__refresh_dimensions(struct ui_browser *browser)
90 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
92 /* 3 == +/- toggle symbol before actual hist_entry rendering */
93 browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]"));
95 * FIXME: Just keeping existing behaviour, but this really should be
96 * before updating browser->width, as it will invalidate the
97 * calculation above. Fix this and the fallout in another
100 ui_browser__refresh_dimensions(browser);
101 hist_browser__update_rows(hb);
104 static void hist_browser__gotorc(struct hist_browser *browser, int row, int column)
106 struct hists *hists = browser->hists;
107 struct perf_hpp_list *hpp_list = hists->hpp_list;
110 header_offset = browser->show_headers ? hpp_list->nr_header_lines : 0;
111 ui_browser__gotorc(&browser->b, row + header_offset, column);
114 static void hist_browser__reset(struct hist_browser *browser)
117 * The hists__remove_entry_filter() already folds non-filtered
118 * entries so we can assume it has 0 callchain rows.
120 browser->nr_callchain_rows = 0;
122 hist_browser__update_nr_entries(browser);
123 browser->b.nr_entries = hist_browser__nr_entries(browser);
124 hist_browser__refresh_dimensions(&browser->b);
125 ui_browser__reset_index(&browser->b);
128 static char tree__folded_sign(bool unfolded)
130 return unfolded ? '-' : '+';
133 static char hist_entry__folded(const struct hist_entry *he)
135 return he->has_children ? tree__folded_sign(he->unfolded) : ' ';
138 static char callchain_list__folded(const struct callchain_list *cl)
140 return cl->has_children ? tree__folded_sign(cl->unfolded) : ' ';
143 static void callchain_list__set_folding(struct callchain_list *cl, bool unfold)
145 cl->unfolded = unfold ? cl->has_children : false;
148 static struct inline_node *inline_node__create(struct map *map, u64 ip)
151 struct inline_node *node;
160 if (dso->kernel != DSO_TYPE_USER)
163 node = dso__parse_addr_inlines(dso,
164 map__rip_2objdump(map, ip));
169 static int inline__count_rows(struct inline_node *node)
171 struct inline_list *ilist;
177 list_for_each_entry(ilist, &node->val, list) {
178 if ((ilist->filename != NULL) || (ilist->funcname != NULL))
185 static int callchain_list__inline_rows(struct callchain_list *chain)
187 struct inline_node *node;
190 node = inline_node__create(chain->ms.map, chain->ip);
194 rows = inline__count_rows(node);
195 inline_node__delete(node);
199 static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
201 int n = 0, inline_rows;
204 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
205 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
206 struct callchain_list *chain;
207 char folded_sign = ' '; /* No children */
209 list_for_each_entry(chain, &child->val, list) {
212 if (symbol_conf.inline_name) {
214 callchain_list__inline_rows(chain);
218 /* We need this because we may not have children */
219 folded_sign = callchain_list__folded(chain);
220 if (folded_sign == '+')
224 if (folded_sign == '-') /* Have children and they're unfolded */
225 n += callchain_node__count_rows_rb_tree(child);
231 static int callchain_node__count_flat_rows(struct callchain_node *node)
233 struct callchain_list *chain;
234 char folded_sign = 0;
237 list_for_each_entry(chain, &node->parent_val, list) {
239 /* only check first chain list entry */
240 folded_sign = callchain_list__folded(chain);
241 if (folded_sign == '+')
247 list_for_each_entry(chain, &node->val, list) {
249 /* node->parent_val list might be empty */
250 folded_sign = callchain_list__folded(chain);
251 if (folded_sign == '+')
260 static int callchain_node__count_folded_rows(struct callchain_node *node __maybe_unused)
265 static int callchain_node__count_rows(struct callchain_node *node)
267 struct callchain_list *chain;
268 bool unfolded = false;
269 int n = 0, inline_rows;
271 if (callchain_param.mode == CHAIN_FLAT)
272 return callchain_node__count_flat_rows(node);
273 else if (callchain_param.mode == CHAIN_FOLDED)
274 return callchain_node__count_folded_rows(node);
276 list_for_each_entry(chain, &node->val, list) {
278 if (symbol_conf.inline_name) {
279 inline_rows = callchain_list__inline_rows(chain);
283 unfolded = chain->unfolded;
287 n += callchain_node__count_rows_rb_tree(node);
292 static int callchain__count_rows(struct rb_root *chain)
297 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
298 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
299 n += callchain_node__count_rows(node);
305 static int hierarchy_count_rows(struct hist_browser *hb, struct hist_entry *he,
306 bool include_children)
309 struct rb_node *node;
310 struct hist_entry *child;
313 return callchain__count_rows(&he->sorted_chain);
315 if (he->has_no_entry)
318 node = rb_first(&he->hroot_out);
322 child = rb_entry(node, struct hist_entry, rb_node);
323 percent = hist_entry__get_percent_limit(child);
325 if (!child->filtered && percent >= hb->min_pcnt) {
328 if (include_children && child->unfolded)
329 count += hierarchy_count_rows(hb, child, true);
332 node = rb_next(node);
337 static bool hist_entry__toggle_fold(struct hist_entry *he)
342 if (!he->has_children)
345 he->unfolded = !he->unfolded;
349 static bool callchain_list__toggle_fold(struct callchain_list *cl)
354 if (!cl->has_children)
357 cl->unfolded = !cl->unfolded;
361 static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
363 struct rb_node *nd = rb_first(&node->rb_root);
365 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
366 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
367 struct callchain_list *chain;
370 list_for_each_entry(chain, &child->val, list) {
373 chain->has_children = chain->list.next != &child->val ||
374 !RB_EMPTY_ROOT(&child->rb_root);
376 chain->has_children = chain->list.next == &child->val &&
377 !RB_EMPTY_ROOT(&child->rb_root);
380 callchain_node__init_have_children_rb_tree(child);
384 static void callchain_node__init_have_children(struct callchain_node *node,
387 struct callchain_list *chain;
389 chain = list_entry(node->val.next, struct callchain_list, list);
390 chain->has_children = has_sibling;
392 if (!list_empty(&node->val)) {
393 chain = list_entry(node->val.prev, struct callchain_list, list);
394 chain->has_children = !RB_EMPTY_ROOT(&node->rb_root);
397 callchain_node__init_have_children_rb_tree(node);
400 static void callchain__init_have_children(struct rb_root *root)
402 struct rb_node *nd = rb_first(root);
403 bool has_sibling = nd && rb_next(nd);
405 for (nd = rb_first(root); nd; nd = rb_next(nd)) {
406 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
407 callchain_node__init_have_children(node, has_sibling);
408 if (callchain_param.mode == CHAIN_FLAT ||
409 callchain_param.mode == CHAIN_FOLDED)
410 callchain_node__make_parent_list(node);
414 static void hist_entry__init_have_children(struct hist_entry *he)
416 if (he->init_have_children)
420 he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
421 callchain__init_have_children(&he->sorted_chain);
423 he->has_children = !RB_EMPTY_ROOT(&he->hroot_out);
426 he->init_have_children = true;
429 static void hist_entry_init_inline_node(struct hist_entry *he)
434 he->inline_node = inline_node__create(he->ms.map, he->ip);
436 if (he->inline_node == NULL)
439 he->has_children = true;
442 static bool hist_browser__toggle_fold(struct hist_browser *browser)
444 struct hist_entry *he = browser->he_selection;
445 struct map_symbol *ms = browser->selection;
446 struct callchain_list *cl = container_of(ms, struct callchain_list, ms);
453 has_children = hist_entry__toggle_fold(he);
455 has_children = callchain_list__toggle_fold(cl);
460 hist_entry__init_have_children(he);
461 browser->b.nr_entries -= he->nr_rows;
464 browser->nr_callchain_rows -= he->nr_rows;
466 browser->nr_hierarchy_entries -= he->nr_rows;
468 if (symbol_conf.report_hierarchy)
469 child_rows = hierarchy_count_rows(browser, he, true);
474 he->nr_rows = inline__count_rows(
477 he->nr_rows = callchain__count_rows(
480 he->nr_rows = hierarchy_count_rows(browser, he, false);
482 /* account grand children */
483 if (symbol_conf.report_hierarchy)
484 browser->b.nr_entries += child_rows - he->nr_rows;
486 if (!he->leaf && he->nr_rows == 0) {
487 he->has_no_entry = true;
491 if (symbol_conf.report_hierarchy)
492 browser->b.nr_entries -= child_rows - he->nr_rows;
494 if (he->has_no_entry)
495 he->has_no_entry = false;
500 browser->b.nr_entries += he->nr_rows;
503 browser->nr_callchain_rows += he->nr_rows;
505 browser->nr_hierarchy_entries += he->nr_rows;
510 /* If it doesn't have children, no toggling performed */
514 static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
519 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
520 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
521 struct callchain_list *chain;
522 bool has_children = false;
524 list_for_each_entry(chain, &child->val, list) {
526 callchain_list__set_folding(chain, unfold);
527 has_children = chain->has_children;
531 n += callchain_node__set_folding_rb_tree(child, unfold);
537 static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
539 struct callchain_list *chain;
540 bool has_children = false;
543 list_for_each_entry(chain, &node->val, list) {
545 callchain_list__set_folding(chain, unfold);
546 has_children = chain->has_children;
550 n += callchain_node__set_folding_rb_tree(node, unfold);
555 static int callchain__set_folding(struct rb_root *chain, bool unfold)
560 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
561 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
562 n += callchain_node__set_folding(node, unfold);
568 static int hierarchy_set_folding(struct hist_browser *hb, struct hist_entry *he,
569 bool unfold __maybe_unused)
573 struct hist_entry *child;
576 for (nd = rb_first(&he->hroot_out); nd; nd = rb_next(nd)) {
577 child = rb_entry(nd, struct hist_entry, rb_node);
578 percent = hist_entry__get_percent_limit(child);
579 if (!child->filtered && percent >= hb->min_pcnt)
586 static void __hist_entry__set_folding(struct hist_entry *he,
587 struct hist_browser *hb, bool unfold)
589 hist_entry__init_have_children(he);
590 he->unfolded = unfold ? he->has_children : false;
592 if (he->has_children) {
596 n = callchain__set_folding(&he->sorted_chain, unfold);
598 n = hierarchy_set_folding(hb, he, unfold);
600 he->nr_rows = unfold ? n : 0;
605 static void hist_entry__set_folding(struct hist_entry *he,
606 struct hist_browser *browser, bool unfold)
610 percent = hist_entry__get_percent_limit(he);
611 if (he->filtered || percent < browser->min_pcnt)
614 __hist_entry__set_folding(he, browser, unfold);
616 if (!he->depth || unfold)
617 browser->nr_hierarchy_entries++;
619 browser->nr_callchain_rows += he->nr_rows;
620 else if (unfold && !hist_entry__has_hierarchy_children(he, browser->min_pcnt)) {
621 browser->nr_hierarchy_entries++;
622 he->has_no_entry = true;
625 he->has_no_entry = false;
629 __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
632 struct hist_entry *he;
634 nd = rb_first(&browser->hists->entries);
636 he = rb_entry(nd, struct hist_entry, rb_node);
638 /* set folding state even if it's currently folded */
639 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
641 hist_entry__set_folding(he, browser, unfold);
645 static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
647 browser->nr_hierarchy_entries = 0;
648 browser->nr_callchain_rows = 0;
649 __hist_browser__set_folding(browser, unfold);
651 browser->b.nr_entries = hist_browser__nr_entries(browser);
652 /* Go to the start, we may be way after valid entries after a collapse */
653 ui_browser__reset_index(&browser->b);
656 static void hist_browser__set_folding_selected(struct hist_browser *browser, bool unfold)
658 if (!browser->he_selection)
661 hist_entry__set_folding(browser->he_selection, browser, unfold);
662 browser->b.nr_entries = hist_browser__nr_entries(browser);
665 static void ui_browser__warn_lost_events(struct ui_browser *browser)
667 ui_browser__warning(browser, 4,
668 "Events are being lost, check IO/CPU overload!\n\n"
669 "You may want to run 'perf' using a RT scheduler policy:\n\n"
670 " perf top -r 80\n\n"
671 "Or reduce the sampling frequency.");
674 static int hist_browser__title(struct hist_browser *browser, char *bf, size_t size)
676 return browser->title ? browser->title(browser, bf, size) : 0;
679 int hist_browser__run(struct hist_browser *browser, const char *help)
683 struct hist_browser_timer *hbt = browser->hbt;
684 int delay_secs = hbt ? hbt->refresh : 0;
686 browser->b.entries = &browser->hists->entries;
687 browser->b.nr_entries = hist_browser__nr_entries(browser);
689 hist_browser__title(browser, title, sizeof(title));
691 if (ui_browser__show(&browser->b, title, "%s", help) < 0)
695 key = ui_browser__run(&browser->b, delay_secs);
700 hbt->timer(hbt->arg);
702 if (hist_browser__has_filter(browser) ||
703 symbol_conf.report_hierarchy)
704 hist_browser__update_nr_entries(browser);
706 nr_entries = hist_browser__nr_entries(browser);
707 ui_browser__update_nr_entries(&browser->b, nr_entries);
709 if (browser->hists->stats.nr_lost_warned !=
710 browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
711 browser->hists->stats.nr_lost_warned =
712 browser->hists->stats.nr_events[PERF_RECORD_LOST];
713 ui_browser__warn_lost_events(&browser->b);
716 hist_browser__title(browser, title, sizeof(title));
717 ui_browser__show_title(&browser->b, title);
720 case 'D': { /* Debug */
722 struct hist_entry *h = rb_entry(browser->b.top,
723 struct hist_entry, rb_node);
725 ui_helpline__fpush("%d: nr_ent=(%d,%d), rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
726 seq++, browser->b.nr_entries,
727 browser->hists->nr_entries,
731 h->row_offset, h->nr_rows);
735 /* Collapse the whole world. */
736 hist_browser__set_folding(browser, false);
739 /* Collapse the selected entry. */
740 hist_browser__set_folding_selected(browser, false);
743 /* Expand the whole world. */
744 hist_browser__set_folding(browser, true);
747 /* Expand the selected entry. */
748 hist_browser__set_folding_selected(browser, true);
751 browser->show_headers = !browser->show_headers;
752 hist_browser__update_rows(browser);
755 if (hist_browser__toggle_fold(browser))
763 ui_browser__hide(&browser->b);
767 struct callchain_print_arg {
768 /* for hists browser */
770 bool is_current_entry;
777 typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
778 struct callchain_list *chain,
779 const char *str, int offset,
781 struct callchain_print_arg *arg);
783 static void hist_browser__show_callchain_entry(struct hist_browser *browser,
784 struct callchain_list *chain,
785 const char *str, int offset,
787 struct callchain_print_arg *arg)
790 char folded_sign = callchain_list__folded(chain);
791 bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
793 color = HE_COLORSET_NORMAL;
794 width = browser->b.width - (offset + 2);
795 if (ui_browser__is_current_entry(&browser->b, row)) {
796 browser->selection = &chain->ms;
797 color = HE_COLORSET_SELECTED;
798 arg->is_current_entry = true;
801 ui_browser__set_color(&browser->b, color);
802 hist_browser__gotorc(browser, row, 0);
803 ui_browser__write_nstring(&browser->b, " ", offset);
804 ui_browser__printf(&browser->b, "%c", folded_sign);
805 ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
806 ui_browser__write_nstring(&browser->b, str, width);
809 static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
810 struct callchain_list *chain,
811 const char *str, int offset,
812 unsigned short row __maybe_unused,
813 struct callchain_print_arg *arg)
815 char folded_sign = callchain_list__folded(chain);
817 arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
821 typedef bool (*check_output_full_fn)(struct hist_browser *browser,
824 static bool hist_browser__check_output_full(struct hist_browser *browser,
827 return browser->b.rows == row;
830 static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
831 unsigned short row __maybe_unused)
836 #define LEVEL_OFFSET_STEP 3
838 static int hist_browser__show_inline(struct hist_browser *browser,
839 struct inline_node *node,
843 struct inline_list *ilist;
845 int color, width, first_row;
848 width = browser->b.width - (LEVEL_OFFSET_STEP + 2);
849 list_for_each_entry(ilist, &node->val, list) {
850 if ((ilist->filename != NULL) || (ilist->funcname != NULL)) {
851 color = HE_COLORSET_NORMAL;
852 if (ui_browser__is_current_entry(&browser->b, row))
853 color = HE_COLORSET_SELECTED;
855 if (callchain_param.key == CCKEY_ADDRESS ||
856 callchain_param.key == CCKEY_SRCLINE) {
857 if (ilist->filename != NULL)
858 scnprintf(buf, sizeof(buf),
863 scnprintf(buf, sizeof(buf), "??");
864 } else if (ilist->funcname != NULL)
865 scnprintf(buf, sizeof(buf), "%s (inline)",
867 else if (ilist->filename != NULL)
868 scnprintf(buf, sizeof(buf),
873 scnprintf(buf, sizeof(buf), "??");
875 ui_browser__set_color(&browser->b, color);
876 hist_browser__gotorc(browser, row, 0);
877 ui_browser__write_nstring(&browser->b, " ",
878 LEVEL_OFFSET_STEP + offset);
879 ui_browser__write_nstring(&browser->b, buf, width);
884 return row - first_row;
887 static size_t show_inline_list(struct hist_browser *browser, struct map *map,
888 u64 ip, int row, int offset)
890 struct inline_node *node;
893 node = inline_node__create(map, ip);
897 ret = hist_browser__show_inline(browser, node, row, offset);
899 inline_node__delete(node);
903 static int hist_browser__show_callchain_list(struct hist_browser *browser,
904 struct callchain_node *node,
905 struct callchain_list *chain,
906 unsigned short row, u64 total,
907 bool need_percent, int offset,
908 print_callchain_entry_fn print,
909 struct callchain_print_arg *arg)
911 char bf[1024], *alloc_str;
912 char buf[64], *alloc_str2;
914 int inline_rows = 0, ret = 1;
916 if (arg->row_offset != 0) {
924 str = callchain_list__sym_name(chain, bf, sizeof(bf),
927 if (symbol_conf.show_branchflag_count) {
929 callchain_list_counts__printf_value(node, chain, NULL,
932 callchain_list_counts__printf_value(NULL, chain, NULL,
935 if (asprintf(&alloc_str2, "%s%s", str, buf) < 0)
936 str = "Not enough memory!";
942 callchain_node__scnprintf_value(node, buf, sizeof(buf),
945 if (asprintf(&alloc_str, "%s %s", buf, str) < 0)
946 str = "Not enough memory!";
951 print(browser, chain, str, offset, row, arg);
955 if (symbol_conf.inline_name) {
956 inline_rows = show_inline_list(browser, chain->ms.map,
957 chain->ip, row + 1, offset);
960 return ret + inline_rows;
963 static bool check_percent_display(struct rb_node *node, u64 parent_total)
965 struct callchain_node *child;
973 child = rb_entry(node, struct callchain_node, rb_node);
974 return callchain_cumul_hits(child) != parent_total;
977 static int hist_browser__show_callchain_flat(struct hist_browser *browser,
978 struct rb_root *root,
979 unsigned short row, u64 total,
981 print_callchain_entry_fn print,
982 struct callchain_print_arg *arg,
983 check_output_full_fn is_output_full)
985 struct rb_node *node;
986 int first_row = row, offset = LEVEL_OFFSET_STEP;
989 node = rb_first(root);
990 need_percent = check_percent_display(node, parent_total);
993 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
994 struct rb_node *next = rb_next(node);
995 struct callchain_list *chain;
996 char folded_sign = ' ';
998 int extra_offset = 0;
1000 list_for_each_entry(chain, &child->parent_val, list) {
1001 bool was_first = first;
1005 else if (need_percent)
1006 extra_offset = LEVEL_OFFSET_STEP;
1008 folded_sign = callchain_list__folded(chain);
1010 row += hist_browser__show_callchain_list(browser, child,
1012 was_first && need_percent,
1013 offset + extra_offset,
1016 if (is_output_full(browser, row))
1019 if (folded_sign == '+')
1023 list_for_each_entry(chain, &child->val, list) {
1024 bool was_first = first;
1028 else if (need_percent)
1029 extra_offset = LEVEL_OFFSET_STEP;
1031 folded_sign = callchain_list__folded(chain);
1033 row += hist_browser__show_callchain_list(browser, child,
1035 was_first && need_percent,
1036 offset + extra_offset,
1039 if (is_output_full(browser, row))
1042 if (folded_sign == '+')
1047 if (is_output_full(browser, row))
1052 return row - first_row;
1055 static char *hist_browser__folded_callchain_str(struct hist_browser *browser,
1056 struct callchain_list *chain,
1057 char *value_str, char *old_str)
1063 str = callchain_list__sym_name(chain, bf, sizeof(bf),
1066 if (asprintf(&new, "%s%s%s", old_str,
1067 symbol_conf.field_sep ?: ";", str) < 0)
1071 if (asprintf(&new, "%s %s", value_str, str) < 0)
1074 if (asprintf(&new, "%s", str) < 0)
1081 static int hist_browser__show_callchain_folded(struct hist_browser *browser,
1082 struct rb_root *root,
1083 unsigned short row, u64 total,
1085 print_callchain_entry_fn print,
1086 struct callchain_print_arg *arg,
1087 check_output_full_fn is_output_full)
1089 struct rb_node *node;
1090 int first_row = row, offset = LEVEL_OFFSET_STEP;
1093 node = rb_first(root);
1094 need_percent = check_percent_display(node, parent_total);
1097 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1098 struct rb_node *next = rb_next(node);
1099 struct callchain_list *chain, *first_chain = NULL;
1101 char *value_str = NULL, *value_str_alloc = NULL;
1102 char *chain_str = NULL, *chain_str_alloc = NULL;
1104 if (arg->row_offset != 0) {
1112 callchain_node__scnprintf_value(child, buf, sizeof(buf), total);
1113 if (asprintf(&value_str, "%s", buf) < 0) {
1114 value_str = (char *)"<...>";
1117 value_str_alloc = value_str;
1120 list_for_each_entry(chain, &child->parent_val, list) {
1121 chain_str = hist_browser__folded_callchain_str(browser,
1122 chain, value_str, chain_str);
1125 first_chain = chain;
1128 if (chain_str == NULL) {
1129 chain_str = (char *)"Not enough memory!";
1133 chain_str_alloc = chain_str;
1136 list_for_each_entry(chain, &child->val, list) {
1137 chain_str = hist_browser__folded_callchain_str(browser,
1138 chain, value_str, chain_str);
1141 first_chain = chain;
1144 if (chain_str == NULL) {
1145 chain_str = (char *)"Not enough memory!";
1149 chain_str_alloc = chain_str;
1153 print(browser, first_chain, chain_str, offset, row++, arg);
1154 free(value_str_alloc);
1155 free(chain_str_alloc);
1158 if (is_output_full(browser, row))
1163 return row - first_row;
1166 static int hist_browser__show_callchain_graph(struct hist_browser *browser,
1167 struct rb_root *root, int level,
1168 unsigned short row, u64 total,
1170 print_callchain_entry_fn print,
1171 struct callchain_print_arg *arg,
1172 check_output_full_fn is_output_full)
1174 struct rb_node *node;
1175 int first_row = row, offset = level * LEVEL_OFFSET_STEP;
1177 u64 percent_total = total;
1179 if (callchain_param.mode == CHAIN_GRAPH_REL)
1180 percent_total = parent_total;
1182 node = rb_first(root);
1183 need_percent = check_percent_display(node, parent_total);
1186 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1187 struct rb_node *next = rb_next(node);
1188 struct callchain_list *chain;
1189 char folded_sign = ' ';
1191 int extra_offset = 0;
1193 list_for_each_entry(chain, &child->val, list) {
1194 bool was_first = first;
1198 else if (need_percent)
1199 extra_offset = LEVEL_OFFSET_STEP;
1201 folded_sign = callchain_list__folded(chain);
1203 row += hist_browser__show_callchain_list(browser, child,
1204 chain, row, percent_total,
1205 was_first && need_percent,
1206 offset + extra_offset,
1209 if (is_output_full(browser, row))
1212 if (folded_sign == '+')
1216 if (folded_sign == '-') {
1217 const int new_level = level + (extra_offset ? 2 : 1);
1219 row += hist_browser__show_callchain_graph(browser, &child->rb_root,
1220 new_level, row, total,
1221 child->children_hit,
1222 print, arg, is_output_full);
1224 if (is_output_full(browser, row))
1229 return row - first_row;
1232 static int hist_browser__show_callchain(struct hist_browser *browser,
1233 struct hist_entry *entry, int level,
1235 print_callchain_entry_fn print,
1236 struct callchain_print_arg *arg,
1237 check_output_full_fn is_output_full)
1239 u64 total = hists__total_period(entry->hists);
1243 if (symbol_conf.cumulate_callchain)
1244 parent_total = entry->stat_acc->period;
1246 parent_total = entry->stat.period;
1248 if (callchain_param.mode == CHAIN_FLAT) {
1249 printed = hist_browser__show_callchain_flat(browser,
1250 &entry->sorted_chain, row,
1251 total, parent_total, print, arg,
1253 } else if (callchain_param.mode == CHAIN_FOLDED) {
1254 printed = hist_browser__show_callchain_folded(browser,
1255 &entry->sorted_chain, row,
1256 total, parent_total, print, arg,
1259 printed = hist_browser__show_callchain_graph(browser,
1260 &entry->sorted_chain, level, row,
1261 total, parent_total, print, arg,
1265 if (arg->is_current_entry)
1266 browser->he_selection = entry;
1272 struct ui_browser *b;
1277 int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
1279 struct hpp_arg *arg = hpp->ptr;
1284 va_start(args, fmt);
1285 len = va_arg(args, int);
1286 percent = va_arg(args, double);
1289 ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
1291 ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
1292 ui_browser__printf(arg->b, "%s", hpp->buf);
1297 #define __HPP_COLOR_PERCENT_FN(_type, _field) \
1298 static u64 __hpp_get_##_field(struct hist_entry *he) \
1300 return he->stat._field; \
1304 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
1305 struct perf_hpp *hpp, \
1306 struct hist_entry *he) \
1308 return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%", \
1309 __hpp__slsmg_color_printf, true); \
1312 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
1313 static u64 __hpp_get_acc_##_field(struct hist_entry *he) \
1315 return he->stat_acc->_field; \
1319 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
1320 struct perf_hpp *hpp, \
1321 struct hist_entry *he) \
1323 if (!symbol_conf.cumulate_callchain) { \
1324 struct hpp_arg *arg = hpp->ptr; \
1325 int len = fmt->user_len ?: fmt->len; \
1326 int ret = scnprintf(hpp->buf, hpp->size, \
1327 "%*s", len, "N/A"); \
1328 ui_browser__printf(arg->b, "%s", hpp->buf); \
1332 return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field, \
1333 " %*.2f%%", __hpp__slsmg_color_printf, true); \
1336 __HPP_COLOR_PERCENT_FN(overhead, period)
1337 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
1338 __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
1339 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
1340 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
1341 __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
1343 #undef __HPP_COLOR_PERCENT_FN
1344 #undef __HPP_COLOR_ACC_PERCENT_FN
1346 void hist_browser__init_hpp(void)
1348 perf_hpp__format[PERF_HPP__OVERHEAD].color =
1349 hist_browser__hpp_color_overhead;
1350 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
1351 hist_browser__hpp_color_overhead_sys;
1352 perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
1353 hist_browser__hpp_color_overhead_us;
1354 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
1355 hist_browser__hpp_color_overhead_guest_sys;
1356 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
1357 hist_browser__hpp_color_overhead_guest_us;
1358 perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
1359 hist_browser__hpp_color_overhead_acc;
1362 static int hist_browser__show_entry(struct hist_browser *browser,
1363 struct hist_entry *entry,
1367 int width = browser->b.width;
1368 char folded_sign = ' ';
1369 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1370 off_t row_offset = entry->row_offset;
1372 struct perf_hpp_fmt *fmt;
1374 if (current_entry) {
1375 browser->he_selection = entry;
1376 browser->selection = &entry->ms;
1379 if (symbol_conf.use_callchain) {
1380 hist_entry__init_have_children(entry);
1381 folded_sign = hist_entry__folded(entry);
1384 if (symbol_conf.inline_name &&
1385 (!entry->has_children)) {
1386 hist_entry_init_inline_node(entry);
1387 folded_sign = hist_entry__folded(entry);
1390 if (row_offset == 0) {
1391 struct hpp_arg arg = {
1393 .folded_sign = folded_sign,
1394 .current_entry = current_entry,
1398 hist_browser__gotorc(browser, row, 0);
1400 hists__for_each_format(browser->hists, fmt) {
1402 struct perf_hpp hpp = {
1408 if (perf_hpp__should_skip(fmt, entry->hists) ||
1409 column++ < browser->b.horiz_scroll)
1412 if (current_entry && browser->b.navkeypressed) {
1413 ui_browser__set_color(&browser->b,
1414 HE_COLORSET_SELECTED);
1416 ui_browser__set_color(&browser->b,
1417 HE_COLORSET_NORMAL);
1421 if (symbol_conf.use_callchain ||
1422 symbol_conf.inline_name) {
1423 ui_browser__printf(&browser->b, "%c ", folded_sign);
1428 ui_browser__printf(&browser->b, " ");
1433 int ret = fmt->color(fmt, &hpp, entry);
1434 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1436 * fmt->color() already used ui_browser to
1437 * print the non alignment bits, skip it (+ret):
1439 ui_browser__printf(&browser->b, "%s", s + ret);
1441 hist_entry__snprintf_alignment(entry, &hpp, fmt, fmt->entry(fmt, &hpp, entry));
1442 ui_browser__printf(&browser->b, "%s", s);
1444 width -= hpp.buf - s;
1447 /* The scroll bar isn't being used */
1448 if (!browser->b.navkeypressed)
1451 ui_browser__write_nstring(&browser->b, "", width);
1458 if (folded_sign == '-' && row != browser->b.rows) {
1459 struct callchain_print_arg arg = {
1460 .row_offset = row_offset,
1461 .is_current_entry = current_entry,
1464 if (entry->inline_node)
1465 printed += hist_browser__show_inline(browser,
1466 entry->inline_node, row, 0);
1468 printed += hist_browser__show_callchain(browser,
1470 hist_browser__show_callchain_entry,
1472 hist_browser__check_output_full);
1478 static int hist_browser__show_hierarchy_entry(struct hist_browser *browser,
1479 struct hist_entry *entry,
1484 int width = browser->b.width;
1485 char folded_sign = ' ';
1486 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1487 off_t row_offset = entry->row_offset;
1489 struct perf_hpp_fmt *fmt;
1490 struct perf_hpp_list_node *fmt_node;
1491 struct hpp_arg arg = {
1493 .current_entry = current_entry,
1496 int hierarchy_indent = (entry->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
1498 if (current_entry) {
1499 browser->he_selection = entry;
1500 browser->selection = &entry->ms;
1503 hist_entry__init_have_children(entry);
1504 folded_sign = hist_entry__folded(entry);
1505 arg.folded_sign = folded_sign;
1507 if (entry->leaf && row_offset) {
1509 goto show_callchain;
1512 hist_browser__gotorc(browser, row, 0);
1514 if (current_entry && browser->b.navkeypressed)
1515 ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1517 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1519 ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1520 width -= level * HIERARCHY_INDENT;
1522 /* the first hpp_list_node is for overhead columns */
1523 fmt_node = list_first_entry(&entry->hists->hpp_formats,
1524 struct perf_hpp_list_node, list);
1525 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1527 struct perf_hpp hpp = {
1533 if (perf_hpp__should_skip(fmt, entry->hists) ||
1534 column++ < browser->b.horiz_scroll)
1537 if (current_entry && browser->b.navkeypressed) {
1538 ui_browser__set_color(&browser->b,
1539 HE_COLORSET_SELECTED);
1541 ui_browser__set_color(&browser->b,
1542 HE_COLORSET_NORMAL);
1546 ui_browser__printf(&browser->b, "%c ", folded_sign);
1550 ui_browser__printf(&browser->b, " ");
1555 int ret = fmt->color(fmt, &hpp, entry);
1556 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1558 * fmt->color() already used ui_browser to
1559 * print the non alignment bits, skip it (+ret):
1561 ui_browser__printf(&browser->b, "%s", s + ret);
1563 int ret = fmt->entry(fmt, &hpp, entry);
1564 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1565 ui_browser__printf(&browser->b, "%s", s);
1567 width -= hpp.buf - s;
1571 ui_browser__write_nstring(&browser->b, "", hierarchy_indent);
1572 width -= hierarchy_indent;
1575 if (column >= browser->b.horiz_scroll) {
1577 struct perf_hpp hpp = {
1583 if (current_entry && browser->b.navkeypressed) {
1584 ui_browser__set_color(&browser->b,
1585 HE_COLORSET_SELECTED);
1587 ui_browser__set_color(&browser->b,
1588 HE_COLORSET_NORMAL);
1591 perf_hpp_list__for_each_format(entry->hpp_list, fmt) {
1593 ui_browser__printf(&browser->b, "%c ", folded_sign);
1596 ui_browser__write_nstring(&browser->b, "", 2);
1602 * No need to call hist_entry__snprintf_alignment()
1603 * since this fmt is always the last column in the
1607 width -= fmt->color(fmt, &hpp, entry);
1611 width -= fmt->entry(fmt, &hpp, entry);
1612 ui_browser__printf(&browser->b, "%s", ltrim(s));
1614 while (isspace(s[i++]))
1620 /* The scroll bar isn't being used */
1621 if (!browser->b.navkeypressed)
1624 ui_browser__write_nstring(&browser->b, "", width);
1630 if (entry->leaf && folded_sign == '-' && row != browser->b.rows) {
1631 struct callchain_print_arg carg = {
1632 .row_offset = row_offset,
1635 printed += hist_browser__show_callchain(browser, entry,
1637 hist_browser__show_callchain_entry, &carg,
1638 hist_browser__check_output_full);
1644 static int hist_browser__show_no_entry(struct hist_browser *browser,
1645 unsigned short row, int level)
1647 int width = browser->b.width;
1648 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1652 struct perf_hpp_fmt *fmt;
1653 struct perf_hpp_list_node *fmt_node;
1654 int indent = browser->hists->nr_hpp_node - 2;
1656 if (current_entry) {
1657 browser->he_selection = NULL;
1658 browser->selection = NULL;
1661 hist_browser__gotorc(browser, row, 0);
1663 if (current_entry && browser->b.navkeypressed)
1664 ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1666 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1668 ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1669 width -= level * HIERARCHY_INDENT;
1671 /* the first hpp_list_node is for overhead columns */
1672 fmt_node = list_first_entry(&browser->hists->hpp_formats,
1673 struct perf_hpp_list_node, list);
1674 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1675 if (perf_hpp__should_skip(fmt, browser->hists) ||
1676 column++ < browser->b.horiz_scroll)
1679 ret = fmt->width(fmt, NULL, browser->hists);
1682 /* for folded sign */
1686 /* space between columns */
1690 ui_browser__write_nstring(&browser->b, "", ret);
1694 ui_browser__write_nstring(&browser->b, "", indent * HIERARCHY_INDENT);
1695 width -= indent * HIERARCHY_INDENT;
1697 if (column >= browser->b.horiz_scroll) {
1700 ret = snprintf(buf, sizeof(buf), "no entry >= %.2f%%", browser->min_pcnt);
1701 ui_browser__printf(&browser->b, " %s", buf);
1705 /* The scroll bar isn't being used */
1706 if (!browser->b.navkeypressed)
1709 ui_browser__write_nstring(&browser->b, "", width);
1713 static int advance_hpp_check(struct perf_hpp *hpp, int inc)
1715 advance_hpp(hpp, inc);
1716 return hpp->size <= 0;
1720 hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf,
1721 size_t size, int line)
1723 struct hists *hists = browser->hists;
1724 struct perf_hpp dummy_hpp = {
1728 struct perf_hpp_fmt *fmt;
1733 if (symbol_conf.use_callchain) {
1734 ret = scnprintf(buf, size, " ");
1735 if (advance_hpp_check(&dummy_hpp, ret))
1739 hists__for_each_format(browser->hists, fmt) {
1740 if (perf_hpp__should_skip(fmt, hists) || column++ < browser->b.horiz_scroll)
1743 ret = fmt->header(fmt, &dummy_hpp, hists, line, &span);
1744 if (advance_hpp_check(&dummy_hpp, ret))
1750 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
1751 if (advance_hpp_check(&dummy_hpp, ret))
1758 static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser, char *buf, size_t size)
1760 struct hists *hists = browser->hists;
1761 struct perf_hpp dummy_hpp = {
1765 struct perf_hpp_fmt *fmt;
1766 struct perf_hpp_list_node *fmt_node;
1769 int indent = hists->nr_hpp_node - 2;
1770 bool first_node, first_col;
1772 ret = scnprintf(buf, size, " ");
1773 if (advance_hpp_check(&dummy_hpp, ret))
1777 /* the first hpp_list_node is for overhead columns */
1778 fmt_node = list_first_entry(&hists->hpp_formats,
1779 struct perf_hpp_list_node, list);
1780 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1781 if (column++ < browser->b.horiz_scroll)
1784 ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1785 if (advance_hpp_check(&dummy_hpp, ret))
1788 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
1789 if (advance_hpp_check(&dummy_hpp, ret))
1796 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "%*s",
1797 indent * HIERARCHY_INDENT, "");
1798 if (advance_hpp_check(&dummy_hpp, ret))
1803 list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
1805 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " / ");
1806 if (advance_hpp_check(&dummy_hpp, ret))
1812 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1815 if (perf_hpp__should_skip(fmt, hists))
1819 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "+");
1820 if (advance_hpp_check(&dummy_hpp, ret))
1825 ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1826 dummy_hpp.buf[ret] = '\0';
1828 start = trim(dummy_hpp.buf);
1829 ret = strlen(start);
1831 if (start != dummy_hpp.buf)
1832 memmove(dummy_hpp.buf, start, ret + 1);
1834 if (advance_hpp_check(&dummy_hpp, ret))
1842 static void hists_browser__hierarchy_headers(struct hist_browser *browser)
1846 hists_browser__scnprintf_hierarchy_headers(browser, headers,
1849 ui_browser__gotorc(&browser->b, 0, 0);
1850 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1851 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1854 static void hists_browser__headers(struct hist_browser *browser)
1856 struct hists *hists = browser->hists;
1857 struct perf_hpp_list *hpp_list = hists->hpp_list;
1861 for (line = 0; line < hpp_list->nr_header_lines; line++) {
1864 hists_browser__scnprintf_headers(browser, headers,
1865 sizeof(headers), line);
1867 ui_browser__gotorc(&browser->b, line, 0);
1868 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1869 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1873 static void hist_browser__show_headers(struct hist_browser *browser)
1875 if (symbol_conf.report_hierarchy)
1876 hists_browser__hierarchy_headers(browser);
1878 hists_browser__headers(browser);
1881 static void ui_browser__hists_init_top(struct ui_browser *browser)
1883 if (browser->top == NULL) {
1884 struct hist_browser *hb;
1886 hb = container_of(browser, struct hist_browser, b);
1887 browser->top = rb_first(&hb->hists->entries);
1891 static unsigned int hist_browser__refresh(struct ui_browser *browser)
1894 u16 header_offset = 0;
1896 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
1897 struct hists *hists = hb->hists;
1899 if (hb->show_headers) {
1900 struct perf_hpp_list *hpp_list = hists->hpp_list;
1902 hist_browser__show_headers(hb);
1903 header_offset = hpp_list->nr_header_lines;
1906 ui_browser__hists_init_top(browser);
1907 hb->he_selection = NULL;
1908 hb->selection = NULL;
1910 for (nd = browser->top; nd; nd = rb_hierarchy_next(nd)) {
1911 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1915 /* let it move to sibling */
1916 h->unfolded = false;
1920 percent = hist_entry__get_percent_limit(h);
1921 if (percent < hb->min_pcnt)
1924 if (symbol_conf.report_hierarchy) {
1925 row += hist_browser__show_hierarchy_entry(hb, h, row,
1927 if (row == browser->rows)
1930 if (h->has_no_entry) {
1931 hist_browser__show_no_entry(hb, row, h->depth + 1);
1935 row += hist_browser__show_entry(hb, h, row);
1938 if (row == browser->rows)
1942 return row + header_offset;
1945 static struct rb_node *hists__filter_entries(struct rb_node *nd,
1948 while (nd != NULL) {
1949 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1950 float percent = hist_entry__get_percent_limit(h);
1952 if (!h->filtered && percent >= min_pcnt)
1956 * If it's filtered, its all children also were filtered.
1957 * So move to sibling node.
1962 nd = rb_hierarchy_next(nd);
1968 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
1971 while (nd != NULL) {
1972 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1973 float percent = hist_entry__get_percent_limit(h);
1975 if (!h->filtered && percent >= min_pcnt)
1978 nd = rb_hierarchy_prev(nd);
1984 static void ui_browser__hists_seek(struct ui_browser *browser,
1985 off_t offset, int whence)
1987 struct hist_entry *h;
1990 struct hist_browser *hb;
1992 hb = container_of(browser, struct hist_browser, b);
1994 if (browser->nr_entries == 0)
1997 ui_browser__hists_init_top(browser);
2001 nd = hists__filter_entries(rb_first(browser->entries),
2008 nd = rb_hierarchy_last(rb_last(browser->entries));
2009 nd = hists__filter_prev_entries(nd, hb->min_pcnt);
2017 * Moves not relative to the first visible entry invalidates its
2020 h = rb_entry(browser->top, struct hist_entry, rb_node);
2024 * Here we have to check if nd is expanded (+), if it is we can't go
2025 * the next top level hist_entry, instead we must compute an offset of
2026 * what _not_ to show and not change the first visible entry.
2028 * This offset increments when we are going from top to bottom and
2029 * decreases when we're going from bottom to top.
2031 * As we don't have backpointers to the top level in the callchains
2032 * structure, we need to always print the whole hist_entry callchain,
2033 * skipping the first ones that are before the first visible entry
2034 * and stop when we printed enough lines to fill the screen.
2042 h = rb_entry(nd, struct hist_entry, rb_node);
2043 if (h->unfolded && h->leaf) {
2044 u16 remaining = h->nr_rows - h->row_offset;
2045 if (offset > remaining) {
2046 offset -= remaining;
2049 h->row_offset += offset;
2055 nd = hists__filter_entries(rb_hierarchy_next(nd),
2061 } while (offset != 0);
2062 } else if (offset < 0) {
2064 h = rb_entry(nd, struct hist_entry, rb_node);
2065 if (h->unfolded && h->leaf) {
2067 if (-offset > h->row_offset) {
2068 offset += h->row_offset;
2071 h->row_offset += offset;
2077 if (-offset > h->nr_rows) {
2078 offset += h->nr_rows;
2081 h->row_offset = h->nr_rows + offset;
2089 nd = hists__filter_prev_entries(rb_hierarchy_prev(nd),
2097 * Last unfiltered hist_entry, check if it is
2098 * unfolded, if it is then we should have
2099 * row_offset at its last entry.
2101 h = rb_entry(nd, struct hist_entry, rb_node);
2102 if (h->unfolded && h->leaf)
2103 h->row_offset = h->nr_rows;
2110 h = rb_entry(nd, struct hist_entry, rb_node);
2115 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
2116 struct hist_entry *he, FILE *fp,
2119 struct callchain_print_arg arg = {
2123 hist_browser__show_callchain(browser, he, level, 0,
2124 hist_browser__fprintf_callchain_entry, &arg,
2125 hist_browser__check_dump_full);
2129 static int hist_browser__fprintf_entry(struct hist_browser *browser,
2130 struct hist_entry *he, FILE *fp)
2134 char folded_sign = ' ';
2135 struct perf_hpp hpp = {
2139 struct perf_hpp_fmt *fmt;
2143 if (symbol_conf.use_callchain) {
2144 folded_sign = hist_entry__folded(he);
2145 printed += fprintf(fp, "%c ", folded_sign);
2148 hists__for_each_format(browser->hists, fmt) {
2149 if (perf_hpp__should_skip(fmt, he->hists))
2153 ret = scnprintf(hpp.buf, hpp.size, " ");
2154 advance_hpp(&hpp, ret);
2158 ret = fmt->entry(fmt, &hpp, he);
2159 ret = hist_entry__snprintf_alignment(he, &hpp, fmt, ret);
2160 advance_hpp(&hpp, ret);
2162 printed += fprintf(fp, "%s\n", s);
2164 if (folded_sign == '-')
2165 printed += hist_browser__fprintf_callchain(browser, he, fp, 1);
2171 static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,
2172 struct hist_entry *he,
2173 FILE *fp, int level)
2177 char folded_sign = ' ';
2178 struct perf_hpp hpp = {
2182 struct perf_hpp_fmt *fmt;
2183 struct perf_hpp_list_node *fmt_node;
2186 int hierarchy_indent = (he->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
2188 printed = fprintf(fp, "%*s", level * HIERARCHY_INDENT, "");
2190 folded_sign = hist_entry__folded(he);
2191 printed += fprintf(fp, "%c", folded_sign);
2193 /* the first hpp_list_node is for overhead columns */
2194 fmt_node = list_first_entry(&he->hists->hpp_formats,
2195 struct perf_hpp_list_node, list);
2196 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
2198 ret = scnprintf(hpp.buf, hpp.size, " ");
2199 advance_hpp(&hpp, ret);
2203 ret = fmt->entry(fmt, &hpp, he);
2204 advance_hpp(&hpp, ret);
2207 ret = scnprintf(hpp.buf, hpp.size, "%*s", hierarchy_indent, "");
2208 advance_hpp(&hpp, ret);
2210 perf_hpp_list__for_each_format(he->hpp_list, fmt) {
2211 ret = scnprintf(hpp.buf, hpp.size, " ");
2212 advance_hpp(&hpp, ret);
2214 ret = fmt->entry(fmt, &hpp, he);
2215 advance_hpp(&hpp, ret);
2218 printed += fprintf(fp, "%s\n", rtrim(s));
2220 if (he->leaf && folded_sign == '-') {
2221 printed += hist_browser__fprintf_callchain(browser, he, fp,
2228 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
2230 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
2235 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
2237 if (symbol_conf.report_hierarchy) {
2238 printed += hist_browser__fprintf_hierarchy_entry(browser,
2242 printed += hist_browser__fprintf_entry(browser, h, fp);
2245 nd = hists__filter_entries(rb_hierarchy_next(nd),
2252 static int hist_browser__dump(struct hist_browser *browser)
2258 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
2259 if (access(filename, F_OK))
2262 * XXX: Just an arbitrary lazy upper limit
2264 if (++browser->print_seq == 8192) {
2265 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
2270 fp = fopen(filename, "w");
2273 const char *err = str_error_r(errno, bf, sizeof(bf));
2274 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
2278 ++browser->print_seq;
2279 hist_browser__fprintf(browser, fp);
2281 ui_helpline__fpush("%s written!", filename);
2286 void hist_browser__init(struct hist_browser *browser,
2287 struct hists *hists)
2289 struct perf_hpp_fmt *fmt;
2291 browser->hists = hists;
2292 browser->b.refresh = hist_browser__refresh;
2293 browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
2294 browser->b.seek = ui_browser__hists_seek;
2295 browser->b.use_navkeypressed = true;
2296 browser->show_headers = symbol_conf.show_hist_headers;
2298 if (symbol_conf.report_hierarchy) {
2299 struct perf_hpp_list_node *fmt_node;
2301 /* count overhead columns (in the first node) */
2302 fmt_node = list_first_entry(&hists->hpp_formats,
2303 struct perf_hpp_list_node, list);
2304 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt)
2305 ++browser->b.columns;
2307 /* add a single column for whole hierarchy sort keys*/
2308 ++browser->b.columns;
2310 hists__for_each_format(hists, fmt)
2311 ++browser->b.columns;
2314 hists__reset_column_width(hists);
2317 struct hist_browser *hist_browser__new(struct hists *hists)
2319 struct hist_browser *browser = zalloc(sizeof(*browser));
2322 hist_browser__init(browser, hists);
2327 static struct hist_browser *
2328 perf_evsel_browser__new(struct perf_evsel *evsel,
2329 struct hist_browser_timer *hbt,
2330 struct perf_env *env)
2332 struct hist_browser *browser = hist_browser__new(evsel__hists(evsel));
2337 browser->title = perf_evsel_browser_title;
2342 void hist_browser__delete(struct hist_browser *browser)
2347 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
2349 return browser->he_selection;
2352 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
2354 return browser->he_selection->thread;
2357 /* Check whether the browser is for 'top' or 'report' */
2358 static inline bool is_report_browser(void *timer)
2360 return timer == NULL;
2363 static int perf_evsel_browser_title(struct hist_browser *browser,
2364 char *bf, size_t size)
2366 struct hist_browser_timer *hbt = browser->hbt;
2367 struct hists *hists = browser->hists;
2370 const struct dso *dso = hists->dso_filter;
2371 const struct thread *thread = hists->thread_filter;
2372 int socket_id = hists->socket_filter;
2373 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
2374 u64 nr_events = hists->stats.total_period;
2375 struct perf_evsel *evsel = hists_to_evsel(hists);
2376 const char *ev_name = perf_evsel__name(evsel);
2378 size_t buflen = sizeof(buf);
2379 char ref[30] = " show reference callgraph, ";
2380 bool enable_ref = false;
2382 if (symbol_conf.filter_relative) {
2383 nr_samples = hists->stats.nr_non_filtered_samples;
2384 nr_events = hists->stats.total_non_filtered_period;
2387 if (perf_evsel__is_group_event(evsel)) {
2388 struct perf_evsel *pos;
2390 perf_evsel__group_desc(evsel, buf, buflen);
2393 for_each_group_member(pos, evsel) {
2394 struct hists *pos_hists = evsel__hists(pos);
2396 if (symbol_conf.filter_relative) {
2397 nr_samples += pos_hists->stats.nr_non_filtered_samples;
2398 nr_events += pos_hists->stats.total_non_filtered_period;
2400 nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
2401 nr_events += pos_hists->stats.total_period;
2406 if (symbol_conf.show_ref_callgraph &&
2407 strstr(ev_name, "call-graph=no"))
2409 nr_samples = convert_unit(nr_samples, &unit);
2410 printed = scnprintf(bf, size,
2411 "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64,
2412 nr_samples, unit, ev_name, enable_ref ? ref : " ", nr_events);
2415 if (hists->uid_filter_str)
2416 printed += snprintf(bf + printed, size - printed,
2417 ", UID: %s", hists->uid_filter_str);
2419 if (hists__has(hists, thread)) {
2420 printed += scnprintf(bf + printed, size - printed,
2422 (thread->comm_set ? thread__comm_str(thread) : ""),
2425 printed += scnprintf(bf + printed, size - printed,
2427 (thread->comm_set ? thread__comm_str(thread) : ""));
2431 printed += scnprintf(bf + printed, size - printed,
2432 ", DSO: %s", dso->short_name);
2434 printed += scnprintf(bf + printed, size - printed,
2435 ", Processor Socket: %d", socket_id);
2436 if (!is_report_browser(hbt)) {
2437 struct perf_top *top = hbt->arg;
2440 printed += scnprintf(bf + printed, size - printed, " [z]");
2446 static inline void free_popup_options(char **options, int n)
2450 for (i = 0; i < n; ++i)
2455 * Only runtime switching of perf data file will make "input_name" point
2456 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
2457 * whether we need to call free() for current "input_name" during the switch.
2459 static bool is_input_name_malloced = false;
2461 static int switch_data_file(void)
2463 char *pwd, *options[32], *abs_path[32], *tmp;
2465 int nr_options = 0, choice = -1, ret = -1;
2466 struct dirent *dent;
2468 pwd = getenv("PWD");
2472 pwd_dir = opendir(pwd);
2476 memset(options, 0, sizeof(options));
2477 memset(abs_path, 0, sizeof(abs_path));
2479 while ((dent = readdir(pwd_dir))) {
2480 char path[PATH_MAX];
2482 char *name = dent->d_name;
2485 if (!(dent->d_type == DT_REG))
2488 snprintf(path, sizeof(path), "%s/%s", pwd, name);
2490 file = fopen(path, "r");
2494 if (fread(&magic, 1, 8, file) < 8)
2495 goto close_file_and_continue;
2497 if (is_perf_magic(magic)) {
2498 options[nr_options] = strdup(name);
2499 if (!options[nr_options])
2500 goto close_file_and_continue;
2502 abs_path[nr_options] = strdup(path);
2503 if (!abs_path[nr_options]) {
2504 zfree(&options[nr_options]);
2505 ui__warning("Can't search all data files due to memory shortage.\n");
2513 close_file_and_continue:
2515 if (nr_options >= 32) {
2516 ui__warning("Too many perf data files in PWD!\n"
2517 "Only the first 32 files will be listed.\n");
2524 choice = ui__popup_menu(nr_options, options);
2525 if (choice < nr_options && choice >= 0) {
2526 tmp = strdup(abs_path[choice]);
2528 if (is_input_name_malloced)
2529 free((void *)input_name);
2531 is_input_name_malloced = true;
2534 ui__warning("Data switch failed due to memory shortage!\n");
2538 free_popup_options(options, nr_options);
2539 free_popup_options(abs_path, nr_options);
2543 struct popup_action {
2544 struct thread *thread;
2545 struct map_symbol ms;
2548 int (*fn)(struct hist_browser *browser, struct popup_action *act);
2552 do_annotate(struct hist_browser *browser, struct popup_action *act)
2554 struct perf_evsel *evsel;
2555 struct annotation *notes;
2556 struct hist_entry *he;
2559 if (!objdump_path && perf_env__lookup_objdump(browser->env))
2562 notes = symbol__annotation(act->ms.sym);
2566 evsel = hists_to_evsel(browser->hists);
2567 err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
2568 he = hist_browser__selected_entry(browser);
2570 * offer option to annotate the other branch source or target
2571 * (if they exists) when returning from annotate
2573 if ((err == 'q' || err == CTRL('c')) && he->branch_info)
2576 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
2578 ui_browser__handle_resize(&browser->b);
2583 add_annotate_opt(struct hist_browser *browser __maybe_unused,
2584 struct popup_action *act, char **optstr,
2585 struct map *map, struct symbol *sym)
2587 if (sym == NULL || map->dso->annotate_warned)
2590 if (asprintf(optstr, "Annotate %s", sym->name) < 0)
2595 act->fn = do_annotate;
2600 do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
2602 struct thread *thread = act->thread;
2604 if ((!hists__has(browser->hists, thread) &&
2605 !hists__has(browser->hists, comm)) || thread == NULL)
2608 if (browser->hists->thread_filter) {
2609 pstack__remove(browser->pstack, &browser->hists->thread_filter);
2610 perf_hpp__set_elide(HISTC_THREAD, false);
2611 thread__zput(browser->hists->thread_filter);
2614 if (hists__has(browser->hists, thread)) {
2615 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
2616 thread->comm_set ? thread__comm_str(thread) : "",
2619 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
2620 thread->comm_set ? thread__comm_str(thread) : "");
2623 browser->hists->thread_filter = thread__get(thread);
2624 perf_hpp__set_elide(HISTC_THREAD, false);
2625 pstack__push(browser->pstack, &browser->hists->thread_filter);
2628 hists__filter_by_thread(browser->hists);
2629 hist_browser__reset(browser);
2634 add_thread_opt(struct hist_browser *browser, struct popup_action *act,
2635 char **optstr, struct thread *thread)
2639 if ((!hists__has(browser->hists, thread) &&
2640 !hists__has(browser->hists, comm)) || thread == NULL)
2643 if (hists__has(browser->hists, thread)) {
2644 ret = asprintf(optstr, "Zoom %s %s(%d) thread",
2645 browser->hists->thread_filter ? "out of" : "into",
2646 thread->comm_set ? thread__comm_str(thread) : "",
2649 ret = asprintf(optstr, "Zoom %s %s thread",
2650 browser->hists->thread_filter ? "out of" : "into",
2651 thread->comm_set ? thread__comm_str(thread) : "");
2656 act->thread = thread;
2657 act->fn = do_zoom_thread;
2662 do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
2664 struct map *map = act->ms.map;
2666 if (!hists__has(browser->hists, dso) || map == NULL)
2669 if (browser->hists->dso_filter) {
2670 pstack__remove(browser->pstack, &browser->hists->dso_filter);
2671 perf_hpp__set_elide(HISTC_DSO, false);
2672 browser->hists->dso_filter = NULL;
2675 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
2676 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
2677 browser->hists->dso_filter = map->dso;
2678 perf_hpp__set_elide(HISTC_DSO, true);
2679 pstack__push(browser->pstack, &browser->hists->dso_filter);
2682 hists__filter_by_dso(browser->hists);
2683 hist_browser__reset(browser);
2688 add_dso_opt(struct hist_browser *browser, struct popup_action *act,
2689 char **optstr, struct map *map)
2691 if (!hists__has(browser->hists, dso) || map == NULL)
2694 if (asprintf(optstr, "Zoom %s %s DSO",
2695 browser->hists->dso_filter ? "out of" : "into",
2696 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
2700 act->fn = do_zoom_dso;
2705 do_browse_map(struct hist_browser *browser __maybe_unused,
2706 struct popup_action *act)
2708 map__browse(act->ms.map);
2713 add_map_opt(struct hist_browser *browser,
2714 struct popup_action *act, char **optstr, struct map *map)
2716 if (!hists__has(browser->hists, dso) || map == NULL)
2719 if (asprintf(optstr, "Browse map details") < 0)
2723 act->fn = do_browse_map;
2728 do_run_script(struct hist_browser *browser __maybe_unused,
2729 struct popup_action *act)
2731 char script_opt[64];
2732 memset(script_opt, 0, sizeof(script_opt));
2735 scnprintf(script_opt, sizeof(script_opt), " -c %s ",
2736 thread__comm_str(act->thread));
2737 } else if (act->ms.sym) {
2738 scnprintf(script_opt, sizeof(script_opt), " -S %s ",
2742 script_browse(script_opt);
2747 add_script_opt(struct hist_browser *browser __maybe_unused,
2748 struct popup_action *act, char **optstr,
2749 struct thread *thread, struct symbol *sym)
2752 if (asprintf(optstr, "Run scripts for samples of thread [%s]",
2753 thread__comm_str(thread)) < 0)
2756 if (asprintf(optstr, "Run scripts for samples of symbol [%s]",
2760 if (asprintf(optstr, "Run scripts for all samples") < 0)
2764 act->thread = thread;
2766 act->fn = do_run_script;
2771 do_switch_data(struct hist_browser *browser __maybe_unused,
2772 struct popup_action *act __maybe_unused)
2774 if (switch_data_file()) {
2775 ui__warning("Won't switch the data files due to\n"
2776 "no valid data file get selected!\n");
2780 return K_SWITCH_INPUT_DATA;
2784 add_switch_opt(struct hist_browser *browser,
2785 struct popup_action *act, char **optstr)
2787 if (!is_report_browser(browser->hbt))
2790 if (asprintf(optstr, "Switch to another data file in PWD") < 0)
2793 act->fn = do_switch_data;
2798 do_exit_browser(struct hist_browser *browser __maybe_unused,
2799 struct popup_action *act __maybe_unused)
2805 add_exit_opt(struct hist_browser *browser __maybe_unused,
2806 struct popup_action *act, char **optstr)
2808 if (asprintf(optstr, "Exit") < 0)
2811 act->fn = do_exit_browser;
2816 do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
2818 if (!hists__has(browser->hists, socket) || act->socket < 0)
2821 if (browser->hists->socket_filter > -1) {
2822 pstack__remove(browser->pstack, &browser->hists->socket_filter);
2823 browser->hists->socket_filter = -1;
2824 perf_hpp__set_elide(HISTC_SOCKET, false);
2826 browser->hists->socket_filter = act->socket;
2827 perf_hpp__set_elide(HISTC_SOCKET, true);
2828 pstack__push(browser->pstack, &browser->hists->socket_filter);
2831 hists__filter_by_socket(browser->hists);
2832 hist_browser__reset(browser);
2837 add_socket_opt(struct hist_browser *browser, struct popup_action *act,
2838 char **optstr, int socket_id)
2840 if (!hists__has(browser->hists, socket) || socket_id < 0)
2843 if (asprintf(optstr, "Zoom %s Processor Socket %d",
2844 (browser->hists->socket_filter > -1) ? "out of" : "into",
2848 act->socket = socket_id;
2849 act->fn = do_zoom_socket;
2853 static void hist_browser__update_nr_entries(struct hist_browser *hb)
2856 struct rb_node *nd = rb_first(&hb->hists->entries);
2858 if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
2859 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
2863 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2865 nd = rb_hierarchy_next(nd);
2868 hb->nr_non_filtered_entries = nr_entries;
2869 hb->nr_hierarchy_entries = nr_entries;
2872 static void hist_browser__update_percent_limit(struct hist_browser *hb,
2875 struct hist_entry *he;
2876 struct rb_node *nd = rb_first(&hb->hists->entries);
2877 u64 total = hists__total_period(hb->hists);
2878 u64 min_callchain_hits = total * (percent / 100);
2880 hb->min_pcnt = callchain_param.min_percent = percent;
2882 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2883 he = rb_entry(nd, struct hist_entry, rb_node);
2885 if (he->has_no_entry) {
2886 he->has_no_entry = false;
2890 if (!he->leaf || !symbol_conf.use_callchain)
2893 if (callchain_param.mode == CHAIN_GRAPH_REL) {
2894 total = he->stat.period;
2896 if (symbol_conf.cumulate_callchain)
2897 total = he->stat_acc->period;
2899 min_callchain_hits = total * (percent / 100);
2902 callchain_param.sort(&he->sorted_chain, he->callchain,
2903 min_callchain_hits, &callchain_param);
2906 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
2908 /* force to re-evaluate folding state of callchains */
2909 he->init_have_children = false;
2910 hist_entry__set_folding(he, hb, false);
2914 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
2915 const char *helpline,
2917 struct hist_browser_timer *hbt,
2919 struct perf_env *env)
2921 struct hists *hists = evsel__hists(evsel);
2922 struct hist_browser *browser = perf_evsel_browser__new(evsel, hbt, env);
2923 struct branch_info *bi;
2924 #define MAX_OPTIONS 16
2925 char *options[MAX_OPTIONS];
2926 struct popup_action actions[MAX_OPTIONS];
2930 int delay_secs = hbt ? hbt->refresh : 0;
2932 #define HIST_BROWSER_HELP_COMMON \
2933 "h/?/F1 Show this window\n" \
2935 "PGDN/SPACE Navigate\n" \
2936 "q/ESC/CTRL+C Exit browser\n\n" \
2937 "For multiple event sessions:\n\n" \
2938 "TAB/UNTAB Switch events\n\n" \
2939 "For symbolic views (--sort has sym):\n\n" \
2940 "ENTER Zoom into DSO/Threads & Annotate current symbol\n" \
2942 "a Annotate current symbol\n" \
2943 "C Collapse all callchains\n" \
2944 "d Zoom into current DSO\n" \
2945 "E Expand all callchains\n" \
2946 "F Toggle percentage of filtered entries\n" \
2947 "H Display column headers\n" \
2948 "L Change percent limit\n" \
2949 "m Display context menu\n" \
2950 "S Zoom into current Processor Socket\n" \
2952 /* help messages are sorted by lexical order of the hotkey */
2953 const char report_help[] = HIST_BROWSER_HELP_COMMON
2954 "i Show header information\n"
2955 "P Print histograms to perf.hist.N\n"
2956 "r Run available scripts\n"
2957 "s Switch to another data file in PWD\n"
2958 "t Zoom into current Thread\n"
2959 "V Verbose (DSO names in callchains, etc)\n"
2960 "/ Filter symbol by name";
2961 const char top_help[] = HIST_BROWSER_HELP_COMMON
2962 "P Print histograms to perf.hist.N\n"
2963 "t Zoom into current Thread\n"
2964 "V Verbose (DSO names in callchains, etc)\n"
2965 "z Toggle zeroing of samples\n"
2966 "f Enable/Disable events\n"
2967 "/ Filter symbol by name";
2969 if (browser == NULL)
2972 /* reset abort key so that it can get Ctrl-C as a key */
2974 SLang_init_tty(0, 0, 0);
2977 browser->min_pcnt = min_pcnt;
2978 hist_browser__update_nr_entries(browser);
2980 browser->pstack = pstack__new(3);
2981 if (browser->pstack == NULL)
2984 ui_helpline__push(helpline);
2986 memset(options, 0, sizeof(options));
2987 memset(actions, 0, sizeof(actions));
2989 if (symbol_conf.col_width_list_str)
2990 perf_hpp__set_user_width(symbol_conf.col_width_list_str);
2993 struct thread *thread = NULL;
2994 struct map *map = NULL;
3000 key = hist_browser__run(browser, helpline);
3002 if (browser->he_selection != NULL) {
3003 thread = hist_browser__selected_thread(browser);
3004 map = browser->selection->map;
3005 socked_id = browser->he_selection->socket;
3013 * Exit the browser, let hists__browser_tree
3014 * go to the next or previous
3016 goto out_free_stack;
3018 if (!hists__has(hists, sym)) {
3019 ui_browser__warning(&browser->b, delay_secs * 2,
3020 "Annotation is only available for symbolic views, "
3021 "include \"sym*\" in --sort to use it.");
3025 if (browser->selection == NULL ||
3026 browser->selection->sym == NULL ||
3027 browser->selection->map->dso->annotate_warned)
3030 actions->ms.map = browser->selection->map;
3031 actions->ms.sym = browser->selection->sym;
3032 do_annotate(browser, actions);
3035 hist_browser__dump(browser);
3038 actions->ms.map = map;
3039 do_zoom_dso(browser, actions);
3042 verbose = (verbose + 1) % 4;
3043 browser->show_dso = verbose > 0;
3044 ui_helpline__fpush("Verbosity level set to %d\n",
3048 actions->thread = thread;
3049 do_zoom_thread(browser, actions);
3052 actions->socket = socked_id;
3053 do_zoom_socket(browser, actions);
3056 if (ui_browser__input_window("Symbol to show",
3057 "Please enter the name of symbol you want to see.\n"
3058 "To remove the filter later, press / + ENTER.",
3059 buf, "ENTER: OK, ESC: Cancel",
3060 delay_secs * 2) == K_ENTER) {
3061 hists->symbol_filter_str = *buf ? buf : NULL;
3062 hists__filter_by_symbol(hists);
3063 hist_browser__reset(browser);
3067 if (is_report_browser(hbt)) {
3068 actions->thread = NULL;
3069 actions->ms.sym = NULL;
3070 do_run_script(browser, actions);
3074 if (is_report_browser(hbt)) {
3075 key = do_switch_data(browser, actions);
3076 if (key == K_SWITCH_INPUT_DATA)
3077 goto out_free_stack;
3081 /* env->arch is NULL for live-mode (i.e. perf top) */
3083 tui__header_window(env);
3086 symbol_conf.filter_relative ^= 1;
3089 if (!is_report_browser(hbt)) {
3090 struct perf_top *top = hbt->arg;
3092 top->zero = !top->zero;
3096 if (ui_browser__input_window("Percent Limit",
3097 "Please enter the value you want to hide entries under that percent.",
3098 buf, "ENTER: OK, ESC: Cancel",
3099 delay_secs * 2) == K_ENTER) {
3101 double new_percent = strtod(buf, &end);
3103 if (new_percent < 0 || new_percent > 100) {
3104 ui_browser__warning(&browser->b, delay_secs * 2,
3105 "Invalid percent: %.2f", new_percent);
3109 hist_browser__update_percent_limit(browser, new_percent);
3110 hist_browser__reset(browser);
3116 ui_browser__help_window(&browser->b,
3117 is_report_browser(hbt) ? report_help : top_help);
3128 if (pstack__empty(browser->pstack)) {
3130 * Go back to the perf_evsel_menu__run or other user
3133 goto out_free_stack;
3136 ui_browser__dialog_yesno(&browser->b,
3137 "Do you really want to exit?"))
3138 goto out_free_stack;
3142 top = pstack__peek(browser->pstack);
3143 if (top == &browser->hists->dso_filter) {
3145 * No need to set actions->dso here since
3146 * it's just to remove the current filter.
3147 * Ditto for thread below.
3149 do_zoom_dso(browser, actions);
3150 } else if (top == &browser->hists->thread_filter) {
3151 do_zoom_thread(browser, actions);
3152 } else if (top == &browser->hists->socket_filter) {
3153 do_zoom_socket(browser, actions);
3159 goto out_free_stack;
3161 if (!is_report_browser(hbt)) {
3162 struct perf_top *top = hbt->arg;
3164 perf_evlist__toggle_enable(top->evlist);
3166 * No need to refresh, resort/decay histogram
3167 * entries if we are not collecting samples:
3169 if (top->evlist->enabled) {
3170 helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
3171 hbt->refresh = delay_secs;
3173 helpline = "Press 'f' again to re-enable the events";
3180 helpline = "Press '?' for help on key bindings";
3184 if (!hists__has(hists, sym) || browser->selection == NULL)
3185 goto skip_annotation;
3187 if (sort__mode == SORT_MODE__BRANCH) {
3188 bi = browser->he_selection->branch_info;
3191 goto skip_annotation;
3193 nr_options += add_annotate_opt(browser,
3194 &actions[nr_options],
3195 &options[nr_options],
3198 if (bi->to.sym != bi->from.sym)
3199 nr_options += add_annotate_opt(browser,
3200 &actions[nr_options],
3201 &options[nr_options],
3205 nr_options += add_annotate_opt(browser,
3206 &actions[nr_options],
3207 &options[nr_options],
3208 browser->selection->map,
3209 browser->selection->sym);
3212 nr_options += add_thread_opt(browser, &actions[nr_options],
3213 &options[nr_options], thread);
3214 nr_options += add_dso_opt(browser, &actions[nr_options],
3215 &options[nr_options], map);
3216 nr_options += add_map_opt(browser, &actions[nr_options],
3217 &options[nr_options],
3218 browser->selection ?
3219 browser->selection->map : NULL);
3220 nr_options += add_socket_opt(browser, &actions[nr_options],
3221 &options[nr_options],
3223 /* perf script support */
3224 if (!is_report_browser(hbt))
3225 goto skip_scripting;
3227 if (browser->he_selection) {
3228 if (hists__has(hists, thread) && thread) {
3229 nr_options += add_script_opt(browser,
3230 &actions[nr_options],
3231 &options[nr_options],
3235 * Note that browser->selection != NULL
3236 * when browser->he_selection is not NULL,
3237 * so we don't need to check browser->selection
3238 * before fetching browser->selection->sym like what
3239 * we do before fetching browser->selection->map.
3241 * See hist_browser__show_entry.
3243 if (hists__has(hists, sym) && browser->selection->sym) {
3244 nr_options += add_script_opt(browser,
3245 &actions[nr_options],
3246 &options[nr_options],
3247 NULL, browser->selection->sym);
3250 nr_options += add_script_opt(browser, &actions[nr_options],
3251 &options[nr_options], NULL, NULL);
3252 nr_options += add_switch_opt(browser, &actions[nr_options],
3253 &options[nr_options]);
3255 nr_options += add_exit_opt(browser, &actions[nr_options],
3256 &options[nr_options]);
3259 struct popup_action *act;
3261 choice = ui__popup_menu(nr_options, options);
3262 if (choice == -1 || choice >= nr_options)
3265 act = &actions[choice];
3266 key = act->fn(browser, act);
3269 if (key == K_SWITCH_INPUT_DATA)
3273 pstack__delete(browser->pstack);
3275 hist_browser__delete(browser);
3276 free_popup_options(options, MAX_OPTIONS);
3280 struct perf_evsel_menu {
3281 struct ui_browser b;
3282 struct perf_evsel *selection;
3283 bool lost_events, lost_events_warned;
3285 struct perf_env *env;
3288 static void perf_evsel_menu__write(struct ui_browser *browser,
3289 void *entry, int row)
3291 struct perf_evsel_menu *menu = container_of(browser,
3292 struct perf_evsel_menu, b);
3293 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
3294 struct hists *hists = evsel__hists(evsel);
3295 bool current_entry = ui_browser__is_current_entry(browser, row);
3296 unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
3297 const char *ev_name = perf_evsel__name(evsel);
3299 const char *warn = " ";
3302 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
3303 HE_COLORSET_NORMAL);
3305 if (perf_evsel__is_group_event(evsel)) {
3306 struct perf_evsel *pos;
3308 ev_name = perf_evsel__group_name(evsel);
3310 for_each_group_member(pos, evsel) {
3311 struct hists *pos_hists = evsel__hists(pos);
3312 nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
3316 nr_events = convert_unit(nr_events, &unit);
3317 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
3318 unit, unit == ' ' ? "" : " ", ev_name);
3319 ui_browser__printf(browser, "%s", bf);
3321 nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
3322 if (nr_events != 0) {
3323 menu->lost_events = true;
3325 ui_browser__set_color(browser, HE_COLORSET_TOP);
3326 nr_events = convert_unit(nr_events, &unit);
3327 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
3328 nr_events, unit, unit == ' ' ? "" : " ");
3332 ui_browser__write_nstring(browser, warn, browser->width - printed);
3335 menu->selection = evsel;
3338 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
3339 int nr_events, const char *help,
3340 struct hist_browser_timer *hbt)
3342 struct perf_evlist *evlist = menu->b.priv;
3343 struct perf_evsel *pos;
3344 const char *title = "Available samples";
3345 int delay_secs = hbt ? hbt->refresh : 0;
3348 if (ui_browser__show(&menu->b, title,
3349 "ESC: exit, ENTER|->: Browse histograms") < 0)
3353 key = ui_browser__run(&menu->b, delay_secs);
3357 hbt->timer(hbt->arg);
3359 if (!menu->lost_events_warned && menu->lost_events) {
3360 ui_browser__warn_lost_events(&menu->b);
3361 menu->lost_events_warned = true;
3366 if (!menu->selection)
3368 pos = menu->selection;
3370 perf_evlist__set_selected(evlist, pos);
3372 * Give the calling tool a chance to populate the non
3373 * default evsel resorted hists tree.
3376 hbt->timer(hbt->arg);
3377 key = perf_evsel__hists_browse(pos, nr_events, help,
3381 ui_browser__show_title(&menu->b, title);
3384 if (pos->node.next == &evlist->entries)
3385 pos = perf_evlist__first(evlist);
3387 pos = perf_evsel__next(pos);
3390 if (pos->node.prev == &evlist->entries)
3391 pos = perf_evlist__last(evlist);
3393 pos = perf_evsel__prev(pos);
3395 case K_SWITCH_INPUT_DATA:
3406 if (!ui_browser__dialog_yesno(&menu->b,
3407 "Do you really want to exit?"))
3419 ui_browser__hide(&menu->b);
3423 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
3426 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
3428 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
3434 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
3435 int nr_entries, const char *help,
3436 struct hist_browser_timer *hbt,
3438 struct perf_env *env)
3440 struct perf_evsel *pos;
3441 struct perf_evsel_menu menu = {
3443 .entries = &evlist->entries,
3444 .refresh = ui_browser__list_head_refresh,
3445 .seek = ui_browser__list_head_seek,
3446 .write = perf_evsel_menu__write,
3447 .filter = filter_group_entries,
3448 .nr_entries = nr_entries,
3451 .min_pcnt = min_pcnt,
3455 ui_helpline__push("Press ESC to exit");
3457 evlist__for_each_entry(evlist, pos) {
3458 const char *ev_name = perf_evsel__name(pos);
3459 size_t line_len = strlen(ev_name) + 7;
3461 if (menu.b.width < line_len)
3462 menu.b.width = line_len;
3465 return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
3468 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
3469 struct hist_browser_timer *hbt,
3471 struct perf_env *env)
3473 int nr_entries = evlist->nr_entries;
3476 if (nr_entries == 1) {
3477 struct perf_evsel *first = perf_evlist__first(evlist);
3479 return perf_evsel__hists_browse(first, nr_entries, help,
3480 false, hbt, min_pcnt,
3484 if (symbol_conf.event_group) {
3485 struct perf_evsel *pos;
3488 evlist__for_each_entry(evlist, pos) {
3489 if (perf_evsel__is_group_leader(pos))
3493 if (nr_entries == 1)
3497 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
3498 hbt, min_pcnt, env);