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"
28 #include "sane_ctype.h"
30 extern void hist_browser__init_hpp(void);
32 static int perf_evsel_browser_title(struct hist_browser *browser,
33 char *bf, size_t size);
34 static void hist_browser__update_nr_entries(struct hist_browser *hb);
36 static struct rb_node *hists__filter_entries(struct rb_node *nd,
39 static bool hist_browser__has_filter(struct hist_browser *hb)
41 return hists__has_filter(hb->hists) || hb->min_pcnt || symbol_conf.has_filter || hb->c2c_filter;
44 static int hist_browser__get_folding(struct hist_browser *browser)
47 struct hists *hists = browser->hists;
48 int unfolded_rows = 0;
50 for (nd = rb_first(&hists->entries);
51 (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
52 nd = rb_hierarchy_next(nd)) {
53 struct hist_entry *he =
54 rb_entry(nd, struct hist_entry, rb_node);
56 if (he->leaf && he->unfolded)
57 unfolded_rows += he->nr_rows;
62 static u32 hist_browser__nr_entries(struct hist_browser *hb)
66 if (symbol_conf.report_hierarchy)
67 nr_entries = hb->nr_hierarchy_entries;
68 else if (hist_browser__has_filter(hb))
69 nr_entries = hb->nr_non_filtered_entries;
71 nr_entries = hb->hists->nr_entries;
73 hb->nr_callchain_rows = hist_browser__get_folding(hb);
74 return nr_entries + hb->nr_callchain_rows;
77 static void hist_browser__update_rows(struct hist_browser *hb)
79 struct ui_browser *browser = &hb->b;
80 struct hists *hists = hb->hists;
81 struct perf_hpp_list *hpp_list = hists->hpp_list;
82 u16 header_offset, index_row;
84 header_offset = hb->show_headers ? hpp_list->nr_header_lines : 0;
85 browser->rows = browser->height - header_offset;
87 * Verify if we were at the last line and that line isn't
88 * visibe because we now show the header line(s).
90 index_row = browser->index - browser->top_idx;
91 if (index_row >= browser->rows)
92 browser->index -= index_row - browser->rows + 1;
95 static void hist_browser__refresh_dimensions(struct ui_browser *browser)
97 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
99 /* 3 == +/- toggle symbol before actual hist_entry rendering */
100 browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]"));
102 * FIXME: Just keeping existing behaviour, but this really should be
103 * before updating browser->width, as it will invalidate the
104 * calculation above. Fix this and the fallout in another
107 ui_browser__refresh_dimensions(browser);
108 hist_browser__update_rows(hb);
111 static void hist_browser__gotorc(struct hist_browser *browser, int row, int column)
113 struct hists *hists = browser->hists;
114 struct perf_hpp_list *hpp_list = hists->hpp_list;
117 header_offset = browser->show_headers ? hpp_list->nr_header_lines : 0;
118 ui_browser__gotorc(&browser->b, row + header_offset, column);
121 static void hist_browser__reset(struct hist_browser *browser)
124 * The hists__remove_entry_filter() already folds non-filtered
125 * entries so we can assume it has 0 callchain rows.
127 browser->nr_callchain_rows = 0;
129 hist_browser__update_nr_entries(browser);
130 browser->b.nr_entries = hist_browser__nr_entries(browser);
131 hist_browser__refresh_dimensions(&browser->b);
132 ui_browser__reset_index(&browser->b);
135 static char tree__folded_sign(bool unfolded)
137 return unfolded ? '-' : '+';
140 static char hist_entry__folded(const struct hist_entry *he)
142 return he->has_children ? tree__folded_sign(he->unfolded) : ' ';
145 static char callchain_list__folded(const struct callchain_list *cl)
147 return cl->has_children ? tree__folded_sign(cl->unfolded) : ' ';
150 static void callchain_list__set_folding(struct callchain_list *cl, bool unfold)
152 cl->unfolded = unfold ? cl->has_children : false;
155 static struct inline_node *inline_node__create(struct map *map, u64 ip)
158 struct inline_node *node;
167 if (dso->kernel != DSO_TYPE_USER)
170 node = dso__parse_addr_inlines(dso,
171 map__rip_2objdump(map, ip));
176 static int inline__count_rows(struct inline_node *node)
178 struct inline_list *ilist;
184 list_for_each_entry(ilist, &node->val, list) {
185 if ((ilist->filename != NULL) || (ilist->funcname != NULL))
192 static int callchain_list__inline_rows(struct callchain_list *chain)
194 struct inline_node *node;
197 node = inline_node__create(chain->ms.map, chain->ip);
201 rows = inline__count_rows(node);
202 inline_node__delete(node);
206 static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
208 int n = 0, inline_rows;
211 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
212 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
213 struct callchain_list *chain;
214 char folded_sign = ' '; /* No children */
216 list_for_each_entry(chain, &child->val, list) {
219 if (symbol_conf.inline_name) {
221 callchain_list__inline_rows(chain);
225 /* We need this because we may not have children */
226 folded_sign = callchain_list__folded(chain);
227 if (folded_sign == '+')
231 if (folded_sign == '-') /* Have children and they're unfolded */
232 n += callchain_node__count_rows_rb_tree(child);
238 static int callchain_node__count_flat_rows(struct callchain_node *node)
240 struct callchain_list *chain;
241 char folded_sign = 0;
244 list_for_each_entry(chain, &node->parent_val, list) {
246 /* only check first chain list entry */
247 folded_sign = callchain_list__folded(chain);
248 if (folded_sign == '+')
254 list_for_each_entry(chain, &node->val, list) {
256 /* node->parent_val list might be empty */
257 folded_sign = callchain_list__folded(chain);
258 if (folded_sign == '+')
267 static int callchain_node__count_folded_rows(struct callchain_node *node __maybe_unused)
272 static int callchain_node__count_rows(struct callchain_node *node)
274 struct callchain_list *chain;
275 bool unfolded = false;
276 int n = 0, inline_rows;
278 if (callchain_param.mode == CHAIN_FLAT)
279 return callchain_node__count_flat_rows(node);
280 else if (callchain_param.mode == CHAIN_FOLDED)
281 return callchain_node__count_folded_rows(node);
283 list_for_each_entry(chain, &node->val, list) {
285 if (symbol_conf.inline_name) {
286 inline_rows = callchain_list__inline_rows(chain);
290 unfolded = chain->unfolded;
294 n += callchain_node__count_rows_rb_tree(node);
299 static int callchain__count_rows(struct rb_root *chain)
304 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
305 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
306 n += callchain_node__count_rows(node);
312 static int hierarchy_count_rows(struct hist_browser *hb, struct hist_entry *he,
313 bool include_children)
316 struct rb_node *node;
317 struct hist_entry *child;
320 return callchain__count_rows(&he->sorted_chain);
322 if (he->has_no_entry)
325 node = rb_first(&he->hroot_out);
329 child = rb_entry(node, struct hist_entry, rb_node);
330 percent = hist_entry__get_percent_limit(child);
332 if (!child->filtered && percent >= hb->min_pcnt) {
335 if (include_children && child->unfolded)
336 count += hierarchy_count_rows(hb, child, true);
339 node = rb_next(node);
344 static bool hist_entry__toggle_fold(struct hist_entry *he)
349 if (!he->has_children)
352 he->unfolded = !he->unfolded;
356 static bool callchain_list__toggle_fold(struct callchain_list *cl)
361 if (!cl->has_children)
364 cl->unfolded = !cl->unfolded;
368 static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
370 struct rb_node *nd = rb_first(&node->rb_root);
372 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
373 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
374 struct callchain_list *chain;
377 list_for_each_entry(chain, &child->val, list) {
380 chain->has_children = chain->list.next != &child->val ||
381 !RB_EMPTY_ROOT(&child->rb_root);
383 chain->has_children = chain->list.next == &child->val &&
384 !RB_EMPTY_ROOT(&child->rb_root);
387 callchain_node__init_have_children_rb_tree(child);
391 static void callchain_node__init_have_children(struct callchain_node *node,
394 struct callchain_list *chain;
396 chain = list_entry(node->val.next, struct callchain_list, list);
397 chain->has_children = has_sibling;
399 if (!list_empty(&node->val)) {
400 chain = list_entry(node->val.prev, struct callchain_list, list);
401 chain->has_children = !RB_EMPTY_ROOT(&node->rb_root);
404 callchain_node__init_have_children_rb_tree(node);
407 static void callchain__init_have_children(struct rb_root *root)
409 struct rb_node *nd = rb_first(root);
410 bool has_sibling = nd && rb_next(nd);
412 for (nd = rb_first(root); nd; nd = rb_next(nd)) {
413 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
414 callchain_node__init_have_children(node, has_sibling);
415 if (callchain_param.mode == CHAIN_FLAT ||
416 callchain_param.mode == CHAIN_FOLDED)
417 callchain_node__make_parent_list(node);
421 static void hist_entry__init_have_children(struct hist_entry *he)
423 if (he->init_have_children)
427 he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
428 callchain__init_have_children(&he->sorted_chain);
430 he->has_children = !RB_EMPTY_ROOT(&he->hroot_out);
433 he->init_have_children = true;
436 static void hist_entry_init_inline_node(struct hist_entry *he)
441 he->inline_node = inline_node__create(he->ms.map, he->ip);
443 if (he->inline_node == NULL)
446 he->has_children = true;
449 static bool hist_browser__toggle_fold(struct hist_browser *browser)
451 struct hist_entry *he = browser->he_selection;
452 struct map_symbol *ms = browser->selection;
453 struct callchain_list *cl = container_of(ms, struct callchain_list, ms);
460 has_children = hist_entry__toggle_fold(he);
462 has_children = callchain_list__toggle_fold(cl);
467 hist_entry__init_have_children(he);
468 browser->b.nr_entries -= he->nr_rows;
471 browser->nr_callchain_rows -= he->nr_rows;
473 browser->nr_hierarchy_entries -= he->nr_rows;
475 if (symbol_conf.report_hierarchy)
476 child_rows = hierarchy_count_rows(browser, he, true);
481 he->nr_rows = inline__count_rows(
484 he->nr_rows = callchain__count_rows(
487 he->nr_rows = hierarchy_count_rows(browser, he, false);
489 /* account grand children */
490 if (symbol_conf.report_hierarchy)
491 browser->b.nr_entries += child_rows - he->nr_rows;
493 if (!he->leaf && he->nr_rows == 0) {
494 he->has_no_entry = true;
498 if (symbol_conf.report_hierarchy)
499 browser->b.nr_entries -= child_rows - he->nr_rows;
501 if (he->has_no_entry)
502 he->has_no_entry = false;
507 browser->b.nr_entries += he->nr_rows;
510 browser->nr_callchain_rows += he->nr_rows;
512 browser->nr_hierarchy_entries += he->nr_rows;
517 /* If it doesn't have children, no toggling performed */
521 static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
526 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
527 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
528 struct callchain_list *chain;
529 bool has_children = false;
531 list_for_each_entry(chain, &child->val, list) {
533 callchain_list__set_folding(chain, unfold);
534 has_children = chain->has_children;
538 n += callchain_node__set_folding_rb_tree(child, unfold);
544 static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
546 struct callchain_list *chain;
547 bool has_children = false;
550 list_for_each_entry(chain, &node->val, list) {
552 callchain_list__set_folding(chain, unfold);
553 has_children = chain->has_children;
557 n += callchain_node__set_folding_rb_tree(node, unfold);
562 static int callchain__set_folding(struct rb_root *chain, bool unfold)
567 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
568 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
569 n += callchain_node__set_folding(node, unfold);
575 static int hierarchy_set_folding(struct hist_browser *hb, struct hist_entry *he,
576 bool unfold __maybe_unused)
580 struct hist_entry *child;
583 for (nd = rb_first(&he->hroot_out); nd; nd = rb_next(nd)) {
584 child = rb_entry(nd, struct hist_entry, rb_node);
585 percent = hist_entry__get_percent_limit(child);
586 if (!child->filtered && percent >= hb->min_pcnt)
593 static void __hist_entry__set_folding(struct hist_entry *he,
594 struct hist_browser *hb, bool unfold)
596 hist_entry__init_have_children(he);
597 he->unfolded = unfold ? he->has_children : false;
599 if (he->has_children) {
603 n = callchain__set_folding(&he->sorted_chain, unfold);
605 n = hierarchy_set_folding(hb, he, unfold);
607 he->nr_rows = unfold ? n : 0;
612 static void hist_entry__set_folding(struct hist_entry *he,
613 struct hist_browser *browser, bool unfold)
617 percent = hist_entry__get_percent_limit(he);
618 if (he->filtered || percent < browser->min_pcnt)
621 __hist_entry__set_folding(he, browser, unfold);
623 if (!he->depth || unfold)
624 browser->nr_hierarchy_entries++;
626 browser->nr_callchain_rows += he->nr_rows;
627 else if (unfold && !hist_entry__has_hierarchy_children(he, browser->min_pcnt)) {
628 browser->nr_hierarchy_entries++;
629 he->has_no_entry = true;
632 he->has_no_entry = false;
636 __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
639 struct hist_entry *he;
641 nd = rb_first(&browser->hists->entries);
643 he = rb_entry(nd, struct hist_entry, rb_node);
645 /* set folding state even if it's currently folded */
646 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
648 hist_entry__set_folding(he, browser, unfold);
652 static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
654 browser->nr_hierarchy_entries = 0;
655 browser->nr_callchain_rows = 0;
656 __hist_browser__set_folding(browser, unfold);
658 browser->b.nr_entries = hist_browser__nr_entries(browser);
659 /* Go to the start, we may be way after valid entries after a collapse */
660 ui_browser__reset_index(&browser->b);
663 static void hist_browser__set_folding_selected(struct hist_browser *browser, bool unfold)
665 if (!browser->he_selection)
668 hist_entry__set_folding(browser->he_selection, browser, unfold);
669 browser->b.nr_entries = hist_browser__nr_entries(browser);
672 static void ui_browser__warn_lost_events(struct ui_browser *browser)
674 ui_browser__warning(browser, 4,
675 "Events are being lost, check IO/CPU overload!\n\n"
676 "You may want to run 'perf' using a RT scheduler policy:\n\n"
677 " perf top -r 80\n\n"
678 "Or reduce the sampling frequency.");
681 static int hist_browser__title(struct hist_browser *browser, char *bf, size_t size)
683 return browser->title ? browser->title(browser, bf, size) : 0;
686 int hist_browser__run(struct hist_browser *browser, const char *help)
690 struct hist_browser_timer *hbt = browser->hbt;
691 int delay_secs = hbt ? hbt->refresh : 0;
693 browser->b.entries = &browser->hists->entries;
694 browser->b.nr_entries = hist_browser__nr_entries(browser);
696 hist_browser__title(browser, title, sizeof(title));
698 if (ui_browser__show(&browser->b, title, "%s", help) < 0)
702 key = ui_browser__run(&browser->b, delay_secs);
707 hbt->timer(hbt->arg);
709 if (hist_browser__has_filter(browser) ||
710 symbol_conf.report_hierarchy)
711 hist_browser__update_nr_entries(browser);
713 nr_entries = hist_browser__nr_entries(browser);
714 ui_browser__update_nr_entries(&browser->b, nr_entries);
716 if (browser->hists->stats.nr_lost_warned !=
717 browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
718 browser->hists->stats.nr_lost_warned =
719 browser->hists->stats.nr_events[PERF_RECORD_LOST];
720 ui_browser__warn_lost_events(&browser->b);
723 hist_browser__title(browser, title, sizeof(title));
724 ui_browser__show_title(&browser->b, title);
727 case 'D': { /* Debug */
729 struct hist_entry *h = rb_entry(browser->b.top,
730 struct hist_entry, rb_node);
732 ui_helpline__fpush("%d: nr_ent=(%d,%d), rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
733 seq++, browser->b.nr_entries,
734 browser->hists->nr_entries,
738 h->row_offset, h->nr_rows);
742 /* Collapse the whole world. */
743 hist_browser__set_folding(browser, false);
746 /* Collapse the selected entry. */
747 hist_browser__set_folding_selected(browser, false);
750 /* Expand the whole world. */
751 hist_browser__set_folding(browser, true);
754 /* Expand the selected entry. */
755 hist_browser__set_folding_selected(browser, true);
758 browser->show_headers = !browser->show_headers;
759 hist_browser__update_rows(browser);
762 if (hist_browser__toggle_fold(browser))
770 ui_browser__hide(&browser->b);
774 struct callchain_print_arg {
775 /* for hists browser */
777 bool is_current_entry;
784 typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
785 struct callchain_list *chain,
786 const char *str, int offset,
788 struct callchain_print_arg *arg);
790 static void hist_browser__show_callchain_entry(struct hist_browser *browser,
791 struct callchain_list *chain,
792 const char *str, int offset,
794 struct callchain_print_arg *arg)
797 char folded_sign = callchain_list__folded(chain);
798 bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
800 color = HE_COLORSET_NORMAL;
801 width = browser->b.width - (offset + 2);
802 if (ui_browser__is_current_entry(&browser->b, row)) {
803 browser->selection = &chain->ms;
804 color = HE_COLORSET_SELECTED;
805 arg->is_current_entry = true;
808 ui_browser__set_color(&browser->b, color);
809 hist_browser__gotorc(browser, row, 0);
810 ui_browser__write_nstring(&browser->b, " ", offset);
811 ui_browser__printf(&browser->b, "%c", folded_sign);
812 ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
813 ui_browser__write_nstring(&browser->b, str, width);
816 static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
817 struct callchain_list *chain,
818 const char *str, int offset,
819 unsigned short row __maybe_unused,
820 struct callchain_print_arg *arg)
822 char folded_sign = callchain_list__folded(chain);
824 arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
828 typedef bool (*check_output_full_fn)(struct hist_browser *browser,
831 static bool hist_browser__check_output_full(struct hist_browser *browser,
834 return browser->b.rows == row;
837 static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
838 unsigned short row __maybe_unused)
843 #define LEVEL_OFFSET_STEP 3
845 static int hist_browser__show_inline(struct hist_browser *browser,
846 struct inline_node *node,
850 struct inline_list *ilist;
852 int color, width, first_row;
855 width = browser->b.width - (LEVEL_OFFSET_STEP + 2);
856 list_for_each_entry(ilist, &node->val, list) {
857 if ((ilist->filename != NULL) || (ilist->funcname != NULL)) {
858 color = HE_COLORSET_NORMAL;
859 if (ui_browser__is_current_entry(&browser->b, row))
860 color = HE_COLORSET_SELECTED;
862 if (callchain_param.key == CCKEY_ADDRESS ||
863 callchain_param.key == CCKEY_SRCLINE) {
864 if (ilist->filename != NULL)
865 scnprintf(buf, sizeof(buf),
870 scnprintf(buf, sizeof(buf), "??");
871 } else if (ilist->funcname != NULL)
872 scnprintf(buf, sizeof(buf), "%s (inline)",
874 else if (ilist->filename != NULL)
875 scnprintf(buf, sizeof(buf),
880 scnprintf(buf, sizeof(buf), "??");
882 ui_browser__set_color(&browser->b, color);
883 hist_browser__gotorc(browser, row, 0);
884 ui_browser__write_nstring(&browser->b, " ",
885 LEVEL_OFFSET_STEP + offset);
886 ui_browser__write_nstring(&browser->b, buf, width);
891 return row - first_row;
894 static size_t show_inline_list(struct hist_browser *browser, struct map *map,
895 u64 ip, int row, int offset)
897 struct inline_node *node;
900 node = inline_node__create(map, ip);
904 ret = hist_browser__show_inline(browser, node, row, offset);
906 inline_node__delete(node);
910 static int hist_browser__show_callchain_list(struct hist_browser *browser,
911 struct callchain_node *node,
912 struct callchain_list *chain,
913 unsigned short row, u64 total,
914 bool need_percent, int offset,
915 print_callchain_entry_fn print,
916 struct callchain_print_arg *arg)
918 char bf[1024], *alloc_str;
919 char buf[64], *alloc_str2;
921 int inline_rows = 0, ret = 1;
923 if (arg->row_offset != 0) {
931 str = callchain_list__sym_name(chain, bf, sizeof(bf),
934 if (symbol_conf.show_branchflag_count) {
936 callchain_list_counts__printf_value(node, chain, NULL,
939 callchain_list_counts__printf_value(NULL, chain, NULL,
942 if (asprintf(&alloc_str2, "%s%s", str, buf) < 0)
943 str = "Not enough memory!";
949 callchain_node__scnprintf_value(node, buf, sizeof(buf),
952 if (asprintf(&alloc_str, "%s %s", buf, str) < 0)
953 str = "Not enough memory!";
958 print(browser, chain, str, offset, row, arg);
962 if (symbol_conf.inline_name) {
963 inline_rows = show_inline_list(browser, chain->ms.map,
964 chain->ip, row + 1, offset);
967 return ret + inline_rows;
970 static bool check_percent_display(struct rb_node *node, u64 parent_total)
972 struct callchain_node *child;
980 child = rb_entry(node, struct callchain_node, rb_node);
981 return callchain_cumul_hits(child) != parent_total;
984 static int hist_browser__show_callchain_flat(struct hist_browser *browser,
985 struct rb_root *root,
986 unsigned short row, u64 total,
988 print_callchain_entry_fn print,
989 struct callchain_print_arg *arg,
990 check_output_full_fn is_output_full)
992 struct rb_node *node;
993 int first_row = row, offset = LEVEL_OFFSET_STEP;
996 node = rb_first(root);
997 need_percent = check_percent_display(node, parent_total);
1000 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1001 struct rb_node *next = rb_next(node);
1002 struct callchain_list *chain;
1003 char folded_sign = ' ';
1005 int extra_offset = 0;
1007 list_for_each_entry(chain, &child->parent_val, list) {
1008 bool was_first = first;
1012 else if (need_percent)
1013 extra_offset = LEVEL_OFFSET_STEP;
1015 folded_sign = callchain_list__folded(chain);
1017 row += hist_browser__show_callchain_list(browser, child,
1019 was_first && need_percent,
1020 offset + extra_offset,
1023 if (is_output_full(browser, row))
1026 if (folded_sign == '+')
1030 list_for_each_entry(chain, &child->val, list) {
1031 bool was_first = first;
1035 else if (need_percent)
1036 extra_offset = LEVEL_OFFSET_STEP;
1038 folded_sign = callchain_list__folded(chain);
1040 row += hist_browser__show_callchain_list(browser, child,
1042 was_first && need_percent,
1043 offset + extra_offset,
1046 if (is_output_full(browser, row))
1049 if (folded_sign == '+')
1054 if (is_output_full(browser, row))
1059 return row - first_row;
1062 static char *hist_browser__folded_callchain_str(struct hist_browser *browser,
1063 struct callchain_list *chain,
1064 char *value_str, char *old_str)
1070 str = callchain_list__sym_name(chain, bf, sizeof(bf),
1073 if (asprintf(&new, "%s%s%s", old_str,
1074 symbol_conf.field_sep ?: ";", str) < 0)
1078 if (asprintf(&new, "%s %s", value_str, str) < 0)
1081 if (asprintf(&new, "%s", str) < 0)
1088 static int hist_browser__show_callchain_folded(struct hist_browser *browser,
1089 struct rb_root *root,
1090 unsigned short row, u64 total,
1092 print_callchain_entry_fn print,
1093 struct callchain_print_arg *arg,
1094 check_output_full_fn is_output_full)
1096 struct rb_node *node;
1097 int first_row = row, offset = LEVEL_OFFSET_STEP;
1100 node = rb_first(root);
1101 need_percent = check_percent_display(node, parent_total);
1104 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1105 struct rb_node *next = rb_next(node);
1106 struct callchain_list *chain, *first_chain = NULL;
1108 char *value_str = NULL, *value_str_alloc = NULL;
1109 char *chain_str = NULL, *chain_str_alloc = NULL;
1111 if (arg->row_offset != 0) {
1119 callchain_node__scnprintf_value(child, buf, sizeof(buf), total);
1120 if (asprintf(&value_str, "%s", buf) < 0) {
1121 value_str = (char *)"<...>";
1124 value_str_alloc = value_str;
1127 list_for_each_entry(chain, &child->parent_val, list) {
1128 chain_str = hist_browser__folded_callchain_str(browser,
1129 chain, value_str, chain_str);
1132 first_chain = chain;
1135 if (chain_str == NULL) {
1136 chain_str = (char *)"Not enough memory!";
1140 chain_str_alloc = chain_str;
1143 list_for_each_entry(chain, &child->val, list) {
1144 chain_str = hist_browser__folded_callchain_str(browser,
1145 chain, value_str, chain_str);
1148 first_chain = chain;
1151 if (chain_str == NULL) {
1152 chain_str = (char *)"Not enough memory!";
1156 chain_str_alloc = chain_str;
1160 print(browser, first_chain, chain_str, offset, row++, arg);
1161 free(value_str_alloc);
1162 free(chain_str_alloc);
1165 if (is_output_full(browser, row))
1170 return row - first_row;
1173 static int hist_browser__show_callchain_graph(struct hist_browser *browser,
1174 struct rb_root *root, int level,
1175 unsigned short row, u64 total,
1177 print_callchain_entry_fn print,
1178 struct callchain_print_arg *arg,
1179 check_output_full_fn is_output_full)
1181 struct rb_node *node;
1182 int first_row = row, offset = level * LEVEL_OFFSET_STEP;
1184 u64 percent_total = total;
1186 if (callchain_param.mode == CHAIN_GRAPH_REL)
1187 percent_total = parent_total;
1189 node = rb_first(root);
1190 need_percent = check_percent_display(node, parent_total);
1193 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1194 struct rb_node *next = rb_next(node);
1195 struct callchain_list *chain;
1196 char folded_sign = ' ';
1198 int extra_offset = 0;
1200 list_for_each_entry(chain, &child->val, list) {
1201 bool was_first = first;
1205 else if (need_percent)
1206 extra_offset = LEVEL_OFFSET_STEP;
1208 folded_sign = callchain_list__folded(chain);
1210 row += hist_browser__show_callchain_list(browser, child,
1211 chain, row, percent_total,
1212 was_first && need_percent,
1213 offset + extra_offset,
1216 if (is_output_full(browser, row))
1219 if (folded_sign == '+')
1223 if (folded_sign == '-') {
1224 const int new_level = level + (extra_offset ? 2 : 1);
1226 row += hist_browser__show_callchain_graph(browser, &child->rb_root,
1227 new_level, row, total,
1228 child->children_hit,
1229 print, arg, is_output_full);
1231 if (is_output_full(browser, row))
1236 return row - first_row;
1239 static int hist_browser__show_callchain(struct hist_browser *browser,
1240 struct hist_entry *entry, int level,
1242 print_callchain_entry_fn print,
1243 struct callchain_print_arg *arg,
1244 check_output_full_fn is_output_full)
1246 u64 total = hists__total_period(entry->hists);
1250 if (symbol_conf.cumulate_callchain)
1251 parent_total = entry->stat_acc->period;
1253 parent_total = entry->stat.period;
1255 if (callchain_param.mode == CHAIN_FLAT) {
1256 printed = hist_browser__show_callchain_flat(browser,
1257 &entry->sorted_chain, row,
1258 total, parent_total, print, arg,
1260 } else if (callchain_param.mode == CHAIN_FOLDED) {
1261 printed = hist_browser__show_callchain_folded(browser,
1262 &entry->sorted_chain, row,
1263 total, parent_total, print, arg,
1266 printed = hist_browser__show_callchain_graph(browser,
1267 &entry->sorted_chain, level, row,
1268 total, parent_total, print, arg,
1272 if (arg->is_current_entry)
1273 browser->he_selection = entry;
1279 struct ui_browser *b;
1284 int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
1286 struct hpp_arg *arg = hpp->ptr;
1291 va_start(args, fmt);
1292 len = va_arg(args, int);
1293 percent = va_arg(args, double);
1296 ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
1298 ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
1299 ui_browser__printf(arg->b, "%s", hpp->buf);
1304 #define __HPP_COLOR_PERCENT_FN(_type, _field) \
1305 static u64 __hpp_get_##_field(struct hist_entry *he) \
1307 return he->stat._field; \
1311 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
1312 struct perf_hpp *hpp, \
1313 struct hist_entry *he) \
1315 return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%", \
1316 __hpp__slsmg_color_printf, true); \
1319 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
1320 static u64 __hpp_get_acc_##_field(struct hist_entry *he) \
1322 return he->stat_acc->_field; \
1326 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
1327 struct perf_hpp *hpp, \
1328 struct hist_entry *he) \
1330 if (!symbol_conf.cumulate_callchain) { \
1331 struct hpp_arg *arg = hpp->ptr; \
1332 int len = fmt->user_len ?: fmt->len; \
1333 int ret = scnprintf(hpp->buf, hpp->size, \
1334 "%*s", len, "N/A"); \
1335 ui_browser__printf(arg->b, "%s", hpp->buf); \
1339 return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field, \
1340 " %*.2f%%", __hpp__slsmg_color_printf, true); \
1343 __HPP_COLOR_PERCENT_FN(overhead, period)
1344 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
1345 __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
1346 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
1347 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
1348 __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
1350 #undef __HPP_COLOR_PERCENT_FN
1351 #undef __HPP_COLOR_ACC_PERCENT_FN
1353 void hist_browser__init_hpp(void)
1355 perf_hpp__format[PERF_HPP__OVERHEAD].color =
1356 hist_browser__hpp_color_overhead;
1357 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
1358 hist_browser__hpp_color_overhead_sys;
1359 perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
1360 hist_browser__hpp_color_overhead_us;
1361 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
1362 hist_browser__hpp_color_overhead_guest_sys;
1363 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
1364 hist_browser__hpp_color_overhead_guest_us;
1365 perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
1366 hist_browser__hpp_color_overhead_acc;
1369 static int hist_browser__show_entry(struct hist_browser *browser,
1370 struct hist_entry *entry,
1374 int width = browser->b.width;
1375 char folded_sign = ' ';
1376 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1377 off_t row_offset = entry->row_offset;
1379 struct perf_hpp_fmt *fmt;
1381 if (current_entry) {
1382 browser->he_selection = entry;
1383 browser->selection = &entry->ms;
1386 if (symbol_conf.use_callchain) {
1387 hist_entry__init_have_children(entry);
1388 folded_sign = hist_entry__folded(entry);
1391 if (symbol_conf.inline_name &&
1392 (!entry->has_children)) {
1393 hist_entry_init_inline_node(entry);
1394 folded_sign = hist_entry__folded(entry);
1397 if (row_offset == 0) {
1398 struct hpp_arg arg = {
1400 .folded_sign = folded_sign,
1401 .current_entry = current_entry,
1405 hist_browser__gotorc(browser, row, 0);
1407 hists__for_each_format(browser->hists, fmt) {
1409 struct perf_hpp hpp = {
1415 if (perf_hpp__should_skip(fmt, entry->hists) ||
1416 column++ < browser->b.horiz_scroll)
1419 if (current_entry && browser->b.navkeypressed) {
1420 ui_browser__set_color(&browser->b,
1421 HE_COLORSET_SELECTED);
1423 ui_browser__set_color(&browser->b,
1424 HE_COLORSET_NORMAL);
1428 if (symbol_conf.use_callchain ||
1429 symbol_conf.inline_name) {
1430 ui_browser__printf(&browser->b, "%c ", folded_sign);
1435 ui_browser__printf(&browser->b, " ");
1440 int ret = fmt->color(fmt, &hpp, entry);
1441 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1443 * fmt->color() already used ui_browser to
1444 * print the non alignment bits, skip it (+ret):
1446 ui_browser__printf(&browser->b, "%s", s + ret);
1448 hist_entry__snprintf_alignment(entry, &hpp, fmt, fmt->entry(fmt, &hpp, entry));
1449 ui_browser__printf(&browser->b, "%s", s);
1451 width -= hpp.buf - s;
1454 /* The scroll bar isn't being used */
1455 if (!browser->b.navkeypressed)
1458 ui_browser__write_nstring(&browser->b, "", width);
1465 if (folded_sign == '-' && row != browser->b.rows) {
1466 struct callchain_print_arg arg = {
1467 .row_offset = row_offset,
1468 .is_current_entry = current_entry,
1471 if (entry->inline_node)
1472 printed += hist_browser__show_inline(browser,
1473 entry->inline_node, row, 0);
1475 printed += hist_browser__show_callchain(browser,
1477 hist_browser__show_callchain_entry,
1479 hist_browser__check_output_full);
1485 static int hist_browser__show_hierarchy_entry(struct hist_browser *browser,
1486 struct hist_entry *entry,
1491 int width = browser->b.width;
1492 char folded_sign = ' ';
1493 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1494 off_t row_offset = entry->row_offset;
1496 struct perf_hpp_fmt *fmt;
1497 struct perf_hpp_list_node *fmt_node;
1498 struct hpp_arg arg = {
1500 .current_entry = current_entry,
1503 int hierarchy_indent = (entry->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
1505 if (current_entry) {
1506 browser->he_selection = entry;
1507 browser->selection = &entry->ms;
1510 hist_entry__init_have_children(entry);
1511 folded_sign = hist_entry__folded(entry);
1512 arg.folded_sign = folded_sign;
1514 if (entry->leaf && row_offset) {
1516 goto show_callchain;
1519 hist_browser__gotorc(browser, row, 0);
1521 if (current_entry && browser->b.navkeypressed)
1522 ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1524 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1526 ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1527 width -= level * HIERARCHY_INDENT;
1529 /* the first hpp_list_node is for overhead columns */
1530 fmt_node = list_first_entry(&entry->hists->hpp_formats,
1531 struct perf_hpp_list_node, list);
1532 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1534 struct perf_hpp hpp = {
1540 if (perf_hpp__should_skip(fmt, entry->hists) ||
1541 column++ < browser->b.horiz_scroll)
1544 if (current_entry && browser->b.navkeypressed) {
1545 ui_browser__set_color(&browser->b,
1546 HE_COLORSET_SELECTED);
1548 ui_browser__set_color(&browser->b,
1549 HE_COLORSET_NORMAL);
1553 ui_browser__printf(&browser->b, "%c ", folded_sign);
1557 ui_browser__printf(&browser->b, " ");
1562 int ret = fmt->color(fmt, &hpp, entry);
1563 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1565 * fmt->color() already used ui_browser to
1566 * print the non alignment bits, skip it (+ret):
1568 ui_browser__printf(&browser->b, "%s", s + ret);
1570 int ret = fmt->entry(fmt, &hpp, entry);
1571 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1572 ui_browser__printf(&browser->b, "%s", s);
1574 width -= hpp.buf - s;
1578 ui_browser__write_nstring(&browser->b, "", hierarchy_indent);
1579 width -= hierarchy_indent;
1582 if (column >= browser->b.horiz_scroll) {
1584 struct perf_hpp hpp = {
1590 if (current_entry && browser->b.navkeypressed) {
1591 ui_browser__set_color(&browser->b,
1592 HE_COLORSET_SELECTED);
1594 ui_browser__set_color(&browser->b,
1595 HE_COLORSET_NORMAL);
1598 perf_hpp_list__for_each_format(entry->hpp_list, fmt) {
1600 ui_browser__printf(&browser->b, "%c ", folded_sign);
1603 ui_browser__write_nstring(&browser->b, "", 2);
1609 * No need to call hist_entry__snprintf_alignment()
1610 * since this fmt is always the last column in the
1614 width -= fmt->color(fmt, &hpp, entry);
1618 width -= fmt->entry(fmt, &hpp, entry);
1619 ui_browser__printf(&browser->b, "%s", ltrim(s));
1621 while (isspace(s[i++]))
1627 /* The scroll bar isn't being used */
1628 if (!browser->b.navkeypressed)
1631 ui_browser__write_nstring(&browser->b, "", width);
1637 if (entry->leaf && folded_sign == '-' && row != browser->b.rows) {
1638 struct callchain_print_arg carg = {
1639 .row_offset = row_offset,
1642 printed += hist_browser__show_callchain(browser, entry,
1644 hist_browser__show_callchain_entry, &carg,
1645 hist_browser__check_output_full);
1651 static int hist_browser__show_no_entry(struct hist_browser *browser,
1652 unsigned short row, int level)
1654 int width = browser->b.width;
1655 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1659 struct perf_hpp_fmt *fmt;
1660 struct perf_hpp_list_node *fmt_node;
1661 int indent = browser->hists->nr_hpp_node - 2;
1663 if (current_entry) {
1664 browser->he_selection = NULL;
1665 browser->selection = NULL;
1668 hist_browser__gotorc(browser, row, 0);
1670 if (current_entry && browser->b.navkeypressed)
1671 ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1673 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1675 ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1676 width -= level * HIERARCHY_INDENT;
1678 /* the first hpp_list_node is for overhead columns */
1679 fmt_node = list_first_entry(&browser->hists->hpp_formats,
1680 struct perf_hpp_list_node, list);
1681 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1682 if (perf_hpp__should_skip(fmt, browser->hists) ||
1683 column++ < browser->b.horiz_scroll)
1686 ret = fmt->width(fmt, NULL, browser->hists);
1689 /* for folded sign */
1693 /* space between columns */
1697 ui_browser__write_nstring(&browser->b, "", ret);
1701 ui_browser__write_nstring(&browser->b, "", indent * HIERARCHY_INDENT);
1702 width -= indent * HIERARCHY_INDENT;
1704 if (column >= browser->b.horiz_scroll) {
1707 ret = snprintf(buf, sizeof(buf), "no entry >= %.2f%%", browser->min_pcnt);
1708 ui_browser__printf(&browser->b, " %s", buf);
1712 /* The scroll bar isn't being used */
1713 if (!browser->b.navkeypressed)
1716 ui_browser__write_nstring(&browser->b, "", width);
1720 static int advance_hpp_check(struct perf_hpp *hpp, int inc)
1722 advance_hpp(hpp, inc);
1723 return hpp->size <= 0;
1727 hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf,
1728 size_t size, int line)
1730 struct hists *hists = browser->hists;
1731 struct perf_hpp dummy_hpp = {
1735 struct perf_hpp_fmt *fmt;
1740 if (symbol_conf.use_callchain) {
1741 ret = scnprintf(buf, size, " ");
1742 if (advance_hpp_check(&dummy_hpp, ret))
1746 hists__for_each_format(browser->hists, fmt) {
1747 if (perf_hpp__should_skip(fmt, hists) || column++ < browser->b.horiz_scroll)
1750 ret = fmt->header(fmt, &dummy_hpp, hists, line, &span);
1751 if (advance_hpp_check(&dummy_hpp, ret))
1757 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
1758 if (advance_hpp_check(&dummy_hpp, ret))
1765 static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser, char *buf, size_t size)
1767 struct hists *hists = browser->hists;
1768 struct perf_hpp dummy_hpp = {
1772 struct perf_hpp_fmt *fmt;
1773 struct perf_hpp_list_node *fmt_node;
1776 int indent = hists->nr_hpp_node - 2;
1777 bool first_node, first_col;
1779 ret = scnprintf(buf, size, " ");
1780 if (advance_hpp_check(&dummy_hpp, ret))
1784 /* the first hpp_list_node is for overhead columns */
1785 fmt_node = list_first_entry(&hists->hpp_formats,
1786 struct perf_hpp_list_node, list);
1787 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1788 if (column++ < browser->b.horiz_scroll)
1791 ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1792 if (advance_hpp_check(&dummy_hpp, ret))
1795 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
1796 if (advance_hpp_check(&dummy_hpp, ret))
1803 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "%*s",
1804 indent * HIERARCHY_INDENT, "");
1805 if (advance_hpp_check(&dummy_hpp, ret))
1810 list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
1812 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " / ");
1813 if (advance_hpp_check(&dummy_hpp, ret))
1819 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1822 if (perf_hpp__should_skip(fmt, hists))
1826 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "+");
1827 if (advance_hpp_check(&dummy_hpp, ret))
1832 ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1833 dummy_hpp.buf[ret] = '\0';
1835 start = trim(dummy_hpp.buf);
1836 ret = strlen(start);
1838 if (start != dummy_hpp.buf)
1839 memmove(dummy_hpp.buf, start, ret + 1);
1841 if (advance_hpp_check(&dummy_hpp, ret))
1849 static void hists_browser__hierarchy_headers(struct hist_browser *browser)
1853 hists_browser__scnprintf_hierarchy_headers(browser, headers,
1856 ui_browser__gotorc(&browser->b, 0, 0);
1857 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1858 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1861 static void hists_browser__headers(struct hist_browser *browser)
1863 struct hists *hists = browser->hists;
1864 struct perf_hpp_list *hpp_list = hists->hpp_list;
1868 for (line = 0; line < hpp_list->nr_header_lines; line++) {
1871 hists_browser__scnprintf_headers(browser, headers,
1872 sizeof(headers), line);
1874 ui_browser__gotorc(&browser->b, line, 0);
1875 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1876 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1880 static void hist_browser__show_headers(struct hist_browser *browser)
1882 if (symbol_conf.report_hierarchy)
1883 hists_browser__hierarchy_headers(browser);
1885 hists_browser__headers(browser);
1888 static void ui_browser__hists_init_top(struct ui_browser *browser)
1890 if (browser->top == NULL) {
1891 struct hist_browser *hb;
1893 hb = container_of(browser, struct hist_browser, b);
1894 browser->top = rb_first(&hb->hists->entries);
1898 static unsigned int hist_browser__refresh(struct ui_browser *browser)
1901 u16 header_offset = 0;
1903 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
1904 struct hists *hists = hb->hists;
1906 if (hb->show_headers) {
1907 struct perf_hpp_list *hpp_list = hists->hpp_list;
1909 hist_browser__show_headers(hb);
1910 header_offset = hpp_list->nr_header_lines;
1913 ui_browser__hists_init_top(browser);
1914 hb->he_selection = NULL;
1915 hb->selection = NULL;
1917 for (nd = browser->top; nd; nd = rb_hierarchy_next(nd)) {
1918 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1922 /* let it move to sibling */
1923 h->unfolded = false;
1927 percent = hist_entry__get_percent_limit(h);
1928 if (percent < hb->min_pcnt)
1931 if (symbol_conf.report_hierarchy) {
1932 row += hist_browser__show_hierarchy_entry(hb, h, row,
1934 if (row == browser->rows)
1937 if (h->has_no_entry) {
1938 hist_browser__show_no_entry(hb, row, h->depth + 1);
1942 row += hist_browser__show_entry(hb, h, row);
1945 if (row == browser->rows)
1949 return row + header_offset;
1952 static struct rb_node *hists__filter_entries(struct rb_node *nd,
1955 while (nd != NULL) {
1956 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1957 float percent = hist_entry__get_percent_limit(h);
1959 if (!h->filtered && percent >= min_pcnt)
1963 * If it's filtered, its all children also were filtered.
1964 * So move to sibling node.
1969 nd = rb_hierarchy_next(nd);
1975 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
1978 while (nd != NULL) {
1979 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1980 float percent = hist_entry__get_percent_limit(h);
1982 if (!h->filtered && percent >= min_pcnt)
1985 nd = rb_hierarchy_prev(nd);
1991 static void ui_browser__hists_seek(struct ui_browser *browser,
1992 off_t offset, int whence)
1994 struct hist_entry *h;
1997 struct hist_browser *hb;
1999 hb = container_of(browser, struct hist_browser, b);
2001 if (browser->nr_entries == 0)
2004 ui_browser__hists_init_top(browser);
2008 nd = hists__filter_entries(rb_first(browser->entries),
2015 nd = rb_hierarchy_last(rb_last(browser->entries));
2016 nd = hists__filter_prev_entries(nd, hb->min_pcnt);
2024 * Moves not relative to the first visible entry invalidates its
2027 h = rb_entry(browser->top, struct hist_entry, rb_node);
2031 * Here we have to check if nd is expanded (+), if it is we can't go
2032 * the next top level hist_entry, instead we must compute an offset of
2033 * what _not_ to show and not change the first visible entry.
2035 * This offset increments when we are going from top to bottom and
2036 * decreases when we're going from bottom to top.
2038 * As we don't have backpointers to the top level in the callchains
2039 * structure, we need to always print the whole hist_entry callchain,
2040 * skipping the first ones that are before the first visible entry
2041 * and stop when we printed enough lines to fill the screen.
2049 h = rb_entry(nd, struct hist_entry, rb_node);
2050 if (h->unfolded && h->leaf) {
2051 u16 remaining = h->nr_rows - h->row_offset;
2052 if (offset > remaining) {
2053 offset -= remaining;
2056 h->row_offset += offset;
2062 nd = hists__filter_entries(rb_hierarchy_next(nd),
2068 } while (offset != 0);
2069 } else if (offset < 0) {
2071 h = rb_entry(nd, struct hist_entry, rb_node);
2072 if (h->unfolded && h->leaf) {
2074 if (-offset > h->row_offset) {
2075 offset += h->row_offset;
2078 h->row_offset += offset;
2084 if (-offset > h->nr_rows) {
2085 offset += h->nr_rows;
2088 h->row_offset = h->nr_rows + offset;
2096 nd = hists__filter_prev_entries(rb_hierarchy_prev(nd),
2104 * Last unfiltered hist_entry, check if it is
2105 * unfolded, if it is then we should have
2106 * row_offset at its last entry.
2108 h = rb_entry(nd, struct hist_entry, rb_node);
2109 if (h->unfolded && h->leaf)
2110 h->row_offset = h->nr_rows;
2117 h = rb_entry(nd, struct hist_entry, rb_node);
2122 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
2123 struct hist_entry *he, FILE *fp,
2126 struct callchain_print_arg arg = {
2130 hist_browser__show_callchain(browser, he, level, 0,
2131 hist_browser__fprintf_callchain_entry, &arg,
2132 hist_browser__check_dump_full);
2136 static int hist_browser__fprintf_entry(struct hist_browser *browser,
2137 struct hist_entry *he, FILE *fp)
2141 char folded_sign = ' ';
2142 struct perf_hpp hpp = {
2146 struct perf_hpp_fmt *fmt;
2150 if (symbol_conf.use_callchain) {
2151 folded_sign = hist_entry__folded(he);
2152 printed += fprintf(fp, "%c ", folded_sign);
2155 hists__for_each_format(browser->hists, fmt) {
2156 if (perf_hpp__should_skip(fmt, he->hists))
2160 ret = scnprintf(hpp.buf, hpp.size, " ");
2161 advance_hpp(&hpp, ret);
2165 ret = fmt->entry(fmt, &hpp, he);
2166 ret = hist_entry__snprintf_alignment(he, &hpp, fmt, ret);
2167 advance_hpp(&hpp, ret);
2169 printed += fprintf(fp, "%s\n", s);
2171 if (folded_sign == '-')
2172 printed += hist_browser__fprintf_callchain(browser, he, fp, 1);
2178 static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,
2179 struct hist_entry *he,
2180 FILE *fp, int level)
2184 char folded_sign = ' ';
2185 struct perf_hpp hpp = {
2189 struct perf_hpp_fmt *fmt;
2190 struct perf_hpp_list_node *fmt_node;
2193 int hierarchy_indent = (he->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
2195 printed = fprintf(fp, "%*s", level * HIERARCHY_INDENT, "");
2197 folded_sign = hist_entry__folded(he);
2198 printed += fprintf(fp, "%c", folded_sign);
2200 /* the first hpp_list_node is for overhead columns */
2201 fmt_node = list_first_entry(&he->hists->hpp_formats,
2202 struct perf_hpp_list_node, list);
2203 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
2205 ret = scnprintf(hpp.buf, hpp.size, " ");
2206 advance_hpp(&hpp, ret);
2210 ret = fmt->entry(fmt, &hpp, he);
2211 advance_hpp(&hpp, ret);
2214 ret = scnprintf(hpp.buf, hpp.size, "%*s", hierarchy_indent, "");
2215 advance_hpp(&hpp, ret);
2217 perf_hpp_list__for_each_format(he->hpp_list, fmt) {
2218 ret = scnprintf(hpp.buf, hpp.size, " ");
2219 advance_hpp(&hpp, ret);
2221 ret = fmt->entry(fmt, &hpp, he);
2222 advance_hpp(&hpp, ret);
2225 printed += fprintf(fp, "%s\n", rtrim(s));
2227 if (he->leaf && folded_sign == '-') {
2228 printed += hist_browser__fprintf_callchain(browser, he, fp,
2235 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
2237 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
2242 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
2244 if (symbol_conf.report_hierarchy) {
2245 printed += hist_browser__fprintf_hierarchy_entry(browser,
2249 printed += hist_browser__fprintf_entry(browser, h, fp);
2252 nd = hists__filter_entries(rb_hierarchy_next(nd),
2259 static int hist_browser__dump(struct hist_browser *browser)
2265 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
2266 if (access(filename, F_OK))
2269 * XXX: Just an arbitrary lazy upper limit
2271 if (++browser->print_seq == 8192) {
2272 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
2277 fp = fopen(filename, "w");
2280 const char *err = str_error_r(errno, bf, sizeof(bf));
2281 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
2285 ++browser->print_seq;
2286 hist_browser__fprintf(browser, fp);
2288 ui_helpline__fpush("%s written!", filename);
2293 void hist_browser__init(struct hist_browser *browser,
2294 struct hists *hists)
2296 struct perf_hpp_fmt *fmt;
2298 browser->hists = hists;
2299 browser->b.refresh = hist_browser__refresh;
2300 browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
2301 browser->b.seek = ui_browser__hists_seek;
2302 browser->b.use_navkeypressed = true;
2303 browser->show_headers = symbol_conf.show_hist_headers;
2305 if (symbol_conf.report_hierarchy) {
2306 struct perf_hpp_list_node *fmt_node;
2308 /* count overhead columns (in the first node) */
2309 fmt_node = list_first_entry(&hists->hpp_formats,
2310 struct perf_hpp_list_node, list);
2311 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt)
2312 ++browser->b.columns;
2314 /* add a single column for whole hierarchy sort keys*/
2315 ++browser->b.columns;
2317 hists__for_each_format(hists, fmt)
2318 ++browser->b.columns;
2321 hists__reset_column_width(hists);
2324 struct hist_browser *hist_browser__new(struct hists *hists)
2326 struct hist_browser *browser = zalloc(sizeof(*browser));
2329 hist_browser__init(browser, hists);
2334 static struct hist_browser *
2335 perf_evsel_browser__new(struct perf_evsel *evsel,
2336 struct hist_browser_timer *hbt,
2337 struct perf_env *env)
2339 struct hist_browser *browser = hist_browser__new(evsel__hists(evsel));
2344 browser->title = perf_evsel_browser_title;
2349 void hist_browser__delete(struct hist_browser *browser)
2354 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
2356 return browser->he_selection;
2359 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
2361 return browser->he_selection->thread;
2364 /* Check whether the browser is for 'top' or 'report' */
2365 static inline bool is_report_browser(void *timer)
2367 return timer == NULL;
2370 static int perf_evsel_browser_title(struct hist_browser *browser,
2371 char *bf, size_t size)
2373 struct hist_browser_timer *hbt = browser->hbt;
2374 struct hists *hists = browser->hists;
2377 const struct dso *dso = hists->dso_filter;
2378 const struct thread *thread = hists->thread_filter;
2379 int socket_id = hists->socket_filter;
2380 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
2381 u64 nr_events = hists->stats.total_period;
2382 struct perf_evsel *evsel = hists_to_evsel(hists);
2383 const char *ev_name = perf_evsel__name(evsel);
2385 size_t buflen = sizeof(buf);
2386 char ref[30] = " show reference callgraph, ";
2387 bool enable_ref = false;
2389 if (symbol_conf.filter_relative) {
2390 nr_samples = hists->stats.nr_non_filtered_samples;
2391 nr_events = hists->stats.total_non_filtered_period;
2394 if (perf_evsel__is_group_event(evsel)) {
2395 struct perf_evsel *pos;
2397 perf_evsel__group_desc(evsel, buf, buflen);
2400 for_each_group_member(pos, evsel) {
2401 struct hists *pos_hists = evsel__hists(pos);
2403 if (symbol_conf.filter_relative) {
2404 nr_samples += pos_hists->stats.nr_non_filtered_samples;
2405 nr_events += pos_hists->stats.total_non_filtered_period;
2407 nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
2408 nr_events += pos_hists->stats.total_period;
2413 if (symbol_conf.show_ref_callgraph &&
2414 strstr(ev_name, "call-graph=no"))
2416 nr_samples = convert_unit(nr_samples, &unit);
2417 printed = scnprintf(bf, size,
2418 "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64,
2419 nr_samples, unit, ev_name, enable_ref ? ref : " ", nr_events);
2422 if (hists->uid_filter_str)
2423 printed += snprintf(bf + printed, size - printed,
2424 ", UID: %s", hists->uid_filter_str);
2426 if (hists__has(hists, thread)) {
2427 printed += scnprintf(bf + printed, size - printed,
2429 (thread->comm_set ? thread__comm_str(thread) : ""),
2432 printed += scnprintf(bf + printed, size - printed,
2434 (thread->comm_set ? thread__comm_str(thread) : ""));
2438 printed += scnprintf(bf + printed, size - printed,
2439 ", DSO: %s", dso->short_name);
2441 printed += scnprintf(bf + printed, size - printed,
2442 ", Processor Socket: %d", socket_id);
2443 if (!is_report_browser(hbt)) {
2444 struct perf_top *top = hbt->arg;
2447 printed += scnprintf(bf + printed, size - printed, " [z]");
2453 static inline void free_popup_options(char **options, int n)
2457 for (i = 0; i < n; ++i)
2462 * Only runtime switching of perf data file will make "input_name" point
2463 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
2464 * whether we need to call free() for current "input_name" during the switch.
2466 static bool is_input_name_malloced = false;
2468 static int switch_data_file(void)
2470 char *pwd, *options[32], *abs_path[32], *tmp;
2472 int nr_options = 0, choice = -1, ret = -1;
2473 struct dirent *dent;
2475 pwd = getenv("PWD");
2479 pwd_dir = opendir(pwd);
2483 memset(options, 0, sizeof(options));
2484 memset(abs_path, 0, sizeof(abs_path));
2486 while ((dent = readdir(pwd_dir))) {
2487 char path[PATH_MAX];
2489 char *name = dent->d_name;
2492 if (!(dent->d_type == DT_REG))
2495 snprintf(path, sizeof(path), "%s/%s", pwd, name);
2497 file = fopen(path, "r");
2501 if (fread(&magic, 1, 8, file) < 8)
2502 goto close_file_and_continue;
2504 if (is_perf_magic(magic)) {
2505 options[nr_options] = strdup(name);
2506 if (!options[nr_options])
2507 goto close_file_and_continue;
2509 abs_path[nr_options] = strdup(path);
2510 if (!abs_path[nr_options]) {
2511 zfree(&options[nr_options]);
2512 ui__warning("Can't search all data files due to memory shortage.\n");
2520 close_file_and_continue:
2522 if (nr_options >= 32) {
2523 ui__warning("Too many perf data files in PWD!\n"
2524 "Only the first 32 files will be listed.\n");
2531 choice = ui__popup_menu(nr_options, options);
2532 if (choice < nr_options && choice >= 0) {
2533 tmp = strdup(abs_path[choice]);
2535 if (is_input_name_malloced)
2536 free((void *)input_name);
2538 is_input_name_malloced = true;
2541 ui__warning("Data switch failed due to memory shortage!\n");
2545 free_popup_options(options, nr_options);
2546 free_popup_options(abs_path, nr_options);
2550 struct popup_action {
2551 struct thread *thread;
2552 struct map_symbol ms;
2555 int (*fn)(struct hist_browser *browser, struct popup_action *act);
2559 do_annotate(struct hist_browser *browser, struct popup_action *act)
2561 struct perf_evsel *evsel;
2562 struct annotation *notes;
2563 struct hist_entry *he;
2566 if (!objdump_path && perf_env__lookup_objdump(browser->env))
2569 notes = symbol__annotation(act->ms.sym);
2573 evsel = hists_to_evsel(browser->hists);
2574 err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
2575 he = hist_browser__selected_entry(browser);
2577 * offer option to annotate the other branch source or target
2578 * (if they exists) when returning from annotate
2580 if ((err == 'q' || err == CTRL('c')) && he->branch_info)
2583 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
2585 ui_browser__handle_resize(&browser->b);
2590 add_annotate_opt(struct hist_browser *browser __maybe_unused,
2591 struct popup_action *act, char **optstr,
2592 struct map *map, struct symbol *sym)
2594 if (sym == NULL || map->dso->annotate_warned)
2597 if (asprintf(optstr, "Annotate %s", sym->name) < 0)
2602 act->fn = do_annotate;
2607 do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
2609 struct thread *thread = act->thread;
2611 if ((!hists__has(browser->hists, thread) &&
2612 !hists__has(browser->hists, comm)) || thread == NULL)
2615 if (browser->hists->thread_filter) {
2616 pstack__remove(browser->pstack, &browser->hists->thread_filter);
2617 perf_hpp__set_elide(HISTC_THREAD, false);
2618 thread__zput(browser->hists->thread_filter);
2621 if (hists__has(browser->hists, thread)) {
2622 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
2623 thread->comm_set ? thread__comm_str(thread) : "",
2626 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
2627 thread->comm_set ? thread__comm_str(thread) : "");
2630 browser->hists->thread_filter = thread__get(thread);
2631 perf_hpp__set_elide(HISTC_THREAD, false);
2632 pstack__push(browser->pstack, &browser->hists->thread_filter);
2635 hists__filter_by_thread(browser->hists);
2636 hist_browser__reset(browser);
2641 add_thread_opt(struct hist_browser *browser, struct popup_action *act,
2642 char **optstr, struct thread *thread)
2646 if ((!hists__has(browser->hists, thread) &&
2647 !hists__has(browser->hists, comm)) || thread == NULL)
2650 if (hists__has(browser->hists, thread)) {
2651 ret = asprintf(optstr, "Zoom %s %s(%d) thread",
2652 browser->hists->thread_filter ? "out of" : "into",
2653 thread->comm_set ? thread__comm_str(thread) : "",
2656 ret = asprintf(optstr, "Zoom %s %s thread",
2657 browser->hists->thread_filter ? "out of" : "into",
2658 thread->comm_set ? thread__comm_str(thread) : "");
2663 act->thread = thread;
2664 act->fn = do_zoom_thread;
2669 do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
2671 struct map *map = act->ms.map;
2673 if (!hists__has(browser->hists, dso) || map == NULL)
2676 if (browser->hists->dso_filter) {
2677 pstack__remove(browser->pstack, &browser->hists->dso_filter);
2678 perf_hpp__set_elide(HISTC_DSO, false);
2679 browser->hists->dso_filter = NULL;
2682 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
2683 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
2684 browser->hists->dso_filter = map->dso;
2685 perf_hpp__set_elide(HISTC_DSO, true);
2686 pstack__push(browser->pstack, &browser->hists->dso_filter);
2689 hists__filter_by_dso(browser->hists);
2690 hist_browser__reset(browser);
2695 add_dso_opt(struct hist_browser *browser, struct popup_action *act,
2696 char **optstr, struct map *map)
2698 if (!hists__has(browser->hists, dso) || map == NULL)
2701 if (asprintf(optstr, "Zoom %s %s DSO",
2702 browser->hists->dso_filter ? "out of" : "into",
2703 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
2707 act->fn = do_zoom_dso;
2712 do_browse_map(struct hist_browser *browser __maybe_unused,
2713 struct popup_action *act)
2715 map__browse(act->ms.map);
2720 add_map_opt(struct hist_browser *browser,
2721 struct popup_action *act, char **optstr, struct map *map)
2723 if (!hists__has(browser->hists, dso) || map == NULL)
2726 if (asprintf(optstr, "Browse map details") < 0)
2730 act->fn = do_browse_map;
2735 do_run_script(struct hist_browser *browser __maybe_unused,
2736 struct popup_action *act)
2738 char script_opt[64];
2739 memset(script_opt, 0, sizeof(script_opt));
2742 scnprintf(script_opt, sizeof(script_opt), " -c %s ",
2743 thread__comm_str(act->thread));
2744 } else if (act->ms.sym) {
2745 scnprintf(script_opt, sizeof(script_opt), " -S %s ",
2749 script_browse(script_opt);
2754 add_script_opt(struct hist_browser *browser __maybe_unused,
2755 struct popup_action *act, char **optstr,
2756 struct thread *thread, struct symbol *sym)
2759 if (asprintf(optstr, "Run scripts for samples of thread [%s]",
2760 thread__comm_str(thread)) < 0)
2763 if (asprintf(optstr, "Run scripts for samples of symbol [%s]",
2767 if (asprintf(optstr, "Run scripts for all samples") < 0)
2771 act->thread = thread;
2773 act->fn = do_run_script;
2778 do_switch_data(struct hist_browser *browser __maybe_unused,
2779 struct popup_action *act __maybe_unused)
2781 if (switch_data_file()) {
2782 ui__warning("Won't switch the data files due to\n"
2783 "no valid data file get selected!\n");
2787 return K_SWITCH_INPUT_DATA;
2791 add_switch_opt(struct hist_browser *browser,
2792 struct popup_action *act, char **optstr)
2794 if (!is_report_browser(browser->hbt))
2797 if (asprintf(optstr, "Switch to another data file in PWD") < 0)
2800 act->fn = do_switch_data;
2805 do_exit_browser(struct hist_browser *browser __maybe_unused,
2806 struct popup_action *act __maybe_unused)
2812 add_exit_opt(struct hist_browser *browser __maybe_unused,
2813 struct popup_action *act, char **optstr)
2815 if (asprintf(optstr, "Exit") < 0)
2818 act->fn = do_exit_browser;
2823 do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
2825 if (!hists__has(browser->hists, socket) || act->socket < 0)
2828 if (browser->hists->socket_filter > -1) {
2829 pstack__remove(browser->pstack, &browser->hists->socket_filter);
2830 browser->hists->socket_filter = -1;
2831 perf_hpp__set_elide(HISTC_SOCKET, false);
2833 browser->hists->socket_filter = act->socket;
2834 perf_hpp__set_elide(HISTC_SOCKET, true);
2835 pstack__push(browser->pstack, &browser->hists->socket_filter);
2838 hists__filter_by_socket(browser->hists);
2839 hist_browser__reset(browser);
2844 add_socket_opt(struct hist_browser *browser, struct popup_action *act,
2845 char **optstr, int socket_id)
2847 if (!hists__has(browser->hists, socket) || socket_id < 0)
2850 if (asprintf(optstr, "Zoom %s Processor Socket %d",
2851 (browser->hists->socket_filter > -1) ? "out of" : "into",
2855 act->socket = socket_id;
2856 act->fn = do_zoom_socket;
2860 static void hist_browser__update_nr_entries(struct hist_browser *hb)
2863 struct rb_node *nd = rb_first(&hb->hists->entries);
2865 if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
2866 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
2870 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2872 nd = rb_hierarchy_next(nd);
2875 hb->nr_non_filtered_entries = nr_entries;
2876 hb->nr_hierarchy_entries = nr_entries;
2879 static void hist_browser__update_percent_limit(struct hist_browser *hb,
2882 struct hist_entry *he;
2883 struct rb_node *nd = rb_first(&hb->hists->entries);
2884 u64 total = hists__total_period(hb->hists);
2885 u64 min_callchain_hits = total * (percent / 100);
2887 hb->min_pcnt = callchain_param.min_percent = percent;
2889 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2890 he = rb_entry(nd, struct hist_entry, rb_node);
2892 if (he->has_no_entry) {
2893 he->has_no_entry = false;
2897 if (!he->leaf || !symbol_conf.use_callchain)
2900 if (callchain_param.mode == CHAIN_GRAPH_REL) {
2901 total = he->stat.period;
2903 if (symbol_conf.cumulate_callchain)
2904 total = he->stat_acc->period;
2906 min_callchain_hits = total * (percent / 100);
2909 callchain_param.sort(&he->sorted_chain, he->callchain,
2910 min_callchain_hits, &callchain_param);
2913 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
2915 /* force to re-evaluate folding state of callchains */
2916 he->init_have_children = false;
2917 hist_entry__set_folding(he, hb, false);
2921 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
2922 const char *helpline,
2924 struct hist_browser_timer *hbt,
2926 struct perf_env *env)
2928 struct hists *hists = evsel__hists(evsel);
2929 struct hist_browser *browser = perf_evsel_browser__new(evsel, hbt, env);
2930 struct branch_info *bi;
2931 #define MAX_OPTIONS 16
2932 char *options[MAX_OPTIONS];
2933 struct popup_action actions[MAX_OPTIONS];
2937 int delay_secs = hbt ? hbt->refresh : 0;
2939 #define HIST_BROWSER_HELP_COMMON \
2940 "h/?/F1 Show this window\n" \
2942 "PGDN/SPACE Navigate\n" \
2943 "q/ESC/CTRL+C Exit browser\n\n" \
2944 "For multiple event sessions:\n\n" \
2945 "TAB/UNTAB Switch events\n\n" \
2946 "For symbolic views (--sort has sym):\n\n" \
2947 "ENTER Zoom into DSO/Threads & Annotate current symbol\n" \
2949 "a Annotate current symbol\n" \
2950 "C Collapse all callchains\n" \
2951 "d Zoom into current DSO\n" \
2952 "E Expand all callchains\n" \
2953 "F Toggle percentage of filtered entries\n" \
2954 "H Display column headers\n" \
2955 "L Change percent limit\n" \
2956 "m Display context menu\n" \
2957 "S Zoom into current Processor Socket\n" \
2959 /* help messages are sorted by lexical order of the hotkey */
2960 const char report_help[] = HIST_BROWSER_HELP_COMMON
2961 "i Show header information\n"
2962 "P Print histograms to perf.hist.N\n"
2963 "r Run available scripts\n"
2964 "s Switch to another data file in PWD\n"
2965 "t Zoom into current Thread\n"
2966 "V Verbose (DSO names in callchains, etc)\n"
2967 "/ Filter symbol by name";
2968 const char top_help[] = HIST_BROWSER_HELP_COMMON
2969 "P Print histograms to perf.hist.N\n"
2970 "t Zoom into current Thread\n"
2971 "V Verbose (DSO names in callchains, etc)\n"
2972 "z Toggle zeroing of samples\n"
2973 "f Enable/Disable events\n"
2974 "/ Filter symbol by name";
2976 if (browser == NULL)
2979 /* reset abort key so that it can get Ctrl-C as a key */
2981 SLang_init_tty(0, 0, 0);
2984 browser->min_pcnt = min_pcnt;
2985 hist_browser__update_nr_entries(browser);
2987 browser->pstack = pstack__new(3);
2988 if (browser->pstack == NULL)
2991 ui_helpline__push(helpline);
2993 memset(options, 0, sizeof(options));
2994 memset(actions, 0, sizeof(actions));
2996 if (symbol_conf.col_width_list_str)
2997 perf_hpp__set_user_width(symbol_conf.col_width_list_str);
3000 struct thread *thread = NULL;
3001 struct map *map = NULL;
3007 key = hist_browser__run(browser, helpline);
3009 if (browser->he_selection != NULL) {
3010 thread = hist_browser__selected_thread(browser);
3011 map = browser->selection->map;
3012 socked_id = browser->he_selection->socket;
3020 * Exit the browser, let hists__browser_tree
3021 * go to the next or previous
3023 goto out_free_stack;
3025 if (!hists__has(hists, sym)) {
3026 ui_browser__warning(&browser->b, delay_secs * 2,
3027 "Annotation is only available for symbolic views, "
3028 "include \"sym*\" in --sort to use it.");
3032 if (browser->selection == NULL ||
3033 browser->selection->sym == NULL ||
3034 browser->selection->map->dso->annotate_warned)
3037 actions->ms.map = browser->selection->map;
3038 actions->ms.sym = browser->selection->sym;
3039 do_annotate(browser, actions);
3042 hist_browser__dump(browser);
3045 actions->ms.map = map;
3046 do_zoom_dso(browser, actions);
3049 verbose = (verbose + 1) % 4;
3050 browser->show_dso = verbose > 0;
3051 ui_helpline__fpush("Verbosity level set to %d\n",
3055 actions->thread = thread;
3056 do_zoom_thread(browser, actions);
3059 actions->socket = socked_id;
3060 do_zoom_socket(browser, actions);
3063 if (ui_browser__input_window("Symbol to show",
3064 "Please enter the name of symbol you want to see.\n"
3065 "To remove the filter later, press / + ENTER.",
3066 buf, "ENTER: OK, ESC: Cancel",
3067 delay_secs * 2) == K_ENTER) {
3068 hists->symbol_filter_str = *buf ? buf : NULL;
3069 hists__filter_by_symbol(hists);
3070 hist_browser__reset(browser);
3074 if (is_report_browser(hbt)) {
3075 actions->thread = NULL;
3076 actions->ms.sym = NULL;
3077 do_run_script(browser, actions);
3081 if (is_report_browser(hbt)) {
3082 key = do_switch_data(browser, actions);
3083 if (key == K_SWITCH_INPUT_DATA)
3084 goto out_free_stack;
3088 /* env->arch is NULL for live-mode (i.e. perf top) */
3090 tui__header_window(env);
3093 symbol_conf.filter_relative ^= 1;
3096 if (!is_report_browser(hbt)) {
3097 struct perf_top *top = hbt->arg;
3099 top->zero = !top->zero;
3103 if (ui_browser__input_window("Percent Limit",
3104 "Please enter the value you want to hide entries under that percent.",
3105 buf, "ENTER: OK, ESC: Cancel",
3106 delay_secs * 2) == K_ENTER) {
3108 double new_percent = strtod(buf, &end);
3110 if (new_percent < 0 || new_percent > 100) {
3111 ui_browser__warning(&browser->b, delay_secs * 2,
3112 "Invalid percent: %.2f", new_percent);
3116 hist_browser__update_percent_limit(browser, new_percent);
3117 hist_browser__reset(browser);
3123 ui_browser__help_window(&browser->b,
3124 is_report_browser(hbt) ? report_help : top_help);
3135 if (pstack__empty(browser->pstack)) {
3137 * Go back to the perf_evsel_menu__run or other user
3140 goto out_free_stack;
3143 ui_browser__dialog_yesno(&browser->b,
3144 "Do you really want to exit?"))
3145 goto out_free_stack;
3149 top = pstack__peek(browser->pstack);
3150 if (top == &browser->hists->dso_filter) {
3152 * No need to set actions->dso here since
3153 * it's just to remove the current filter.
3154 * Ditto for thread below.
3156 do_zoom_dso(browser, actions);
3157 } else if (top == &browser->hists->thread_filter) {
3158 do_zoom_thread(browser, actions);
3159 } else if (top == &browser->hists->socket_filter) {
3160 do_zoom_socket(browser, actions);
3166 goto out_free_stack;
3168 if (!is_report_browser(hbt)) {
3169 struct perf_top *top = hbt->arg;
3171 perf_evlist__toggle_enable(top->evlist);
3173 * No need to refresh, resort/decay histogram
3174 * entries if we are not collecting samples:
3176 if (top->evlist->enabled) {
3177 helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
3178 hbt->refresh = delay_secs;
3180 helpline = "Press 'f' again to re-enable the events";
3187 helpline = "Press '?' for help on key bindings";
3191 if (!hists__has(hists, sym) || browser->selection == NULL)
3192 goto skip_annotation;
3194 if (sort__mode == SORT_MODE__BRANCH) {
3195 bi = browser->he_selection->branch_info;
3198 goto skip_annotation;
3200 nr_options += add_annotate_opt(browser,
3201 &actions[nr_options],
3202 &options[nr_options],
3205 if (bi->to.sym != bi->from.sym)
3206 nr_options += add_annotate_opt(browser,
3207 &actions[nr_options],
3208 &options[nr_options],
3212 nr_options += add_annotate_opt(browser,
3213 &actions[nr_options],
3214 &options[nr_options],
3215 browser->selection->map,
3216 browser->selection->sym);
3219 nr_options += add_thread_opt(browser, &actions[nr_options],
3220 &options[nr_options], thread);
3221 nr_options += add_dso_opt(browser, &actions[nr_options],
3222 &options[nr_options], map);
3223 nr_options += add_map_opt(browser, &actions[nr_options],
3224 &options[nr_options],
3225 browser->selection ?
3226 browser->selection->map : NULL);
3227 nr_options += add_socket_opt(browser, &actions[nr_options],
3228 &options[nr_options],
3230 /* perf script support */
3231 if (!is_report_browser(hbt))
3232 goto skip_scripting;
3234 if (browser->he_selection) {
3235 if (hists__has(hists, thread) && thread) {
3236 nr_options += add_script_opt(browser,
3237 &actions[nr_options],
3238 &options[nr_options],
3242 * Note that browser->selection != NULL
3243 * when browser->he_selection is not NULL,
3244 * so we don't need to check browser->selection
3245 * before fetching browser->selection->sym like what
3246 * we do before fetching browser->selection->map.
3248 * See hist_browser__show_entry.
3250 if (hists__has(hists, sym) && browser->selection->sym) {
3251 nr_options += add_script_opt(browser,
3252 &actions[nr_options],
3253 &options[nr_options],
3254 NULL, browser->selection->sym);
3257 nr_options += add_script_opt(browser, &actions[nr_options],
3258 &options[nr_options], NULL, NULL);
3259 nr_options += add_switch_opt(browser, &actions[nr_options],
3260 &options[nr_options]);
3262 nr_options += add_exit_opt(browser, &actions[nr_options],
3263 &options[nr_options]);
3266 struct popup_action *act;
3268 choice = ui__popup_menu(nr_options, options);
3269 if (choice == -1 || choice >= nr_options)
3272 act = &actions[choice];
3273 key = act->fn(browser, act);
3276 if (key == K_SWITCH_INPUT_DATA)
3280 pstack__delete(browser->pstack);
3282 hist_browser__delete(browser);
3283 free_popup_options(options, MAX_OPTIONS);
3287 struct perf_evsel_menu {
3288 struct ui_browser b;
3289 struct perf_evsel *selection;
3290 bool lost_events, lost_events_warned;
3292 struct perf_env *env;
3295 static void perf_evsel_menu__write(struct ui_browser *browser,
3296 void *entry, int row)
3298 struct perf_evsel_menu *menu = container_of(browser,
3299 struct perf_evsel_menu, b);
3300 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
3301 struct hists *hists = evsel__hists(evsel);
3302 bool current_entry = ui_browser__is_current_entry(browser, row);
3303 unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
3304 const char *ev_name = perf_evsel__name(evsel);
3306 const char *warn = " ";
3309 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
3310 HE_COLORSET_NORMAL);
3312 if (perf_evsel__is_group_event(evsel)) {
3313 struct perf_evsel *pos;
3315 ev_name = perf_evsel__group_name(evsel);
3317 for_each_group_member(pos, evsel) {
3318 struct hists *pos_hists = evsel__hists(pos);
3319 nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
3323 nr_events = convert_unit(nr_events, &unit);
3324 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
3325 unit, unit == ' ' ? "" : " ", ev_name);
3326 ui_browser__printf(browser, "%s", bf);
3328 nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
3329 if (nr_events != 0) {
3330 menu->lost_events = true;
3332 ui_browser__set_color(browser, HE_COLORSET_TOP);
3333 nr_events = convert_unit(nr_events, &unit);
3334 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
3335 nr_events, unit, unit == ' ' ? "" : " ");
3339 ui_browser__write_nstring(browser, warn, browser->width - printed);
3342 menu->selection = evsel;
3345 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
3346 int nr_events, const char *help,
3347 struct hist_browser_timer *hbt)
3349 struct perf_evlist *evlist = menu->b.priv;
3350 struct perf_evsel *pos;
3351 const char *title = "Available samples";
3352 int delay_secs = hbt ? hbt->refresh : 0;
3355 if (ui_browser__show(&menu->b, title,
3356 "ESC: exit, ENTER|->: Browse histograms") < 0)
3360 key = ui_browser__run(&menu->b, delay_secs);
3364 hbt->timer(hbt->arg);
3366 if (!menu->lost_events_warned && menu->lost_events) {
3367 ui_browser__warn_lost_events(&menu->b);
3368 menu->lost_events_warned = true;
3373 if (!menu->selection)
3375 pos = menu->selection;
3377 perf_evlist__set_selected(evlist, pos);
3379 * Give the calling tool a chance to populate the non
3380 * default evsel resorted hists tree.
3383 hbt->timer(hbt->arg);
3384 key = perf_evsel__hists_browse(pos, nr_events, help,
3388 ui_browser__show_title(&menu->b, title);
3391 if (pos->node.next == &evlist->entries)
3392 pos = perf_evlist__first(evlist);
3394 pos = perf_evsel__next(pos);
3397 if (pos->node.prev == &evlist->entries)
3398 pos = perf_evlist__last(evlist);
3400 pos = perf_evsel__prev(pos);
3402 case K_SWITCH_INPUT_DATA:
3413 if (!ui_browser__dialog_yesno(&menu->b,
3414 "Do you really want to exit?"))
3426 ui_browser__hide(&menu->b);
3430 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
3433 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
3435 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
3441 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
3442 int nr_entries, const char *help,
3443 struct hist_browser_timer *hbt,
3445 struct perf_env *env)
3447 struct perf_evsel *pos;
3448 struct perf_evsel_menu menu = {
3450 .entries = &evlist->entries,
3451 .refresh = ui_browser__list_head_refresh,
3452 .seek = ui_browser__list_head_seek,
3453 .write = perf_evsel_menu__write,
3454 .filter = filter_group_entries,
3455 .nr_entries = nr_entries,
3458 .min_pcnt = min_pcnt,
3462 ui_helpline__push("Press ESC to exit");
3464 evlist__for_each_entry(evlist, pos) {
3465 const char *ev_name = perf_evsel__name(pos);
3466 size_t line_len = strlen(ev_name) + 7;
3468 if (menu.b.width < line_len)
3469 menu.b.width = line_len;
3472 return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
3475 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
3476 struct hist_browser_timer *hbt,
3478 struct perf_env *env)
3480 int nr_entries = evlist->nr_entries;
3483 if (nr_entries == 1) {
3484 struct perf_evsel *first = perf_evlist__first(evlist);
3486 return perf_evsel__hists_browse(first, nr_entries, help,
3487 false, hbt, min_pcnt,
3491 if (symbol_conf.event_group) {
3492 struct perf_evsel *pos;
3495 evlist__for_each_entry(evlist, pos) {
3496 if (perf_evsel__is_group_leader(pos))
3500 if (nr_entries == 1)
3504 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
3505 hbt, min_pcnt, env);