1 // SPDX-License-Identifier: GPL-2.0
8 #include <linux/rbtree.h>
9 #include <sys/ttydefaults.h>
10 #include <linux/time64.h>
12 #include "../../util/callchain.h"
13 #include "../../util/evsel.h"
14 #include "../../util/evlist.h"
15 #include "../../util/hist.h"
16 #include "../../util/map.h"
17 #include "../../util/symbol.h"
18 #include "../../util/pstack.h"
19 #include "../../util/sort.h"
20 #include "../../util/util.h"
21 #include "../../util/top.h"
22 #include "../../util/thread.h"
23 #include "../../arch/common.h"
25 #include "../browsers/hists.h"
26 #include "../helpline.h"
34 #include "time-utils.h"
36 #include "sane_ctype.h"
38 extern void hist_browser__init_hpp(void);
40 static int hists_browser__scnprintf_title(struct hist_browser *browser, char *bf, size_t size);
41 static void hist_browser__update_nr_entries(struct hist_browser *hb);
43 static struct rb_node *hists__filter_entries(struct rb_node *nd,
46 static bool hist_browser__has_filter(struct hist_browser *hb)
48 return hists__has_filter(hb->hists) || hb->min_pcnt || symbol_conf.has_filter || hb->c2c_filter;
51 static int hist_browser__get_folding(struct hist_browser *browser)
54 struct hists *hists = browser->hists;
55 int unfolded_rows = 0;
57 for (nd = rb_first_cached(&hists->entries);
58 (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
59 nd = rb_hierarchy_next(nd)) {
60 struct hist_entry *he =
61 rb_entry(nd, struct hist_entry, rb_node);
63 if (he->leaf && he->unfolded)
64 unfolded_rows += he->nr_rows;
69 static void hist_browser__set_title_space(struct hist_browser *hb)
71 struct ui_browser *browser = &hb->b;
72 struct hists *hists = hb->hists;
73 struct perf_hpp_list *hpp_list = hists->hpp_list;
75 browser->extra_title_lines = hb->show_headers ? hpp_list->nr_header_lines : 0;
78 static u32 hist_browser__nr_entries(struct hist_browser *hb)
82 if (symbol_conf.report_hierarchy)
83 nr_entries = hb->nr_hierarchy_entries;
84 else if (hist_browser__has_filter(hb))
85 nr_entries = hb->nr_non_filtered_entries;
87 nr_entries = hb->hists->nr_entries;
89 hb->nr_callchain_rows = hist_browser__get_folding(hb);
90 return nr_entries + hb->nr_callchain_rows;
93 static void hist_browser__update_rows(struct hist_browser *hb)
95 struct ui_browser *browser = &hb->b;
96 struct hists *hists = hb->hists;
97 struct perf_hpp_list *hpp_list = hists->hpp_list;
100 if (!hb->show_headers) {
101 browser->rows += browser->extra_title_lines;
102 browser->extra_title_lines = 0;
106 browser->extra_title_lines = hpp_list->nr_header_lines;
107 browser->rows -= browser->extra_title_lines;
109 * Verify if we were at the last line and that line isn't
110 * visibe because we now show the header line(s).
112 index_row = browser->index - browser->top_idx;
113 if (index_row >= browser->rows)
114 browser->index -= index_row - browser->rows + 1;
117 static void hist_browser__refresh_dimensions(struct ui_browser *browser)
119 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
121 /* 3 == +/- toggle symbol before actual hist_entry rendering */
122 browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]"));
124 * FIXME: Just keeping existing behaviour, but this really should be
125 * before updating browser->width, as it will invalidate the
126 * calculation above. Fix this and the fallout in another
129 ui_browser__refresh_dimensions(browser);
132 static void hist_browser__reset(struct hist_browser *browser)
135 * The hists__remove_entry_filter() already folds non-filtered
136 * entries so we can assume it has 0 callchain rows.
138 browser->nr_callchain_rows = 0;
140 hist_browser__update_nr_entries(browser);
141 browser->b.nr_entries = hist_browser__nr_entries(browser);
142 hist_browser__refresh_dimensions(&browser->b);
143 ui_browser__reset_index(&browser->b);
146 static char tree__folded_sign(bool unfolded)
148 return unfolded ? '-' : '+';
151 static char hist_entry__folded(const struct hist_entry *he)
153 return he->has_children ? tree__folded_sign(he->unfolded) : ' ';
156 static char callchain_list__folded(const struct callchain_list *cl)
158 return cl->has_children ? tree__folded_sign(cl->unfolded) : ' ';
161 static void callchain_list__set_folding(struct callchain_list *cl, bool unfold)
163 cl->unfolded = unfold ? cl->has_children : false;
166 static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
171 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
172 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
173 struct callchain_list *chain;
174 char folded_sign = ' '; /* No children */
176 list_for_each_entry(chain, &child->val, list) {
179 /* We need this because we may not have children */
180 folded_sign = callchain_list__folded(chain);
181 if (folded_sign == '+')
185 if (folded_sign == '-') /* Have children and they're unfolded */
186 n += callchain_node__count_rows_rb_tree(child);
192 static int callchain_node__count_flat_rows(struct callchain_node *node)
194 struct callchain_list *chain;
195 char folded_sign = 0;
198 list_for_each_entry(chain, &node->parent_val, list) {
200 /* only check first chain list entry */
201 folded_sign = callchain_list__folded(chain);
202 if (folded_sign == '+')
208 list_for_each_entry(chain, &node->val, list) {
210 /* node->parent_val list might be empty */
211 folded_sign = callchain_list__folded(chain);
212 if (folded_sign == '+')
221 static int callchain_node__count_folded_rows(struct callchain_node *node __maybe_unused)
226 static int callchain_node__count_rows(struct callchain_node *node)
228 struct callchain_list *chain;
229 bool unfolded = false;
232 if (callchain_param.mode == CHAIN_FLAT)
233 return callchain_node__count_flat_rows(node);
234 else if (callchain_param.mode == CHAIN_FOLDED)
235 return callchain_node__count_folded_rows(node);
237 list_for_each_entry(chain, &node->val, list) {
240 unfolded = chain->unfolded;
244 n += callchain_node__count_rows_rb_tree(node);
249 static int callchain__count_rows(struct rb_root *chain)
254 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
255 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
256 n += callchain_node__count_rows(node);
262 static int hierarchy_count_rows(struct hist_browser *hb, struct hist_entry *he,
263 bool include_children)
266 struct rb_node *node;
267 struct hist_entry *child;
270 return callchain__count_rows(&he->sorted_chain);
272 if (he->has_no_entry)
275 node = rb_first_cached(&he->hroot_out);
279 child = rb_entry(node, struct hist_entry, rb_node);
280 percent = hist_entry__get_percent_limit(child);
282 if (!child->filtered && percent >= hb->min_pcnt) {
285 if (include_children && child->unfolded)
286 count += hierarchy_count_rows(hb, child, true);
289 node = rb_next(node);
294 static bool hist_entry__toggle_fold(struct hist_entry *he)
299 if (!he->has_children)
302 he->unfolded = !he->unfolded;
306 static bool callchain_list__toggle_fold(struct callchain_list *cl)
311 if (!cl->has_children)
314 cl->unfolded = !cl->unfolded;
318 static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
320 struct rb_node *nd = rb_first(&node->rb_root);
322 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
323 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
324 struct callchain_list *chain;
327 list_for_each_entry(chain, &child->val, list) {
330 chain->has_children = chain->list.next != &child->val ||
331 !RB_EMPTY_ROOT(&child->rb_root);
333 chain->has_children = chain->list.next == &child->val &&
334 !RB_EMPTY_ROOT(&child->rb_root);
337 callchain_node__init_have_children_rb_tree(child);
341 static void callchain_node__init_have_children(struct callchain_node *node,
344 struct callchain_list *chain;
346 chain = list_entry(node->val.next, struct callchain_list, list);
347 chain->has_children = has_sibling;
349 if (!list_empty(&node->val)) {
350 chain = list_entry(node->val.prev, struct callchain_list, list);
351 chain->has_children = !RB_EMPTY_ROOT(&node->rb_root);
354 callchain_node__init_have_children_rb_tree(node);
357 static void callchain__init_have_children(struct rb_root *root)
359 struct rb_node *nd = rb_first(root);
360 bool has_sibling = nd && rb_next(nd);
362 for (nd = rb_first(root); nd; nd = rb_next(nd)) {
363 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
364 callchain_node__init_have_children(node, has_sibling);
365 if (callchain_param.mode == CHAIN_FLAT ||
366 callchain_param.mode == CHAIN_FOLDED)
367 callchain_node__make_parent_list(node);
371 static void hist_entry__init_have_children(struct hist_entry *he)
373 if (he->init_have_children)
377 he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
378 callchain__init_have_children(&he->sorted_chain);
380 he->has_children = !RB_EMPTY_ROOT(&he->hroot_out.rb_root);
383 he->init_have_children = true;
386 static bool hist_browser__toggle_fold(struct hist_browser *browser)
388 struct hist_entry *he = browser->he_selection;
389 struct map_symbol *ms = browser->selection;
390 struct callchain_list *cl = container_of(ms, struct callchain_list, ms);
397 has_children = hist_entry__toggle_fold(he);
399 has_children = callchain_list__toggle_fold(cl);
404 hist_entry__init_have_children(he);
405 browser->b.nr_entries -= he->nr_rows;
408 browser->nr_callchain_rows -= he->nr_rows;
410 browser->nr_hierarchy_entries -= he->nr_rows;
412 if (symbol_conf.report_hierarchy)
413 child_rows = hierarchy_count_rows(browser, he, true);
417 he->nr_rows = callchain__count_rows(
420 he->nr_rows = hierarchy_count_rows(browser, he, false);
422 /* account grand children */
423 if (symbol_conf.report_hierarchy)
424 browser->b.nr_entries += child_rows - he->nr_rows;
426 if (!he->leaf && he->nr_rows == 0) {
427 he->has_no_entry = true;
431 if (symbol_conf.report_hierarchy)
432 browser->b.nr_entries -= child_rows - he->nr_rows;
434 if (he->has_no_entry)
435 he->has_no_entry = false;
440 browser->b.nr_entries += he->nr_rows;
443 browser->nr_callchain_rows += he->nr_rows;
445 browser->nr_hierarchy_entries += he->nr_rows;
450 /* If it doesn't have children, no toggling performed */
454 static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
459 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
460 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
461 struct callchain_list *chain;
462 bool has_children = false;
464 list_for_each_entry(chain, &child->val, list) {
466 callchain_list__set_folding(chain, unfold);
467 has_children = chain->has_children;
471 n += callchain_node__set_folding_rb_tree(child, unfold);
477 static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
479 struct callchain_list *chain;
480 bool has_children = false;
483 list_for_each_entry(chain, &node->val, list) {
485 callchain_list__set_folding(chain, unfold);
486 has_children = chain->has_children;
490 n += callchain_node__set_folding_rb_tree(node, unfold);
495 static int callchain__set_folding(struct rb_root *chain, bool unfold)
500 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
501 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
502 n += callchain_node__set_folding(node, unfold);
508 static int hierarchy_set_folding(struct hist_browser *hb, struct hist_entry *he,
509 bool unfold __maybe_unused)
513 struct hist_entry *child;
516 for (nd = rb_first_cached(&he->hroot_out); nd; nd = rb_next(nd)) {
517 child = rb_entry(nd, struct hist_entry, rb_node);
518 percent = hist_entry__get_percent_limit(child);
519 if (!child->filtered && percent >= hb->min_pcnt)
526 static void __hist_entry__set_folding(struct hist_entry *he,
527 struct hist_browser *hb, bool unfold)
529 hist_entry__init_have_children(he);
530 he->unfolded = unfold ? he->has_children : false;
532 if (he->has_children) {
536 n = callchain__set_folding(&he->sorted_chain, unfold);
538 n = hierarchy_set_folding(hb, he, unfold);
540 he->nr_rows = unfold ? n : 0;
545 static void hist_entry__set_folding(struct hist_entry *he,
546 struct hist_browser *browser, bool unfold)
550 percent = hist_entry__get_percent_limit(he);
551 if (he->filtered || percent < browser->min_pcnt)
554 __hist_entry__set_folding(he, browser, unfold);
556 if (!he->depth || unfold)
557 browser->nr_hierarchy_entries++;
559 browser->nr_callchain_rows += he->nr_rows;
560 else if (unfold && !hist_entry__has_hierarchy_children(he, browser->min_pcnt)) {
561 browser->nr_hierarchy_entries++;
562 he->has_no_entry = true;
565 he->has_no_entry = false;
569 __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
572 struct hist_entry *he;
574 nd = rb_first_cached(&browser->hists->entries);
576 he = rb_entry(nd, struct hist_entry, rb_node);
578 /* set folding state even if it's currently folded */
579 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
581 hist_entry__set_folding(he, browser, unfold);
585 static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
587 browser->nr_hierarchy_entries = 0;
588 browser->nr_callchain_rows = 0;
589 __hist_browser__set_folding(browser, unfold);
591 browser->b.nr_entries = hist_browser__nr_entries(browser);
592 /* Go to the start, we may be way after valid entries after a collapse */
593 ui_browser__reset_index(&browser->b);
596 static void hist_browser__set_folding_selected(struct hist_browser *browser, bool unfold)
598 if (!browser->he_selection)
601 hist_entry__set_folding(browser->he_selection, browser, unfold);
602 browser->b.nr_entries = hist_browser__nr_entries(browser);
605 static void ui_browser__warn_lost_events(struct ui_browser *browser)
607 ui_browser__warning(browser, 4,
608 "Events are being lost, check IO/CPU overload!\n\n"
609 "You may want to run 'perf' using a RT scheduler policy:\n\n"
610 " perf top -r 80\n\n"
611 "Or reduce the sampling frequency.");
614 static int hist_browser__title(struct hist_browser *browser, char *bf, size_t size)
616 return browser->title ? browser->title(browser, bf, size) : 0;
619 int hist_browser__run(struct hist_browser *browser, const char *help,
620 bool warn_lost_event)
624 struct hist_browser_timer *hbt = browser->hbt;
625 int delay_secs = hbt ? hbt->refresh : 0;
627 browser->b.entries = &browser->hists->entries;
628 browser->b.nr_entries = hist_browser__nr_entries(browser);
630 hist_browser__title(browser, title, sizeof(title));
632 if (ui_browser__show(&browser->b, title, "%s", help) < 0)
636 key = ui_browser__run(&browser->b, delay_secs);
641 hbt->timer(hbt->arg);
643 if (hist_browser__has_filter(browser) ||
644 symbol_conf.report_hierarchy)
645 hist_browser__update_nr_entries(browser);
647 nr_entries = hist_browser__nr_entries(browser);
648 ui_browser__update_nr_entries(&browser->b, nr_entries);
650 if (warn_lost_event &&
651 (browser->hists->stats.nr_lost_warned !=
652 browser->hists->stats.nr_events[PERF_RECORD_LOST])) {
653 browser->hists->stats.nr_lost_warned =
654 browser->hists->stats.nr_events[PERF_RECORD_LOST];
655 ui_browser__warn_lost_events(&browser->b);
658 hist_browser__title(browser, title, sizeof(title));
659 ui_browser__show_title(&browser->b, title);
662 case 'D': { /* Debug */
664 struct hist_entry *h = rb_entry(browser->b.top,
665 struct hist_entry, rb_node);
667 ui_helpline__fpush("%d: nr_ent=(%d,%d), etl: %d, rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
668 seq++, browser->b.nr_entries,
669 browser->hists->nr_entries,
670 browser->b.extra_title_lines,
674 h->row_offset, h->nr_rows);
678 /* Collapse the whole world. */
679 hist_browser__set_folding(browser, false);
682 /* Collapse the selected entry. */
683 hist_browser__set_folding_selected(browser, false);
686 /* Expand the whole world. */
687 hist_browser__set_folding(browser, true);
690 /* Expand the selected entry. */
691 hist_browser__set_folding_selected(browser, true);
694 browser->show_headers = !browser->show_headers;
695 hist_browser__update_rows(browser);
698 if (hist_browser__toggle_fold(browser))
706 ui_browser__hide(&browser->b);
710 struct callchain_print_arg {
711 /* for hists browser */
713 bool is_current_entry;
720 typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
721 struct callchain_list *chain,
722 const char *str, int offset,
724 struct callchain_print_arg *arg);
726 static void hist_browser__show_callchain_entry(struct hist_browser *browser,
727 struct callchain_list *chain,
728 const char *str, int offset,
730 struct callchain_print_arg *arg)
733 char folded_sign = callchain_list__folded(chain);
734 bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
736 color = HE_COLORSET_NORMAL;
737 width = browser->b.width - (offset + 2);
738 if (ui_browser__is_current_entry(&browser->b, row)) {
739 browser->selection = &chain->ms;
740 color = HE_COLORSET_SELECTED;
741 arg->is_current_entry = true;
744 ui_browser__set_color(&browser->b, color);
745 ui_browser__gotorc(&browser->b, row, 0);
746 ui_browser__write_nstring(&browser->b, " ", offset);
747 ui_browser__printf(&browser->b, "%c", folded_sign);
748 ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
749 ui_browser__write_nstring(&browser->b, str, width);
752 static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
753 struct callchain_list *chain,
754 const char *str, int offset,
755 unsigned short row __maybe_unused,
756 struct callchain_print_arg *arg)
758 char folded_sign = callchain_list__folded(chain);
760 arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
764 typedef bool (*check_output_full_fn)(struct hist_browser *browser,
767 static bool hist_browser__check_output_full(struct hist_browser *browser,
770 return browser->b.rows == row;
773 static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
774 unsigned short row __maybe_unused)
779 #define LEVEL_OFFSET_STEP 3
781 static int hist_browser__show_callchain_list(struct hist_browser *browser,
782 struct callchain_node *node,
783 struct callchain_list *chain,
784 unsigned short row, u64 total,
785 bool need_percent, int offset,
786 print_callchain_entry_fn print,
787 struct callchain_print_arg *arg)
789 char bf[1024], *alloc_str;
790 char buf[64], *alloc_str2;
794 if (arg->row_offset != 0) {
802 str = callchain_list__sym_name(chain, bf, sizeof(bf),
805 if (symbol_conf.show_branchflag_count) {
806 callchain_list_counts__printf_value(chain, NULL,
809 if (asprintf(&alloc_str2, "%s%s", str, buf) < 0)
810 str = "Not enough memory!";
816 callchain_node__scnprintf_value(node, buf, sizeof(buf),
819 if (asprintf(&alloc_str, "%s %s", buf, str) < 0)
820 str = "Not enough memory!";
825 print(browser, chain, str, offset, row, arg);
832 static bool check_percent_display(struct rb_node *node, u64 parent_total)
834 struct callchain_node *child;
842 child = rb_entry(node, struct callchain_node, rb_node);
843 return callchain_cumul_hits(child) != parent_total;
846 static int hist_browser__show_callchain_flat(struct hist_browser *browser,
847 struct rb_root *root,
848 unsigned short row, u64 total,
850 print_callchain_entry_fn print,
851 struct callchain_print_arg *arg,
852 check_output_full_fn is_output_full)
854 struct rb_node *node;
855 int first_row = row, offset = LEVEL_OFFSET_STEP;
858 node = rb_first(root);
859 need_percent = check_percent_display(node, parent_total);
862 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
863 struct rb_node *next = rb_next(node);
864 struct callchain_list *chain;
865 char folded_sign = ' ';
867 int extra_offset = 0;
869 list_for_each_entry(chain, &child->parent_val, list) {
870 bool was_first = first;
874 else if (need_percent)
875 extra_offset = LEVEL_OFFSET_STEP;
877 folded_sign = callchain_list__folded(chain);
879 row += hist_browser__show_callchain_list(browser, child,
881 was_first && need_percent,
882 offset + extra_offset,
885 if (is_output_full(browser, row))
888 if (folded_sign == '+')
892 list_for_each_entry(chain, &child->val, list) {
893 bool was_first = first;
897 else if (need_percent)
898 extra_offset = LEVEL_OFFSET_STEP;
900 folded_sign = callchain_list__folded(chain);
902 row += hist_browser__show_callchain_list(browser, child,
904 was_first && need_percent,
905 offset + extra_offset,
908 if (is_output_full(browser, row))
911 if (folded_sign == '+')
916 if (is_output_full(browser, row))
921 return row - first_row;
924 static char *hist_browser__folded_callchain_str(struct hist_browser *browser,
925 struct callchain_list *chain,
926 char *value_str, char *old_str)
932 str = callchain_list__sym_name(chain, bf, sizeof(bf),
935 if (asprintf(&new, "%s%s%s", old_str,
936 symbol_conf.field_sep ?: ";", str) < 0)
940 if (asprintf(&new, "%s %s", value_str, str) < 0)
943 if (asprintf(&new, "%s", str) < 0)
950 static int hist_browser__show_callchain_folded(struct hist_browser *browser,
951 struct rb_root *root,
952 unsigned short row, u64 total,
954 print_callchain_entry_fn print,
955 struct callchain_print_arg *arg,
956 check_output_full_fn is_output_full)
958 struct rb_node *node;
959 int first_row = row, offset = LEVEL_OFFSET_STEP;
962 node = rb_first(root);
963 need_percent = check_percent_display(node, parent_total);
966 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
967 struct rb_node *next = rb_next(node);
968 struct callchain_list *chain, *first_chain = NULL;
970 char *value_str = NULL, *value_str_alloc = NULL;
971 char *chain_str = NULL, *chain_str_alloc = NULL;
973 if (arg->row_offset != 0) {
981 callchain_node__scnprintf_value(child, buf, sizeof(buf), total);
982 if (asprintf(&value_str, "%s", buf) < 0) {
983 value_str = (char *)"<...>";
986 value_str_alloc = value_str;
989 list_for_each_entry(chain, &child->parent_val, list) {
990 chain_str = hist_browser__folded_callchain_str(browser,
991 chain, value_str, chain_str);
997 if (chain_str == NULL) {
998 chain_str = (char *)"Not enough memory!";
1002 chain_str_alloc = chain_str;
1005 list_for_each_entry(chain, &child->val, list) {
1006 chain_str = hist_browser__folded_callchain_str(browser,
1007 chain, value_str, chain_str);
1010 first_chain = chain;
1013 if (chain_str == NULL) {
1014 chain_str = (char *)"Not enough memory!";
1018 chain_str_alloc = chain_str;
1022 print(browser, first_chain, chain_str, offset, row++, arg);
1023 free(value_str_alloc);
1024 free(chain_str_alloc);
1027 if (is_output_full(browser, row))
1032 return row - first_row;
1035 static int hist_browser__show_callchain_graph(struct hist_browser *browser,
1036 struct rb_root *root, int level,
1037 unsigned short row, u64 total,
1039 print_callchain_entry_fn print,
1040 struct callchain_print_arg *arg,
1041 check_output_full_fn is_output_full)
1043 struct rb_node *node;
1044 int first_row = row, offset = level * LEVEL_OFFSET_STEP;
1046 u64 percent_total = total;
1048 if (callchain_param.mode == CHAIN_GRAPH_REL)
1049 percent_total = parent_total;
1051 node = rb_first(root);
1052 need_percent = check_percent_display(node, parent_total);
1055 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1056 struct rb_node *next = rb_next(node);
1057 struct callchain_list *chain;
1058 char folded_sign = ' ';
1060 int extra_offset = 0;
1062 list_for_each_entry(chain, &child->val, list) {
1063 bool was_first = first;
1067 else if (need_percent)
1068 extra_offset = LEVEL_OFFSET_STEP;
1070 folded_sign = callchain_list__folded(chain);
1072 row += hist_browser__show_callchain_list(browser, child,
1073 chain, row, percent_total,
1074 was_first && need_percent,
1075 offset + extra_offset,
1078 if (is_output_full(browser, row))
1081 if (folded_sign == '+')
1085 if (folded_sign == '-') {
1086 const int new_level = level + (extra_offset ? 2 : 1);
1088 row += hist_browser__show_callchain_graph(browser, &child->rb_root,
1089 new_level, row, total,
1090 child->children_hit,
1091 print, arg, is_output_full);
1093 if (is_output_full(browser, row))
1098 return row - first_row;
1101 static int hist_browser__show_callchain(struct hist_browser *browser,
1102 struct hist_entry *entry, int level,
1104 print_callchain_entry_fn print,
1105 struct callchain_print_arg *arg,
1106 check_output_full_fn is_output_full)
1108 u64 total = hists__total_period(entry->hists);
1112 if (symbol_conf.cumulate_callchain)
1113 parent_total = entry->stat_acc->period;
1115 parent_total = entry->stat.period;
1117 if (callchain_param.mode == CHAIN_FLAT) {
1118 printed = hist_browser__show_callchain_flat(browser,
1119 &entry->sorted_chain, row,
1120 total, parent_total, print, arg,
1122 } else if (callchain_param.mode == CHAIN_FOLDED) {
1123 printed = hist_browser__show_callchain_folded(browser,
1124 &entry->sorted_chain, row,
1125 total, parent_total, print, arg,
1128 printed = hist_browser__show_callchain_graph(browser,
1129 &entry->sorted_chain, level, row,
1130 total, parent_total, print, arg,
1134 if (arg->is_current_entry)
1135 browser->he_selection = entry;
1141 struct ui_browser *b;
1146 int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
1148 struct hpp_arg *arg = hpp->ptr;
1153 va_start(args, fmt);
1154 len = va_arg(args, int);
1155 percent = va_arg(args, double);
1158 ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
1160 ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
1161 ui_browser__printf(arg->b, "%s", hpp->buf);
1166 #define __HPP_COLOR_PERCENT_FN(_type, _field) \
1167 static u64 __hpp_get_##_field(struct hist_entry *he) \
1169 return he->stat._field; \
1173 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
1174 struct perf_hpp *hpp, \
1175 struct hist_entry *he) \
1177 return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%", \
1178 __hpp__slsmg_color_printf, true); \
1181 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
1182 static u64 __hpp_get_acc_##_field(struct hist_entry *he) \
1184 return he->stat_acc->_field; \
1188 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
1189 struct perf_hpp *hpp, \
1190 struct hist_entry *he) \
1192 if (!symbol_conf.cumulate_callchain) { \
1193 struct hpp_arg *arg = hpp->ptr; \
1194 int len = fmt->user_len ?: fmt->len; \
1195 int ret = scnprintf(hpp->buf, hpp->size, \
1196 "%*s", len, "N/A"); \
1197 ui_browser__printf(arg->b, "%s", hpp->buf); \
1201 return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field, \
1202 " %*.2f%%", __hpp__slsmg_color_printf, true); \
1205 __HPP_COLOR_PERCENT_FN(overhead, period)
1206 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
1207 __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
1208 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
1209 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
1210 __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
1212 #undef __HPP_COLOR_PERCENT_FN
1213 #undef __HPP_COLOR_ACC_PERCENT_FN
1215 void hist_browser__init_hpp(void)
1217 perf_hpp__format[PERF_HPP__OVERHEAD].color =
1218 hist_browser__hpp_color_overhead;
1219 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
1220 hist_browser__hpp_color_overhead_sys;
1221 perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
1222 hist_browser__hpp_color_overhead_us;
1223 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
1224 hist_browser__hpp_color_overhead_guest_sys;
1225 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
1226 hist_browser__hpp_color_overhead_guest_us;
1227 perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
1228 hist_browser__hpp_color_overhead_acc;
1233 static int hist_browser__show_entry(struct hist_browser *browser,
1234 struct hist_entry *entry,
1238 int width = browser->b.width;
1239 char folded_sign = ' ';
1240 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1241 bool use_callchain = hist_entry__has_callchains(entry) && symbol_conf.use_callchain;
1242 off_t row_offset = entry->row_offset;
1244 struct perf_hpp_fmt *fmt;
1246 if (current_entry) {
1247 browser->he_selection = entry;
1248 browser->selection = &entry->ms;
1251 if (use_callchain) {
1252 hist_entry__init_have_children(entry);
1253 folded_sign = hist_entry__folded(entry);
1256 if (row_offset == 0) {
1257 struct hpp_arg arg = {
1259 .folded_sign = folded_sign,
1260 .current_entry = current_entry,
1264 ui_browser__gotorc(&browser->b, row, 0);
1266 hists__for_each_format(browser->hists, fmt) {
1268 struct perf_hpp hpp = {
1274 if (perf_hpp__should_skip(fmt, entry->hists) ||
1275 column++ < browser->b.horiz_scroll)
1278 if (current_entry && browser->b.navkeypressed) {
1279 ui_browser__set_color(&browser->b,
1280 HE_COLORSET_SELECTED);
1282 ui_browser__set_color(&browser->b,
1283 HE_COLORSET_NORMAL);
1287 if (use_callchain) {
1288 ui_browser__printf(&browser->b, "%c ", folded_sign);
1293 ui_browser__printf(&browser->b, " ");
1298 int ret = fmt->color(fmt, &hpp, entry);
1299 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1301 * fmt->color() already used ui_browser to
1302 * print the non alignment bits, skip it (+ret):
1304 ui_browser__printf(&browser->b, "%s", s + ret);
1306 hist_entry__snprintf_alignment(entry, &hpp, fmt, fmt->entry(fmt, &hpp, entry));
1307 ui_browser__printf(&browser->b, "%s", s);
1309 width -= hpp.buf - s;
1312 /* The scroll bar isn't being used */
1313 if (!browser->b.navkeypressed)
1316 ui_browser__write_nstring(&browser->b, "", width);
1323 if (folded_sign == '-' && row != browser->b.rows) {
1324 struct callchain_print_arg arg = {
1325 .row_offset = row_offset,
1326 .is_current_entry = current_entry,
1329 printed += hist_browser__show_callchain(browser,
1331 hist_browser__show_callchain_entry,
1333 hist_browser__check_output_full);
1339 static int hist_browser__show_hierarchy_entry(struct hist_browser *browser,
1340 struct hist_entry *entry,
1345 int width = browser->b.width;
1346 char folded_sign = ' ';
1347 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1348 off_t row_offset = entry->row_offset;
1350 struct perf_hpp_fmt *fmt;
1351 struct perf_hpp_list_node *fmt_node;
1352 struct hpp_arg arg = {
1354 .current_entry = current_entry,
1357 int hierarchy_indent = (entry->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
1359 if (current_entry) {
1360 browser->he_selection = entry;
1361 browser->selection = &entry->ms;
1364 hist_entry__init_have_children(entry);
1365 folded_sign = hist_entry__folded(entry);
1366 arg.folded_sign = folded_sign;
1368 if (entry->leaf && row_offset) {
1370 goto show_callchain;
1373 ui_browser__gotorc(&browser->b, row, 0);
1375 if (current_entry && browser->b.navkeypressed)
1376 ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1378 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1380 ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1381 width -= level * HIERARCHY_INDENT;
1383 /* the first hpp_list_node is for overhead columns */
1384 fmt_node = list_first_entry(&entry->hists->hpp_formats,
1385 struct perf_hpp_list_node, list);
1386 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1388 struct perf_hpp hpp = {
1394 if (perf_hpp__should_skip(fmt, entry->hists) ||
1395 column++ < browser->b.horiz_scroll)
1398 if (current_entry && browser->b.navkeypressed) {
1399 ui_browser__set_color(&browser->b,
1400 HE_COLORSET_SELECTED);
1402 ui_browser__set_color(&browser->b,
1403 HE_COLORSET_NORMAL);
1407 ui_browser__printf(&browser->b, "%c ", folded_sign);
1411 ui_browser__printf(&browser->b, " ");
1416 int ret = fmt->color(fmt, &hpp, entry);
1417 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1419 * fmt->color() already used ui_browser to
1420 * print the non alignment bits, skip it (+ret):
1422 ui_browser__printf(&browser->b, "%s", s + ret);
1424 int ret = fmt->entry(fmt, &hpp, entry);
1425 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1426 ui_browser__printf(&browser->b, "%s", s);
1428 width -= hpp.buf - s;
1432 ui_browser__write_nstring(&browser->b, "", hierarchy_indent);
1433 width -= hierarchy_indent;
1436 if (column >= browser->b.horiz_scroll) {
1438 struct perf_hpp hpp = {
1444 if (current_entry && browser->b.navkeypressed) {
1445 ui_browser__set_color(&browser->b,
1446 HE_COLORSET_SELECTED);
1448 ui_browser__set_color(&browser->b,
1449 HE_COLORSET_NORMAL);
1452 perf_hpp_list__for_each_format(entry->hpp_list, fmt) {
1454 ui_browser__printf(&browser->b, "%c ", folded_sign);
1457 ui_browser__write_nstring(&browser->b, "", 2);
1463 * No need to call hist_entry__snprintf_alignment()
1464 * since this fmt is always the last column in the
1468 width -= fmt->color(fmt, &hpp, entry);
1472 width -= fmt->entry(fmt, &hpp, entry);
1473 ui_browser__printf(&browser->b, "%s", ltrim(s));
1475 while (isspace(s[i++]))
1481 /* The scroll bar isn't being used */
1482 if (!browser->b.navkeypressed)
1485 ui_browser__write_nstring(&browser->b, "", width);
1491 if (entry->leaf && folded_sign == '-' && row != browser->b.rows) {
1492 struct callchain_print_arg carg = {
1493 .row_offset = row_offset,
1496 printed += hist_browser__show_callchain(browser, entry,
1498 hist_browser__show_callchain_entry, &carg,
1499 hist_browser__check_output_full);
1505 static int hist_browser__show_no_entry(struct hist_browser *browser,
1506 unsigned short row, int level)
1508 int width = browser->b.width;
1509 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1513 struct perf_hpp_fmt *fmt;
1514 struct perf_hpp_list_node *fmt_node;
1515 int indent = browser->hists->nr_hpp_node - 2;
1517 if (current_entry) {
1518 browser->he_selection = NULL;
1519 browser->selection = NULL;
1522 ui_browser__gotorc(&browser->b, row, 0);
1524 if (current_entry && browser->b.navkeypressed)
1525 ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1527 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1529 ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1530 width -= level * HIERARCHY_INDENT;
1532 /* the first hpp_list_node is for overhead columns */
1533 fmt_node = list_first_entry(&browser->hists->hpp_formats,
1534 struct perf_hpp_list_node, list);
1535 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1536 if (perf_hpp__should_skip(fmt, browser->hists) ||
1537 column++ < browser->b.horiz_scroll)
1540 ret = fmt->width(fmt, NULL, browser->hists);
1543 /* for folded sign */
1547 /* space between columns */
1551 ui_browser__write_nstring(&browser->b, "", ret);
1555 ui_browser__write_nstring(&browser->b, "", indent * HIERARCHY_INDENT);
1556 width -= indent * HIERARCHY_INDENT;
1558 if (column >= browser->b.horiz_scroll) {
1561 ret = snprintf(buf, sizeof(buf), "no entry >= %.2f%%", browser->min_pcnt);
1562 ui_browser__printf(&browser->b, " %s", buf);
1566 /* The scroll bar isn't being used */
1567 if (!browser->b.navkeypressed)
1570 ui_browser__write_nstring(&browser->b, "", width);
1574 static int advance_hpp_check(struct perf_hpp *hpp, int inc)
1576 advance_hpp(hpp, inc);
1577 return hpp->size <= 0;
1581 hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf,
1582 size_t size, int line)
1584 struct hists *hists = browser->hists;
1585 struct perf_hpp dummy_hpp = {
1589 struct perf_hpp_fmt *fmt;
1594 if (hists__has_callchains(hists) && symbol_conf.use_callchain) {
1595 ret = scnprintf(buf, size, " ");
1596 if (advance_hpp_check(&dummy_hpp, ret))
1600 hists__for_each_format(browser->hists, fmt) {
1601 if (perf_hpp__should_skip(fmt, hists) || column++ < browser->b.horiz_scroll)
1604 ret = fmt->header(fmt, &dummy_hpp, hists, line, &span);
1605 if (advance_hpp_check(&dummy_hpp, ret))
1611 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
1612 if (advance_hpp_check(&dummy_hpp, ret))
1619 static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser, char *buf, size_t size)
1621 struct hists *hists = browser->hists;
1622 struct perf_hpp dummy_hpp = {
1626 struct perf_hpp_fmt *fmt;
1627 struct perf_hpp_list_node *fmt_node;
1630 int indent = hists->nr_hpp_node - 2;
1631 bool first_node, first_col;
1633 ret = scnprintf(buf, size, " ");
1634 if (advance_hpp_check(&dummy_hpp, ret))
1638 /* the first hpp_list_node is for overhead columns */
1639 fmt_node = list_first_entry(&hists->hpp_formats,
1640 struct perf_hpp_list_node, list);
1641 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1642 if (column++ < browser->b.horiz_scroll)
1645 ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1646 if (advance_hpp_check(&dummy_hpp, ret))
1649 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
1650 if (advance_hpp_check(&dummy_hpp, ret))
1657 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "%*s",
1658 indent * HIERARCHY_INDENT, "");
1659 if (advance_hpp_check(&dummy_hpp, ret))
1664 list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
1666 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " / ");
1667 if (advance_hpp_check(&dummy_hpp, ret))
1673 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1676 if (perf_hpp__should_skip(fmt, hists))
1680 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "+");
1681 if (advance_hpp_check(&dummy_hpp, ret))
1686 ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1687 dummy_hpp.buf[ret] = '\0';
1689 start = trim(dummy_hpp.buf);
1690 ret = strlen(start);
1692 if (start != dummy_hpp.buf)
1693 memmove(dummy_hpp.buf, start, ret + 1);
1695 if (advance_hpp_check(&dummy_hpp, ret))
1703 static void hists_browser__hierarchy_headers(struct hist_browser *browser)
1707 hists_browser__scnprintf_hierarchy_headers(browser, headers,
1710 ui_browser__gotorc(&browser->b, 0, 0);
1711 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1712 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1715 static void hists_browser__headers(struct hist_browser *browser)
1717 struct hists *hists = browser->hists;
1718 struct perf_hpp_list *hpp_list = hists->hpp_list;
1722 for (line = 0; line < hpp_list->nr_header_lines; line++) {
1725 hists_browser__scnprintf_headers(browser, headers,
1726 sizeof(headers), line);
1728 ui_browser__gotorc_title(&browser->b, line, 0);
1729 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1730 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1734 static void hist_browser__show_headers(struct hist_browser *browser)
1736 if (symbol_conf.report_hierarchy)
1737 hists_browser__hierarchy_headers(browser);
1739 hists_browser__headers(browser);
1742 static void ui_browser__hists_init_top(struct ui_browser *browser)
1744 if (browser->top == NULL) {
1745 struct hist_browser *hb;
1747 hb = container_of(browser, struct hist_browser, b);
1748 browser->top = rb_first_cached(&hb->hists->entries);
1752 static unsigned int hist_browser__refresh(struct ui_browser *browser)
1756 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
1758 if (hb->show_headers)
1759 hist_browser__show_headers(hb);
1761 ui_browser__hists_init_top(browser);
1762 hb->he_selection = NULL;
1763 hb->selection = NULL;
1765 for (nd = browser->top; nd; nd = rb_hierarchy_next(nd)) {
1766 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1770 /* let it move to sibling */
1771 h->unfolded = false;
1775 percent = hist_entry__get_percent_limit(h);
1776 if (percent < hb->min_pcnt)
1779 if (symbol_conf.report_hierarchy) {
1780 row += hist_browser__show_hierarchy_entry(hb, h, row,
1782 if (row == browser->rows)
1785 if (h->has_no_entry) {
1786 hist_browser__show_no_entry(hb, row, h->depth + 1);
1790 row += hist_browser__show_entry(hb, h, row);
1793 if (row == browser->rows)
1800 static struct rb_node *hists__filter_entries(struct rb_node *nd,
1803 while (nd != NULL) {
1804 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1805 float percent = hist_entry__get_percent_limit(h);
1807 if (!h->filtered && percent >= min_pcnt)
1811 * If it's filtered, its all children also were filtered.
1812 * So move to sibling node.
1817 nd = rb_hierarchy_next(nd);
1823 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
1826 while (nd != NULL) {
1827 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1828 float percent = hist_entry__get_percent_limit(h);
1830 if (!h->filtered && percent >= min_pcnt)
1833 nd = rb_hierarchy_prev(nd);
1839 static void ui_browser__hists_seek(struct ui_browser *browser,
1840 off_t offset, int whence)
1842 struct hist_entry *h;
1845 struct hist_browser *hb;
1847 hb = container_of(browser, struct hist_browser, b);
1849 if (browser->nr_entries == 0)
1852 ui_browser__hists_init_top(browser);
1856 nd = hists__filter_entries(rb_first(browser->entries),
1863 nd = rb_hierarchy_last(rb_last(browser->entries));
1864 nd = hists__filter_prev_entries(nd, hb->min_pcnt);
1872 * Moves not relative to the first visible entry invalidates its
1875 h = rb_entry(browser->top, struct hist_entry, rb_node);
1879 * Here we have to check if nd is expanded (+), if it is we can't go
1880 * the next top level hist_entry, instead we must compute an offset of
1881 * what _not_ to show and not change the first visible entry.
1883 * This offset increments when we are going from top to bottom and
1884 * decreases when we're going from bottom to top.
1886 * As we don't have backpointers to the top level in the callchains
1887 * structure, we need to always print the whole hist_entry callchain,
1888 * skipping the first ones that are before the first visible entry
1889 * and stop when we printed enough lines to fill the screen.
1897 h = rb_entry(nd, struct hist_entry, rb_node);
1898 if (h->unfolded && h->leaf) {
1899 u16 remaining = h->nr_rows - h->row_offset;
1900 if (offset > remaining) {
1901 offset -= remaining;
1904 h->row_offset += offset;
1910 nd = hists__filter_entries(rb_hierarchy_next(nd),
1916 } while (offset != 0);
1917 } else if (offset < 0) {
1919 h = rb_entry(nd, struct hist_entry, rb_node);
1920 if (h->unfolded && h->leaf) {
1922 if (-offset > h->row_offset) {
1923 offset += h->row_offset;
1926 h->row_offset += offset;
1932 if (-offset > h->nr_rows) {
1933 offset += h->nr_rows;
1936 h->row_offset = h->nr_rows + offset;
1944 nd = hists__filter_prev_entries(rb_hierarchy_prev(nd),
1952 * Last unfiltered hist_entry, check if it is
1953 * unfolded, if it is then we should have
1954 * row_offset at its last entry.
1956 h = rb_entry(nd, struct hist_entry, rb_node);
1957 if (h->unfolded && h->leaf)
1958 h->row_offset = h->nr_rows;
1965 h = rb_entry(nd, struct hist_entry, rb_node);
1970 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1971 struct hist_entry *he, FILE *fp,
1974 struct callchain_print_arg arg = {
1978 hist_browser__show_callchain(browser, he, level, 0,
1979 hist_browser__fprintf_callchain_entry, &arg,
1980 hist_browser__check_dump_full);
1984 static int hist_browser__fprintf_entry(struct hist_browser *browser,
1985 struct hist_entry *he, FILE *fp)
1989 char folded_sign = ' ';
1990 struct perf_hpp hpp = {
1994 struct perf_hpp_fmt *fmt;
1998 if (hist_entry__has_callchains(he) && symbol_conf.use_callchain) {
1999 folded_sign = hist_entry__folded(he);
2000 printed += fprintf(fp, "%c ", folded_sign);
2003 hists__for_each_format(browser->hists, fmt) {
2004 if (perf_hpp__should_skip(fmt, he->hists))
2008 ret = scnprintf(hpp.buf, hpp.size, " ");
2009 advance_hpp(&hpp, ret);
2013 ret = fmt->entry(fmt, &hpp, he);
2014 ret = hist_entry__snprintf_alignment(he, &hpp, fmt, ret);
2015 advance_hpp(&hpp, ret);
2017 printed += fprintf(fp, "%s\n", s);
2019 if (folded_sign == '-')
2020 printed += hist_browser__fprintf_callchain(browser, he, fp, 1);
2026 static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,
2027 struct hist_entry *he,
2028 FILE *fp, int level)
2032 char folded_sign = ' ';
2033 struct perf_hpp hpp = {
2037 struct perf_hpp_fmt *fmt;
2038 struct perf_hpp_list_node *fmt_node;
2041 int hierarchy_indent = (he->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
2043 printed = fprintf(fp, "%*s", level * HIERARCHY_INDENT, "");
2045 folded_sign = hist_entry__folded(he);
2046 printed += fprintf(fp, "%c", folded_sign);
2048 /* the first hpp_list_node is for overhead columns */
2049 fmt_node = list_first_entry(&he->hists->hpp_formats,
2050 struct perf_hpp_list_node, list);
2051 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
2053 ret = scnprintf(hpp.buf, hpp.size, " ");
2054 advance_hpp(&hpp, ret);
2058 ret = fmt->entry(fmt, &hpp, he);
2059 advance_hpp(&hpp, ret);
2062 ret = scnprintf(hpp.buf, hpp.size, "%*s", hierarchy_indent, "");
2063 advance_hpp(&hpp, ret);
2065 perf_hpp_list__for_each_format(he->hpp_list, fmt) {
2066 ret = scnprintf(hpp.buf, hpp.size, " ");
2067 advance_hpp(&hpp, ret);
2069 ret = fmt->entry(fmt, &hpp, he);
2070 advance_hpp(&hpp, ret);
2073 printed += fprintf(fp, "%s\n", rtrim(s));
2075 if (he->leaf && folded_sign == '-') {
2076 printed += hist_browser__fprintf_callchain(browser, he, fp,
2083 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
2085 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
2090 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
2092 if (symbol_conf.report_hierarchy) {
2093 printed += hist_browser__fprintf_hierarchy_entry(browser,
2097 printed += hist_browser__fprintf_entry(browser, h, fp);
2100 nd = hists__filter_entries(rb_hierarchy_next(nd),
2107 static int hist_browser__dump(struct hist_browser *browser)
2113 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
2114 if (access(filename, F_OK))
2117 * XXX: Just an arbitrary lazy upper limit
2119 if (++browser->print_seq == 8192) {
2120 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
2125 fp = fopen(filename, "w");
2128 const char *err = str_error_r(errno, bf, sizeof(bf));
2129 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
2133 ++browser->print_seq;
2134 hist_browser__fprintf(browser, fp);
2136 ui_helpline__fpush("%s written!", filename);
2141 void hist_browser__init(struct hist_browser *browser,
2142 struct hists *hists)
2144 struct perf_hpp_fmt *fmt;
2146 browser->hists = hists;
2147 browser->b.refresh = hist_browser__refresh;
2148 browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
2149 browser->b.seek = ui_browser__hists_seek;
2150 browser->b.use_navkeypressed = true;
2151 browser->show_headers = symbol_conf.show_hist_headers;
2152 hist_browser__set_title_space(browser);
2154 if (symbol_conf.report_hierarchy) {
2155 struct perf_hpp_list_node *fmt_node;
2157 /* count overhead columns (in the first node) */
2158 fmt_node = list_first_entry(&hists->hpp_formats,
2159 struct perf_hpp_list_node, list);
2160 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt)
2161 ++browser->b.columns;
2163 /* add a single column for whole hierarchy sort keys*/
2164 ++browser->b.columns;
2166 hists__for_each_format(hists, fmt)
2167 ++browser->b.columns;
2170 hists__reset_column_width(hists);
2173 struct hist_browser *hist_browser__new(struct hists *hists)
2175 struct hist_browser *browser = zalloc(sizeof(*browser));
2178 hist_browser__init(browser, hists);
2183 static struct hist_browser *
2184 perf_evsel_browser__new(struct perf_evsel *evsel,
2185 struct hist_browser_timer *hbt,
2186 struct perf_env *env,
2187 struct annotation_options *annotation_opts)
2189 struct hist_browser *browser = hist_browser__new(evsel__hists(evsel));
2194 browser->title = hists_browser__scnprintf_title;
2195 browser->annotation_opts = annotation_opts;
2200 void hist_browser__delete(struct hist_browser *browser)
2205 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
2207 return browser->he_selection;
2210 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
2212 return browser->he_selection->thread;
2215 /* Check whether the browser is for 'top' or 'report' */
2216 static inline bool is_report_browser(void *timer)
2218 return timer == NULL;
2221 static int hists_browser__scnprintf_title(struct hist_browser *browser, char *bf, size_t size)
2223 struct hist_browser_timer *hbt = browser->hbt;
2224 int printed = __hists__scnprintf_title(browser->hists, bf, size, !is_report_browser(hbt));
2226 if (!is_report_browser(hbt)) {
2227 struct perf_top *top = hbt->arg;
2229 printed += scnprintf(bf + printed, size - printed,
2230 " lost: %" PRIu64 "/%" PRIu64,
2231 top->lost, top->lost_total);
2233 printed += scnprintf(bf + printed, size - printed,
2234 " drop: %" PRIu64 "/%" PRIu64,
2235 top->drop, top->drop_total);
2238 printed += scnprintf(bf + printed, size - printed, " [z]");
2240 perf_top__reset_sample_counters(top);
2247 static inline void free_popup_options(char **options, int n)
2251 for (i = 0; i < n; ++i)
2256 * Only runtime switching of perf data file will make "input_name" point
2257 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
2258 * whether we need to call free() for current "input_name" during the switch.
2260 static bool is_input_name_malloced = false;
2262 static int switch_data_file(void)
2264 char *pwd, *options[32], *abs_path[32], *tmp;
2266 int nr_options = 0, choice = -1, ret = -1;
2267 struct dirent *dent;
2269 pwd = getenv("PWD");
2273 pwd_dir = opendir(pwd);
2277 memset(options, 0, sizeof(options));
2278 memset(abs_path, 0, sizeof(abs_path));
2280 while ((dent = readdir(pwd_dir))) {
2281 char path[PATH_MAX];
2283 char *name = dent->d_name;
2286 if (!(dent->d_type == DT_REG))
2289 snprintf(path, sizeof(path), "%s/%s", pwd, name);
2291 file = fopen(path, "r");
2295 if (fread(&magic, 1, 8, file) < 8)
2296 goto close_file_and_continue;
2298 if (is_perf_magic(magic)) {
2299 options[nr_options] = strdup(name);
2300 if (!options[nr_options])
2301 goto close_file_and_continue;
2303 abs_path[nr_options] = strdup(path);
2304 if (!abs_path[nr_options]) {
2305 zfree(&options[nr_options]);
2306 ui__warning("Can't search all data files due to memory shortage.\n");
2314 close_file_and_continue:
2316 if (nr_options >= 32) {
2317 ui__warning("Too many perf data files in PWD!\n"
2318 "Only the first 32 files will be listed.\n");
2325 choice = ui__popup_menu(nr_options, options);
2326 if (choice < nr_options && choice >= 0) {
2327 tmp = strdup(abs_path[choice]);
2329 if (is_input_name_malloced)
2330 free((void *)input_name);
2332 is_input_name_malloced = true;
2335 ui__warning("Data switch failed due to memory shortage!\n");
2339 free_popup_options(options, nr_options);
2340 free_popup_options(abs_path, nr_options);
2344 struct popup_action {
2346 struct thread *thread;
2347 struct map_symbol ms;
2349 struct perf_evsel *evsel;
2352 int (*fn)(struct hist_browser *browser, struct popup_action *act);
2356 do_annotate(struct hist_browser *browser, struct popup_action *act)
2358 struct perf_evsel *evsel;
2359 struct annotation *notes;
2360 struct hist_entry *he;
2363 if (!browser->annotation_opts->objdump_path &&
2364 perf_env__lookup_objdump(browser->env, &browser->annotation_opts->objdump_path))
2367 notes = symbol__annotation(act->ms.sym);
2371 evsel = hists_to_evsel(browser->hists);
2372 err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt,
2373 browser->annotation_opts);
2374 he = hist_browser__selected_entry(browser);
2376 * offer option to annotate the other branch source or target
2377 * (if they exists) when returning from annotate
2379 if ((err == 'q' || err == CTRL('c')) && he->branch_info)
2382 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
2384 ui_browser__handle_resize(&browser->b);
2389 add_annotate_opt(struct hist_browser *browser __maybe_unused,
2390 struct popup_action *act, char **optstr,
2391 struct map *map, struct symbol *sym)
2393 if (sym == NULL || map->dso->annotate_warned)
2396 if (asprintf(optstr, "Annotate %s", sym->name) < 0)
2401 act->fn = do_annotate;
2406 do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
2408 struct thread *thread = act->thread;
2410 if ((!hists__has(browser->hists, thread) &&
2411 !hists__has(browser->hists, comm)) || thread == NULL)
2414 if (browser->hists->thread_filter) {
2415 pstack__remove(browser->pstack, &browser->hists->thread_filter);
2416 perf_hpp__set_elide(HISTC_THREAD, false);
2417 thread__zput(browser->hists->thread_filter);
2420 if (hists__has(browser->hists, thread)) {
2421 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
2422 thread->comm_set ? thread__comm_str(thread) : "",
2425 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
2426 thread->comm_set ? thread__comm_str(thread) : "");
2429 browser->hists->thread_filter = thread__get(thread);
2430 perf_hpp__set_elide(HISTC_THREAD, false);
2431 pstack__push(browser->pstack, &browser->hists->thread_filter);
2434 hists__filter_by_thread(browser->hists);
2435 hist_browser__reset(browser);
2440 add_thread_opt(struct hist_browser *browser, struct popup_action *act,
2441 char **optstr, struct thread *thread)
2445 if ((!hists__has(browser->hists, thread) &&
2446 !hists__has(browser->hists, comm)) || thread == NULL)
2449 if (hists__has(browser->hists, thread)) {
2450 ret = asprintf(optstr, "Zoom %s %s(%d) thread",
2451 browser->hists->thread_filter ? "out of" : "into",
2452 thread->comm_set ? thread__comm_str(thread) : "",
2455 ret = asprintf(optstr, "Zoom %s %s thread",
2456 browser->hists->thread_filter ? "out of" : "into",
2457 thread->comm_set ? thread__comm_str(thread) : "");
2462 act->thread = thread;
2463 act->fn = do_zoom_thread;
2468 do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
2470 struct map *map = act->ms.map;
2472 if (!hists__has(browser->hists, dso) || map == NULL)
2475 if (browser->hists->dso_filter) {
2476 pstack__remove(browser->pstack, &browser->hists->dso_filter);
2477 perf_hpp__set_elide(HISTC_DSO, false);
2478 browser->hists->dso_filter = NULL;
2481 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
2482 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
2483 browser->hists->dso_filter = map->dso;
2484 perf_hpp__set_elide(HISTC_DSO, true);
2485 pstack__push(browser->pstack, &browser->hists->dso_filter);
2488 hists__filter_by_dso(browser->hists);
2489 hist_browser__reset(browser);
2494 add_dso_opt(struct hist_browser *browser, struct popup_action *act,
2495 char **optstr, struct map *map)
2497 if (!hists__has(browser->hists, dso) || map == NULL)
2500 if (asprintf(optstr, "Zoom %s %s DSO",
2501 browser->hists->dso_filter ? "out of" : "into",
2502 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
2506 act->fn = do_zoom_dso;
2511 do_browse_map(struct hist_browser *browser __maybe_unused,
2512 struct popup_action *act)
2514 map__browse(act->ms.map);
2519 add_map_opt(struct hist_browser *browser,
2520 struct popup_action *act, char **optstr, struct map *map)
2522 if (!hists__has(browser->hists, dso) || map == NULL)
2525 if (asprintf(optstr, "Browse map details") < 0)
2529 act->fn = do_browse_map;
2534 do_run_script(struct hist_browser *browser __maybe_unused,
2535 struct popup_action *act)
2543 len += strlen(thread__comm_str(act->thread));
2544 else if (act->ms.sym)
2545 len += strlen(act->ms.sym->name);
2546 script_opt = malloc(len);
2552 n = scnprintf(script_opt, len, " -c %s ",
2553 thread__comm_str(act->thread));
2554 } else if (act->ms.sym) {
2555 n = scnprintf(script_opt, len, " -S %s ",
2560 char start[32], end[32];
2561 unsigned long starttime = act->time;
2562 unsigned long endtime = act->time + symbol_conf.time_quantum;
2564 if (starttime == endtime) { /* Display 1ms as fallback */
2565 starttime -= 1*NSEC_PER_MSEC;
2566 endtime += 1*NSEC_PER_MSEC;
2568 timestamp__scnprintf_usec(starttime, start, sizeof start);
2569 timestamp__scnprintf_usec(endtime, end, sizeof end);
2570 n += snprintf(script_opt + n, len - n, " --time %s,%s", start, end);
2573 script_browse(script_opt, act->evsel);
2579 do_res_sample_script(struct hist_browser *browser __maybe_unused,
2580 struct popup_action *act)
2582 struct hist_entry *he;
2584 he = hist_browser__selected_entry(browser);
2585 res_sample_browse(he->res_samples, he->num_res, act->evsel, act->rstype);
2590 add_script_opt_2(struct hist_browser *browser __maybe_unused,
2591 struct popup_action *act, char **optstr,
2592 struct thread *thread, struct symbol *sym,
2593 struct perf_evsel *evsel, const char *tstr)
2597 if (asprintf(optstr, "Run scripts for samples of thread [%s]%s",
2598 thread__comm_str(thread), tstr) < 0)
2601 if (asprintf(optstr, "Run scripts for samples of symbol [%s]%s",
2602 sym->name, tstr) < 0)
2605 if (asprintf(optstr, "Run scripts for all samples%s", tstr) < 0)
2609 act->thread = thread;
2612 act->fn = do_run_script;
2617 add_script_opt(struct hist_browser *browser,
2618 struct popup_action *act, char **optstr,
2619 struct thread *thread, struct symbol *sym,
2620 struct perf_evsel *evsel)
2623 struct hist_entry *he;
2625 n = add_script_opt_2(browser, act, optstr, thread, sym, evsel, "");
2627 he = hist_browser__selected_entry(browser);
2628 if (sort_order && strstr(sort_order, "time")) {
2633 j = sprintf(tstr, " in ");
2634 j += timestamp__scnprintf_usec(he->time, tstr + j,
2636 j += sprintf(tstr + j, "-");
2637 timestamp__scnprintf_usec(he->time + symbol_conf.time_quantum,
2638 tstr + j, sizeof tstr - j);
2639 n += add_script_opt_2(browser, act, optstr, thread, sym,
2641 act->time = he->time;
2647 add_res_sample_opt(struct hist_browser *browser __maybe_unused,
2648 struct popup_action *act, char **optstr,
2649 struct res_sample *res_sample,
2650 struct perf_evsel *evsel,
2656 if (asprintf(optstr, "Show context for individual samples %s",
2657 type == A_ASM ? "with assembler" :
2658 type == A_SOURCE ? "with source" : "") < 0)
2661 act->fn = do_res_sample_script;
2668 do_switch_data(struct hist_browser *browser __maybe_unused,
2669 struct popup_action *act __maybe_unused)
2671 if (switch_data_file()) {
2672 ui__warning("Won't switch the data files due to\n"
2673 "no valid data file get selected!\n");
2677 return K_SWITCH_INPUT_DATA;
2681 add_switch_opt(struct hist_browser *browser,
2682 struct popup_action *act, char **optstr)
2684 if (!is_report_browser(browser->hbt))
2687 if (asprintf(optstr, "Switch to another data file in PWD") < 0)
2690 act->fn = do_switch_data;
2695 do_exit_browser(struct hist_browser *browser __maybe_unused,
2696 struct popup_action *act __maybe_unused)
2702 add_exit_opt(struct hist_browser *browser __maybe_unused,
2703 struct popup_action *act, char **optstr)
2705 if (asprintf(optstr, "Exit") < 0)
2708 act->fn = do_exit_browser;
2713 do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
2715 if (!hists__has(browser->hists, socket) || act->socket < 0)
2718 if (browser->hists->socket_filter > -1) {
2719 pstack__remove(browser->pstack, &browser->hists->socket_filter);
2720 browser->hists->socket_filter = -1;
2721 perf_hpp__set_elide(HISTC_SOCKET, false);
2723 browser->hists->socket_filter = act->socket;
2724 perf_hpp__set_elide(HISTC_SOCKET, true);
2725 pstack__push(browser->pstack, &browser->hists->socket_filter);
2728 hists__filter_by_socket(browser->hists);
2729 hist_browser__reset(browser);
2734 add_socket_opt(struct hist_browser *browser, struct popup_action *act,
2735 char **optstr, int socket_id)
2737 if (!hists__has(browser->hists, socket) || socket_id < 0)
2740 if (asprintf(optstr, "Zoom %s Processor Socket %d",
2741 (browser->hists->socket_filter > -1) ? "out of" : "into",
2745 act->socket = socket_id;
2746 act->fn = do_zoom_socket;
2750 static void hist_browser__update_nr_entries(struct hist_browser *hb)
2753 struct rb_node *nd = rb_first_cached(&hb->hists->entries);
2755 if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
2756 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
2760 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2762 nd = rb_hierarchy_next(nd);
2765 hb->nr_non_filtered_entries = nr_entries;
2766 hb->nr_hierarchy_entries = nr_entries;
2769 static void hist_browser__update_percent_limit(struct hist_browser *hb,
2772 struct hist_entry *he;
2773 struct rb_node *nd = rb_first_cached(&hb->hists->entries);
2774 u64 total = hists__total_period(hb->hists);
2775 u64 min_callchain_hits = total * (percent / 100);
2777 hb->min_pcnt = callchain_param.min_percent = percent;
2779 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2780 he = rb_entry(nd, struct hist_entry, rb_node);
2782 if (he->has_no_entry) {
2783 he->has_no_entry = false;
2787 if (!he->leaf || !hist_entry__has_callchains(he) || !symbol_conf.use_callchain)
2790 if (callchain_param.mode == CHAIN_GRAPH_REL) {
2791 total = he->stat.period;
2793 if (symbol_conf.cumulate_callchain)
2794 total = he->stat_acc->period;
2796 min_callchain_hits = total * (percent / 100);
2799 callchain_param.sort(&he->sorted_chain, he->callchain,
2800 min_callchain_hits, &callchain_param);
2803 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
2805 /* force to re-evaluate folding state of callchains */
2806 he->init_have_children = false;
2807 hist_entry__set_folding(he, hb, false);
2811 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
2812 const char *helpline,
2814 struct hist_browser_timer *hbt,
2816 struct perf_env *env,
2817 bool warn_lost_event,
2818 struct annotation_options *annotation_opts)
2820 struct hists *hists = evsel__hists(evsel);
2821 struct hist_browser *browser = perf_evsel_browser__new(evsel, hbt, env, annotation_opts);
2822 struct branch_info *bi;
2823 #define MAX_OPTIONS 16
2824 char *options[MAX_OPTIONS];
2825 struct popup_action actions[MAX_OPTIONS];
2829 int delay_secs = hbt ? hbt->refresh : 0;
2831 #define HIST_BROWSER_HELP_COMMON \
2832 "h/?/F1 Show this window\n" \
2834 "PGDN/SPACE Navigate\n" \
2835 "q/ESC/CTRL+C Exit browser or go back to previous screen\n\n" \
2836 "For multiple event sessions:\n\n" \
2837 "TAB/UNTAB Switch events\n\n" \
2838 "For symbolic views (--sort has sym):\n\n" \
2839 "ENTER Zoom into DSO/Threads & Annotate current symbol\n" \
2841 "a Annotate current symbol\n" \
2842 "C Collapse all callchains\n" \
2843 "d Zoom into current DSO\n" \
2844 "E Expand all callchains\n" \
2845 "F Toggle percentage of filtered entries\n" \
2846 "H Display column headers\n" \
2847 "L Change percent limit\n" \
2848 "m Display context menu\n" \
2849 "S Zoom into current Processor Socket\n" \
2851 /* help messages are sorted by lexical order of the hotkey */
2852 static const char report_help[] = HIST_BROWSER_HELP_COMMON
2853 "i Show header information\n"
2854 "P Print histograms to perf.hist.N\n"
2855 "r Run available scripts\n"
2856 "s Switch to another data file in PWD\n"
2857 "t Zoom into current Thread\n"
2858 "V Verbose (DSO names in callchains, etc)\n"
2859 "/ Filter symbol by name";
2860 static const char top_help[] = HIST_BROWSER_HELP_COMMON
2861 "P Print histograms to perf.hist.N\n"
2862 "t Zoom into current Thread\n"
2863 "V Verbose (DSO names in callchains, etc)\n"
2864 "z Toggle zeroing of samples\n"
2865 "f Enable/Disable events\n"
2866 "/ Filter symbol by name";
2868 if (browser == NULL)
2871 /* reset abort key so that it can get Ctrl-C as a key */
2873 SLang_init_tty(0, 0, 0);
2876 browser->min_pcnt = min_pcnt;
2877 hist_browser__update_nr_entries(browser);
2879 browser->pstack = pstack__new(3);
2880 if (browser->pstack == NULL)
2883 ui_helpline__push(helpline);
2885 memset(options, 0, sizeof(options));
2886 memset(actions, 0, sizeof(actions));
2888 if (symbol_conf.col_width_list_str)
2889 perf_hpp__set_user_width(symbol_conf.col_width_list_str);
2892 struct thread *thread = NULL;
2893 struct map *map = NULL;
2899 key = hist_browser__run(browser, helpline,
2902 if (browser->he_selection != NULL) {
2903 thread = hist_browser__selected_thread(browser);
2904 map = browser->selection->map;
2905 socked_id = browser->he_selection->socket;
2913 * Exit the browser, let hists__browser_tree
2914 * go to the next or previous
2916 goto out_free_stack;
2918 if (!hists__has(hists, sym)) {
2919 ui_browser__warning(&browser->b, delay_secs * 2,
2920 "Annotation is only available for symbolic views, "
2921 "include \"sym*\" in --sort to use it.");
2925 if (browser->selection == NULL ||
2926 browser->selection->sym == NULL ||
2927 browser->selection->map->dso->annotate_warned)
2930 actions->ms.map = browser->selection->map;
2931 actions->ms.sym = browser->selection->sym;
2932 do_annotate(browser, actions);
2935 hist_browser__dump(browser);
2938 actions->ms.map = map;
2939 do_zoom_dso(browser, actions);
2942 verbose = (verbose + 1) % 4;
2943 browser->show_dso = verbose > 0;
2944 ui_helpline__fpush("Verbosity level set to %d\n",
2948 actions->thread = thread;
2949 do_zoom_thread(browser, actions);
2952 actions->socket = socked_id;
2953 do_zoom_socket(browser, actions);
2956 if (ui_browser__input_window("Symbol to show",
2957 "Please enter the name of symbol you want to see.\n"
2958 "To remove the filter later, press / + ENTER.",
2959 buf, "ENTER: OK, ESC: Cancel",
2960 delay_secs * 2) == K_ENTER) {
2961 hists->symbol_filter_str = *buf ? buf : NULL;
2962 hists__filter_by_symbol(hists);
2963 hist_browser__reset(browser);
2967 if (is_report_browser(hbt)) {
2968 actions->thread = NULL;
2969 actions->ms.sym = NULL;
2970 do_run_script(browser, actions);
2974 if (is_report_browser(hbt)) {
2975 key = do_switch_data(browser, actions);
2976 if (key == K_SWITCH_INPUT_DATA)
2977 goto out_free_stack;
2981 /* env->arch is NULL for live-mode (i.e. perf top) */
2983 tui__header_window(env);
2986 symbol_conf.filter_relative ^= 1;
2989 if (!is_report_browser(hbt)) {
2990 struct perf_top *top = hbt->arg;
2992 top->zero = !top->zero;
2996 if (ui_browser__input_window("Percent Limit",
2997 "Please enter the value you want to hide entries under that percent.",
2998 buf, "ENTER: OK, ESC: Cancel",
2999 delay_secs * 2) == K_ENTER) {
3001 double new_percent = strtod(buf, &end);
3003 if (new_percent < 0 || new_percent > 100) {
3004 ui_browser__warning(&browser->b, delay_secs * 2,
3005 "Invalid percent: %.2f", new_percent);
3009 hist_browser__update_percent_limit(browser, new_percent);
3010 hist_browser__reset(browser);
3016 ui_browser__help_window(&browser->b,
3017 is_report_browser(hbt) ? report_help : top_help);
3028 if (pstack__empty(browser->pstack)) {
3030 * Go back to the perf_evsel_menu__run or other user
3033 goto out_free_stack;
3036 ui_browser__dialog_yesno(&browser->b,
3037 "Do you really want to exit?"))
3038 goto out_free_stack;
3042 top = pstack__peek(browser->pstack);
3043 if (top == &browser->hists->dso_filter) {
3045 * No need to set actions->dso here since
3046 * it's just to remove the current filter.
3047 * Ditto for thread below.
3049 do_zoom_dso(browser, actions);
3050 } else if (top == &browser->hists->thread_filter) {
3051 do_zoom_thread(browser, actions);
3052 } else if (top == &browser->hists->socket_filter) {
3053 do_zoom_socket(browser, actions);
3059 goto out_free_stack;
3061 if (!is_report_browser(hbt)) {
3062 struct perf_top *top = hbt->arg;
3064 perf_evlist__toggle_enable(top->evlist);
3066 * No need to refresh, resort/decay histogram
3067 * entries if we are not collecting samples:
3069 if (top->evlist->enabled) {
3070 helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
3071 hbt->refresh = delay_secs;
3073 helpline = "Press 'f' again to re-enable the events";
3080 helpline = "Press '?' for help on key bindings";
3084 if (!hists__has(hists, sym) || browser->selection == NULL)
3085 goto skip_annotation;
3087 if (sort__mode == SORT_MODE__BRANCH) {
3088 bi = browser->he_selection->branch_info;
3091 goto skip_annotation;
3093 nr_options += add_annotate_opt(browser,
3094 &actions[nr_options],
3095 &options[nr_options],
3098 if (bi->to.sym != bi->from.sym)
3099 nr_options += add_annotate_opt(browser,
3100 &actions[nr_options],
3101 &options[nr_options],
3105 nr_options += add_annotate_opt(browser,
3106 &actions[nr_options],
3107 &options[nr_options],
3108 browser->selection->map,
3109 browser->selection->sym);
3112 nr_options += add_thread_opt(browser, &actions[nr_options],
3113 &options[nr_options], thread);
3114 nr_options += add_dso_opt(browser, &actions[nr_options],
3115 &options[nr_options], map);
3116 nr_options += add_map_opt(browser, &actions[nr_options],
3117 &options[nr_options],
3118 browser->selection ?
3119 browser->selection->map : NULL);
3120 nr_options += add_socket_opt(browser, &actions[nr_options],
3121 &options[nr_options],
3123 /* perf script support */
3124 if (!is_report_browser(hbt))
3125 goto skip_scripting;
3127 if (browser->he_selection) {
3128 if (hists__has(hists, thread) && thread) {
3129 nr_options += add_script_opt(browser,
3130 &actions[nr_options],
3131 &options[nr_options],
3132 thread, NULL, evsel);
3135 * Note that browser->selection != NULL
3136 * when browser->he_selection is not NULL,
3137 * so we don't need to check browser->selection
3138 * before fetching browser->selection->sym like what
3139 * we do before fetching browser->selection->map.
3141 * See hist_browser__show_entry.
3143 if (hists__has(hists, sym) && browser->selection->sym) {
3144 nr_options += add_script_opt(browser,
3145 &actions[nr_options],
3146 &options[nr_options],
3147 NULL, browser->selection->sym,
3151 nr_options += add_script_opt(browser, &actions[nr_options],
3152 &options[nr_options], NULL, NULL, evsel);
3153 nr_options += add_res_sample_opt(browser, &actions[nr_options],
3154 &options[nr_options],
3155 hist_browser__selected_entry(browser)->res_samples,
3157 nr_options += add_res_sample_opt(browser, &actions[nr_options],
3158 &options[nr_options],
3159 hist_browser__selected_entry(browser)->res_samples,
3161 nr_options += add_res_sample_opt(browser, &actions[nr_options],
3162 &options[nr_options],
3163 hist_browser__selected_entry(browser)->res_samples,
3165 nr_options += add_switch_opt(browser, &actions[nr_options],
3166 &options[nr_options]);
3168 nr_options += add_exit_opt(browser, &actions[nr_options],
3169 &options[nr_options]);
3172 struct popup_action *act;
3174 choice = ui__popup_menu(nr_options, options);
3175 if (choice == -1 || choice >= nr_options)
3178 act = &actions[choice];
3179 key = act->fn(browser, act);
3182 if (key == K_SWITCH_INPUT_DATA)
3186 pstack__delete(browser->pstack);
3188 hist_browser__delete(browser);
3189 free_popup_options(options, MAX_OPTIONS);
3193 struct perf_evsel_menu {
3194 struct ui_browser b;
3195 struct perf_evsel *selection;
3196 struct annotation_options *annotation_opts;
3197 bool lost_events, lost_events_warned;
3199 struct perf_env *env;
3202 static void perf_evsel_menu__write(struct ui_browser *browser,
3203 void *entry, int row)
3205 struct perf_evsel_menu *menu = container_of(browser,
3206 struct perf_evsel_menu, b);
3207 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
3208 struct hists *hists = evsel__hists(evsel);
3209 bool current_entry = ui_browser__is_current_entry(browser, row);
3210 unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
3211 const char *ev_name = perf_evsel__name(evsel);
3213 const char *warn = " ";
3216 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
3217 HE_COLORSET_NORMAL);
3219 if (perf_evsel__is_group_event(evsel)) {
3220 struct perf_evsel *pos;
3222 ev_name = perf_evsel__group_name(evsel);
3224 for_each_group_member(pos, evsel) {
3225 struct hists *pos_hists = evsel__hists(pos);
3226 nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
3230 nr_events = convert_unit(nr_events, &unit);
3231 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
3232 unit, unit == ' ' ? "" : " ", ev_name);
3233 ui_browser__printf(browser, "%s", bf);
3235 nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
3236 if (nr_events != 0) {
3237 menu->lost_events = true;
3239 ui_browser__set_color(browser, HE_COLORSET_TOP);
3240 nr_events = convert_unit(nr_events, &unit);
3241 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
3242 nr_events, unit, unit == ' ' ? "" : " ");
3246 ui_browser__write_nstring(browser, warn, browser->width - printed);
3249 menu->selection = evsel;
3252 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
3253 int nr_events, const char *help,
3254 struct hist_browser_timer *hbt,
3255 bool warn_lost_event)
3257 struct perf_evlist *evlist = menu->b.priv;
3258 struct perf_evsel *pos;
3259 const char *title = "Available samples";
3260 int delay_secs = hbt ? hbt->refresh : 0;
3263 if (ui_browser__show(&menu->b, title,
3264 "ESC: exit, ENTER|->: Browse histograms") < 0)
3268 key = ui_browser__run(&menu->b, delay_secs);
3272 hbt->timer(hbt->arg);
3274 if (!menu->lost_events_warned &&
3275 menu->lost_events &&
3277 ui_browser__warn_lost_events(&menu->b);
3278 menu->lost_events_warned = true;
3283 if (!menu->selection)
3285 pos = menu->selection;
3287 perf_evlist__set_selected(evlist, pos);
3289 * Give the calling tool a chance to populate the non
3290 * default evsel resorted hists tree.
3293 hbt->timer(hbt->arg);
3294 key = perf_evsel__hists_browse(pos, nr_events, help,
3299 menu->annotation_opts);
3300 ui_browser__show_title(&menu->b, title);
3303 if (pos->node.next == &evlist->entries)
3304 pos = perf_evlist__first(evlist);
3306 pos = perf_evsel__next(pos);
3309 if (pos->node.prev == &evlist->entries)
3310 pos = perf_evlist__last(evlist);
3312 pos = perf_evsel__prev(pos);
3314 case K_SWITCH_INPUT_DATA:
3325 if (!ui_browser__dialog_yesno(&menu->b,
3326 "Do you really want to exit?"))
3338 ui_browser__hide(&menu->b);
3342 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
3345 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
3347 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
3353 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
3354 int nr_entries, const char *help,
3355 struct hist_browser_timer *hbt,
3357 struct perf_env *env,
3358 bool warn_lost_event,
3359 struct annotation_options *annotation_opts)
3361 struct perf_evsel *pos;
3362 struct perf_evsel_menu menu = {
3364 .entries = &evlist->entries,
3365 .refresh = ui_browser__list_head_refresh,
3366 .seek = ui_browser__list_head_seek,
3367 .write = perf_evsel_menu__write,
3368 .filter = filter_group_entries,
3369 .nr_entries = nr_entries,
3372 .min_pcnt = min_pcnt,
3374 .annotation_opts = annotation_opts,
3377 ui_helpline__push("Press ESC to exit");
3379 evlist__for_each_entry(evlist, pos) {
3380 const char *ev_name = perf_evsel__name(pos);
3381 size_t line_len = strlen(ev_name) + 7;
3383 if (menu.b.width < line_len)
3384 menu.b.width = line_len;
3387 return perf_evsel_menu__run(&menu, nr_entries, help,
3388 hbt, warn_lost_event);
3391 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
3392 struct hist_browser_timer *hbt,
3394 struct perf_env *env,
3395 bool warn_lost_event,
3396 struct annotation_options *annotation_opts)
3398 int nr_entries = evlist->nr_entries;
3401 if (nr_entries == 1) {
3402 struct perf_evsel *first = perf_evlist__first(evlist);
3404 return perf_evsel__hists_browse(first, nr_entries, help,
3405 false, hbt, min_pcnt,
3406 env, warn_lost_event,
3410 if (symbol_conf.event_group) {
3411 struct perf_evsel *pos;
3414 evlist__for_each_entry(evlist, pos) {
3415 if (perf_evsel__is_group_leader(pos))
3419 if (nr_entries == 1)
3423 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,