7 #include <linux/rbtree.h>
8 #include <sys/ttydefaults.h>
10 #include "../../util/evsel.h"
11 #include "../../util/evlist.h"
12 #include "../../util/hist.h"
13 #include "../../util/pstack.h"
14 #include "../../util/sort.h"
15 #include "../../util/util.h"
16 #include "../../util/top.h"
17 #include "../../arch/common.h"
19 #include "../browsers/hists.h"
20 #include "../helpline.h"
29 #include "sane_ctype.h"
31 extern void hist_browser__init_hpp(void);
33 static int perf_evsel_browser_title(struct hist_browser *browser,
34 char *bf, size_t size);
35 static void hist_browser__update_nr_entries(struct hist_browser *hb);
37 static struct rb_node *hists__filter_entries(struct rb_node *nd,
40 static bool hist_browser__has_filter(struct hist_browser *hb)
42 return hists__has_filter(hb->hists) || hb->min_pcnt || symbol_conf.has_filter || hb->c2c_filter;
45 static int hist_browser__get_folding(struct hist_browser *browser)
48 struct hists *hists = browser->hists;
49 int unfolded_rows = 0;
51 for (nd = rb_first(&hists->entries);
52 (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
53 nd = rb_hierarchy_next(nd)) {
54 struct hist_entry *he =
55 rb_entry(nd, struct hist_entry, rb_node);
57 if (he->leaf && he->unfolded)
58 unfolded_rows += he->nr_rows;
63 static u32 hist_browser__nr_entries(struct hist_browser *hb)
67 if (symbol_conf.report_hierarchy)
68 nr_entries = hb->nr_hierarchy_entries;
69 else if (hist_browser__has_filter(hb))
70 nr_entries = hb->nr_non_filtered_entries;
72 nr_entries = hb->hists->nr_entries;
74 hb->nr_callchain_rows = hist_browser__get_folding(hb);
75 return nr_entries + hb->nr_callchain_rows;
78 static void hist_browser__update_rows(struct hist_browser *hb)
80 struct ui_browser *browser = &hb->b;
81 struct hists *hists = hb->hists;
82 struct perf_hpp_list *hpp_list = hists->hpp_list;
83 u16 header_offset, index_row;
85 header_offset = hb->show_headers ? hpp_list->nr_header_lines : 0;
86 browser->rows = browser->height - header_offset;
88 * Verify if we were at the last line and that line isn't
89 * visibe because we now show the header line(s).
91 index_row = browser->index - browser->top_idx;
92 if (index_row >= browser->rows)
93 browser->index -= index_row - browser->rows + 1;
96 static void hist_browser__refresh_dimensions(struct ui_browser *browser)
98 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
100 /* 3 == +/- toggle symbol before actual hist_entry rendering */
101 browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]"));
103 * FIXME: Just keeping existing behaviour, but this really should be
104 * before updating browser->width, as it will invalidate the
105 * calculation above. Fix this and the fallout in another
108 ui_browser__refresh_dimensions(browser);
109 hist_browser__update_rows(hb);
112 static void hist_browser__gotorc(struct hist_browser *browser, int row, int column)
114 struct hists *hists = browser->hists;
115 struct perf_hpp_list *hpp_list = hists->hpp_list;
118 header_offset = browser->show_headers ? hpp_list->nr_header_lines : 0;
119 ui_browser__gotorc(&browser->b, row + header_offset, column);
122 static void hist_browser__reset(struct hist_browser *browser)
125 * The hists__remove_entry_filter() already folds non-filtered
126 * entries so we can assume it has 0 callchain rows.
128 browser->nr_callchain_rows = 0;
130 hist_browser__update_nr_entries(browser);
131 browser->b.nr_entries = hist_browser__nr_entries(browser);
132 hist_browser__refresh_dimensions(&browser->b);
133 ui_browser__reset_index(&browser->b);
136 static char tree__folded_sign(bool unfolded)
138 return unfolded ? '-' : '+';
141 static char hist_entry__folded(const struct hist_entry *he)
143 return he->has_children ? tree__folded_sign(he->unfolded) : ' ';
146 static char callchain_list__folded(const struct callchain_list *cl)
148 return cl->has_children ? tree__folded_sign(cl->unfolded) : ' ';
151 static void callchain_list__set_folding(struct callchain_list *cl, bool unfold)
153 cl->unfolded = unfold ? cl->has_children : false;
156 static struct inline_node *inline_node__create(struct map *map, u64 ip)
159 struct inline_node *node;
168 if (dso->kernel != DSO_TYPE_USER)
171 node = dso__parse_addr_inlines(dso,
172 map__rip_2objdump(map, ip));
177 static int inline__count_rows(struct inline_node *node)
179 struct inline_list *ilist;
185 list_for_each_entry(ilist, &node->val, list) {
186 if ((ilist->filename != NULL) || (ilist->funcname != NULL))
193 static int callchain_list__inline_rows(struct callchain_list *chain)
195 struct inline_node *node;
198 node = inline_node__create(chain->ms.map, chain->ip);
202 rows = inline__count_rows(node);
203 inline_node__delete(node);
207 static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
209 int n = 0, inline_rows;
212 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
213 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
214 struct callchain_list *chain;
215 char folded_sign = ' '; /* No children */
217 list_for_each_entry(chain, &child->val, list) {
220 if (symbol_conf.inline_name) {
222 callchain_list__inline_rows(chain);
226 /* We need this because we may not have children */
227 folded_sign = callchain_list__folded(chain);
228 if (folded_sign == '+')
232 if (folded_sign == '-') /* Have children and they're unfolded */
233 n += callchain_node__count_rows_rb_tree(child);
239 static int callchain_node__count_flat_rows(struct callchain_node *node)
241 struct callchain_list *chain;
242 char folded_sign = 0;
245 list_for_each_entry(chain, &node->parent_val, list) {
247 /* only check first chain list entry */
248 folded_sign = callchain_list__folded(chain);
249 if (folded_sign == '+')
255 list_for_each_entry(chain, &node->val, list) {
257 /* node->parent_val list might be empty */
258 folded_sign = callchain_list__folded(chain);
259 if (folded_sign == '+')
268 static int callchain_node__count_folded_rows(struct callchain_node *node __maybe_unused)
273 static int callchain_node__count_rows(struct callchain_node *node)
275 struct callchain_list *chain;
276 bool unfolded = false;
277 int n = 0, inline_rows;
279 if (callchain_param.mode == CHAIN_FLAT)
280 return callchain_node__count_flat_rows(node);
281 else if (callchain_param.mode == CHAIN_FOLDED)
282 return callchain_node__count_folded_rows(node);
284 list_for_each_entry(chain, &node->val, list) {
286 if (symbol_conf.inline_name) {
287 inline_rows = callchain_list__inline_rows(chain);
291 unfolded = chain->unfolded;
295 n += callchain_node__count_rows_rb_tree(node);
300 static int callchain__count_rows(struct rb_root *chain)
305 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
306 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
307 n += callchain_node__count_rows(node);
313 static int hierarchy_count_rows(struct hist_browser *hb, struct hist_entry *he,
314 bool include_children)
317 struct rb_node *node;
318 struct hist_entry *child;
321 return callchain__count_rows(&he->sorted_chain);
323 if (he->has_no_entry)
326 node = rb_first(&he->hroot_out);
330 child = rb_entry(node, struct hist_entry, rb_node);
331 percent = hist_entry__get_percent_limit(child);
333 if (!child->filtered && percent >= hb->min_pcnt) {
336 if (include_children && child->unfolded)
337 count += hierarchy_count_rows(hb, child, true);
340 node = rb_next(node);
345 static bool hist_entry__toggle_fold(struct hist_entry *he)
350 if (!he->has_children)
353 he->unfolded = !he->unfolded;
357 static bool callchain_list__toggle_fold(struct callchain_list *cl)
362 if (!cl->has_children)
365 cl->unfolded = !cl->unfolded;
369 static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
371 struct rb_node *nd = rb_first(&node->rb_root);
373 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
374 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
375 struct callchain_list *chain;
378 list_for_each_entry(chain, &child->val, list) {
381 chain->has_children = chain->list.next != &child->val ||
382 !RB_EMPTY_ROOT(&child->rb_root);
384 chain->has_children = chain->list.next == &child->val &&
385 !RB_EMPTY_ROOT(&child->rb_root);
388 callchain_node__init_have_children_rb_tree(child);
392 static void callchain_node__init_have_children(struct callchain_node *node,
395 struct callchain_list *chain;
397 chain = list_entry(node->val.next, struct callchain_list, list);
398 chain->has_children = has_sibling;
400 if (!list_empty(&node->val)) {
401 chain = list_entry(node->val.prev, struct callchain_list, list);
402 chain->has_children = !RB_EMPTY_ROOT(&node->rb_root);
405 callchain_node__init_have_children_rb_tree(node);
408 static void callchain__init_have_children(struct rb_root *root)
410 struct rb_node *nd = rb_first(root);
411 bool has_sibling = nd && rb_next(nd);
413 for (nd = rb_first(root); nd; nd = rb_next(nd)) {
414 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
415 callchain_node__init_have_children(node, has_sibling);
416 if (callchain_param.mode == CHAIN_FLAT ||
417 callchain_param.mode == CHAIN_FOLDED)
418 callchain_node__make_parent_list(node);
422 static void hist_entry__init_have_children(struct hist_entry *he)
424 if (he->init_have_children)
428 he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
429 callchain__init_have_children(&he->sorted_chain);
431 he->has_children = !RB_EMPTY_ROOT(&he->hroot_out);
434 he->init_have_children = true;
437 static void hist_entry_init_inline_node(struct hist_entry *he)
442 he->inline_node = inline_node__create(he->ms.map, he->ip);
444 if (he->inline_node == NULL)
447 he->has_children = true;
450 static bool hist_browser__toggle_fold(struct hist_browser *browser)
452 struct hist_entry *he = browser->he_selection;
453 struct map_symbol *ms = browser->selection;
454 struct callchain_list *cl = container_of(ms, struct callchain_list, ms);
461 has_children = hist_entry__toggle_fold(he);
463 has_children = callchain_list__toggle_fold(cl);
468 hist_entry__init_have_children(he);
469 browser->b.nr_entries -= he->nr_rows;
472 browser->nr_callchain_rows -= he->nr_rows;
474 browser->nr_hierarchy_entries -= he->nr_rows;
476 if (symbol_conf.report_hierarchy)
477 child_rows = hierarchy_count_rows(browser, he, true);
482 he->nr_rows = inline__count_rows(
485 he->nr_rows = callchain__count_rows(
488 he->nr_rows = hierarchy_count_rows(browser, he, false);
490 /* account grand children */
491 if (symbol_conf.report_hierarchy)
492 browser->b.nr_entries += child_rows - he->nr_rows;
494 if (!he->leaf && he->nr_rows == 0) {
495 he->has_no_entry = true;
499 if (symbol_conf.report_hierarchy)
500 browser->b.nr_entries -= child_rows - he->nr_rows;
502 if (he->has_no_entry)
503 he->has_no_entry = false;
508 browser->b.nr_entries += he->nr_rows;
511 browser->nr_callchain_rows += he->nr_rows;
513 browser->nr_hierarchy_entries += he->nr_rows;
518 /* If it doesn't have children, no toggling performed */
522 static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
527 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
528 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
529 struct callchain_list *chain;
530 bool has_children = false;
532 list_for_each_entry(chain, &child->val, list) {
534 callchain_list__set_folding(chain, unfold);
535 has_children = chain->has_children;
539 n += callchain_node__set_folding_rb_tree(child, unfold);
545 static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
547 struct callchain_list *chain;
548 bool has_children = false;
551 list_for_each_entry(chain, &node->val, list) {
553 callchain_list__set_folding(chain, unfold);
554 has_children = chain->has_children;
558 n += callchain_node__set_folding_rb_tree(node, unfold);
563 static int callchain__set_folding(struct rb_root *chain, bool unfold)
568 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
569 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
570 n += callchain_node__set_folding(node, unfold);
576 static int hierarchy_set_folding(struct hist_browser *hb, struct hist_entry *he,
577 bool unfold __maybe_unused)
581 struct hist_entry *child;
584 for (nd = rb_first(&he->hroot_out); nd; nd = rb_next(nd)) {
585 child = rb_entry(nd, struct hist_entry, rb_node);
586 percent = hist_entry__get_percent_limit(child);
587 if (!child->filtered && percent >= hb->min_pcnt)
594 static void __hist_entry__set_folding(struct hist_entry *he,
595 struct hist_browser *hb, bool unfold)
597 hist_entry__init_have_children(he);
598 he->unfolded = unfold ? he->has_children : false;
600 if (he->has_children) {
604 n = callchain__set_folding(&he->sorted_chain, unfold);
606 n = hierarchy_set_folding(hb, he, unfold);
608 he->nr_rows = unfold ? n : 0;
613 static void hist_entry__set_folding(struct hist_entry *he,
614 struct hist_browser *browser, bool unfold)
618 percent = hist_entry__get_percent_limit(he);
619 if (he->filtered || percent < browser->min_pcnt)
622 __hist_entry__set_folding(he, browser, unfold);
624 if (!he->depth || unfold)
625 browser->nr_hierarchy_entries++;
627 browser->nr_callchain_rows += he->nr_rows;
628 else if (unfold && !hist_entry__has_hierarchy_children(he, browser->min_pcnt)) {
629 browser->nr_hierarchy_entries++;
630 he->has_no_entry = true;
633 he->has_no_entry = false;
637 __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
640 struct hist_entry *he;
642 nd = rb_first(&browser->hists->entries);
644 he = rb_entry(nd, struct hist_entry, rb_node);
646 /* set folding state even if it's currently folded */
647 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
649 hist_entry__set_folding(he, browser, unfold);
653 static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
655 browser->nr_hierarchy_entries = 0;
656 browser->nr_callchain_rows = 0;
657 __hist_browser__set_folding(browser, unfold);
659 browser->b.nr_entries = hist_browser__nr_entries(browser);
660 /* Go to the start, we may be way after valid entries after a collapse */
661 ui_browser__reset_index(&browser->b);
664 static void hist_browser__set_folding_selected(struct hist_browser *browser, bool unfold)
666 if (!browser->he_selection)
669 hist_entry__set_folding(browser->he_selection, browser, unfold);
670 browser->b.nr_entries = hist_browser__nr_entries(browser);
673 static void ui_browser__warn_lost_events(struct ui_browser *browser)
675 ui_browser__warning(browser, 4,
676 "Events are being lost, check IO/CPU overload!\n\n"
677 "You may want to run 'perf' using a RT scheduler policy:\n\n"
678 " perf top -r 80\n\n"
679 "Or reduce the sampling frequency.");
682 static int hist_browser__title(struct hist_browser *browser, char *bf, size_t size)
684 return browser->title ? browser->title(browser, bf, size) : 0;
687 int hist_browser__run(struct hist_browser *browser, const char *help)
691 struct hist_browser_timer *hbt = browser->hbt;
692 int delay_secs = hbt ? hbt->refresh : 0;
694 browser->b.entries = &browser->hists->entries;
695 browser->b.nr_entries = hist_browser__nr_entries(browser);
697 hist_browser__title(browser, title, sizeof(title));
699 if (ui_browser__show(&browser->b, title, "%s", help) < 0)
703 key = ui_browser__run(&browser->b, delay_secs);
708 hbt->timer(hbt->arg);
710 if (hist_browser__has_filter(browser) ||
711 symbol_conf.report_hierarchy)
712 hist_browser__update_nr_entries(browser);
714 nr_entries = hist_browser__nr_entries(browser);
715 ui_browser__update_nr_entries(&browser->b, nr_entries);
717 if (browser->hists->stats.nr_lost_warned !=
718 browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
719 browser->hists->stats.nr_lost_warned =
720 browser->hists->stats.nr_events[PERF_RECORD_LOST];
721 ui_browser__warn_lost_events(&browser->b);
724 hist_browser__title(browser, title, sizeof(title));
725 ui_browser__show_title(&browser->b, title);
728 case 'D': { /* Debug */
730 struct hist_entry *h = rb_entry(browser->b.top,
731 struct hist_entry, rb_node);
733 ui_helpline__fpush("%d: nr_ent=(%d,%d), rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
734 seq++, browser->b.nr_entries,
735 browser->hists->nr_entries,
739 h->row_offset, h->nr_rows);
743 /* Collapse the whole world. */
744 hist_browser__set_folding(browser, false);
747 /* Collapse the selected entry. */
748 hist_browser__set_folding_selected(browser, false);
751 /* Expand the whole world. */
752 hist_browser__set_folding(browser, true);
755 /* Expand the selected entry. */
756 hist_browser__set_folding_selected(browser, true);
759 browser->show_headers = !browser->show_headers;
760 hist_browser__update_rows(browser);
763 if (hist_browser__toggle_fold(browser))
771 ui_browser__hide(&browser->b);
775 struct callchain_print_arg {
776 /* for hists browser */
778 bool is_current_entry;
785 typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
786 struct callchain_list *chain,
787 const char *str, int offset,
789 struct callchain_print_arg *arg);
791 static void hist_browser__show_callchain_entry(struct hist_browser *browser,
792 struct callchain_list *chain,
793 const char *str, int offset,
795 struct callchain_print_arg *arg)
798 char folded_sign = callchain_list__folded(chain);
799 bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
801 color = HE_COLORSET_NORMAL;
802 width = browser->b.width - (offset + 2);
803 if (ui_browser__is_current_entry(&browser->b, row)) {
804 browser->selection = &chain->ms;
805 color = HE_COLORSET_SELECTED;
806 arg->is_current_entry = true;
809 ui_browser__set_color(&browser->b, color);
810 hist_browser__gotorc(browser, row, 0);
811 ui_browser__write_nstring(&browser->b, " ", offset);
812 ui_browser__printf(&browser->b, "%c", folded_sign);
813 ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
814 ui_browser__write_nstring(&browser->b, str, width);
817 static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
818 struct callchain_list *chain,
819 const char *str, int offset,
820 unsigned short row __maybe_unused,
821 struct callchain_print_arg *arg)
823 char folded_sign = callchain_list__folded(chain);
825 arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
829 typedef bool (*check_output_full_fn)(struct hist_browser *browser,
832 static bool hist_browser__check_output_full(struct hist_browser *browser,
835 return browser->b.rows == row;
838 static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
839 unsigned short row __maybe_unused)
844 #define LEVEL_OFFSET_STEP 3
846 static int hist_browser__show_inline(struct hist_browser *browser,
847 struct inline_node *node,
851 struct inline_list *ilist;
853 int color, width, first_row;
856 width = browser->b.width - (LEVEL_OFFSET_STEP + 2);
857 list_for_each_entry(ilist, &node->val, list) {
858 if ((ilist->filename != NULL) || (ilist->funcname != NULL)) {
859 color = HE_COLORSET_NORMAL;
860 if (ui_browser__is_current_entry(&browser->b, row))
861 color = HE_COLORSET_SELECTED;
863 if (callchain_param.key == CCKEY_ADDRESS ||
864 callchain_param.key == CCKEY_SRCLINE) {
865 if (ilist->filename != NULL)
866 scnprintf(buf, sizeof(buf),
871 scnprintf(buf, sizeof(buf), "??");
872 } else if (ilist->funcname != NULL)
873 scnprintf(buf, sizeof(buf), "%s (inline)",
875 else if (ilist->filename != NULL)
876 scnprintf(buf, sizeof(buf),
881 scnprintf(buf, sizeof(buf), "??");
883 ui_browser__set_color(&browser->b, color);
884 hist_browser__gotorc(browser, row, 0);
885 ui_browser__write_nstring(&browser->b, " ",
886 LEVEL_OFFSET_STEP + offset);
887 ui_browser__write_nstring(&browser->b, buf, width);
892 return row - first_row;
895 static size_t show_inline_list(struct hist_browser *browser, struct map *map,
896 u64 ip, int row, int offset)
898 struct inline_node *node;
901 node = inline_node__create(map, ip);
905 ret = hist_browser__show_inline(browser, node, row, offset);
907 inline_node__delete(node);
911 static int hist_browser__show_callchain_list(struct hist_browser *browser,
912 struct callchain_node *node,
913 struct callchain_list *chain,
914 unsigned short row, u64 total,
915 bool need_percent, int offset,
916 print_callchain_entry_fn print,
917 struct callchain_print_arg *arg)
919 char bf[1024], *alloc_str;
920 char buf[64], *alloc_str2;
922 int inline_rows = 0, ret = 1;
924 if (arg->row_offset != 0) {
932 str = callchain_list__sym_name(chain, bf, sizeof(bf),
935 if (symbol_conf.show_branchflag_count) {
937 callchain_list_counts__printf_value(node, chain, NULL,
940 callchain_list_counts__printf_value(NULL, chain, NULL,
943 if (asprintf(&alloc_str2, "%s%s", str, buf) < 0)
944 str = "Not enough memory!";
950 callchain_node__scnprintf_value(node, buf, sizeof(buf),
953 if (asprintf(&alloc_str, "%s %s", buf, str) < 0)
954 str = "Not enough memory!";
959 print(browser, chain, str, offset, row, arg);
963 if (symbol_conf.inline_name) {
964 inline_rows = show_inline_list(browser, chain->ms.map,
965 chain->ip, row + 1, offset);
968 return ret + inline_rows;
971 static bool check_percent_display(struct rb_node *node, u64 parent_total)
973 struct callchain_node *child;
981 child = rb_entry(node, struct callchain_node, rb_node);
982 return callchain_cumul_hits(child) != parent_total;
985 static int hist_browser__show_callchain_flat(struct hist_browser *browser,
986 struct rb_root *root,
987 unsigned short row, u64 total,
989 print_callchain_entry_fn print,
990 struct callchain_print_arg *arg,
991 check_output_full_fn is_output_full)
993 struct rb_node *node;
994 int first_row = row, offset = LEVEL_OFFSET_STEP;
997 node = rb_first(root);
998 need_percent = check_percent_display(node, parent_total);
1001 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1002 struct rb_node *next = rb_next(node);
1003 struct callchain_list *chain;
1004 char folded_sign = ' ';
1006 int extra_offset = 0;
1008 list_for_each_entry(chain, &child->parent_val, list) {
1009 bool was_first = first;
1013 else if (need_percent)
1014 extra_offset = LEVEL_OFFSET_STEP;
1016 folded_sign = callchain_list__folded(chain);
1018 row += hist_browser__show_callchain_list(browser, child,
1020 was_first && need_percent,
1021 offset + extra_offset,
1024 if (is_output_full(browser, row))
1027 if (folded_sign == '+')
1031 list_for_each_entry(chain, &child->val, list) {
1032 bool was_first = first;
1036 else if (need_percent)
1037 extra_offset = LEVEL_OFFSET_STEP;
1039 folded_sign = callchain_list__folded(chain);
1041 row += hist_browser__show_callchain_list(browser, child,
1043 was_first && need_percent,
1044 offset + extra_offset,
1047 if (is_output_full(browser, row))
1050 if (folded_sign == '+')
1055 if (is_output_full(browser, row))
1060 return row - first_row;
1063 static char *hist_browser__folded_callchain_str(struct hist_browser *browser,
1064 struct callchain_list *chain,
1065 char *value_str, char *old_str)
1071 str = callchain_list__sym_name(chain, bf, sizeof(bf),
1074 if (asprintf(&new, "%s%s%s", old_str,
1075 symbol_conf.field_sep ?: ";", str) < 0)
1079 if (asprintf(&new, "%s %s", value_str, str) < 0)
1082 if (asprintf(&new, "%s", str) < 0)
1089 static int hist_browser__show_callchain_folded(struct hist_browser *browser,
1090 struct rb_root *root,
1091 unsigned short row, u64 total,
1093 print_callchain_entry_fn print,
1094 struct callchain_print_arg *arg,
1095 check_output_full_fn is_output_full)
1097 struct rb_node *node;
1098 int first_row = row, offset = LEVEL_OFFSET_STEP;
1101 node = rb_first(root);
1102 need_percent = check_percent_display(node, parent_total);
1105 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1106 struct rb_node *next = rb_next(node);
1107 struct callchain_list *chain, *first_chain = NULL;
1109 char *value_str = NULL, *value_str_alloc = NULL;
1110 char *chain_str = NULL, *chain_str_alloc = NULL;
1112 if (arg->row_offset != 0) {
1120 callchain_node__scnprintf_value(child, buf, sizeof(buf), total);
1121 if (asprintf(&value_str, "%s", buf) < 0) {
1122 value_str = (char *)"<...>";
1125 value_str_alloc = value_str;
1128 list_for_each_entry(chain, &child->parent_val, list) {
1129 chain_str = hist_browser__folded_callchain_str(browser,
1130 chain, value_str, chain_str);
1133 first_chain = chain;
1136 if (chain_str == NULL) {
1137 chain_str = (char *)"Not enough memory!";
1141 chain_str_alloc = chain_str;
1144 list_for_each_entry(chain, &child->val, list) {
1145 chain_str = hist_browser__folded_callchain_str(browser,
1146 chain, value_str, chain_str);
1149 first_chain = chain;
1152 if (chain_str == NULL) {
1153 chain_str = (char *)"Not enough memory!";
1157 chain_str_alloc = chain_str;
1161 print(browser, first_chain, chain_str, offset, row++, arg);
1162 free(value_str_alloc);
1163 free(chain_str_alloc);
1166 if (is_output_full(browser, row))
1171 return row - first_row;
1174 static int hist_browser__show_callchain_graph(struct hist_browser *browser,
1175 struct rb_root *root, int level,
1176 unsigned short row, u64 total,
1178 print_callchain_entry_fn print,
1179 struct callchain_print_arg *arg,
1180 check_output_full_fn is_output_full)
1182 struct rb_node *node;
1183 int first_row = row, offset = level * LEVEL_OFFSET_STEP;
1185 u64 percent_total = total;
1187 if (callchain_param.mode == CHAIN_GRAPH_REL)
1188 percent_total = parent_total;
1190 node = rb_first(root);
1191 need_percent = check_percent_display(node, parent_total);
1194 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1195 struct rb_node *next = rb_next(node);
1196 struct callchain_list *chain;
1197 char folded_sign = ' ';
1199 int extra_offset = 0;
1201 list_for_each_entry(chain, &child->val, list) {
1202 bool was_first = first;
1206 else if (need_percent)
1207 extra_offset = LEVEL_OFFSET_STEP;
1209 folded_sign = callchain_list__folded(chain);
1211 row += hist_browser__show_callchain_list(browser, child,
1212 chain, row, percent_total,
1213 was_first && need_percent,
1214 offset + extra_offset,
1217 if (is_output_full(browser, row))
1220 if (folded_sign == '+')
1224 if (folded_sign == '-') {
1225 const int new_level = level + (extra_offset ? 2 : 1);
1227 row += hist_browser__show_callchain_graph(browser, &child->rb_root,
1228 new_level, row, total,
1229 child->children_hit,
1230 print, arg, is_output_full);
1232 if (is_output_full(browser, row))
1237 return row - first_row;
1240 static int hist_browser__show_callchain(struct hist_browser *browser,
1241 struct hist_entry *entry, int level,
1243 print_callchain_entry_fn print,
1244 struct callchain_print_arg *arg,
1245 check_output_full_fn is_output_full)
1247 u64 total = hists__total_period(entry->hists);
1251 if (symbol_conf.cumulate_callchain)
1252 parent_total = entry->stat_acc->period;
1254 parent_total = entry->stat.period;
1256 if (callchain_param.mode == CHAIN_FLAT) {
1257 printed = hist_browser__show_callchain_flat(browser,
1258 &entry->sorted_chain, row,
1259 total, parent_total, print, arg,
1261 } else if (callchain_param.mode == CHAIN_FOLDED) {
1262 printed = hist_browser__show_callchain_folded(browser,
1263 &entry->sorted_chain, row,
1264 total, parent_total, print, arg,
1267 printed = hist_browser__show_callchain_graph(browser,
1268 &entry->sorted_chain, level, row,
1269 total, parent_total, print, arg,
1273 if (arg->is_current_entry)
1274 browser->he_selection = entry;
1280 struct ui_browser *b;
1285 int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
1287 struct hpp_arg *arg = hpp->ptr;
1292 va_start(args, fmt);
1293 len = va_arg(args, int);
1294 percent = va_arg(args, double);
1297 ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
1299 ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
1300 ui_browser__printf(arg->b, "%s", hpp->buf);
1305 #define __HPP_COLOR_PERCENT_FN(_type, _field) \
1306 static u64 __hpp_get_##_field(struct hist_entry *he) \
1308 return he->stat._field; \
1312 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
1313 struct perf_hpp *hpp, \
1314 struct hist_entry *he) \
1316 return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%", \
1317 __hpp__slsmg_color_printf, true); \
1320 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
1321 static u64 __hpp_get_acc_##_field(struct hist_entry *he) \
1323 return he->stat_acc->_field; \
1327 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
1328 struct perf_hpp *hpp, \
1329 struct hist_entry *he) \
1331 if (!symbol_conf.cumulate_callchain) { \
1332 struct hpp_arg *arg = hpp->ptr; \
1333 int len = fmt->user_len ?: fmt->len; \
1334 int ret = scnprintf(hpp->buf, hpp->size, \
1335 "%*s", len, "N/A"); \
1336 ui_browser__printf(arg->b, "%s", hpp->buf); \
1340 return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field, \
1341 " %*.2f%%", __hpp__slsmg_color_printf, true); \
1344 __HPP_COLOR_PERCENT_FN(overhead, period)
1345 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
1346 __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
1347 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
1348 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
1349 __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
1351 #undef __HPP_COLOR_PERCENT_FN
1352 #undef __HPP_COLOR_ACC_PERCENT_FN
1354 void hist_browser__init_hpp(void)
1356 perf_hpp__format[PERF_HPP__OVERHEAD].color =
1357 hist_browser__hpp_color_overhead;
1358 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
1359 hist_browser__hpp_color_overhead_sys;
1360 perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
1361 hist_browser__hpp_color_overhead_us;
1362 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
1363 hist_browser__hpp_color_overhead_guest_sys;
1364 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
1365 hist_browser__hpp_color_overhead_guest_us;
1366 perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
1367 hist_browser__hpp_color_overhead_acc;
1370 static int hist_browser__show_entry(struct hist_browser *browser,
1371 struct hist_entry *entry,
1375 int width = browser->b.width;
1376 char folded_sign = ' ';
1377 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1378 off_t row_offset = entry->row_offset;
1380 struct perf_hpp_fmt *fmt;
1382 if (current_entry) {
1383 browser->he_selection = entry;
1384 browser->selection = &entry->ms;
1387 if (symbol_conf.use_callchain) {
1388 hist_entry__init_have_children(entry);
1389 folded_sign = hist_entry__folded(entry);
1392 if (symbol_conf.inline_name &&
1393 (!entry->has_children)) {
1394 hist_entry_init_inline_node(entry);
1395 folded_sign = hist_entry__folded(entry);
1398 if (row_offset == 0) {
1399 struct hpp_arg arg = {
1401 .folded_sign = folded_sign,
1402 .current_entry = current_entry,
1406 hist_browser__gotorc(browser, row, 0);
1408 hists__for_each_format(browser->hists, fmt) {
1410 struct perf_hpp hpp = {
1416 if (perf_hpp__should_skip(fmt, entry->hists) ||
1417 column++ < browser->b.horiz_scroll)
1420 if (current_entry && browser->b.navkeypressed) {
1421 ui_browser__set_color(&browser->b,
1422 HE_COLORSET_SELECTED);
1424 ui_browser__set_color(&browser->b,
1425 HE_COLORSET_NORMAL);
1429 if (symbol_conf.use_callchain ||
1430 symbol_conf.inline_name) {
1431 ui_browser__printf(&browser->b, "%c ", folded_sign);
1436 ui_browser__printf(&browser->b, " ");
1441 int ret = fmt->color(fmt, &hpp, entry);
1442 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1444 * fmt->color() already used ui_browser to
1445 * print the non alignment bits, skip it (+ret):
1447 ui_browser__printf(&browser->b, "%s", s + ret);
1449 hist_entry__snprintf_alignment(entry, &hpp, fmt, fmt->entry(fmt, &hpp, entry));
1450 ui_browser__printf(&browser->b, "%s", s);
1452 width -= hpp.buf - s;
1455 /* The scroll bar isn't being used */
1456 if (!browser->b.navkeypressed)
1459 ui_browser__write_nstring(&browser->b, "", width);
1466 if (folded_sign == '-' && row != browser->b.rows) {
1467 struct callchain_print_arg arg = {
1468 .row_offset = row_offset,
1469 .is_current_entry = current_entry,
1472 if (entry->inline_node)
1473 printed += hist_browser__show_inline(browser,
1474 entry->inline_node, row, 0);
1476 printed += hist_browser__show_callchain(browser,
1478 hist_browser__show_callchain_entry,
1480 hist_browser__check_output_full);
1486 static int hist_browser__show_hierarchy_entry(struct hist_browser *browser,
1487 struct hist_entry *entry,
1492 int width = browser->b.width;
1493 char folded_sign = ' ';
1494 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1495 off_t row_offset = entry->row_offset;
1497 struct perf_hpp_fmt *fmt;
1498 struct perf_hpp_list_node *fmt_node;
1499 struct hpp_arg arg = {
1501 .current_entry = current_entry,
1504 int hierarchy_indent = (entry->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
1506 if (current_entry) {
1507 browser->he_selection = entry;
1508 browser->selection = &entry->ms;
1511 hist_entry__init_have_children(entry);
1512 folded_sign = hist_entry__folded(entry);
1513 arg.folded_sign = folded_sign;
1515 if (entry->leaf && row_offset) {
1517 goto show_callchain;
1520 hist_browser__gotorc(browser, row, 0);
1522 if (current_entry && browser->b.navkeypressed)
1523 ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1525 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1527 ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1528 width -= level * HIERARCHY_INDENT;
1530 /* the first hpp_list_node is for overhead columns */
1531 fmt_node = list_first_entry(&entry->hists->hpp_formats,
1532 struct perf_hpp_list_node, list);
1533 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1535 struct perf_hpp hpp = {
1541 if (perf_hpp__should_skip(fmt, entry->hists) ||
1542 column++ < browser->b.horiz_scroll)
1545 if (current_entry && browser->b.navkeypressed) {
1546 ui_browser__set_color(&browser->b,
1547 HE_COLORSET_SELECTED);
1549 ui_browser__set_color(&browser->b,
1550 HE_COLORSET_NORMAL);
1554 ui_browser__printf(&browser->b, "%c ", folded_sign);
1558 ui_browser__printf(&browser->b, " ");
1563 int ret = fmt->color(fmt, &hpp, entry);
1564 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1566 * fmt->color() already used ui_browser to
1567 * print the non alignment bits, skip it (+ret):
1569 ui_browser__printf(&browser->b, "%s", s + ret);
1571 int ret = fmt->entry(fmt, &hpp, entry);
1572 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1573 ui_browser__printf(&browser->b, "%s", s);
1575 width -= hpp.buf - s;
1579 ui_browser__write_nstring(&browser->b, "", hierarchy_indent);
1580 width -= hierarchy_indent;
1583 if (column >= browser->b.horiz_scroll) {
1585 struct perf_hpp hpp = {
1591 if (current_entry && browser->b.navkeypressed) {
1592 ui_browser__set_color(&browser->b,
1593 HE_COLORSET_SELECTED);
1595 ui_browser__set_color(&browser->b,
1596 HE_COLORSET_NORMAL);
1599 perf_hpp_list__for_each_format(entry->hpp_list, fmt) {
1601 ui_browser__printf(&browser->b, "%c ", folded_sign);
1604 ui_browser__write_nstring(&browser->b, "", 2);
1610 * No need to call hist_entry__snprintf_alignment()
1611 * since this fmt is always the last column in the
1615 width -= fmt->color(fmt, &hpp, entry);
1619 width -= fmt->entry(fmt, &hpp, entry);
1620 ui_browser__printf(&browser->b, "%s", ltrim(s));
1622 while (isspace(s[i++]))
1628 /* The scroll bar isn't being used */
1629 if (!browser->b.navkeypressed)
1632 ui_browser__write_nstring(&browser->b, "", width);
1638 if (entry->leaf && folded_sign == '-' && row != browser->b.rows) {
1639 struct callchain_print_arg carg = {
1640 .row_offset = row_offset,
1643 printed += hist_browser__show_callchain(browser, entry,
1645 hist_browser__show_callchain_entry, &carg,
1646 hist_browser__check_output_full);
1652 static int hist_browser__show_no_entry(struct hist_browser *browser,
1653 unsigned short row, int level)
1655 int width = browser->b.width;
1656 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1660 struct perf_hpp_fmt *fmt;
1661 struct perf_hpp_list_node *fmt_node;
1662 int indent = browser->hists->nr_hpp_node - 2;
1664 if (current_entry) {
1665 browser->he_selection = NULL;
1666 browser->selection = NULL;
1669 hist_browser__gotorc(browser, row, 0);
1671 if (current_entry && browser->b.navkeypressed)
1672 ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1674 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1676 ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1677 width -= level * HIERARCHY_INDENT;
1679 /* the first hpp_list_node is for overhead columns */
1680 fmt_node = list_first_entry(&browser->hists->hpp_formats,
1681 struct perf_hpp_list_node, list);
1682 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1683 if (perf_hpp__should_skip(fmt, browser->hists) ||
1684 column++ < browser->b.horiz_scroll)
1687 ret = fmt->width(fmt, NULL, browser->hists);
1690 /* for folded sign */
1694 /* space between columns */
1698 ui_browser__write_nstring(&browser->b, "", ret);
1702 ui_browser__write_nstring(&browser->b, "", indent * HIERARCHY_INDENT);
1703 width -= indent * HIERARCHY_INDENT;
1705 if (column >= browser->b.horiz_scroll) {
1708 ret = snprintf(buf, sizeof(buf), "no entry >= %.2f%%", browser->min_pcnt);
1709 ui_browser__printf(&browser->b, " %s", buf);
1713 /* The scroll bar isn't being used */
1714 if (!browser->b.navkeypressed)
1717 ui_browser__write_nstring(&browser->b, "", width);
1721 static int advance_hpp_check(struct perf_hpp *hpp, int inc)
1723 advance_hpp(hpp, inc);
1724 return hpp->size <= 0;
1728 hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf,
1729 size_t size, int line)
1731 struct hists *hists = browser->hists;
1732 struct perf_hpp dummy_hpp = {
1736 struct perf_hpp_fmt *fmt;
1741 if (symbol_conf.use_callchain) {
1742 ret = scnprintf(buf, size, " ");
1743 if (advance_hpp_check(&dummy_hpp, ret))
1747 hists__for_each_format(browser->hists, fmt) {
1748 if (perf_hpp__should_skip(fmt, hists) || column++ < browser->b.horiz_scroll)
1751 ret = fmt->header(fmt, &dummy_hpp, hists, line, &span);
1752 if (advance_hpp_check(&dummy_hpp, ret))
1758 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
1759 if (advance_hpp_check(&dummy_hpp, ret))
1766 static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser, char *buf, size_t size)
1768 struct hists *hists = browser->hists;
1769 struct perf_hpp dummy_hpp = {
1773 struct perf_hpp_fmt *fmt;
1774 struct perf_hpp_list_node *fmt_node;
1777 int indent = hists->nr_hpp_node - 2;
1778 bool first_node, first_col;
1780 ret = scnprintf(buf, size, " ");
1781 if (advance_hpp_check(&dummy_hpp, ret))
1785 /* the first hpp_list_node is for overhead columns */
1786 fmt_node = list_first_entry(&hists->hpp_formats,
1787 struct perf_hpp_list_node, list);
1788 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1789 if (column++ < browser->b.horiz_scroll)
1792 ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1793 if (advance_hpp_check(&dummy_hpp, ret))
1796 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
1797 if (advance_hpp_check(&dummy_hpp, ret))
1804 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "%*s",
1805 indent * HIERARCHY_INDENT, "");
1806 if (advance_hpp_check(&dummy_hpp, ret))
1811 list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
1813 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " / ");
1814 if (advance_hpp_check(&dummy_hpp, ret))
1820 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1823 if (perf_hpp__should_skip(fmt, hists))
1827 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "+");
1828 if (advance_hpp_check(&dummy_hpp, ret))
1833 ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1834 dummy_hpp.buf[ret] = '\0';
1836 start = trim(dummy_hpp.buf);
1837 ret = strlen(start);
1839 if (start != dummy_hpp.buf)
1840 memmove(dummy_hpp.buf, start, ret + 1);
1842 if (advance_hpp_check(&dummy_hpp, ret))
1850 static void hists_browser__hierarchy_headers(struct hist_browser *browser)
1854 hists_browser__scnprintf_hierarchy_headers(browser, headers,
1857 ui_browser__gotorc(&browser->b, 0, 0);
1858 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1859 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1862 static void hists_browser__headers(struct hist_browser *browser)
1864 struct hists *hists = browser->hists;
1865 struct perf_hpp_list *hpp_list = hists->hpp_list;
1869 for (line = 0; line < hpp_list->nr_header_lines; line++) {
1872 hists_browser__scnprintf_headers(browser, headers,
1873 sizeof(headers), line);
1875 ui_browser__gotorc(&browser->b, line, 0);
1876 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1877 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1881 static void hist_browser__show_headers(struct hist_browser *browser)
1883 if (symbol_conf.report_hierarchy)
1884 hists_browser__hierarchy_headers(browser);
1886 hists_browser__headers(browser);
1889 static void ui_browser__hists_init_top(struct ui_browser *browser)
1891 if (browser->top == NULL) {
1892 struct hist_browser *hb;
1894 hb = container_of(browser, struct hist_browser, b);
1895 browser->top = rb_first(&hb->hists->entries);
1899 static unsigned int hist_browser__refresh(struct ui_browser *browser)
1902 u16 header_offset = 0;
1904 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
1905 struct hists *hists = hb->hists;
1907 if (hb->show_headers) {
1908 struct perf_hpp_list *hpp_list = hists->hpp_list;
1910 hist_browser__show_headers(hb);
1911 header_offset = hpp_list->nr_header_lines;
1914 ui_browser__hists_init_top(browser);
1915 hb->he_selection = NULL;
1916 hb->selection = NULL;
1918 for (nd = browser->top; nd; nd = rb_hierarchy_next(nd)) {
1919 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1923 /* let it move to sibling */
1924 h->unfolded = false;
1928 percent = hist_entry__get_percent_limit(h);
1929 if (percent < hb->min_pcnt)
1932 if (symbol_conf.report_hierarchy) {
1933 row += hist_browser__show_hierarchy_entry(hb, h, row,
1935 if (row == browser->rows)
1938 if (h->has_no_entry) {
1939 hist_browser__show_no_entry(hb, row, h->depth + 1);
1943 row += hist_browser__show_entry(hb, h, row);
1946 if (row == browser->rows)
1950 return row + header_offset;
1953 static struct rb_node *hists__filter_entries(struct rb_node *nd,
1956 while (nd != NULL) {
1957 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1958 float percent = hist_entry__get_percent_limit(h);
1960 if (!h->filtered && percent >= min_pcnt)
1964 * If it's filtered, its all children also were filtered.
1965 * So move to sibling node.
1970 nd = rb_hierarchy_next(nd);
1976 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
1979 while (nd != NULL) {
1980 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1981 float percent = hist_entry__get_percent_limit(h);
1983 if (!h->filtered && percent >= min_pcnt)
1986 nd = rb_hierarchy_prev(nd);
1992 static void ui_browser__hists_seek(struct ui_browser *browser,
1993 off_t offset, int whence)
1995 struct hist_entry *h;
1998 struct hist_browser *hb;
2000 hb = container_of(browser, struct hist_browser, b);
2002 if (browser->nr_entries == 0)
2005 ui_browser__hists_init_top(browser);
2009 nd = hists__filter_entries(rb_first(browser->entries),
2016 nd = rb_hierarchy_last(rb_last(browser->entries));
2017 nd = hists__filter_prev_entries(nd, hb->min_pcnt);
2025 * Moves not relative to the first visible entry invalidates its
2028 h = rb_entry(browser->top, struct hist_entry, rb_node);
2032 * Here we have to check if nd is expanded (+), if it is we can't go
2033 * the next top level hist_entry, instead we must compute an offset of
2034 * what _not_ to show and not change the first visible entry.
2036 * This offset increments when we are going from top to bottom and
2037 * decreases when we're going from bottom to top.
2039 * As we don't have backpointers to the top level in the callchains
2040 * structure, we need to always print the whole hist_entry callchain,
2041 * skipping the first ones that are before the first visible entry
2042 * and stop when we printed enough lines to fill the screen.
2050 h = rb_entry(nd, struct hist_entry, rb_node);
2051 if (h->unfolded && h->leaf) {
2052 u16 remaining = h->nr_rows - h->row_offset;
2053 if (offset > remaining) {
2054 offset -= remaining;
2057 h->row_offset += offset;
2063 nd = hists__filter_entries(rb_hierarchy_next(nd),
2069 } while (offset != 0);
2070 } else if (offset < 0) {
2072 h = rb_entry(nd, struct hist_entry, rb_node);
2073 if (h->unfolded && h->leaf) {
2075 if (-offset > h->row_offset) {
2076 offset += h->row_offset;
2079 h->row_offset += offset;
2085 if (-offset > h->nr_rows) {
2086 offset += h->nr_rows;
2089 h->row_offset = h->nr_rows + offset;
2097 nd = hists__filter_prev_entries(rb_hierarchy_prev(nd),
2105 * Last unfiltered hist_entry, check if it is
2106 * unfolded, if it is then we should have
2107 * row_offset at its last entry.
2109 h = rb_entry(nd, struct hist_entry, rb_node);
2110 if (h->unfolded && h->leaf)
2111 h->row_offset = h->nr_rows;
2118 h = rb_entry(nd, struct hist_entry, rb_node);
2123 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
2124 struct hist_entry *he, FILE *fp,
2127 struct callchain_print_arg arg = {
2131 hist_browser__show_callchain(browser, he, level, 0,
2132 hist_browser__fprintf_callchain_entry, &arg,
2133 hist_browser__check_dump_full);
2137 static int hist_browser__fprintf_entry(struct hist_browser *browser,
2138 struct hist_entry *he, FILE *fp)
2142 char folded_sign = ' ';
2143 struct perf_hpp hpp = {
2147 struct perf_hpp_fmt *fmt;
2151 if (symbol_conf.use_callchain) {
2152 folded_sign = hist_entry__folded(he);
2153 printed += fprintf(fp, "%c ", folded_sign);
2156 hists__for_each_format(browser->hists, fmt) {
2157 if (perf_hpp__should_skip(fmt, he->hists))
2161 ret = scnprintf(hpp.buf, hpp.size, " ");
2162 advance_hpp(&hpp, ret);
2166 ret = fmt->entry(fmt, &hpp, he);
2167 ret = hist_entry__snprintf_alignment(he, &hpp, fmt, ret);
2168 advance_hpp(&hpp, ret);
2170 printed += fprintf(fp, "%s\n", s);
2172 if (folded_sign == '-')
2173 printed += hist_browser__fprintf_callchain(browser, he, fp, 1);
2179 static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,
2180 struct hist_entry *he,
2181 FILE *fp, int level)
2185 char folded_sign = ' ';
2186 struct perf_hpp hpp = {
2190 struct perf_hpp_fmt *fmt;
2191 struct perf_hpp_list_node *fmt_node;
2194 int hierarchy_indent = (he->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
2196 printed = fprintf(fp, "%*s", level * HIERARCHY_INDENT, "");
2198 folded_sign = hist_entry__folded(he);
2199 printed += fprintf(fp, "%c", folded_sign);
2201 /* the first hpp_list_node is for overhead columns */
2202 fmt_node = list_first_entry(&he->hists->hpp_formats,
2203 struct perf_hpp_list_node, list);
2204 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
2206 ret = scnprintf(hpp.buf, hpp.size, " ");
2207 advance_hpp(&hpp, ret);
2211 ret = fmt->entry(fmt, &hpp, he);
2212 advance_hpp(&hpp, ret);
2215 ret = scnprintf(hpp.buf, hpp.size, "%*s", hierarchy_indent, "");
2216 advance_hpp(&hpp, ret);
2218 perf_hpp_list__for_each_format(he->hpp_list, fmt) {
2219 ret = scnprintf(hpp.buf, hpp.size, " ");
2220 advance_hpp(&hpp, ret);
2222 ret = fmt->entry(fmt, &hpp, he);
2223 advance_hpp(&hpp, ret);
2226 printed += fprintf(fp, "%s\n", rtrim(s));
2228 if (he->leaf && folded_sign == '-') {
2229 printed += hist_browser__fprintf_callchain(browser, he, fp,
2236 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
2238 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
2243 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
2245 if (symbol_conf.report_hierarchy) {
2246 printed += hist_browser__fprintf_hierarchy_entry(browser,
2250 printed += hist_browser__fprintf_entry(browser, h, fp);
2253 nd = hists__filter_entries(rb_hierarchy_next(nd),
2260 static int hist_browser__dump(struct hist_browser *browser)
2266 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
2267 if (access(filename, F_OK))
2270 * XXX: Just an arbitrary lazy upper limit
2272 if (++browser->print_seq == 8192) {
2273 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
2278 fp = fopen(filename, "w");
2281 const char *err = str_error_r(errno, bf, sizeof(bf));
2282 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
2286 ++browser->print_seq;
2287 hist_browser__fprintf(browser, fp);
2289 ui_helpline__fpush("%s written!", filename);
2294 void hist_browser__init(struct hist_browser *browser,
2295 struct hists *hists)
2297 struct perf_hpp_fmt *fmt;
2299 browser->hists = hists;
2300 browser->b.refresh = hist_browser__refresh;
2301 browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
2302 browser->b.seek = ui_browser__hists_seek;
2303 browser->b.use_navkeypressed = true;
2304 browser->show_headers = symbol_conf.show_hist_headers;
2306 if (symbol_conf.report_hierarchy) {
2307 struct perf_hpp_list_node *fmt_node;
2309 /* count overhead columns (in the first node) */
2310 fmt_node = list_first_entry(&hists->hpp_formats,
2311 struct perf_hpp_list_node, list);
2312 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt)
2313 ++browser->b.columns;
2315 /* add a single column for whole hierarchy sort keys*/
2316 ++browser->b.columns;
2318 hists__for_each_format(hists, fmt)
2319 ++browser->b.columns;
2322 hists__reset_column_width(hists);
2325 struct hist_browser *hist_browser__new(struct hists *hists)
2327 struct hist_browser *browser = zalloc(sizeof(*browser));
2330 hist_browser__init(browser, hists);
2335 static struct hist_browser *
2336 perf_evsel_browser__new(struct perf_evsel *evsel,
2337 struct hist_browser_timer *hbt,
2338 struct perf_env *env)
2340 struct hist_browser *browser = hist_browser__new(evsel__hists(evsel));
2345 browser->title = perf_evsel_browser_title;
2350 void hist_browser__delete(struct hist_browser *browser)
2355 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
2357 return browser->he_selection;
2360 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
2362 return browser->he_selection->thread;
2365 /* Check whether the browser is for 'top' or 'report' */
2366 static inline bool is_report_browser(void *timer)
2368 return timer == NULL;
2371 static int perf_evsel_browser_title(struct hist_browser *browser,
2372 char *bf, size_t size)
2374 struct hist_browser_timer *hbt = browser->hbt;
2375 struct hists *hists = browser->hists;
2378 const struct dso *dso = hists->dso_filter;
2379 const struct thread *thread = hists->thread_filter;
2380 int socket_id = hists->socket_filter;
2381 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
2382 u64 nr_events = hists->stats.total_period;
2383 struct perf_evsel *evsel = hists_to_evsel(hists);
2384 const char *ev_name = perf_evsel__name(evsel);
2386 size_t buflen = sizeof(buf);
2387 char ref[30] = " show reference callgraph, ";
2388 bool enable_ref = false;
2390 if (symbol_conf.filter_relative) {
2391 nr_samples = hists->stats.nr_non_filtered_samples;
2392 nr_events = hists->stats.total_non_filtered_period;
2395 if (perf_evsel__is_group_event(evsel)) {
2396 struct perf_evsel *pos;
2398 perf_evsel__group_desc(evsel, buf, buflen);
2401 for_each_group_member(pos, evsel) {
2402 struct hists *pos_hists = evsel__hists(pos);
2404 if (symbol_conf.filter_relative) {
2405 nr_samples += pos_hists->stats.nr_non_filtered_samples;
2406 nr_events += pos_hists->stats.total_non_filtered_period;
2408 nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
2409 nr_events += pos_hists->stats.total_period;
2414 if (symbol_conf.show_ref_callgraph &&
2415 strstr(ev_name, "call-graph=no"))
2417 nr_samples = convert_unit(nr_samples, &unit);
2418 printed = scnprintf(bf, size,
2419 "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64,
2420 nr_samples, unit, ev_name, enable_ref ? ref : " ", nr_events);
2423 if (hists->uid_filter_str)
2424 printed += snprintf(bf + printed, size - printed,
2425 ", UID: %s", hists->uid_filter_str);
2427 if (hists__has(hists, thread)) {
2428 printed += scnprintf(bf + printed, size - printed,
2430 (thread->comm_set ? thread__comm_str(thread) : ""),
2433 printed += scnprintf(bf + printed, size - printed,
2435 (thread->comm_set ? thread__comm_str(thread) : ""));
2439 printed += scnprintf(bf + printed, size - printed,
2440 ", DSO: %s", dso->short_name);
2442 printed += scnprintf(bf + printed, size - printed,
2443 ", Processor Socket: %d", socket_id);
2444 if (!is_report_browser(hbt)) {
2445 struct perf_top *top = hbt->arg;
2448 printed += scnprintf(bf + printed, size - printed, " [z]");
2454 static inline void free_popup_options(char **options, int n)
2458 for (i = 0; i < n; ++i)
2463 * Only runtime switching of perf data file will make "input_name" point
2464 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
2465 * whether we need to call free() for current "input_name" during the switch.
2467 static bool is_input_name_malloced = false;
2469 static int switch_data_file(void)
2471 char *pwd, *options[32], *abs_path[32], *tmp;
2473 int nr_options = 0, choice = -1, ret = -1;
2474 struct dirent *dent;
2476 pwd = getenv("PWD");
2480 pwd_dir = opendir(pwd);
2484 memset(options, 0, sizeof(options));
2485 memset(abs_path, 0, sizeof(abs_path));
2487 while ((dent = readdir(pwd_dir))) {
2488 char path[PATH_MAX];
2490 char *name = dent->d_name;
2493 if (!(dent->d_type == DT_REG))
2496 snprintf(path, sizeof(path), "%s/%s", pwd, name);
2498 file = fopen(path, "r");
2502 if (fread(&magic, 1, 8, file) < 8)
2503 goto close_file_and_continue;
2505 if (is_perf_magic(magic)) {
2506 options[nr_options] = strdup(name);
2507 if (!options[nr_options])
2508 goto close_file_and_continue;
2510 abs_path[nr_options] = strdup(path);
2511 if (!abs_path[nr_options]) {
2512 zfree(&options[nr_options]);
2513 ui__warning("Can't search all data files due to memory shortage.\n");
2521 close_file_and_continue:
2523 if (nr_options >= 32) {
2524 ui__warning("Too many perf data files in PWD!\n"
2525 "Only the first 32 files will be listed.\n");
2532 choice = ui__popup_menu(nr_options, options);
2533 if (choice < nr_options && choice >= 0) {
2534 tmp = strdup(abs_path[choice]);
2536 if (is_input_name_malloced)
2537 free((void *)input_name);
2539 is_input_name_malloced = true;
2542 ui__warning("Data switch failed due to memory shortage!\n");
2546 free_popup_options(options, nr_options);
2547 free_popup_options(abs_path, nr_options);
2551 struct popup_action {
2552 struct thread *thread;
2553 struct map_symbol ms;
2556 int (*fn)(struct hist_browser *browser, struct popup_action *act);
2560 do_annotate(struct hist_browser *browser, struct popup_action *act)
2562 struct perf_evsel *evsel;
2563 struct annotation *notes;
2564 struct hist_entry *he;
2567 if (!objdump_path && perf_env__lookup_objdump(browser->env))
2570 notes = symbol__annotation(act->ms.sym);
2574 evsel = hists_to_evsel(browser->hists);
2575 err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
2576 he = hist_browser__selected_entry(browser);
2578 * offer option to annotate the other branch source or target
2579 * (if they exists) when returning from annotate
2581 if ((err == 'q' || err == CTRL('c')) && he->branch_info)
2584 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
2586 ui_browser__handle_resize(&browser->b);
2591 add_annotate_opt(struct hist_browser *browser __maybe_unused,
2592 struct popup_action *act, char **optstr,
2593 struct map *map, struct symbol *sym)
2595 if (sym == NULL || map->dso->annotate_warned)
2598 if (asprintf(optstr, "Annotate %s", sym->name) < 0)
2603 act->fn = do_annotate;
2608 do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
2610 struct thread *thread = act->thread;
2612 if ((!hists__has(browser->hists, thread) &&
2613 !hists__has(browser->hists, comm)) || thread == NULL)
2616 if (browser->hists->thread_filter) {
2617 pstack__remove(browser->pstack, &browser->hists->thread_filter);
2618 perf_hpp__set_elide(HISTC_THREAD, false);
2619 thread__zput(browser->hists->thread_filter);
2622 if (hists__has(browser->hists, thread)) {
2623 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
2624 thread->comm_set ? thread__comm_str(thread) : "",
2627 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
2628 thread->comm_set ? thread__comm_str(thread) : "");
2631 browser->hists->thread_filter = thread__get(thread);
2632 perf_hpp__set_elide(HISTC_THREAD, false);
2633 pstack__push(browser->pstack, &browser->hists->thread_filter);
2636 hists__filter_by_thread(browser->hists);
2637 hist_browser__reset(browser);
2642 add_thread_opt(struct hist_browser *browser, struct popup_action *act,
2643 char **optstr, struct thread *thread)
2647 if ((!hists__has(browser->hists, thread) &&
2648 !hists__has(browser->hists, comm)) || thread == NULL)
2651 if (hists__has(browser->hists, thread)) {
2652 ret = asprintf(optstr, "Zoom %s %s(%d) thread",
2653 browser->hists->thread_filter ? "out of" : "into",
2654 thread->comm_set ? thread__comm_str(thread) : "",
2657 ret = asprintf(optstr, "Zoom %s %s thread",
2658 browser->hists->thread_filter ? "out of" : "into",
2659 thread->comm_set ? thread__comm_str(thread) : "");
2664 act->thread = thread;
2665 act->fn = do_zoom_thread;
2670 do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
2672 struct map *map = act->ms.map;
2674 if (!hists__has(browser->hists, dso) || map == NULL)
2677 if (browser->hists->dso_filter) {
2678 pstack__remove(browser->pstack, &browser->hists->dso_filter);
2679 perf_hpp__set_elide(HISTC_DSO, false);
2680 browser->hists->dso_filter = NULL;
2683 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
2684 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
2685 browser->hists->dso_filter = map->dso;
2686 perf_hpp__set_elide(HISTC_DSO, true);
2687 pstack__push(browser->pstack, &browser->hists->dso_filter);
2690 hists__filter_by_dso(browser->hists);
2691 hist_browser__reset(browser);
2696 add_dso_opt(struct hist_browser *browser, struct popup_action *act,
2697 char **optstr, struct map *map)
2699 if (!hists__has(browser->hists, dso) || map == NULL)
2702 if (asprintf(optstr, "Zoom %s %s DSO",
2703 browser->hists->dso_filter ? "out of" : "into",
2704 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
2708 act->fn = do_zoom_dso;
2713 do_browse_map(struct hist_browser *browser __maybe_unused,
2714 struct popup_action *act)
2716 map__browse(act->ms.map);
2721 add_map_opt(struct hist_browser *browser,
2722 struct popup_action *act, char **optstr, struct map *map)
2724 if (!hists__has(browser->hists, dso) || map == NULL)
2727 if (asprintf(optstr, "Browse map details") < 0)
2731 act->fn = do_browse_map;
2736 do_run_script(struct hist_browser *browser __maybe_unused,
2737 struct popup_action *act)
2739 char script_opt[64];
2740 memset(script_opt, 0, sizeof(script_opt));
2743 scnprintf(script_opt, sizeof(script_opt), " -c %s ",
2744 thread__comm_str(act->thread));
2745 } else if (act->ms.sym) {
2746 scnprintf(script_opt, sizeof(script_opt), " -S %s ",
2750 script_browse(script_opt);
2755 add_script_opt(struct hist_browser *browser __maybe_unused,
2756 struct popup_action *act, char **optstr,
2757 struct thread *thread, struct symbol *sym)
2760 if (asprintf(optstr, "Run scripts for samples of thread [%s]",
2761 thread__comm_str(thread)) < 0)
2764 if (asprintf(optstr, "Run scripts for samples of symbol [%s]",
2768 if (asprintf(optstr, "Run scripts for all samples") < 0)
2772 act->thread = thread;
2774 act->fn = do_run_script;
2779 do_switch_data(struct hist_browser *browser __maybe_unused,
2780 struct popup_action *act __maybe_unused)
2782 if (switch_data_file()) {
2783 ui__warning("Won't switch the data files due to\n"
2784 "no valid data file get selected!\n");
2788 return K_SWITCH_INPUT_DATA;
2792 add_switch_opt(struct hist_browser *browser,
2793 struct popup_action *act, char **optstr)
2795 if (!is_report_browser(browser->hbt))
2798 if (asprintf(optstr, "Switch to another data file in PWD") < 0)
2801 act->fn = do_switch_data;
2806 do_exit_browser(struct hist_browser *browser __maybe_unused,
2807 struct popup_action *act __maybe_unused)
2813 add_exit_opt(struct hist_browser *browser __maybe_unused,
2814 struct popup_action *act, char **optstr)
2816 if (asprintf(optstr, "Exit") < 0)
2819 act->fn = do_exit_browser;
2824 do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
2826 if (!hists__has(browser->hists, socket) || act->socket < 0)
2829 if (browser->hists->socket_filter > -1) {
2830 pstack__remove(browser->pstack, &browser->hists->socket_filter);
2831 browser->hists->socket_filter = -1;
2832 perf_hpp__set_elide(HISTC_SOCKET, false);
2834 browser->hists->socket_filter = act->socket;
2835 perf_hpp__set_elide(HISTC_SOCKET, true);
2836 pstack__push(browser->pstack, &browser->hists->socket_filter);
2839 hists__filter_by_socket(browser->hists);
2840 hist_browser__reset(browser);
2845 add_socket_opt(struct hist_browser *browser, struct popup_action *act,
2846 char **optstr, int socket_id)
2848 if (!hists__has(browser->hists, socket) || socket_id < 0)
2851 if (asprintf(optstr, "Zoom %s Processor Socket %d",
2852 (browser->hists->socket_filter > -1) ? "out of" : "into",
2856 act->socket = socket_id;
2857 act->fn = do_zoom_socket;
2861 static void hist_browser__update_nr_entries(struct hist_browser *hb)
2864 struct rb_node *nd = rb_first(&hb->hists->entries);
2866 if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
2867 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
2871 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2873 nd = rb_hierarchy_next(nd);
2876 hb->nr_non_filtered_entries = nr_entries;
2877 hb->nr_hierarchy_entries = nr_entries;
2880 static void hist_browser__update_percent_limit(struct hist_browser *hb,
2883 struct hist_entry *he;
2884 struct rb_node *nd = rb_first(&hb->hists->entries);
2885 u64 total = hists__total_period(hb->hists);
2886 u64 min_callchain_hits = total * (percent / 100);
2888 hb->min_pcnt = callchain_param.min_percent = percent;
2890 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2891 he = rb_entry(nd, struct hist_entry, rb_node);
2893 if (he->has_no_entry) {
2894 he->has_no_entry = false;
2898 if (!he->leaf || !symbol_conf.use_callchain)
2901 if (callchain_param.mode == CHAIN_GRAPH_REL) {
2902 total = he->stat.period;
2904 if (symbol_conf.cumulate_callchain)
2905 total = he->stat_acc->period;
2907 min_callchain_hits = total * (percent / 100);
2910 callchain_param.sort(&he->sorted_chain, he->callchain,
2911 min_callchain_hits, &callchain_param);
2914 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
2916 /* force to re-evaluate folding state of callchains */
2917 he->init_have_children = false;
2918 hist_entry__set_folding(he, hb, false);
2922 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
2923 const char *helpline,
2925 struct hist_browser_timer *hbt,
2927 struct perf_env *env)
2929 struct hists *hists = evsel__hists(evsel);
2930 struct hist_browser *browser = perf_evsel_browser__new(evsel, hbt, env);
2931 struct branch_info *bi;
2932 #define MAX_OPTIONS 16
2933 char *options[MAX_OPTIONS];
2934 struct popup_action actions[MAX_OPTIONS];
2938 int delay_secs = hbt ? hbt->refresh : 0;
2940 #define HIST_BROWSER_HELP_COMMON \
2941 "h/?/F1 Show this window\n" \
2943 "PGDN/SPACE Navigate\n" \
2944 "q/ESC/CTRL+C Exit browser\n\n" \
2945 "For multiple event sessions:\n\n" \
2946 "TAB/UNTAB Switch events\n\n" \
2947 "For symbolic views (--sort has sym):\n\n" \
2948 "ENTER Zoom into DSO/Threads & Annotate current symbol\n" \
2950 "a Annotate current symbol\n" \
2951 "C Collapse all callchains\n" \
2952 "d Zoom into current DSO\n" \
2953 "E Expand all callchains\n" \
2954 "F Toggle percentage of filtered entries\n" \
2955 "H Display column headers\n" \
2956 "L Change percent limit\n" \
2957 "m Display context menu\n" \
2958 "S Zoom into current Processor Socket\n" \
2960 /* help messages are sorted by lexical order of the hotkey */
2961 const char report_help[] = HIST_BROWSER_HELP_COMMON
2962 "i Show header information\n"
2963 "P Print histograms to perf.hist.N\n"
2964 "r Run available scripts\n"
2965 "s Switch to another data file in PWD\n"
2966 "t Zoom into current Thread\n"
2967 "V Verbose (DSO names in callchains, etc)\n"
2968 "/ Filter symbol by name";
2969 const char top_help[] = HIST_BROWSER_HELP_COMMON
2970 "P Print histograms to perf.hist.N\n"
2971 "t Zoom into current Thread\n"
2972 "V Verbose (DSO names in callchains, etc)\n"
2973 "z Toggle zeroing of samples\n"
2974 "f Enable/Disable events\n"
2975 "/ Filter symbol by name";
2977 if (browser == NULL)
2980 /* reset abort key so that it can get Ctrl-C as a key */
2982 SLang_init_tty(0, 0, 0);
2985 browser->min_pcnt = min_pcnt;
2986 hist_browser__update_nr_entries(browser);
2988 browser->pstack = pstack__new(3);
2989 if (browser->pstack == NULL)
2992 ui_helpline__push(helpline);
2994 memset(options, 0, sizeof(options));
2995 memset(actions, 0, sizeof(actions));
2997 if (symbol_conf.col_width_list_str)
2998 perf_hpp__set_user_width(symbol_conf.col_width_list_str);
3001 struct thread *thread = NULL;
3002 struct map *map = NULL;
3008 key = hist_browser__run(browser, helpline);
3010 if (browser->he_selection != NULL) {
3011 thread = hist_browser__selected_thread(browser);
3012 map = browser->selection->map;
3013 socked_id = browser->he_selection->socket;
3021 * Exit the browser, let hists__browser_tree
3022 * go to the next or previous
3024 goto out_free_stack;
3026 if (!hists__has(hists, sym)) {
3027 ui_browser__warning(&browser->b, delay_secs * 2,
3028 "Annotation is only available for symbolic views, "
3029 "include \"sym*\" in --sort to use it.");
3033 if (browser->selection == NULL ||
3034 browser->selection->sym == NULL ||
3035 browser->selection->map->dso->annotate_warned)
3038 actions->ms.map = browser->selection->map;
3039 actions->ms.sym = browser->selection->sym;
3040 do_annotate(browser, actions);
3043 hist_browser__dump(browser);
3046 actions->ms.map = map;
3047 do_zoom_dso(browser, actions);
3050 verbose = (verbose + 1) % 4;
3051 browser->show_dso = verbose > 0;
3052 ui_helpline__fpush("Verbosity level set to %d\n",
3056 actions->thread = thread;
3057 do_zoom_thread(browser, actions);
3060 actions->socket = socked_id;
3061 do_zoom_socket(browser, actions);
3064 if (ui_browser__input_window("Symbol to show",
3065 "Please enter the name of symbol you want to see.\n"
3066 "To remove the filter later, press / + ENTER.",
3067 buf, "ENTER: OK, ESC: Cancel",
3068 delay_secs * 2) == K_ENTER) {
3069 hists->symbol_filter_str = *buf ? buf : NULL;
3070 hists__filter_by_symbol(hists);
3071 hist_browser__reset(browser);
3075 if (is_report_browser(hbt)) {
3076 actions->thread = NULL;
3077 actions->ms.sym = NULL;
3078 do_run_script(browser, actions);
3082 if (is_report_browser(hbt)) {
3083 key = do_switch_data(browser, actions);
3084 if (key == K_SWITCH_INPUT_DATA)
3085 goto out_free_stack;
3089 /* env->arch is NULL for live-mode (i.e. perf top) */
3091 tui__header_window(env);
3094 symbol_conf.filter_relative ^= 1;
3097 if (!is_report_browser(hbt)) {
3098 struct perf_top *top = hbt->arg;
3100 top->zero = !top->zero;
3104 if (ui_browser__input_window("Percent Limit",
3105 "Please enter the value you want to hide entries under that percent.",
3106 buf, "ENTER: OK, ESC: Cancel",
3107 delay_secs * 2) == K_ENTER) {
3109 double new_percent = strtod(buf, &end);
3111 if (new_percent < 0 || new_percent > 100) {
3112 ui_browser__warning(&browser->b, delay_secs * 2,
3113 "Invalid percent: %.2f", new_percent);
3117 hist_browser__update_percent_limit(browser, new_percent);
3118 hist_browser__reset(browser);
3124 ui_browser__help_window(&browser->b,
3125 is_report_browser(hbt) ? report_help : top_help);
3136 if (pstack__empty(browser->pstack)) {
3138 * Go back to the perf_evsel_menu__run or other user
3141 goto out_free_stack;
3144 ui_browser__dialog_yesno(&browser->b,
3145 "Do you really want to exit?"))
3146 goto out_free_stack;
3150 top = pstack__peek(browser->pstack);
3151 if (top == &browser->hists->dso_filter) {
3153 * No need to set actions->dso here since
3154 * it's just to remove the current filter.
3155 * Ditto for thread below.
3157 do_zoom_dso(browser, actions);
3158 } else if (top == &browser->hists->thread_filter) {
3159 do_zoom_thread(browser, actions);
3160 } else if (top == &browser->hists->socket_filter) {
3161 do_zoom_socket(browser, actions);
3167 goto out_free_stack;
3169 if (!is_report_browser(hbt)) {
3170 struct perf_top *top = hbt->arg;
3172 perf_evlist__toggle_enable(top->evlist);
3174 * No need to refresh, resort/decay histogram
3175 * entries if we are not collecting samples:
3177 if (top->evlist->enabled) {
3178 helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
3179 hbt->refresh = delay_secs;
3181 helpline = "Press 'f' again to re-enable the events";
3188 helpline = "Press '?' for help on key bindings";
3192 if (!hists__has(hists, sym) || browser->selection == NULL)
3193 goto skip_annotation;
3195 if (sort__mode == SORT_MODE__BRANCH) {
3196 bi = browser->he_selection->branch_info;
3199 goto skip_annotation;
3201 nr_options += add_annotate_opt(browser,
3202 &actions[nr_options],
3203 &options[nr_options],
3206 if (bi->to.sym != bi->from.sym)
3207 nr_options += add_annotate_opt(browser,
3208 &actions[nr_options],
3209 &options[nr_options],
3213 nr_options += add_annotate_opt(browser,
3214 &actions[nr_options],
3215 &options[nr_options],
3216 browser->selection->map,
3217 browser->selection->sym);
3220 nr_options += add_thread_opt(browser, &actions[nr_options],
3221 &options[nr_options], thread);
3222 nr_options += add_dso_opt(browser, &actions[nr_options],
3223 &options[nr_options], map);
3224 nr_options += add_map_opt(browser, &actions[nr_options],
3225 &options[nr_options],
3226 browser->selection ?
3227 browser->selection->map : NULL);
3228 nr_options += add_socket_opt(browser, &actions[nr_options],
3229 &options[nr_options],
3231 /* perf script support */
3232 if (!is_report_browser(hbt))
3233 goto skip_scripting;
3235 if (browser->he_selection) {
3236 if (hists__has(hists, thread) && thread) {
3237 nr_options += add_script_opt(browser,
3238 &actions[nr_options],
3239 &options[nr_options],
3243 * Note that browser->selection != NULL
3244 * when browser->he_selection is not NULL,
3245 * so we don't need to check browser->selection
3246 * before fetching browser->selection->sym like what
3247 * we do before fetching browser->selection->map.
3249 * See hist_browser__show_entry.
3251 if (hists__has(hists, sym) && browser->selection->sym) {
3252 nr_options += add_script_opt(browser,
3253 &actions[nr_options],
3254 &options[nr_options],
3255 NULL, browser->selection->sym);
3258 nr_options += add_script_opt(browser, &actions[nr_options],
3259 &options[nr_options], NULL, NULL);
3260 nr_options += add_switch_opt(browser, &actions[nr_options],
3261 &options[nr_options]);
3263 nr_options += add_exit_opt(browser, &actions[nr_options],
3264 &options[nr_options]);
3267 struct popup_action *act;
3269 choice = ui__popup_menu(nr_options, options);
3270 if (choice == -1 || choice >= nr_options)
3273 act = &actions[choice];
3274 key = act->fn(browser, act);
3277 if (key == K_SWITCH_INPUT_DATA)
3281 pstack__delete(browser->pstack);
3283 hist_browser__delete(browser);
3284 free_popup_options(options, MAX_OPTIONS);
3288 struct perf_evsel_menu {
3289 struct ui_browser b;
3290 struct perf_evsel *selection;
3291 bool lost_events, lost_events_warned;
3293 struct perf_env *env;
3296 static void perf_evsel_menu__write(struct ui_browser *browser,
3297 void *entry, int row)
3299 struct perf_evsel_menu *menu = container_of(browser,
3300 struct perf_evsel_menu, b);
3301 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
3302 struct hists *hists = evsel__hists(evsel);
3303 bool current_entry = ui_browser__is_current_entry(browser, row);
3304 unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
3305 const char *ev_name = perf_evsel__name(evsel);
3307 const char *warn = " ";
3310 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
3311 HE_COLORSET_NORMAL);
3313 if (perf_evsel__is_group_event(evsel)) {
3314 struct perf_evsel *pos;
3316 ev_name = perf_evsel__group_name(evsel);
3318 for_each_group_member(pos, evsel) {
3319 struct hists *pos_hists = evsel__hists(pos);
3320 nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
3324 nr_events = convert_unit(nr_events, &unit);
3325 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
3326 unit, unit == ' ' ? "" : " ", ev_name);
3327 ui_browser__printf(browser, "%s", bf);
3329 nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
3330 if (nr_events != 0) {
3331 menu->lost_events = true;
3333 ui_browser__set_color(browser, HE_COLORSET_TOP);
3334 nr_events = convert_unit(nr_events, &unit);
3335 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
3336 nr_events, unit, unit == ' ' ? "" : " ");
3340 ui_browser__write_nstring(browser, warn, browser->width - printed);
3343 menu->selection = evsel;
3346 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
3347 int nr_events, const char *help,
3348 struct hist_browser_timer *hbt)
3350 struct perf_evlist *evlist = menu->b.priv;
3351 struct perf_evsel *pos;
3352 const char *title = "Available samples";
3353 int delay_secs = hbt ? hbt->refresh : 0;
3356 if (ui_browser__show(&menu->b, title,
3357 "ESC: exit, ENTER|->: Browse histograms") < 0)
3361 key = ui_browser__run(&menu->b, delay_secs);
3365 hbt->timer(hbt->arg);
3367 if (!menu->lost_events_warned && menu->lost_events) {
3368 ui_browser__warn_lost_events(&menu->b);
3369 menu->lost_events_warned = true;
3374 if (!menu->selection)
3376 pos = menu->selection;
3378 perf_evlist__set_selected(evlist, pos);
3380 * Give the calling tool a chance to populate the non
3381 * default evsel resorted hists tree.
3384 hbt->timer(hbt->arg);
3385 key = perf_evsel__hists_browse(pos, nr_events, help,
3389 ui_browser__show_title(&menu->b, title);
3392 if (pos->node.next == &evlist->entries)
3393 pos = perf_evlist__first(evlist);
3395 pos = perf_evsel__next(pos);
3398 if (pos->node.prev == &evlist->entries)
3399 pos = perf_evlist__last(evlist);
3401 pos = perf_evsel__prev(pos);
3403 case K_SWITCH_INPUT_DATA:
3414 if (!ui_browser__dialog_yesno(&menu->b,
3415 "Do you really want to exit?"))
3427 ui_browser__hide(&menu->b);
3431 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
3434 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
3436 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
3442 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
3443 int nr_entries, const char *help,
3444 struct hist_browser_timer *hbt,
3446 struct perf_env *env)
3448 struct perf_evsel *pos;
3449 struct perf_evsel_menu menu = {
3451 .entries = &evlist->entries,
3452 .refresh = ui_browser__list_head_refresh,
3453 .seek = ui_browser__list_head_seek,
3454 .write = perf_evsel_menu__write,
3455 .filter = filter_group_entries,
3456 .nr_entries = nr_entries,
3459 .min_pcnt = min_pcnt,
3463 ui_helpline__push("Press ESC to exit");
3465 evlist__for_each_entry(evlist, pos) {
3466 const char *ev_name = perf_evsel__name(pos);
3467 size_t line_len = strlen(ev_name) + 7;
3469 if (menu.b.width < line_len)
3470 menu.b.width = line_len;
3473 return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
3476 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
3477 struct hist_browser_timer *hbt,
3479 struct perf_env *env)
3481 int nr_entries = evlist->nr_entries;
3484 if (nr_entries == 1) {
3485 struct perf_evsel *first = perf_evlist__first(evlist);
3487 return perf_evsel__hists_browse(first, nr_entries, help,
3488 false, hbt, min_pcnt,
3492 if (symbol_conf.event_group) {
3493 struct perf_evsel *pos;
3496 evlist__for_each_entry(evlist, pos) {
3497 if (perf_evsel__is_group_leader(pos))
3501 if (nr_entries == 1)
3505 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
3506 hbt, min_pcnt, env);