1 // SPDX-License-Identifier: GPL-2.0
8 #include <linux/rbtree.h>
9 #include <linux/string.h>
10 #include <sys/ttydefaults.h>
11 #include <linux/time64.h>
12 #include <linux/zalloc.h>
14 #include "../../util/debug.h"
15 #include "../../util/dso.h"
16 #include "../../util/callchain.h"
17 #include "../../util/evsel.h"
18 #include "../../util/evlist.h"
19 #include "../../util/header.h"
20 #include "../../util/hist.h"
21 #include "../../util/machine.h"
22 #include "../../util/map.h"
23 #include "../../util/maps.h"
24 #include "../../util/symbol.h"
25 #include "../../util/map_symbol.h"
26 #include "../../util/branch.h"
27 #include "../../util/pstack.h"
28 #include "../../util/sort.h"
29 #include "../../util/top.h"
30 #include "../../util/thread.h"
31 #include "../../util/block-info.h"
32 #include "../../arch/common.h"
33 #include "../../perf.h"
35 #include "../browsers/hists.h"
36 #include "../helpline.h"
44 #include "time-utils.h"
46 #include <linux/ctype.h>
48 extern void hist_browser__init_hpp(void);
50 static int hists_browser__scnprintf_title(struct hist_browser *browser, char *bf, size_t size);
51 static void hist_browser__update_nr_entries(struct hist_browser *hb);
53 static struct rb_node *hists__filter_entries(struct rb_node *nd,
56 static bool hist_browser__has_filter(struct hist_browser *hb)
58 return hists__has_filter(hb->hists) || hb->min_pcnt || symbol_conf.has_filter || hb->c2c_filter;
61 static int hist_browser__get_folding(struct hist_browser *browser)
64 struct hists *hists = browser->hists;
65 int unfolded_rows = 0;
67 for (nd = rb_first_cached(&hists->entries);
68 (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
69 nd = rb_hierarchy_next(nd)) {
70 struct hist_entry *he =
71 rb_entry(nd, struct hist_entry, rb_node);
73 if (he->leaf && he->unfolded)
74 unfolded_rows += he->nr_rows;
79 static void hist_browser__set_title_space(struct hist_browser *hb)
81 struct ui_browser *browser = &hb->b;
82 struct hists *hists = hb->hists;
83 struct perf_hpp_list *hpp_list = hists->hpp_list;
85 browser->extra_title_lines = hb->show_headers ? hpp_list->nr_header_lines : 0;
88 static u32 hist_browser__nr_entries(struct hist_browser *hb)
92 if (symbol_conf.report_hierarchy)
93 nr_entries = hb->nr_hierarchy_entries;
94 else if (hist_browser__has_filter(hb))
95 nr_entries = hb->nr_non_filtered_entries;
97 nr_entries = hb->hists->nr_entries;
99 hb->nr_callchain_rows = hist_browser__get_folding(hb);
100 return nr_entries + hb->nr_callchain_rows;
103 static void hist_browser__update_rows(struct hist_browser *hb)
105 struct ui_browser *browser = &hb->b;
106 struct hists *hists = hb->hists;
107 struct perf_hpp_list *hpp_list = hists->hpp_list;
110 if (!hb->show_headers) {
111 browser->rows += browser->extra_title_lines;
112 browser->extra_title_lines = 0;
116 browser->extra_title_lines = hpp_list->nr_header_lines;
117 browser->rows -= browser->extra_title_lines;
119 * Verify if we were at the last line and that line isn't
120 * visibe because we now show the header line(s).
122 index_row = browser->index - browser->top_idx;
123 if (index_row >= browser->rows)
124 browser->index -= index_row - browser->rows + 1;
127 static void hist_browser__refresh_dimensions(struct ui_browser *browser)
129 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
131 /* 3 == +/- toggle symbol before actual hist_entry rendering */
132 browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]"));
134 * FIXME: Just keeping existing behaviour, but this really should be
135 * before updating browser->width, as it will invalidate the
136 * calculation above. Fix this and the fallout in another
139 ui_browser__refresh_dimensions(browser);
142 static void hist_browser__reset(struct hist_browser *browser)
145 * The hists__remove_entry_filter() already folds non-filtered
146 * entries so we can assume it has 0 callchain rows.
148 browser->nr_callchain_rows = 0;
150 hist_browser__update_nr_entries(browser);
151 browser->b.nr_entries = hist_browser__nr_entries(browser);
152 hist_browser__refresh_dimensions(&browser->b);
153 ui_browser__reset_index(&browser->b);
156 static char tree__folded_sign(bool unfolded)
158 return unfolded ? '-' : '+';
161 static char hist_entry__folded(const struct hist_entry *he)
163 return he->has_children ? tree__folded_sign(he->unfolded) : ' ';
166 static char callchain_list__folded(const struct callchain_list *cl)
168 return cl->has_children ? tree__folded_sign(cl->unfolded) : ' ';
171 static void callchain_list__set_folding(struct callchain_list *cl, bool unfold)
173 cl->unfolded = unfold ? cl->has_children : false;
176 static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
181 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
182 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
183 struct callchain_list *chain;
184 char folded_sign = ' '; /* No children */
186 list_for_each_entry(chain, &child->val, list) {
189 /* We need this because we may not have children */
190 folded_sign = callchain_list__folded(chain);
191 if (folded_sign == '+')
195 if (folded_sign == '-') /* Have children and they're unfolded */
196 n += callchain_node__count_rows_rb_tree(child);
202 static int callchain_node__count_flat_rows(struct callchain_node *node)
204 struct callchain_list *chain;
205 char folded_sign = 0;
208 list_for_each_entry(chain, &node->parent_val, list) {
210 /* only check first chain list entry */
211 folded_sign = callchain_list__folded(chain);
212 if (folded_sign == '+')
218 list_for_each_entry(chain, &node->val, list) {
220 /* node->parent_val list might be empty */
221 folded_sign = callchain_list__folded(chain);
222 if (folded_sign == '+')
231 static int callchain_node__count_folded_rows(struct callchain_node *node __maybe_unused)
236 static int callchain_node__count_rows(struct callchain_node *node)
238 struct callchain_list *chain;
239 bool unfolded = false;
242 if (callchain_param.mode == CHAIN_FLAT)
243 return callchain_node__count_flat_rows(node);
244 else if (callchain_param.mode == CHAIN_FOLDED)
245 return callchain_node__count_folded_rows(node);
247 list_for_each_entry(chain, &node->val, list) {
250 unfolded = chain->unfolded;
254 n += callchain_node__count_rows_rb_tree(node);
259 static int callchain__count_rows(struct rb_root *chain)
264 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
265 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
266 n += callchain_node__count_rows(node);
272 static int hierarchy_count_rows(struct hist_browser *hb, struct hist_entry *he,
273 bool include_children)
276 struct rb_node *node;
277 struct hist_entry *child;
280 return callchain__count_rows(&he->sorted_chain);
282 if (he->has_no_entry)
285 node = rb_first_cached(&he->hroot_out);
289 child = rb_entry(node, struct hist_entry, rb_node);
290 percent = hist_entry__get_percent_limit(child);
292 if (!child->filtered && percent >= hb->min_pcnt) {
295 if (include_children && child->unfolded)
296 count += hierarchy_count_rows(hb, child, true);
299 node = rb_next(node);
304 static bool hist_entry__toggle_fold(struct hist_entry *he)
309 if (!he->has_children)
312 he->unfolded = !he->unfolded;
316 static bool callchain_list__toggle_fold(struct callchain_list *cl)
321 if (!cl->has_children)
324 cl->unfolded = !cl->unfolded;
328 static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
330 struct rb_node *nd = rb_first(&node->rb_root);
332 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
333 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
334 struct callchain_list *chain;
337 list_for_each_entry(chain, &child->val, list) {
340 chain->has_children = chain->list.next != &child->val ||
341 !RB_EMPTY_ROOT(&child->rb_root);
343 chain->has_children = chain->list.next == &child->val &&
344 !RB_EMPTY_ROOT(&child->rb_root);
347 callchain_node__init_have_children_rb_tree(child);
351 static void callchain_node__init_have_children(struct callchain_node *node,
354 struct callchain_list *chain;
356 chain = list_entry(node->val.next, struct callchain_list, list);
357 chain->has_children = has_sibling;
359 if (!list_empty(&node->val)) {
360 chain = list_entry(node->val.prev, struct callchain_list, list);
361 chain->has_children = !RB_EMPTY_ROOT(&node->rb_root);
364 callchain_node__init_have_children_rb_tree(node);
367 static void callchain__init_have_children(struct rb_root *root)
369 struct rb_node *nd = rb_first(root);
370 bool has_sibling = nd && rb_next(nd);
372 for (nd = rb_first(root); nd; nd = rb_next(nd)) {
373 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
374 callchain_node__init_have_children(node, has_sibling);
375 if (callchain_param.mode == CHAIN_FLAT ||
376 callchain_param.mode == CHAIN_FOLDED)
377 callchain_node__make_parent_list(node);
381 static void hist_entry__init_have_children(struct hist_entry *he)
383 if (he->init_have_children)
387 he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
388 callchain__init_have_children(&he->sorted_chain);
390 he->has_children = !RB_EMPTY_ROOT(&he->hroot_out.rb_root);
393 he->init_have_children = true;
396 static bool hist_browser__selection_has_children(struct hist_browser *browser)
398 struct hist_entry *he = browser->he_selection;
399 struct map_symbol *ms = browser->selection;
405 return he->has_children;
407 return container_of(ms, struct callchain_list, ms)->has_children;
410 static bool hist_browser__selection_unfolded(struct hist_browser *browser)
412 struct hist_entry *he = browser->he_selection;
413 struct map_symbol *ms = browser->selection;
421 return container_of(ms, struct callchain_list, ms)->unfolded;
424 static char *hist_browser__selection_sym_name(struct hist_browser *browser, char *bf, size_t size)
426 struct hist_entry *he = browser->he_selection;
427 struct map_symbol *ms = browser->selection;
428 struct callchain_list *callchain_entry;
434 hist_entry__sym_snprintf(he, bf, size, 0);
435 return bf + 4; // skip the level, e.g. '[k] '
438 callchain_entry = container_of(ms, struct callchain_list, ms);
439 return callchain_list__sym_name(callchain_entry, bf, size, browser->show_dso);
442 static bool hist_browser__toggle_fold(struct hist_browser *browser)
444 struct hist_entry *he = browser->he_selection;
445 struct map_symbol *ms = browser->selection;
446 struct callchain_list *cl = container_of(ms, struct callchain_list, ms);
453 has_children = hist_entry__toggle_fold(he);
455 has_children = callchain_list__toggle_fold(cl);
460 hist_entry__init_have_children(he);
461 browser->b.nr_entries -= he->nr_rows;
464 browser->nr_callchain_rows -= he->nr_rows;
466 browser->nr_hierarchy_entries -= he->nr_rows;
468 if (symbol_conf.report_hierarchy)
469 child_rows = hierarchy_count_rows(browser, he, true);
473 he->nr_rows = callchain__count_rows(
476 he->nr_rows = hierarchy_count_rows(browser, he, false);
478 /* account grand children */
479 if (symbol_conf.report_hierarchy)
480 browser->b.nr_entries += child_rows - he->nr_rows;
482 if (!he->leaf && he->nr_rows == 0) {
483 he->has_no_entry = true;
487 if (symbol_conf.report_hierarchy)
488 browser->b.nr_entries -= child_rows - he->nr_rows;
490 if (he->has_no_entry)
491 he->has_no_entry = false;
496 browser->b.nr_entries += he->nr_rows;
499 browser->nr_callchain_rows += he->nr_rows;
501 browser->nr_hierarchy_entries += he->nr_rows;
506 /* If it doesn't have children, no toggling performed */
510 static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
515 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
516 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
517 struct callchain_list *chain;
518 bool has_children = false;
520 list_for_each_entry(chain, &child->val, list) {
522 callchain_list__set_folding(chain, unfold);
523 has_children = chain->has_children;
527 n += callchain_node__set_folding_rb_tree(child, unfold);
533 static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
535 struct callchain_list *chain;
536 bool has_children = false;
539 list_for_each_entry(chain, &node->val, list) {
541 callchain_list__set_folding(chain, unfold);
542 has_children = chain->has_children;
546 n += callchain_node__set_folding_rb_tree(node, unfold);
551 static int callchain__set_folding(struct rb_root *chain, bool unfold)
556 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
557 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
558 n += callchain_node__set_folding(node, unfold);
564 static int hierarchy_set_folding(struct hist_browser *hb, struct hist_entry *he,
565 bool unfold __maybe_unused)
569 struct hist_entry *child;
572 for (nd = rb_first_cached(&he->hroot_out); nd; nd = rb_next(nd)) {
573 child = rb_entry(nd, struct hist_entry, rb_node);
574 percent = hist_entry__get_percent_limit(child);
575 if (!child->filtered && percent >= hb->min_pcnt)
582 static void __hist_entry__set_folding(struct hist_entry *he,
583 struct hist_browser *hb, bool unfold)
585 hist_entry__init_have_children(he);
586 he->unfolded = unfold ? he->has_children : false;
588 if (he->has_children) {
592 n = callchain__set_folding(&he->sorted_chain, unfold);
594 n = hierarchy_set_folding(hb, he, unfold);
596 he->nr_rows = unfold ? n : 0;
601 static void hist_entry__set_folding(struct hist_entry *he,
602 struct hist_browser *browser, bool unfold)
606 percent = hist_entry__get_percent_limit(he);
607 if (he->filtered || percent < browser->min_pcnt)
610 __hist_entry__set_folding(he, browser, unfold);
612 if (!he->depth || unfold)
613 browser->nr_hierarchy_entries++;
615 browser->nr_callchain_rows += he->nr_rows;
616 else if (unfold && !hist_entry__has_hierarchy_children(he, browser->min_pcnt)) {
617 browser->nr_hierarchy_entries++;
618 he->has_no_entry = true;
621 he->has_no_entry = false;
625 __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
628 struct hist_entry *he;
630 nd = rb_first_cached(&browser->hists->entries);
632 he = rb_entry(nd, struct hist_entry, rb_node);
634 /* set folding state even if it's currently folded */
635 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
637 hist_entry__set_folding(he, browser, unfold);
641 static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
643 browser->nr_hierarchy_entries = 0;
644 browser->nr_callchain_rows = 0;
645 __hist_browser__set_folding(browser, unfold);
647 browser->b.nr_entries = hist_browser__nr_entries(browser);
648 /* Go to the start, we may be way after valid entries after a collapse */
649 ui_browser__reset_index(&browser->b);
652 static void hist_browser__set_folding_selected(struct hist_browser *browser, bool unfold)
654 if (!browser->he_selection)
657 hist_entry__set_folding(browser->he_selection, browser, unfold);
658 browser->b.nr_entries = hist_browser__nr_entries(browser);
661 static void ui_browser__warn_lost_events(struct ui_browser *browser)
663 ui_browser__warning(browser, 4,
664 "Events are being lost, check IO/CPU overload!\n\n"
665 "You may want to run 'perf' using a RT scheduler policy:\n\n"
666 " perf top -r 80\n\n"
667 "Or reduce the sampling frequency.");
670 static int hist_browser__title(struct hist_browser *browser, char *bf, size_t size)
672 return browser->title ? browser->title(browser, bf, size) : 0;
675 int hist_browser__run(struct hist_browser *browser, const char *help,
676 bool warn_lost_event)
680 struct hist_browser_timer *hbt = browser->hbt;
681 int delay_secs = hbt ? hbt->refresh : 0;
683 browser->b.entries = &browser->hists->entries;
684 browser->b.nr_entries = hist_browser__nr_entries(browser);
686 hist_browser__title(browser, title, sizeof(title));
688 if (ui_browser__show(&browser->b, title, "%s", help) < 0)
692 key = ui_browser__run(&browser->b, delay_secs);
701 hbt->timer(hbt->arg);
703 if (hist_browser__has_filter(browser) ||
704 symbol_conf.report_hierarchy)
705 hist_browser__update_nr_entries(browser);
707 nr_entries = hist_browser__nr_entries(browser);
708 ui_browser__update_nr_entries(&browser->b, nr_entries);
710 if (warn_lost_event &&
711 (browser->hists->stats.nr_lost_warned !=
712 browser->hists->stats.nr_events[PERF_RECORD_LOST])) {
713 browser->hists->stats.nr_lost_warned =
714 browser->hists->stats.nr_events[PERF_RECORD_LOST];
715 ui_browser__warn_lost_events(&browser->b);
718 hist_browser__title(browser, title, sizeof(title));
719 ui_browser__show_title(&browser->b, title);
722 case 'D': { /* Debug */
724 struct hist_entry *h = rb_entry(browser->b.top,
725 struct hist_entry, rb_node);
727 ui_helpline__fpush("%d: nr_ent=(%d,%d), etl: %d, rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
728 seq++, browser->b.nr_entries,
729 browser->hists->nr_entries,
730 browser->b.extra_title_lines,
734 h->row_offset, h->nr_rows);
738 /* Collapse the whole world. */
739 hist_browser__set_folding(browser, false);
742 /* Collapse the selected entry. */
743 hist_browser__set_folding_selected(browser, false);
746 /* Expand the whole world. */
747 hist_browser__set_folding(browser, true);
750 /* Expand the selected entry. */
751 hist_browser__set_folding_selected(browser, true);
754 browser->show_headers = !browser->show_headers;
755 hist_browser__update_rows(browser);
758 if (hist_browser__toggle_fold(browser))
766 ui_browser__hide(&browser->b);
770 struct callchain_print_arg {
771 /* for hists browser */
773 bool is_current_entry;
780 typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
781 struct callchain_list *chain,
782 const char *str, int offset,
784 struct callchain_print_arg *arg);
786 static void hist_browser__show_callchain_entry(struct hist_browser *browser,
787 struct callchain_list *chain,
788 const char *str, int offset,
790 struct callchain_print_arg *arg)
793 char folded_sign = callchain_list__folded(chain);
794 bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
796 color = HE_COLORSET_NORMAL;
797 width = browser->b.width - (offset + 2);
798 if (ui_browser__is_current_entry(&browser->b, row)) {
799 browser->selection = &chain->ms;
800 color = HE_COLORSET_SELECTED;
801 arg->is_current_entry = true;
804 ui_browser__set_color(&browser->b, color);
805 ui_browser__gotorc(&browser->b, row, 0);
806 ui_browser__write_nstring(&browser->b, " ", offset);
807 ui_browser__printf(&browser->b, "%c", folded_sign);
808 ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
809 ui_browser__write_nstring(&browser->b, str, width);
812 static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
813 struct callchain_list *chain,
814 const char *str, int offset,
815 unsigned short row __maybe_unused,
816 struct callchain_print_arg *arg)
818 char folded_sign = callchain_list__folded(chain);
820 arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
824 typedef bool (*check_output_full_fn)(struct hist_browser *browser,
827 static bool hist_browser__check_output_full(struct hist_browser *browser,
830 return browser->b.rows == row;
833 static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
834 unsigned short row __maybe_unused)
839 #define LEVEL_OFFSET_STEP 3
841 static int hist_browser__show_callchain_list(struct hist_browser *browser,
842 struct callchain_node *node,
843 struct callchain_list *chain,
844 unsigned short row, u64 total,
845 bool need_percent, int offset,
846 print_callchain_entry_fn print,
847 struct callchain_print_arg *arg)
849 char bf[1024], *alloc_str;
850 char buf[64], *alloc_str2;
854 if (arg->row_offset != 0) {
862 str = callchain_list__sym_name(chain, bf, sizeof(bf),
865 if (symbol_conf.show_branchflag_count) {
866 callchain_list_counts__printf_value(chain, NULL,
869 if (asprintf(&alloc_str2, "%s%s", str, buf) < 0)
870 str = "Not enough memory!";
876 callchain_node__scnprintf_value(node, buf, sizeof(buf),
879 if (asprintf(&alloc_str, "%s %s", buf, str) < 0)
880 str = "Not enough memory!";
885 print(browser, chain, str, offset, row, arg);
892 static bool check_percent_display(struct rb_node *node, u64 parent_total)
894 struct callchain_node *child;
902 child = rb_entry(node, struct callchain_node, rb_node);
903 return callchain_cumul_hits(child) != parent_total;
906 static int hist_browser__show_callchain_flat(struct hist_browser *browser,
907 struct rb_root *root,
908 unsigned short row, u64 total,
910 print_callchain_entry_fn print,
911 struct callchain_print_arg *arg,
912 check_output_full_fn is_output_full)
914 struct rb_node *node;
915 int first_row = row, offset = LEVEL_OFFSET_STEP;
918 node = rb_first(root);
919 need_percent = check_percent_display(node, parent_total);
922 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
923 struct rb_node *next = rb_next(node);
924 struct callchain_list *chain;
925 char folded_sign = ' ';
927 int extra_offset = 0;
929 list_for_each_entry(chain, &child->parent_val, list) {
930 bool was_first = first;
934 else if (need_percent)
935 extra_offset = LEVEL_OFFSET_STEP;
937 folded_sign = callchain_list__folded(chain);
939 row += hist_browser__show_callchain_list(browser, child,
941 was_first && need_percent,
942 offset + extra_offset,
945 if (is_output_full(browser, row))
948 if (folded_sign == '+')
952 list_for_each_entry(chain, &child->val, list) {
953 bool was_first = first;
957 else if (need_percent)
958 extra_offset = LEVEL_OFFSET_STEP;
960 folded_sign = callchain_list__folded(chain);
962 row += hist_browser__show_callchain_list(browser, child,
964 was_first && need_percent,
965 offset + extra_offset,
968 if (is_output_full(browser, row))
971 if (folded_sign == '+')
976 if (is_output_full(browser, row))
981 return row - first_row;
984 static char *hist_browser__folded_callchain_str(struct hist_browser *browser,
985 struct callchain_list *chain,
986 char *value_str, char *old_str)
992 str = callchain_list__sym_name(chain, bf, sizeof(bf),
995 if (asprintf(&new, "%s%s%s", old_str,
996 symbol_conf.field_sep ?: ";", str) < 0)
1000 if (asprintf(&new, "%s %s", value_str, str) < 0)
1003 if (asprintf(&new, "%s", str) < 0)
1010 static int hist_browser__show_callchain_folded(struct hist_browser *browser,
1011 struct rb_root *root,
1012 unsigned short row, u64 total,
1014 print_callchain_entry_fn print,
1015 struct callchain_print_arg *arg,
1016 check_output_full_fn is_output_full)
1018 struct rb_node *node;
1019 int first_row = row, offset = LEVEL_OFFSET_STEP;
1022 node = rb_first(root);
1023 need_percent = check_percent_display(node, parent_total);
1026 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1027 struct rb_node *next = rb_next(node);
1028 struct callchain_list *chain, *first_chain = NULL;
1030 char *value_str = NULL, *value_str_alloc = NULL;
1031 char *chain_str = NULL, *chain_str_alloc = NULL;
1033 if (arg->row_offset != 0) {
1041 callchain_node__scnprintf_value(child, buf, sizeof(buf), total);
1042 if (asprintf(&value_str, "%s", buf) < 0) {
1043 value_str = (char *)"<...>";
1046 value_str_alloc = value_str;
1049 list_for_each_entry(chain, &child->parent_val, list) {
1050 chain_str = hist_browser__folded_callchain_str(browser,
1051 chain, value_str, chain_str);
1054 first_chain = chain;
1057 if (chain_str == NULL) {
1058 chain_str = (char *)"Not enough memory!";
1062 chain_str_alloc = chain_str;
1065 list_for_each_entry(chain, &child->val, list) {
1066 chain_str = hist_browser__folded_callchain_str(browser,
1067 chain, value_str, chain_str);
1070 first_chain = chain;
1073 if (chain_str == NULL) {
1074 chain_str = (char *)"Not enough memory!";
1078 chain_str_alloc = chain_str;
1082 print(browser, first_chain, chain_str, offset, row++, arg);
1083 free(value_str_alloc);
1084 free(chain_str_alloc);
1087 if (is_output_full(browser, row))
1092 return row - first_row;
1095 static int hist_browser__show_callchain_graph(struct hist_browser *browser,
1096 struct rb_root *root, int level,
1097 unsigned short row, u64 total,
1099 print_callchain_entry_fn print,
1100 struct callchain_print_arg *arg,
1101 check_output_full_fn is_output_full)
1103 struct rb_node *node;
1104 int first_row = row, offset = level * LEVEL_OFFSET_STEP;
1106 u64 percent_total = total;
1108 if (callchain_param.mode == CHAIN_GRAPH_REL)
1109 percent_total = parent_total;
1111 node = rb_first(root);
1112 need_percent = check_percent_display(node, parent_total);
1115 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1116 struct rb_node *next = rb_next(node);
1117 struct callchain_list *chain;
1118 char folded_sign = ' ';
1120 int extra_offset = 0;
1122 list_for_each_entry(chain, &child->val, list) {
1123 bool was_first = first;
1127 else if (need_percent)
1128 extra_offset = LEVEL_OFFSET_STEP;
1130 folded_sign = callchain_list__folded(chain);
1132 row += hist_browser__show_callchain_list(browser, child,
1133 chain, row, percent_total,
1134 was_first && need_percent,
1135 offset + extra_offset,
1138 if (is_output_full(browser, row))
1141 if (folded_sign == '+')
1145 if (folded_sign == '-') {
1146 const int new_level = level + (extra_offset ? 2 : 1);
1148 row += hist_browser__show_callchain_graph(browser, &child->rb_root,
1149 new_level, row, total,
1150 child->children_hit,
1151 print, arg, is_output_full);
1153 if (is_output_full(browser, row))
1158 return row - first_row;
1161 static int hist_browser__show_callchain(struct hist_browser *browser,
1162 struct hist_entry *entry, int level,
1164 print_callchain_entry_fn print,
1165 struct callchain_print_arg *arg,
1166 check_output_full_fn is_output_full)
1168 u64 total = hists__total_period(entry->hists);
1172 if (symbol_conf.cumulate_callchain)
1173 parent_total = entry->stat_acc->period;
1175 parent_total = entry->stat.period;
1177 if (callchain_param.mode == CHAIN_FLAT) {
1178 printed = hist_browser__show_callchain_flat(browser,
1179 &entry->sorted_chain, row,
1180 total, parent_total, print, arg,
1182 } else if (callchain_param.mode == CHAIN_FOLDED) {
1183 printed = hist_browser__show_callchain_folded(browser,
1184 &entry->sorted_chain, row,
1185 total, parent_total, print, arg,
1188 printed = hist_browser__show_callchain_graph(browser,
1189 &entry->sorted_chain, level, row,
1190 total, parent_total, print, arg,
1194 if (arg->is_current_entry)
1195 browser->he_selection = entry;
1201 struct ui_browser *b;
1206 int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
1208 struct hpp_arg *arg = hpp->ptr;
1213 va_start(args, fmt);
1214 len = va_arg(args, int);
1215 percent = va_arg(args, double);
1218 ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
1220 ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
1221 ui_browser__printf(arg->b, "%s", hpp->buf);
1226 #define __HPP_COLOR_PERCENT_FN(_type, _field) \
1227 static u64 __hpp_get_##_field(struct hist_entry *he) \
1229 return he->stat._field; \
1233 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
1234 struct perf_hpp *hpp, \
1235 struct hist_entry *he) \
1237 return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%", \
1238 __hpp__slsmg_color_printf, true); \
1241 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
1242 static u64 __hpp_get_acc_##_field(struct hist_entry *he) \
1244 return he->stat_acc->_field; \
1248 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
1249 struct perf_hpp *hpp, \
1250 struct hist_entry *he) \
1252 if (!symbol_conf.cumulate_callchain) { \
1253 struct hpp_arg *arg = hpp->ptr; \
1254 int len = fmt->user_len ?: fmt->len; \
1255 int ret = scnprintf(hpp->buf, hpp->size, \
1256 "%*s", len, "N/A"); \
1257 ui_browser__printf(arg->b, "%s", hpp->buf); \
1261 return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field, \
1262 " %*.2f%%", __hpp__slsmg_color_printf, true); \
1265 __HPP_COLOR_PERCENT_FN(overhead, period)
1266 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
1267 __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
1268 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
1269 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
1270 __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
1272 #undef __HPP_COLOR_PERCENT_FN
1273 #undef __HPP_COLOR_ACC_PERCENT_FN
1275 void hist_browser__init_hpp(void)
1277 perf_hpp__format[PERF_HPP__OVERHEAD].color =
1278 hist_browser__hpp_color_overhead;
1279 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
1280 hist_browser__hpp_color_overhead_sys;
1281 perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
1282 hist_browser__hpp_color_overhead_us;
1283 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
1284 hist_browser__hpp_color_overhead_guest_sys;
1285 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
1286 hist_browser__hpp_color_overhead_guest_us;
1287 perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
1288 hist_browser__hpp_color_overhead_acc;
1293 static int hist_browser__show_entry(struct hist_browser *browser,
1294 struct hist_entry *entry,
1298 int width = browser->b.width;
1299 char folded_sign = ' ';
1300 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1301 bool use_callchain = hist_entry__has_callchains(entry) && symbol_conf.use_callchain;
1302 off_t row_offset = entry->row_offset;
1304 struct perf_hpp_fmt *fmt;
1306 if (current_entry) {
1307 browser->he_selection = entry;
1308 browser->selection = &entry->ms;
1311 if (use_callchain) {
1312 hist_entry__init_have_children(entry);
1313 folded_sign = hist_entry__folded(entry);
1316 if (row_offset == 0) {
1317 struct hpp_arg arg = {
1319 .folded_sign = folded_sign,
1320 .current_entry = current_entry,
1324 ui_browser__gotorc(&browser->b, row, 0);
1326 hists__for_each_format(browser->hists, fmt) {
1328 struct perf_hpp hpp = {
1334 if (perf_hpp__should_skip(fmt, entry->hists) ||
1335 column++ < browser->b.horiz_scroll)
1338 if (current_entry && browser->b.navkeypressed) {
1339 ui_browser__set_color(&browser->b,
1340 HE_COLORSET_SELECTED);
1342 ui_browser__set_color(&browser->b,
1343 HE_COLORSET_NORMAL);
1347 if (use_callchain) {
1348 ui_browser__printf(&browser->b, "%c ", folded_sign);
1353 ui_browser__printf(&browser->b, " ");
1358 int ret = fmt->color(fmt, &hpp, entry);
1359 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1361 * fmt->color() already used ui_browser to
1362 * print the non alignment bits, skip it (+ret):
1364 ui_browser__printf(&browser->b, "%s", s + ret);
1366 hist_entry__snprintf_alignment(entry, &hpp, fmt, fmt->entry(fmt, &hpp, entry));
1367 ui_browser__printf(&browser->b, "%s", s);
1369 width -= hpp.buf - s;
1372 /* The scroll bar isn't being used */
1373 if (!browser->b.navkeypressed)
1376 ui_browser__write_nstring(&browser->b, "", width);
1383 if (folded_sign == '-' && row != browser->b.rows) {
1384 struct callchain_print_arg arg = {
1385 .row_offset = row_offset,
1386 .is_current_entry = current_entry,
1389 printed += hist_browser__show_callchain(browser,
1391 hist_browser__show_callchain_entry,
1393 hist_browser__check_output_full);
1399 static int hist_browser__show_hierarchy_entry(struct hist_browser *browser,
1400 struct hist_entry *entry,
1405 int width = browser->b.width;
1406 char folded_sign = ' ';
1407 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1408 off_t row_offset = entry->row_offset;
1410 struct perf_hpp_fmt *fmt;
1411 struct perf_hpp_list_node *fmt_node;
1412 struct hpp_arg arg = {
1414 .current_entry = current_entry,
1417 int hierarchy_indent = (entry->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
1419 if (current_entry) {
1420 browser->he_selection = entry;
1421 browser->selection = &entry->ms;
1424 hist_entry__init_have_children(entry);
1425 folded_sign = hist_entry__folded(entry);
1426 arg.folded_sign = folded_sign;
1428 if (entry->leaf && row_offset) {
1430 goto show_callchain;
1433 ui_browser__gotorc(&browser->b, row, 0);
1435 if (current_entry && browser->b.navkeypressed)
1436 ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1438 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1440 ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1441 width -= level * HIERARCHY_INDENT;
1443 /* the first hpp_list_node is for overhead columns */
1444 fmt_node = list_first_entry(&entry->hists->hpp_formats,
1445 struct perf_hpp_list_node, list);
1446 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1448 struct perf_hpp hpp = {
1454 if (perf_hpp__should_skip(fmt, entry->hists) ||
1455 column++ < browser->b.horiz_scroll)
1458 if (current_entry && browser->b.navkeypressed) {
1459 ui_browser__set_color(&browser->b,
1460 HE_COLORSET_SELECTED);
1462 ui_browser__set_color(&browser->b,
1463 HE_COLORSET_NORMAL);
1467 ui_browser__printf(&browser->b, "%c ", folded_sign);
1471 ui_browser__printf(&browser->b, " ");
1476 int ret = fmt->color(fmt, &hpp, entry);
1477 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1479 * fmt->color() already used ui_browser to
1480 * print the non alignment bits, skip it (+ret):
1482 ui_browser__printf(&browser->b, "%s", s + ret);
1484 int ret = fmt->entry(fmt, &hpp, entry);
1485 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1486 ui_browser__printf(&browser->b, "%s", s);
1488 width -= hpp.buf - s;
1492 ui_browser__write_nstring(&browser->b, "", hierarchy_indent);
1493 width -= hierarchy_indent;
1496 if (column >= browser->b.horiz_scroll) {
1498 struct perf_hpp hpp = {
1504 if (current_entry && browser->b.navkeypressed) {
1505 ui_browser__set_color(&browser->b,
1506 HE_COLORSET_SELECTED);
1508 ui_browser__set_color(&browser->b,
1509 HE_COLORSET_NORMAL);
1512 perf_hpp_list__for_each_format(entry->hpp_list, fmt) {
1514 ui_browser__printf(&browser->b, "%c ", folded_sign);
1517 ui_browser__write_nstring(&browser->b, "", 2);
1523 * No need to call hist_entry__snprintf_alignment()
1524 * since this fmt is always the last column in the
1528 width -= fmt->color(fmt, &hpp, entry);
1532 width -= fmt->entry(fmt, &hpp, entry);
1533 ui_browser__printf(&browser->b, "%s", skip_spaces(s));
1535 while (isspace(s[i++]))
1541 /* The scroll bar isn't being used */
1542 if (!browser->b.navkeypressed)
1545 ui_browser__write_nstring(&browser->b, "", width);
1551 if (entry->leaf && folded_sign == '-' && row != browser->b.rows) {
1552 struct callchain_print_arg carg = {
1553 .row_offset = row_offset,
1556 printed += hist_browser__show_callchain(browser, entry,
1558 hist_browser__show_callchain_entry, &carg,
1559 hist_browser__check_output_full);
1565 static int hist_browser__show_no_entry(struct hist_browser *browser,
1566 unsigned short row, int level)
1568 int width = browser->b.width;
1569 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1573 struct perf_hpp_fmt *fmt;
1574 struct perf_hpp_list_node *fmt_node;
1575 int indent = browser->hists->nr_hpp_node - 2;
1577 if (current_entry) {
1578 browser->he_selection = NULL;
1579 browser->selection = NULL;
1582 ui_browser__gotorc(&browser->b, row, 0);
1584 if (current_entry && browser->b.navkeypressed)
1585 ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1587 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1589 ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1590 width -= level * HIERARCHY_INDENT;
1592 /* the first hpp_list_node is for overhead columns */
1593 fmt_node = list_first_entry(&browser->hists->hpp_formats,
1594 struct perf_hpp_list_node, list);
1595 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1596 if (perf_hpp__should_skip(fmt, browser->hists) ||
1597 column++ < browser->b.horiz_scroll)
1600 ret = fmt->width(fmt, NULL, browser->hists);
1603 /* for folded sign */
1607 /* space between columns */
1611 ui_browser__write_nstring(&browser->b, "", ret);
1615 ui_browser__write_nstring(&browser->b, "", indent * HIERARCHY_INDENT);
1616 width -= indent * HIERARCHY_INDENT;
1618 if (column >= browser->b.horiz_scroll) {
1621 ret = snprintf(buf, sizeof(buf), "no entry >= %.2f%%", browser->min_pcnt);
1622 ui_browser__printf(&browser->b, " %s", buf);
1626 /* The scroll bar isn't being used */
1627 if (!browser->b.navkeypressed)
1630 ui_browser__write_nstring(&browser->b, "", width);
1634 static int advance_hpp_check(struct perf_hpp *hpp, int inc)
1636 advance_hpp(hpp, inc);
1637 return hpp->size <= 0;
1641 hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf,
1642 size_t size, int line)
1644 struct hists *hists = browser->hists;
1645 struct perf_hpp dummy_hpp = {
1649 struct perf_hpp_fmt *fmt;
1654 if (hists__has_callchains(hists) && symbol_conf.use_callchain) {
1655 ret = scnprintf(buf, size, " ");
1656 if (advance_hpp_check(&dummy_hpp, ret))
1660 hists__for_each_format(browser->hists, fmt) {
1661 if (perf_hpp__should_skip(fmt, hists) || column++ < browser->b.horiz_scroll)
1664 ret = fmt->header(fmt, &dummy_hpp, hists, line, &span);
1665 if (advance_hpp_check(&dummy_hpp, ret))
1671 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
1672 if (advance_hpp_check(&dummy_hpp, ret))
1679 static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser, char *buf, size_t size)
1681 struct hists *hists = browser->hists;
1682 struct perf_hpp dummy_hpp = {
1686 struct perf_hpp_fmt *fmt;
1687 struct perf_hpp_list_node *fmt_node;
1690 int indent = hists->nr_hpp_node - 2;
1691 bool first_node, first_col;
1693 ret = scnprintf(buf, size, " ");
1694 if (advance_hpp_check(&dummy_hpp, ret))
1698 /* the first hpp_list_node is for overhead columns */
1699 fmt_node = list_first_entry(&hists->hpp_formats,
1700 struct perf_hpp_list_node, list);
1701 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1702 if (column++ < browser->b.horiz_scroll)
1705 ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1706 if (advance_hpp_check(&dummy_hpp, ret))
1709 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
1710 if (advance_hpp_check(&dummy_hpp, ret))
1717 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "%*s",
1718 indent * HIERARCHY_INDENT, "");
1719 if (advance_hpp_check(&dummy_hpp, ret))
1724 list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
1726 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " / ");
1727 if (advance_hpp_check(&dummy_hpp, ret))
1733 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1736 if (perf_hpp__should_skip(fmt, hists))
1740 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "+");
1741 if (advance_hpp_check(&dummy_hpp, ret))
1746 ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1747 dummy_hpp.buf[ret] = '\0';
1749 start = strim(dummy_hpp.buf);
1750 ret = strlen(start);
1752 if (start != dummy_hpp.buf)
1753 memmove(dummy_hpp.buf, start, ret + 1);
1755 if (advance_hpp_check(&dummy_hpp, ret))
1763 static void hists_browser__hierarchy_headers(struct hist_browser *browser)
1767 hists_browser__scnprintf_hierarchy_headers(browser, headers,
1770 ui_browser__gotorc(&browser->b, 0, 0);
1771 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1772 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1775 static void hists_browser__headers(struct hist_browser *browser)
1777 struct hists *hists = browser->hists;
1778 struct perf_hpp_list *hpp_list = hists->hpp_list;
1782 for (line = 0; line < hpp_list->nr_header_lines; line++) {
1785 hists_browser__scnprintf_headers(browser, headers,
1786 sizeof(headers), line);
1788 ui_browser__gotorc_title(&browser->b, line, 0);
1789 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1790 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1794 static void hist_browser__show_headers(struct hist_browser *browser)
1796 if (symbol_conf.report_hierarchy)
1797 hists_browser__hierarchy_headers(browser);
1799 hists_browser__headers(browser);
1802 static void ui_browser__hists_init_top(struct ui_browser *browser)
1804 if (browser->top == NULL) {
1805 struct hist_browser *hb;
1807 hb = container_of(browser, struct hist_browser, b);
1808 browser->top = rb_first_cached(&hb->hists->entries);
1812 static unsigned int hist_browser__refresh(struct ui_browser *browser)
1816 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
1818 if (hb->show_headers)
1819 hist_browser__show_headers(hb);
1821 ui_browser__hists_init_top(browser);
1822 hb->he_selection = NULL;
1823 hb->selection = NULL;
1825 for (nd = browser->top; nd; nd = rb_hierarchy_next(nd)) {
1826 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1830 /* let it move to sibling */
1831 h->unfolded = false;
1835 if (symbol_conf.report_individual_block)
1836 percent = block_info__total_cycles_percent(h);
1838 percent = hist_entry__get_percent_limit(h);
1840 if (percent < hb->min_pcnt)
1843 if (symbol_conf.report_hierarchy) {
1844 row += hist_browser__show_hierarchy_entry(hb, h, row,
1846 if (row == browser->rows)
1849 if (h->has_no_entry) {
1850 hist_browser__show_no_entry(hb, row, h->depth + 1);
1854 row += hist_browser__show_entry(hb, h, row);
1857 if (row == browser->rows)
1864 static struct rb_node *hists__filter_entries(struct rb_node *nd,
1867 while (nd != NULL) {
1868 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1869 float percent = hist_entry__get_percent_limit(h);
1871 if (!h->filtered && percent >= min_pcnt)
1875 * If it's filtered, its all children also were filtered.
1876 * So move to sibling node.
1881 nd = rb_hierarchy_next(nd);
1887 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
1890 while (nd != NULL) {
1891 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1892 float percent = hist_entry__get_percent_limit(h);
1894 if (!h->filtered && percent >= min_pcnt)
1897 nd = rb_hierarchy_prev(nd);
1903 static void ui_browser__hists_seek(struct ui_browser *browser,
1904 off_t offset, int whence)
1906 struct hist_entry *h;
1909 struct hist_browser *hb;
1911 hb = container_of(browser, struct hist_browser, b);
1913 if (browser->nr_entries == 0)
1916 ui_browser__hists_init_top(browser);
1920 nd = hists__filter_entries(rb_first(browser->entries),
1927 nd = rb_hierarchy_last(rb_last(browser->entries));
1928 nd = hists__filter_prev_entries(nd, hb->min_pcnt);
1936 * Moves not relative to the first visible entry invalidates its
1939 h = rb_entry(browser->top, struct hist_entry, rb_node);
1943 * Here we have to check if nd is expanded (+), if it is we can't go
1944 * the next top level hist_entry, instead we must compute an offset of
1945 * what _not_ to show and not change the first visible entry.
1947 * This offset increments when we are going from top to bottom and
1948 * decreases when we're going from bottom to top.
1950 * As we don't have backpointers to the top level in the callchains
1951 * structure, we need to always print the whole hist_entry callchain,
1952 * skipping the first ones that are before the first visible entry
1953 * and stop when we printed enough lines to fill the screen.
1961 h = rb_entry(nd, struct hist_entry, rb_node);
1962 if (h->unfolded && h->leaf) {
1963 u16 remaining = h->nr_rows - h->row_offset;
1964 if (offset > remaining) {
1965 offset -= remaining;
1968 h->row_offset += offset;
1974 nd = hists__filter_entries(rb_hierarchy_next(nd),
1980 } while (offset != 0);
1981 } else if (offset < 0) {
1983 h = rb_entry(nd, struct hist_entry, rb_node);
1984 if (h->unfolded && h->leaf) {
1986 if (-offset > h->row_offset) {
1987 offset += h->row_offset;
1990 h->row_offset += offset;
1996 if (-offset > h->nr_rows) {
1997 offset += h->nr_rows;
2000 h->row_offset = h->nr_rows + offset;
2008 nd = hists__filter_prev_entries(rb_hierarchy_prev(nd),
2016 * Last unfiltered hist_entry, check if it is
2017 * unfolded, if it is then we should have
2018 * row_offset at its last entry.
2020 h = rb_entry(nd, struct hist_entry, rb_node);
2021 if (h->unfolded && h->leaf)
2022 h->row_offset = h->nr_rows;
2029 h = rb_entry(nd, struct hist_entry, rb_node);
2034 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
2035 struct hist_entry *he, FILE *fp,
2038 struct callchain_print_arg arg = {
2042 hist_browser__show_callchain(browser, he, level, 0,
2043 hist_browser__fprintf_callchain_entry, &arg,
2044 hist_browser__check_dump_full);
2048 static int hist_browser__fprintf_entry(struct hist_browser *browser,
2049 struct hist_entry *he, FILE *fp)
2053 char folded_sign = ' ';
2054 struct perf_hpp hpp = {
2058 struct perf_hpp_fmt *fmt;
2062 if (hist_entry__has_callchains(he) && symbol_conf.use_callchain) {
2063 folded_sign = hist_entry__folded(he);
2064 printed += fprintf(fp, "%c ", folded_sign);
2067 hists__for_each_format(browser->hists, fmt) {
2068 if (perf_hpp__should_skip(fmt, he->hists))
2072 ret = scnprintf(hpp.buf, hpp.size, " ");
2073 advance_hpp(&hpp, ret);
2077 ret = fmt->entry(fmt, &hpp, he);
2078 ret = hist_entry__snprintf_alignment(he, &hpp, fmt, ret);
2079 advance_hpp(&hpp, ret);
2081 printed += fprintf(fp, "%s\n", s);
2083 if (folded_sign == '-')
2084 printed += hist_browser__fprintf_callchain(browser, he, fp, 1);
2090 static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,
2091 struct hist_entry *he,
2092 FILE *fp, int level)
2096 char folded_sign = ' ';
2097 struct perf_hpp hpp = {
2101 struct perf_hpp_fmt *fmt;
2102 struct perf_hpp_list_node *fmt_node;
2105 int hierarchy_indent = (he->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
2107 printed = fprintf(fp, "%*s", level * HIERARCHY_INDENT, "");
2109 folded_sign = hist_entry__folded(he);
2110 printed += fprintf(fp, "%c", folded_sign);
2112 /* the first hpp_list_node is for overhead columns */
2113 fmt_node = list_first_entry(&he->hists->hpp_formats,
2114 struct perf_hpp_list_node, list);
2115 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
2117 ret = scnprintf(hpp.buf, hpp.size, " ");
2118 advance_hpp(&hpp, ret);
2122 ret = fmt->entry(fmt, &hpp, he);
2123 advance_hpp(&hpp, ret);
2126 ret = scnprintf(hpp.buf, hpp.size, "%*s", hierarchy_indent, "");
2127 advance_hpp(&hpp, ret);
2129 perf_hpp_list__for_each_format(he->hpp_list, fmt) {
2130 ret = scnprintf(hpp.buf, hpp.size, " ");
2131 advance_hpp(&hpp, ret);
2133 ret = fmt->entry(fmt, &hpp, he);
2134 advance_hpp(&hpp, ret);
2138 printed += fprintf(fp, "%s\n", s);
2140 if (he->leaf && folded_sign == '-') {
2141 printed += hist_browser__fprintf_callchain(browser, he, fp,
2148 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
2150 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
2155 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
2157 if (symbol_conf.report_hierarchy) {
2158 printed += hist_browser__fprintf_hierarchy_entry(browser,
2162 printed += hist_browser__fprintf_entry(browser, h, fp);
2165 nd = hists__filter_entries(rb_hierarchy_next(nd),
2172 static int hist_browser__dump(struct hist_browser *browser)
2178 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
2179 if (access(filename, F_OK))
2182 * XXX: Just an arbitrary lazy upper limit
2184 if (++browser->print_seq == 8192) {
2185 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
2190 fp = fopen(filename, "w");
2193 const char *err = str_error_r(errno, bf, sizeof(bf));
2194 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
2198 ++browser->print_seq;
2199 hist_browser__fprintf(browser, fp);
2201 ui_helpline__fpush("%s written!", filename);
2206 void hist_browser__init(struct hist_browser *browser,
2207 struct hists *hists)
2209 struct perf_hpp_fmt *fmt;
2211 browser->hists = hists;
2212 browser->b.refresh = hist_browser__refresh;
2213 browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
2214 browser->b.seek = ui_browser__hists_seek;
2215 browser->b.use_navkeypressed = true;
2216 browser->show_headers = symbol_conf.show_hist_headers;
2217 hist_browser__set_title_space(browser);
2219 if (symbol_conf.report_hierarchy) {
2220 struct perf_hpp_list_node *fmt_node;
2222 /* count overhead columns (in the first node) */
2223 fmt_node = list_first_entry(&hists->hpp_formats,
2224 struct perf_hpp_list_node, list);
2225 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt)
2226 ++browser->b.columns;
2228 /* add a single column for whole hierarchy sort keys*/
2229 ++browser->b.columns;
2231 hists__for_each_format(hists, fmt)
2232 ++browser->b.columns;
2235 hists__reset_column_width(hists);
2238 struct hist_browser *hist_browser__new(struct hists *hists)
2240 struct hist_browser *browser = zalloc(sizeof(*browser));
2243 hist_browser__init(browser, hists);
2248 static struct hist_browser *
2249 perf_evsel_browser__new(struct evsel *evsel,
2250 struct hist_browser_timer *hbt,
2251 struct perf_env *env,
2252 struct annotation_options *annotation_opts)
2254 struct hist_browser *browser = hist_browser__new(evsel__hists(evsel));
2259 browser->title = hists_browser__scnprintf_title;
2260 browser->annotation_opts = annotation_opts;
2265 void hist_browser__delete(struct hist_browser *browser)
2270 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
2272 return browser->he_selection;
2275 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
2277 return browser->he_selection->thread;
2280 /* Check whether the browser is for 'top' or 'report' */
2281 static inline bool is_report_browser(void *timer)
2283 return timer == NULL;
2286 static int hists_browser__scnprintf_title(struct hist_browser *browser, char *bf, size_t size)
2288 struct hist_browser_timer *hbt = browser->hbt;
2289 int printed = __hists__scnprintf_title(browser->hists, bf, size, !is_report_browser(hbt));
2291 if (!is_report_browser(hbt)) {
2292 struct perf_top *top = hbt->arg;
2294 printed += scnprintf(bf + printed, size - printed,
2295 " lost: %" PRIu64 "/%" PRIu64,
2296 top->lost, top->lost_total);
2298 printed += scnprintf(bf + printed, size - printed,
2299 " drop: %" PRIu64 "/%" PRIu64,
2300 top->drop, top->drop_total);
2303 printed += scnprintf(bf + printed, size - printed, " [z]");
2305 perf_top__reset_sample_counters(top);
2312 static inline void free_popup_options(char **options, int n)
2316 for (i = 0; i < n; ++i)
2321 * Only runtime switching of perf data file will make "input_name" point
2322 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
2323 * whether we need to call free() for current "input_name" during the switch.
2325 static bool is_input_name_malloced = false;
2327 static int switch_data_file(void)
2329 char *pwd, *options[32], *abs_path[32], *tmp;
2331 int nr_options = 0, choice = -1, ret = -1;
2332 struct dirent *dent;
2334 pwd = getenv("PWD");
2338 pwd_dir = opendir(pwd);
2342 memset(options, 0, sizeof(options));
2343 memset(abs_path, 0, sizeof(abs_path));
2345 while ((dent = readdir(pwd_dir))) {
2346 char path[PATH_MAX];
2348 char *name = dent->d_name;
2351 if (!(dent->d_type == DT_REG))
2354 snprintf(path, sizeof(path), "%s/%s", pwd, name);
2356 file = fopen(path, "r");
2360 if (fread(&magic, 1, 8, file) < 8)
2361 goto close_file_and_continue;
2363 if (is_perf_magic(magic)) {
2364 options[nr_options] = strdup(name);
2365 if (!options[nr_options])
2366 goto close_file_and_continue;
2368 abs_path[nr_options] = strdup(path);
2369 if (!abs_path[nr_options]) {
2370 zfree(&options[nr_options]);
2371 ui__warning("Can't search all data files due to memory shortage.\n");
2379 close_file_and_continue:
2381 if (nr_options >= 32) {
2382 ui__warning("Too many perf data files in PWD!\n"
2383 "Only the first 32 files will be listed.\n");
2390 choice = ui__popup_menu(nr_options, options);
2391 if (choice < nr_options && choice >= 0) {
2392 tmp = strdup(abs_path[choice]);
2394 if (is_input_name_malloced)
2395 free((void *)input_name);
2397 is_input_name_malloced = true;
2400 ui__warning("Data switch failed due to memory shortage!\n");
2404 free_popup_options(options, nr_options);
2405 free_popup_options(abs_path, nr_options);
2409 struct popup_action {
2411 struct thread *thread;
2412 struct map_symbol ms;
2414 struct evsel *evsel;
2417 int (*fn)(struct hist_browser *browser, struct popup_action *act);
2421 do_annotate(struct hist_browser *browser, struct popup_action *act)
2423 struct evsel *evsel;
2424 struct annotation *notes;
2425 struct hist_entry *he;
2428 if (!browser->annotation_opts->objdump_path &&
2429 perf_env__lookup_objdump(browser->env, &browser->annotation_opts->objdump_path))
2432 notes = symbol__annotation(act->ms.sym);
2436 if (browser->block_evsel)
2437 evsel = browser->block_evsel;
2439 evsel = hists_to_evsel(browser->hists);
2441 err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt,
2442 browser->annotation_opts);
2443 he = hist_browser__selected_entry(browser);
2445 * offer option to annotate the other branch source or target
2446 * (if they exists) when returning from annotate
2448 if ((err == 'q' || err == CTRL('c')) && he->branch_info)
2451 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
2453 ui_browser__handle_resize(&browser->b);
2458 add_annotate_opt(struct hist_browser *browser __maybe_unused,
2459 struct popup_action *act, char **optstr,
2460 struct map_symbol *ms)
2462 if (ms->sym == NULL || ms->map->dso->annotate_warned)
2465 if (asprintf(optstr, "Annotate %s", ms->sym->name) < 0)
2469 act->fn = do_annotate;
2474 do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
2476 struct thread *thread = act->thread;
2478 if ((!hists__has(browser->hists, thread) &&
2479 !hists__has(browser->hists, comm)) || thread == NULL)
2482 if (browser->hists->thread_filter) {
2483 pstack__remove(browser->pstack, &browser->hists->thread_filter);
2484 perf_hpp__set_elide(HISTC_THREAD, false);
2485 thread__zput(browser->hists->thread_filter);
2488 if (hists__has(browser->hists, thread)) {
2489 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
2490 thread->comm_set ? thread__comm_str(thread) : "",
2493 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
2494 thread->comm_set ? thread__comm_str(thread) : "");
2497 browser->hists->thread_filter = thread__get(thread);
2498 perf_hpp__set_elide(HISTC_THREAD, false);
2499 pstack__push(browser->pstack, &browser->hists->thread_filter);
2502 hists__filter_by_thread(browser->hists);
2503 hist_browser__reset(browser);
2508 add_thread_opt(struct hist_browser *browser, struct popup_action *act,
2509 char **optstr, struct thread *thread)
2513 if ((!hists__has(browser->hists, thread) &&
2514 !hists__has(browser->hists, comm)) || thread == NULL)
2517 if (hists__has(browser->hists, thread)) {
2518 ret = asprintf(optstr, "Zoom %s %s(%d) thread",
2519 browser->hists->thread_filter ? "out of" : "into",
2520 thread->comm_set ? thread__comm_str(thread) : "",
2523 ret = asprintf(optstr, "Zoom %s %s thread",
2524 browser->hists->thread_filter ? "out of" : "into",
2525 thread->comm_set ? thread__comm_str(thread) : "");
2530 act->thread = thread;
2531 act->fn = do_zoom_thread;
2535 static int hists_browser__zoom_map(struct hist_browser *browser, struct map *map)
2537 if (!hists__has(browser->hists, dso) || map == NULL)
2540 if (browser->hists->dso_filter) {
2541 pstack__remove(browser->pstack, &browser->hists->dso_filter);
2542 perf_hpp__set_elide(HISTC_DSO, false);
2543 browser->hists->dso_filter = NULL;
2546 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
2547 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
2548 browser->hists->dso_filter = map->dso;
2549 perf_hpp__set_elide(HISTC_DSO, true);
2550 pstack__push(browser->pstack, &browser->hists->dso_filter);
2553 hists__filter_by_dso(browser->hists);
2554 hist_browser__reset(browser);
2559 do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
2561 return hists_browser__zoom_map(browser, act->ms.map);
2565 add_dso_opt(struct hist_browser *browser, struct popup_action *act,
2566 char **optstr, struct map *map)
2568 if (!hists__has(browser->hists, dso) || map == NULL)
2571 if (asprintf(optstr, "Zoom %s %s DSO (use the 'k' hotkey to zoom directly into the kernel)",
2572 browser->hists->dso_filter ? "out of" : "into",
2573 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
2577 act->fn = do_zoom_dso;
2581 static int do_toggle_callchain(struct hist_browser *browser, struct popup_action *act __maybe_unused)
2583 hist_browser__toggle_fold(browser);
2587 static int add_callchain_toggle_opt(struct hist_browser *browser, struct popup_action *act, char **optstr)
2591 if (!hist_browser__selection_has_children(browser))
2594 if (asprintf(optstr, "%s [%s] callchain (one level, same as '+' hotkey, use 'e'/'c' for the whole main level entry)",
2595 hist_browser__selection_unfolded(browser) ? "Collapse" : "Expand",
2596 hist_browser__selection_sym_name(browser, sym_name, sizeof(sym_name))) < 0)
2599 act->fn = do_toggle_callchain;
2604 do_browse_map(struct hist_browser *browser __maybe_unused,
2605 struct popup_action *act)
2607 map__browse(act->ms.map);
2612 add_map_opt(struct hist_browser *browser,
2613 struct popup_action *act, char **optstr, struct map *map)
2615 if (!hists__has(browser->hists, dso) || map == NULL)
2618 if (asprintf(optstr, "Browse map details") < 0)
2622 act->fn = do_browse_map;
2627 do_run_script(struct hist_browser *browser __maybe_unused,
2628 struct popup_action *act)
2636 len += strlen(thread__comm_str(act->thread));
2637 else if (act->ms.sym)
2638 len += strlen(act->ms.sym->name);
2639 script_opt = malloc(len);
2645 n = scnprintf(script_opt, len, " -c %s ",
2646 thread__comm_str(act->thread));
2647 } else if (act->ms.sym) {
2648 n = scnprintf(script_opt, len, " -S %s ",
2653 char start[32], end[32];
2654 unsigned long starttime = act->time;
2655 unsigned long endtime = act->time + symbol_conf.time_quantum;
2657 if (starttime == endtime) { /* Display 1ms as fallback */
2658 starttime -= 1*NSEC_PER_MSEC;
2659 endtime += 1*NSEC_PER_MSEC;
2661 timestamp__scnprintf_usec(starttime, start, sizeof start);
2662 timestamp__scnprintf_usec(endtime, end, sizeof end);
2663 n += snprintf(script_opt + n, len - n, " --time %s,%s", start, end);
2666 script_browse(script_opt, act->evsel);
2672 do_res_sample_script(struct hist_browser *browser __maybe_unused,
2673 struct popup_action *act)
2675 struct hist_entry *he;
2677 he = hist_browser__selected_entry(browser);
2678 res_sample_browse(he->res_samples, he->num_res, act->evsel, act->rstype);
2683 add_script_opt_2(struct hist_browser *browser __maybe_unused,
2684 struct popup_action *act, char **optstr,
2685 struct thread *thread, struct symbol *sym,
2686 struct evsel *evsel, const char *tstr)
2690 if (asprintf(optstr, "Run scripts for samples of thread [%s]%s",
2691 thread__comm_str(thread), tstr) < 0)
2694 if (asprintf(optstr, "Run scripts for samples of symbol [%s]%s",
2695 sym->name, tstr) < 0)
2698 if (asprintf(optstr, "Run scripts for all samples%s", tstr) < 0)
2702 act->thread = thread;
2705 act->fn = do_run_script;
2710 add_script_opt(struct hist_browser *browser,
2711 struct popup_action *act, char **optstr,
2712 struct thread *thread, struct symbol *sym,
2713 struct evsel *evsel)
2716 struct hist_entry *he;
2718 n = add_script_opt_2(browser, act, optstr, thread, sym, evsel, "");
2720 he = hist_browser__selected_entry(browser);
2721 if (sort_order && strstr(sort_order, "time")) {
2726 j = sprintf(tstr, " in ");
2727 j += timestamp__scnprintf_usec(he->time, tstr + j,
2729 j += sprintf(tstr + j, "-");
2730 timestamp__scnprintf_usec(he->time + symbol_conf.time_quantum,
2731 tstr + j, sizeof tstr - j);
2732 n += add_script_opt_2(browser, act, optstr, thread, sym,
2734 act->time = he->time;
2740 add_res_sample_opt(struct hist_browser *browser __maybe_unused,
2741 struct popup_action *act, char **optstr,
2742 struct res_sample *res_sample,
2743 struct evsel *evsel,
2749 if (asprintf(optstr, "Show context for individual samples %s",
2750 type == A_ASM ? "with assembler" :
2751 type == A_SOURCE ? "with source" : "") < 0)
2754 act->fn = do_res_sample_script;
2761 do_switch_data(struct hist_browser *browser __maybe_unused,
2762 struct popup_action *act __maybe_unused)
2764 if (switch_data_file()) {
2765 ui__warning("Won't switch the data files due to\n"
2766 "no valid data file get selected!\n");
2770 return K_SWITCH_INPUT_DATA;
2774 add_switch_opt(struct hist_browser *browser,
2775 struct popup_action *act, char **optstr)
2777 if (!is_report_browser(browser->hbt))
2780 if (asprintf(optstr, "Switch to another data file in PWD") < 0)
2783 act->fn = do_switch_data;
2788 do_exit_browser(struct hist_browser *browser __maybe_unused,
2789 struct popup_action *act __maybe_unused)
2795 add_exit_opt(struct hist_browser *browser __maybe_unused,
2796 struct popup_action *act, char **optstr)
2798 if (asprintf(optstr, "Exit") < 0)
2801 act->fn = do_exit_browser;
2806 do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
2808 if (!hists__has(browser->hists, socket) || act->socket < 0)
2811 if (browser->hists->socket_filter > -1) {
2812 pstack__remove(browser->pstack, &browser->hists->socket_filter);
2813 browser->hists->socket_filter = -1;
2814 perf_hpp__set_elide(HISTC_SOCKET, false);
2816 browser->hists->socket_filter = act->socket;
2817 perf_hpp__set_elide(HISTC_SOCKET, true);
2818 pstack__push(browser->pstack, &browser->hists->socket_filter);
2821 hists__filter_by_socket(browser->hists);
2822 hist_browser__reset(browser);
2827 add_socket_opt(struct hist_browser *browser, struct popup_action *act,
2828 char **optstr, int socket_id)
2830 if (!hists__has(browser->hists, socket) || socket_id < 0)
2833 if (asprintf(optstr, "Zoom %s Processor Socket %d",
2834 (browser->hists->socket_filter > -1) ? "out of" : "into",
2838 act->socket = socket_id;
2839 act->fn = do_zoom_socket;
2843 static void hist_browser__update_nr_entries(struct hist_browser *hb)
2846 struct rb_node *nd = rb_first_cached(&hb->hists->entries);
2848 if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
2849 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
2853 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2855 nd = rb_hierarchy_next(nd);
2858 hb->nr_non_filtered_entries = nr_entries;
2859 hb->nr_hierarchy_entries = nr_entries;
2862 static void hist_browser__update_percent_limit(struct hist_browser *hb,
2865 struct hist_entry *he;
2866 struct rb_node *nd = rb_first_cached(&hb->hists->entries);
2867 u64 total = hists__total_period(hb->hists);
2868 u64 min_callchain_hits = total * (percent / 100);
2870 hb->min_pcnt = callchain_param.min_percent = percent;
2872 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2873 he = rb_entry(nd, struct hist_entry, rb_node);
2875 if (he->has_no_entry) {
2876 he->has_no_entry = false;
2880 if (!he->leaf || !hist_entry__has_callchains(he) || !symbol_conf.use_callchain)
2883 if (callchain_param.mode == CHAIN_GRAPH_REL) {
2884 total = he->stat.period;
2886 if (symbol_conf.cumulate_callchain)
2887 total = he->stat_acc->period;
2889 min_callchain_hits = total * (percent / 100);
2892 callchain_param.sort(&he->sorted_chain, he->callchain,
2893 min_callchain_hits, &callchain_param);
2896 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
2898 /* force to re-evaluate folding state of callchains */
2899 he->init_have_children = false;
2900 hist_entry__set_folding(he, hb, false);
2904 static int perf_evsel__hists_browse(struct evsel *evsel, int nr_events,
2905 const char *helpline,
2907 struct hist_browser_timer *hbt,
2909 struct perf_env *env,
2910 bool warn_lost_event,
2911 struct annotation_options *annotation_opts)
2913 struct hists *hists = evsel__hists(evsel);
2914 struct hist_browser *browser = perf_evsel_browser__new(evsel, hbt, env, annotation_opts);
2915 struct branch_info *bi = NULL;
2916 #define MAX_OPTIONS 16
2917 char *options[MAX_OPTIONS];
2918 struct popup_action actions[MAX_OPTIONS];
2922 int delay_secs = hbt ? hbt->refresh : 0;
2924 #define HIST_BROWSER_HELP_COMMON \
2925 "h/?/F1 Show this window\n" \
2927 "PGDN/SPACE Navigate\n" \
2928 "q/ESC/CTRL+C Exit browser or go back to previous screen\n\n" \
2929 "For multiple event sessions:\n\n" \
2930 "TAB/UNTAB Switch events\n\n" \
2931 "For symbolic views (--sort has sym):\n\n" \
2932 "ENTER Zoom into DSO/Threads & Annotate current symbol\n" \
2934 "+ Expand/Collapse one callchain level\n" \
2935 "a Annotate current symbol\n" \
2936 "C Collapse all callchains\n" \
2937 "d Zoom into current DSO\n" \
2938 "E Expand all callchains\n" \
2939 "F Toggle percentage of filtered entries\n" \
2940 "H Display column headers\n" \
2941 "k Zoom into the kernel map\n" \
2942 "L Change percent limit\n" \
2943 "m Display context menu\n" \
2944 "S Zoom into current Processor Socket\n" \
2946 /* help messages are sorted by lexical order of the hotkey */
2947 static const char report_help[] = HIST_BROWSER_HELP_COMMON
2948 "i Show header information\n"
2949 "P Print histograms to perf.hist.N\n"
2950 "r Run available scripts\n"
2951 "s Switch to another data file in PWD\n"
2952 "t Zoom into current Thread\n"
2953 "V Verbose (DSO names in callchains, etc)\n"
2954 "/ Filter symbol by name";
2955 static const char top_help[] = HIST_BROWSER_HELP_COMMON
2956 "P Print histograms to perf.hist.N\n"
2957 "t Zoom into current Thread\n"
2958 "V Verbose (DSO names in callchains, etc)\n"
2959 "z Toggle zeroing of samples\n"
2960 "f Enable/Disable events\n"
2961 "/ Filter symbol by name";
2963 if (browser == NULL)
2966 /* reset abort key so that it can get Ctrl-C as a key */
2968 SLang_init_tty(0, 0, 0);
2971 browser->min_pcnt = min_pcnt;
2972 hist_browser__update_nr_entries(browser);
2974 browser->pstack = pstack__new(3);
2975 if (browser->pstack == NULL)
2978 ui_helpline__push(helpline);
2980 memset(options, 0, sizeof(options));
2981 memset(actions, 0, sizeof(actions));
2983 if (symbol_conf.col_width_list_str)
2984 perf_hpp__set_user_width(symbol_conf.col_width_list_str);
2986 if (!is_report_browser(hbt))
2987 browser->b.no_samples_msg = "Collecting samples...";
2990 struct thread *thread = NULL;
2991 struct map *map = NULL;
2997 key = hist_browser__run(browser, helpline,
3000 if (browser->he_selection != NULL) {
3001 thread = hist_browser__selected_thread(browser);
3002 map = browser->selection->map;
3003 socked_id = browser->he_selection->socket;
3011 * Exit the browser, let hists__browser_tree
3012 * go to the next or previous
3014 goto out_free_stack;
3016 if (!hists__has(hists, sym)) {
3017 ui_browser__warning(&browser->b, delay_secs * 2,
3018 "Annotation is only available for symbolic views, "
3019 "include \"sym*\" in --sort to use it.");
3023 if (browser->selection == NULL ||
3024 browser->selection->sym == NULL ||
3025 browser->selection->map->dso->annotate_warned)
3028 actions->ms.map = browser->selection->map;
3029 actions->ms.sym = browser->selection->sym;
3030 do_annotate(browser, actions);
3033 hist_browser__dump(browser);
3036 actions->ms.map = map;
3037 do_zoom_dso(browser, actions);
3040 if (browser->selection != NULL)
3041 hists_browser__zoom_map(browser, browser->selection->maps->machine->vmlinux_map);
3044 verbose = (verbose + 1) % 4;
3045 browser->show_dso = verbose > 0;
3046 ui_helpline__fpush("Verbosity level set to %d\n",
3050 actions->thread = thread;
3051 do_zoom_thread(browser, actions);
3054 actions->socket = socked_id;
3055 do_zoom_socket(browser, actions);
3058 if (ui_browser__input_window("Symbol to show",
3059 "Please enter the name of symbol you want to see.\n"
3060 "To remove the filter later, press / + ENTER.",
3061 buf, "ENTER: OK, ESC: Cancel",
3062 delay_secs * 2) == K_ENTER) {
3063 hists->symbol_filter_str = *buf ? buf : NULL;
3064 hists__filter_by_symbol(hists);
3065 hist_browser__reset(browser);
3069 if (is_report_browser(hbt)) {
3070 actions->thread = NULL;
3071 actions->ms.sym = NULL;
3072 do_run_script(browser, actions);
3076 if (is_report_browser(hbt)) {
3077 key = do_switch_data(browser, actions);
3078 if (key == K_SWITCH_INPUT_DATA)
3079 goto out_free_stack;
3083 /* env->arch is NULL for live-mode (i.e. perf top) */
3085 tui__header_window(env);
3088 symbol_conf.filter_relative ^= 1;
3091 if (!is_report_browser(hbt)) {
3092 struct perf_top *top = hbt->arg;
3094 top->zero = !top->zero;
3098 if (ui_browser__input_window("Percent Limit",
3099 "Please enter the value you want to hide entries under that percent.",
3100 buf, "ENTER: OK, ESC: Cancel",
3101 delay_secs * 2) == K_ENTER) {
3103 double new_percent = strtod(buf, &end);
3105 if (new_percent < 0 || new_percent > 100) {
3106 ui_browser__warning(&browser->b, delay_secs * 2,
3107 "Invalid percent: %.2f", new_percent);
3111 hist_browser__update_percent_limit(browser, new_percent);
3112 hist_browser__reset(browser);
3118 ui_browser__help_window(&browser->b,
3119 is_report_browser(hbt) ? report_help : top_help);
3130 if (pstack__empty(browser->pstack)) {
3132 * Go back to the perf_evsel_menu__run or other user
3135 goto out_free_stack;
3138 ui_browser__dialog_yesno(&browser->b,
3139 "Do you really want to exit?"))
3140 goto out_free_stack;
3144 actions->ms.map = map;
3145 top = pstack__peek(browser->pstack);
3146 if (top == &browser->hists->dso_filter) {
3148 * No need to set actions->dso here since
3149 * it's just to remove the current filter.
3150 * Ditto for thread below.
3152 do_zoom_dso(browser, actions);
3153 } else if (top == &browser->hists->thread_filter) {
3154 do_zoom_thread(browser, actions);
3155 } else if (top == &browser->hists->socket_filter) {
3156 do_zoom_socket(browser, actions);
3162 goto out_free_stack;
3164 if (!is_report_browser(hbt)) {
3165 struct perf_top *top = hbt->arg;
3167 perf_evlist__toggle_enable(top->evlist);
3169 * No need to refresh, resort/decay histogram
3170 * entries if we are not collecting samples:
3172 if (top->evlist->enabled) {
3173 helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
3174 hbt->refresh = delay_secs;
3176 helpline = "Press 'f' again to re-enable the events";
3183 helpline = "Press '?' for help on key bindings";
3187 if (!hists__has(hists, sym) || browser->selection == NULL)
3188 goto skip_annotation;
3190 if (sort__mode == SORT_MODE__BRANCH) {
3192 if (browser->he_selection)
3193 bi = browser->he_selection->branch_info;
3196 goto skip_annotation;
3198 nr_options += add_annotate_opt(browser,
3199 &actions[nr_options],
3200 &options[nr_options],
3202 if (bi->to.ms.sym != bi->from.ms.sym)
3203 nr_options += add_annotate_opt(browser,
3204 &actions[nr_options],
3205 &options[nr_options],
3208 nr_options += add_annotate_opt(browser,
3209 &actions[nr_options],
3210 &options[nr_options],
3211 browser->selection);
3214 nr_options += add_thread_opt(browser, &actions[nr_options],
3215 &options[nr_options], thread);
3216 nr_options += add_dso_opt(browser, &actions[nr_options],
3217 &options[nr_options], map);
3218 nr_options += add_callchain_toggle_opt(browser, &actions[nr_options], &options[nr_options]);
3219 nr_options += add_map_opt(browser, &actions[nr_options],
3220 &options[nr_options],
3221 browser->selection ?
3222 browser->selection->map : NULL);
3223 nr_options += add_socket_opt(browser, &actions[nr_options],
3224 &options[nr_options],
3226 /* perf script support */
3227 if (!is_report_browser(hbt))
3228 goto skip_scripting;
3230 if (browser->he_selection) {
3231 if (hists__has(hists, thread) && thread) {
3232 nr_options += add_script_opt(browser,
3233 &actions[nr_options],
3234 &options[nr_options],
3235 thread, NULL, evsel);
3238 * Note that browser->selection != NULL
3239 * when browser->he_selection is not NULL,
3240 * so we don't need to check browser->selection
3241 * before fetching browser->selection->sym like what
3242 * we do before fetching browser->selection->map.
3244 * See hist_browser__show_entry.
3246 if (hists__has(hists, sym) && browser->selection->sym) {
3247 nr_options += add_script_opt(browser,
3248 &actions[nr_options],
3249 &options[nr_options],
3250 NULL, browser->selection->sym,
3254 nr_options += add_script_opt(browser, &actions[nr_options],
3255 &options[nr_options], NULL, NULL, evsel);
3256 nr_options += add_res_sample_opt(browser, &actions[nr_options],
3257 &options[nr_options],
3258 hist_browser__selected_entry(browser)->res_samples,
3260 nr_options += add_res_sample_opt(browser, &actions[nr_options],
3261 &options[nr_options],
3262 hist_browser__selected_entry(browser)->res_samples,
3264 nr_options += add_res_sample_opt(browser, &actions[nr_options],
3265 &options[nr_options],
3266 hist_browser__selected_entry(browser)->res_samples,
3268 nr_options += add_switch_opt(browser, &actions[nr_options],
3269 &options[nr_options]);
3271 nr_options += add_exit_opt(browser, &actions[nr_options],
3272 &options[nr_options]);
3275 struct popup_action *act;
3277 choice = ui__popup_menu(nr_options, options);
3278 if (choice == -1 || choice >= nr_options)
3281 act = &actions[choice];
3282 key = act->fn(browser, act);
3285 if (key == K_SWITCH_INPUT_DATA)
3289 pstack__delete(browser->pstack);
3291 hist_browser__delete(browser);
3292 free_popup_options(options, MAX_OPTIONS);
3297 struct ui_browser b;
3298 struct evsel *selection;
3299 struct annotation_options *annotation_opts;
3300 bool lost_events, lost_events_warned;
3302 struct perf_env *env;
3305 static void perf_evsel_menu__write(struct ui_browser *browser,
3306 void *entry, int row)
3308 struct evsel_menu *menu = container_of(browser,
3309 struct evsel_menu, b);
3310 struct evsel *evsel = list_entry(entry, struct evsel, core.node);
3311 struct hists *hists = evsel__hists(evsel);
3312 bool current_entry = ui_browser__is_current_entry(browser, row);
3313 unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
3314 const char *ev_name = perf_evsel__name(evsel);
3316 const char *warn = " ";
3319 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
3320 HE_COLORSET_NORMAL);
3322 if (perf_evsel__is_group_event(evsel)) {
3325 ev_name = perf_evsel__group_name(evsel);
3327 for_each_group_member(pos, evsel) {
3328 struct hists *pos_hists = evsel__hists(pos);
3329 nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
3333 nr_events = convert_unit(nr_events, &unit);
3334 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
3335 unit, unit == ' ' ? "" : " ", ev_name);
3336 ui_browser__printf(browser, "%s", bf);
3338 nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
3339 if (nr_events != 0) {
3340 menu->lost_events = true;
3342 ui_browser__set_color(browser, HE_COLORSET_TOP);
3343 nr_events = convert_unit(nr_events, &unit);
3344 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
3345 nr_events, unit, unit == ' ' ? "" : " ");
3349 ui_browser__write_nstring(browser, warn, browser->width - printed);
3352 menu->selection = evsel;
3355 static int perf_evsel_menu__run(struct evsel_menu *menu,
3356 int nr_events, const char *help,
3357 struct hist_browser_timer *hbt,
3358 bool warn_lost_event)
3360 struct evlist *evlist = menu->b.priv;
3362 const char *title = "Available samples";
3363 int delay_secs = hbt ? hbt->refresh : 0;
3366 if (ui_browser__show(&menu->b, title,
3367 "ESC: exit, ENTER|->: Browse histograms") < 0)
3371 key = ui_browser__run(&menu->b, delay_secs);
3376 hbt->timer(hbt->arg);
3378 if (!menu->lost_events_warned &&
3379 menu->lost_events &&
3381 ui_browser__warn_lost_events(&menu->b);
3382 menu->lost_events_warned = true;
3387 if (!menu->selection)
3389 pos = menu->selection;
3391 perf_evlist__set_selected(evlist, pos);
3393 * Give the calling tool a chance to populate the non
3394 * default evsel resorted hists tree.
3397 hbt->timer(hbt->arg);
3398 key = perf_evsel__hists_browse(pos, nr_events, help,
3403 menu->annotation_opts);
3404 ui_browser__show_title(&menu->b, title);
3407 if (pos->core.node.next == &evlist->core.entries)
3408 pos = evlist__first(evlist);
3410 pos = perf_evsel__next(pos);
3413 if (pos->core.node.prev == &evlist->core.entries)
3414 pos = evlist__last(evlist);
3416 pos = perf_evsel__prev(pos);
3418 case K_SWITCH_INPUT_DATA:
3429 if (!ui_browser__dialog_yesno(&menu->b,
3430 "Do you really want to exit?"))
3442 ui_browser__hide(&menu->b);
3446 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
3449 struct evsel *evsel = list_entry(entry, struct evsel, core.node);
3451 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
3457 static int __perf_evlist__tui_browse_hists(struct evlist *evlist,
3458 int nr_entries, const char *help,
3459 struct hist_browser_timer *hbt,
3461 struct perf_env *env,
3462 bool warn_lost_event,
3463 struct annotation_options *annotation_opts)
3466 struct evsel_menu menu = {
3468 .entries = &evlist->core.entries,
3469 .refresh = ui_browser__list_head_refresh,
3470 .seek = ui_browser__list_head_seek,
3471 .write = perf_evsel_menu__write,
3472 .filter = filter_group_entries,
3473 .nr_entries = nr_entries,
3476 .min_pcnt = min_pcnt,
3478 .annotation_opts = annotation_opts,
3481 ui_helpline__push("Press ESC to exit");
3483 evlist__for_each_entry(evlist, pos) {
3484 const char *ev_name = perf_evsel__name(pos);
3485 size_t line_len = strlen(ev_name) + 7;
3487 if (menu.b.width < line_len)
3488 menu.b.width = line_len;
3491 return perf_evsel_menu__run(&menu, nr_entries, help,
3492 hbt, warn_lost_event);
3495 int perf_evlist__tui_browse_hists(struct evlist *evlist, const char *help,
3496 struct hist_browser_timer *hbt,
3498 struct perf_env *env,
3499 bool warn_lost_event,
3500 struct annotation_options *annotation_opts)
3502 int nr_entries = evlist->core.nr_entries;
3505 if (nr_entries == 1) {
3506 struct evsel *first = evlist__first(evlist);
3508 return perf_evsel__hists_browse(first, nr_entries, help,
3509 false, hbt, min_pcnt,
3510 env, warn_lost_event,
3514 if (symbol_conf.event_group) {
3518 evlist__for_each_entry(evlist, pos) {
3519 if (perf_evsel__is_group_leader(pos))
3523 if (nr_entries == 1)
3527 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
3533 static int block_hists_browser__title(struct hist_browser *browser, char *bf,
3536 struct hists *hists = evsel__hists(browser->block_evsel);
3537 const char *evname = perf_evsel__name(browser->block_evsel);
3538 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
3541 ret = scnprintf(bf, size, "# Samples: %lu", nr_samples);
3543 scnprintf(bf + ret, size - ret, " of event '%s'", evname);
3548 int block_hists_tui_browse(struct block_hist *bh, struct evsel *evsel,
3549 float min_percent, struct perf_env *env,
3550 struct annotation_options *annotation_opts)
3552 struct hists *hists = &bh->block_hists;
3553 struct hist_browser *browser;
3555 struct popup_action action;
3556 static const char help[] =
3559 browser = hist_browser__new(hists);
3563 browser->block_evsel = evsel;
3564 browser->title = block_hists_browser__title;
3565 browser->min_pcnt = min_percent;
3567 browser->annotation_opts = annotation_opts;
3569 /* reset abort key so that it can get Ctrl-C as a key */
3571 SLang_init_tty(0, 0, 0);
3573 memset(&action, 0, sizeof(action));
3576 key = hist_browser__run(browser, "? - help", true);
3582 ui_browser__help_window(&browser->b, help);
3586 if (!browser->selection ||
3587 !browser->selection->sym) {
3591 action.ms.map = browser->selection->map;
3592 action.ms.sym = browser->selection->sym;
3593 do_annotate(browser, &action);
3601 hist_browser__delete(browser);