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;
602 __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
605 struct hist_entry *he;
608 nd = rb_first_cached(&browser->hists->entries);
610 he = rb_entry(nd, struct hist_entry, rb_node);
612 /* set folding state even if it's currently folded */
613 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
615 hist_entry__set_folding(he, browser, unfold);
617 percent = hist_entry__get_percent_limit(he);
618 if (he->filtered || percent < browser->min_pcnt)
621 if (!he->depth || unfold)
622 browser->nr_hierarchy_entries++;
624 browser->nr_callchain_rows += he->nr_rows;
625 else if (unfold && !hist_entry__has_hierarchy_children(he, browser->min_pcnt)) {
626 browser->nr_hierarchy_entries++;
627 he->has_no_entry = true;
630 he->has_no_entry = false;
634 static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
636 browser->nr_hierarchy_entries = 0;
637 browser->nr_callchain_rows = 0;
638 __hist_browser__set_folding(browser, unfold);
640 browser->b.nr_entries = hist_browser__nr_entries(browser);
641 /* Go to the start, we may be way after valid entries after a collapse */
642 ui_browser__reset_index(&browser->b);
645 static void hist_browser__set_folding_selected(struct hist_browser *browser, bool unfold)
647 if (!browser->he_selection)
650 if (unfold == browser->he_selection->unfolded)
653 hist_browser__toggle_fold(browser);
656 static void ui_browser__warn_lost_events(struct ui_browser *browser)
658 ui_browser__warning(browser, 4,
659 "Events are being lost, check IO/CPU overload!\n\n"
660 "You may want to run 'perf' using a RT scheduler policy:\n\n"
661 " perf top -r 80\n\n"
662 "Or reduce the sampling frequency.");
665 static int hist_browser__title(struct hist_browser *browser, char *bf, size_t size)
667 return browser->title ? browser->title(browser, bf, size) : 0;
670 static int hist_browser__handle_hotkey(struct hist_browser *browser, bool warn_lost_event, char *title, size_t size, int key)
674 struct hist_browser_timer *hbt = browser->hbt;
680 hbt->timer(hbt->arg);
682 if (hist_browser__has_filter(browser) || symbol_conf.report_hierarchy)
683 hist_browser__update_nr_entries(browser);
685 nr_entries = hist_browser__nr_entries(browser);
686 ui_browser__update_nr_entries(&browser->b, nr_entries);
688 if (warn_lost_event &&
689 (browser->hists->stats.nr_lost_warned !=
690 browser->hists->stats.nr_events[PERF_RECORD_LOST])) {
691 browser->hists->stats.nr_lost_warned =
692 browser->hists->stats.nr_events[PERF_RECORD_LOST];
693 ui_browser__warn_lost_events(&browser->b);
696 hist_browser__title(browser, title, size);
697 ui_browser__show_title(&browser->b, title);
700 case 'D': { /* Debug */
701 struct hist_entry *h = rb_entry(browser->b.top, struct hist_entry, rb_node);
705 ui_helpline__fpush("%d: nr_ent=(%d,%d), etl: %d, rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
706 seq++, browser->b.nr_entries, browser->hists->nr_entries,
707 browser->b.extra_title_lines, browser->b.rows,
708 browser->b.index, browser->b.top_idx, h->row_offset, h->nr_rows);
712 /* Collapse the whole world. */
713 hist_browser__set_folding(browser, false);
716 /* Collapse the selected entry. */
717 hist_browser__set_folding_selected(browser, false);
720 /* Expand the whole world. */
721 hist_browser__set_folding(browser, true);
724 /* Toggle expand/collapse the selected entry. */
725 hist_browser__toggle_fold(browser);
728 browser->show_headers = !browser->show_headers;
729 hist_browser__update_rows(browser);
732 if (hist_browser__toggle_fold(browser))
742 int hist_browser__run(struct hist_browser *browser, const char *help,
743 bool warn_lost_event, int key)
746 struct hist_browser_timer *hbt = browser->hbt;
747 int delay_secs = hbt ? hbt->refresh : 0;
749 browser->b.entries = &browser->hists->entries;
750 browser->b.nr_entries = hist_browser__nr_entries(browser);
752 hist_browser__title(browser, title, sizeof(title));
754 if (ui_browser__show(&browser->b, title, "%s", help) < 0)
757 if (key && hist_browser__handle_hotkey(browser, warn_lost_event, title, sizeof(title), key))
761 key = ui_browser__run(&browser->b, delay_secs);
763 if (hist_browser__handle_hotkey(browser, warn_lost_event, title, sizeof(title), key))
767 ui_browser__hide(&browser->b);
771 struct callchain_print_arg {
772 /* for hists browser */
774 bool is_current_entry;
781 typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
782 struct callchain_list *chain,
783 const char *str, int offset,
785 struct callchain_print_arg *arg);
787 static void hist_browser__show_callchain_entry(struct hist_browser *browser,
788 struct callchain_list *chain,
789 const char *str, int offset,
791 struct callchain_print_arg *arg)
794 char folded_sign = callchain_list__folded(chain);
795 bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
797 color = HE_COLORSET_NORMAL;
798 width = browser->b.width - (offset + 2);
799 if (ui_browser__is_current_entry(&browser->b, row)) {
800 browser->selection = &chain->ms;
801 color = HE_COLORSET_SELECTED;
802 arg->is_current_entry = true;
805 ui_browser__set_color(&browser->b, color);
806 ui_browser__gotorc(&browser->b, row, 0);
807 ui_browser__write_nstring(&browser->b, " ", offset);
808 ui_browser__printf(&browser->b, "%c", folded_sign);
809 ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
810 ui_browser__write_nstring(&browser->b, str, width);
813 static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
814 struct callchain_list *chain,
815 const char *str, int offset,
816 unsigned short row __maybe_unused,
817 struct callchain_print_arg *arg)
819 char folded_sign = callchain_list__folded(chain);
821 arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
825 typedef bool (*check_output_full_fn)(struct hist_browser *browser,
828 static bool hist_browser__check_output_full(struct hist_browser *browser,
831 return browser->b.rows == row;
834 static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
835 unsigned short row __maybe_unused)
840 #define LEVEL_OFFSET_STEP 3
842 static int hist_browser__show_callchain_list(struct hist_browser *browser,
843 struct callchain_node *node,
844 struct callchain_list *chain,
845 unsigned short row, u64 total,
846 bool need_percent, int offset,
847 print_callchain_entry_fn print,
848 struct callchain_print_arg *arg)
850 char bf[1024], *alloc_str;
851 char buf[64], *alloc_str2;
855 if (arg->row_offset != 0) {
863 str = callchain_list__sym_name(chain, bf, sizeof(bf),
866 if (symbol_conf.show_branchflag_count) {
867 callchain_list_counts__printf_value(chain, NULL,
870 if (asprintf(&alloc_str2, "%s%s", str, buf) < 0)
871 str = "Not enough memory!";
877 callchain_node__scnprintf_value(node, buf, sizeof(buf),
880 if (asprintf(&alloc_str, "%s %s", buf, str) < 0)
881 str = "Not enough memory!";
886 print(browser, chain, str, offset, row, arg);
893 static bool check_percent_display(struct rb_node *node, u64 parent_total)
895 struct callchain_node *child;
903 child = rb_entry(node, struct callchain_node, rb_node);
904 return callchain_cumul_hits(child) != parent_total;
907 static int hist_browser__show_callchain_flat(struct hist_browser *browser,
908 struct rb_root *root,
909 unsigned short row, u64 total,
911 print_callchain_entry_fn print,
912 struct callchain_print_arg *arg,
913 check_output_full_fn is_output_full)
915 struct rb_node *node;
916 int first_row = row, offset = LEVEL_OFFSET_STEP;
919 node = rb_first(root);
920 need_percent = check_percent_display(node, parent_total);
923 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
924 struct rb_node *next = rb_next(node);
925 struct callchain_list *chain;
926 char folded_sign = ' ';
928 int extra_offset = 0;
930 list_for_each_entry(chain, &child->parent_val, list) {
931 bool was_first = first;
935 else if (need_percent)
936 extra_offset = LEVEL_OFFSET_STEP;
938 folded_sign = callchain_list__folded(chain);
940 row += hist_browser__show_callchain_list(browser, child,
942 was_first && need_percent,
943 offset + extra_offset,
946 if (is_output_full(browser, row))
949 if (folded_sign == '+')
953 list_for_each_entry(chain, &child->val, list) {
954 bool was_first = first;
958 else if (need_percent)
959 extra_offset = LEVEL_OFFSET_STEP;
961 folded_sign = callchain_list__folded(chain);
963 row += hist_browser__show_callchain_list(browser, child,
965 was_first && need_percent,
966 offset + extra_offset,
969 if (is_output_full(browser, row))
972 if (folded_sign == '+')
977 if (is_output_full(browser, row))
982 return row - first_row;
985 static char *hist_browser__folded_callchain_str(struct hist_browser *browser,
986 struct callchain_list *chain,
987 char *value_str, char *old_str)
993 str = callchain_list__sym_name(chain, bf, sizeof(bf),
996 if (asprintf(&new, "%s%s%s", old_str,
997 symbol_conf.field_sep ?: ";", str) < 0)
1001 if (asprintf(&new, "%s %s", value_str, str) < 0)
1004 if (asprintf(&new, "%s", str) < 0)
1011 static int hist_browser__show_callchain_folded(struct hist_browser *browser,
1012 struct rb_root *root,
1013 unsigned short row, u64 total,
1015 print_callchain_entry_fn print,
1016 struct callchain_print_arg *arg,
1017 check_output_full_fn is_output_full)
1019 struct rb_node *node;
1020 int first_row = row, offset = LEVEL_OFFSET_STEP;
1023 node = rb_first(root);
1024 need_percent = check_percent_display(node, parent_total);
1027 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1028 struct rb_node *next = rb_next(node);
1029 struct callchain_list *chain, *first_chain = NULL;
1031 char *value_str = NULL, *value_str_alloc = NULL;
1032 char *chain_str = NULL, *chain_str_alloc = NULL;
1034 if (arg->row_offset != 0) {
1042 callchain_node__scnprintf_value(child, buf, sizeof(buf), total);
1043 if (asprintf(&value_str, "%s", buf) < 0) {
1044 value_str = (char *)"<...>";
1047 value_str_alloc = value_str;
1050 list_for_each_entry(chain, &child->parent_val, list) {
1051 chain_str = hist_browser__folded_callchain_str(browser,
1052 chain, value_str, chain_str);
1055 first_chain = chain;
1058 if (chain_str == NULL) {
1059 chain_str = (char *)"Not enough memory!";
1063 chain_str_alloc = chain_str;
1066 list_for_each_entry(chain, &child->val, list) {
1067 chain_str = hist_browser__folded_callchain_str(browser,
1068 chain, value_str, chain_str);
1071 first_chain = chain;
1074 if (chain_str == NULL) {
1075 chain_str = (char *)"Not enough memory!";
1079 chain_str_alloc = chain_str;
1083 print(browser, first_chain, chain_str, offset, row++, arg);
1084 free(value_str_alloc);
1085 free(chain_str_alloc);
1088 if (is_output_full(browser, row))
1093 return row - first_row;
1096 static int hist_browser__show_callchain_graph(struct hist_browser *browser,
1097 struct rb_root *root, int level,
1098 unsigned short row, u64 total,
1100 print_callchain_entry_fn print,
1101 struct callchain_print_arg *arg,
1102 check_output_full_fn is_output_full)
1104 struct rb_node *node;
1105 int first_row = row, offset = level * LEVEL_OFFSET_STEP;
1107 u64 percent_total = total;
1109 if (callchain_param.mode == CHAIN_GRAPH_REL)
1110 percent_total = parent_total;
1112 node = rb_first(root);
1113 need_percent = check_percent_display(node, parent_total);
1116 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1117 struct rb_node *next = rb_next(node);
1118 struct callchain_list *chain;
1119 char folded_sign = ' ';
1121 int extra_offset = 0;
1123 list_for_each_entry(chain, &child->val, list) {
1124 bool was_first = first;
1128 else if (need_percent)
1129 extra_offset = LEVEL_OFFSET_STEP;
1131 folded_sign = callchain_list__folded(chain);
1133 row += hist_browser__show_callchain_list(browser, child,
1134 chain, row, percent_total,
1135 was_first && need_percent,
1136 offset + extra_offset,
1139 if (is_output_full(browser, row))
1142 if (folded_sign == '+')
1146 if (folded_sign == '-') {
1147 const int new_level = level + (extra_offset ? 2 : 1);
1149 row += hist_browser__show_callchain_graph(browser, &child->rb_root,
1150 new_level, row, total,
1151 child->children_hit,
1152 print, arg, is_output_full);
1154 if (is_output_full(browser, row))
1159 return row - first_row;
1162 static int hist_browser__show_callchain(struct hist_browser *browser,
1163 struct hist_entry *entry, int level,
1165 print_callchain_entry_fn print,
1166 struct callchain_print_arg *arg,
1167 check_output_full_fn is_output_full)
1169 u64 total = hists__total_period(entry->hists);
1173 if (symbol_conf.cumulate_callchain)
1174 parent_total = entry->stat_acc->period;
1176 parent_total = entry->stat.period;
1178 if (callchain_param.mode == CHAIN_FLAT) {
1179 printed = hist_browser__show_callchain_flat(browser,
1180 &entry->sorted_chain, row,
1181 total, parent_total, print, arg,
1183 } else if (callchain_param.mode == CHAIN_FOLDED) {
1184 printed = hist_browser__show_callchain_folded(browser,
1185 &entry->sorted_chain, row,
1186 total, parent_total, print, arg,
1189 printed = hist_browser__show_callchain_graph(browser,
1190 &entry->sorted_chain, level, row,
1191 total, parent_total, print, arg,
1195 if (arg->is_current_entry)
1196 browser->he_selection = entry;
1202 struct ui_browser *b;
1207 int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
1209 struct hpp_arg *arg = hpp->ptr;
1214 va_start(args, fmt);
1215 len = va_arg(args, int);
1216 percent = va_arg(args, double);
1219 ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
1221 ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
1222 ui_browser__printf(arg->b, "%s", hpp->buf);
1227 #define __HPP_COLOR_PERCENT_FN(_type, _field) \
1228 static u64 __hpp_get_##_field(struct hist_entry *he) \
1230 return he->stat._field; \
1234 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
1235 struct perf_hpp *hpp, \
1236 struct hist_entry *he) \
1238 return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%", \
1239 __hpp__slsmg_color_printf, true); \
1242 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
1243 static u64 __hpp_get_acc_##_field(struct hist_entry *he) \
1245 return he->stat_acc->_field; \
1249 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
1250 struct perf_hpp *hpp, \
1251 struct hist_entry *he) \
1253 if (!symbol_conf.cumulate_callchain) { \
1254 struct hpp_arg *arg = hpp->ptr; \
1255 int len = fmt->user_len ?: fmt->len; \
1256 int ret = scnprintf(hpp->buf, hpp->size, \
1257 "%*s", len, "N/A"); \
1258 ui_browser__printf(arg->b, "%s", hpp->buf); \
1262 return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field, \
1263 " %*.2f%%", __hpp__slsmg_color_printf, true); \
1266 __HPP_COLOR_PERCENT_FN(overhead, period)
1267 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
1268 __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
1269 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
1270 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
1271 __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
1273 #undef __HPP_COLOR_PERCENT_FN
1274 #undef __HPP_COLOR_ACC_PERCENT_FN
1276 void hist_browser__init_hpp(void)
1278 perf_hpp__format[PERF_HPP__OVERHEAD].color =
1279 hist_browser__hpp_color_overhead;
1280 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
1281 hist_browser__hpp_color_overhead_sys;
1282 perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
1283 hist_browser__hpp_color_overhead_us;
1284 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
1285 hist_browser__hpp_color_overhead_guest_sys;
1286 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
1287 hist_browser__hpp_color_overhead_guest_us;
1288 perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
1289 hist_browser__hpp_color_overhead_acc;
1294 static int hist_browser__show_entry(struct hist_browser *browser,
1295 struct hist_entry *entry,
1299 int width = browser->b.width;
1300 char folded_sign = ' ';
1301 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1302 bool use_callchain = hist_entry__has_callchains(entry) && symbol_conf.use_callchain;
1303 off_t row_offset = entry->row_offset;
1305 struct perf_hpp_fmt *fmt;
1307 if (current_entry) {
1308 browser->he_selection = entry;
1309 browser->selection = &entry->ms;
1312 if (use_callchain) {
1313 hist_entry__init_have_children(entry);
1314 folded_sign = hist_entry__folded(entry);
1317 if (row_offset == 0) {
1318 struct hpp_arg arg = {
1320 .folded_sign = folded_sign,
1321 .current_entry = current_entry,
1325 ui_browser__gotorc(&browser->b, row, 0);
1327 hists__for_each_format(browser->hists, fmt) {
1329 struct perf_hpp hpp = {
1335 if (perf_hpp__should_skip(fmt, entry->hists) ||
1336 column++ < browser->b.horiz_scroll)
1339 if (current_entry && browser->b.navkeypressed) {
1340 ui_browser__set_color(&browser->b,
1341 HE_COLORSET_SELECTED);
1343 ui_browser__set_color(&browser->b,
1344 HE_COLORSET_NORMAL);
1348 if (use_callchain) {
1349 ui_browser__printf(&browser->b, "%c ", folded_sign);
1354 ui_browser__printf(&browser->b, " ");
1359 int ret = fmt->color(fmt, &hpp, entry);
1360 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1362 * fmt->color() already used ui_browser to
1363 * print the non alignment bits, skip it (+ret):
1365 ui_browser__printf(&browser->b, "%s", s + ret);
1367 hist_entry__snprintf_alignment(entry, &hpp, fmt, fmt->entry(fmt, &hpp, entry));
1368 ui_browser__printf(&browser->b, "%s", s);
1370 width -= hpp.buf - s;
1373 /* The scroll bar isn't being used */
1374 if (!browser->b.navkeypressed)
1377 ui_browser__write_nstring(&browser->b, "", width);
1384 if (folded_sign == '-' && row != browser->b.rows) {
1385 struct callchain_print_arg arg = {
1386 .row_offset = row_offset,
1387 .is_current_entry = current_entry,
1390 printed += hist_browser__show_callchain(browser,
1392 hist_browser__show_callchain_entry,
1394 hist_browser__check_output_full);
1400 static int hist_browser__show_hierarchy_entry(struct hist_browser *browser,
1401 struct hist_entry *entry,
1406 int width = browser->b.width;
1407 char folded_sign = ' ';
1408 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1409 off_t row_offset = entry->row_offset;
1411 struct perf_hpp_fmt *fmt;
1412 struct perf_hpp_list_node *fmt_node;
1413 struct hpp_arg arg = {
1415 .current_entry = current_entry,
1418 int hierarchy_indent = (entry->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
1420 if (current_entry) {
1421 browser->he_selection = entry;
1422 browser->selection = &entry->ms;
1425 hist_entry__init_have_children(entry);
1426 folded_sign = hist_entry__folded(entry);
1427 arg.folded_sign = folded_sign;
1429 if (entry->leaf && row_offset) {
1431 goto show_callchain;
1434 ui_browser__gotorc(&browser->b, row, 0);
1436 if (current_entry && browser->b.navkeypressed)
1437 ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1439 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1441 ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1442 width -= level * HIERARCHY_INDENT;
1444 /* the first hpp_list_node is for overhead columns */
1445 fmt_node = list_first_entry(&entry->hists->hpp_formats,
1446 struct perf_hpp_list_node, list);
1447 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1449 struct perf_hpp hpp = {
1455 if (perf_hpp__should_skip(fmt, entry->hists) ||
1456 column++ < browser->b.horiz_scroll)
1459 if (current_entry && browser->b.navkeypressed) {
1460 ui_browser__set_color(&browser->b,
1461 HE_COLORSET_SELECTED);
1463 ui_browser__set_color(&browser->b,
1464 HE_COLORSET_NORMAL);
1468 ui_browser__printf(&browser->b, "%c ", folded_sign);
1472 ui_browser__printf(&browser->b, " ");
1477 int ret = fmt->color(fmt, &hpp, entry);
1478 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1480 * fmt->color() already used ui_browser to
1481 * print the non alignment bits, skip it (+ret):
1483 ui_browser__printf(&browser->b, "%s", s + ret);
1485 int ret = fmt->entry(fmt, &hpp, entry);
1486 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1487 ui_browser__printf(&browser->b, "%s", s);
1489 width -= hpp.buf - s;
1493 ui_browser__write_nstring(&browser->b, "", hierarchy_indent);
1494 width -= hierarchy_indent;
1497 if (column >= browser->b.horiz_scroll) {
1499 struct perf_hpp hpp = {
1505 if (current_entry && browser->b.navkeypressed) {
1506 ui_browser__set_color(&browser->b,
1507 HE_COLORSET_SELECTED);
1509 ui_browser__set_color(&browser->b,
1510 HE_COLORSET_NORMAL);
1513 perf_hpp_list__for_each_format(entry->hpp_list, fmt) {
1515 ui_browser__printf(&browser->b, "%c ", folded_sign);
1518 ui_browser__write_nstring(&browser->b, "", 2);
1524 * No need to call hist_entry__snprintf_alignment()
1525 * since this fmt is always the last column in the
1529 width -= fmt->color(fmt, &hpp, entry);
1533 width -= fmt->entry(fmt, &hpp, entry);
1534 ui_browser__printf(&browser->b, "%s", skip_spaces(s));
1536 while (isspace(s[i++]))
1542 /* The scroll bar isn't being used */
1543 if (!browser->b.navkeypressed)
1546 ui_browser__write_nstring(&browser->b, "", width);
1552 if (entry->leaf && folded_sign == '-' && row != browser->b.rows) {
1553 struct callchain_print_arg carg = {
1554 .row_offset = row_offset,
1557 printed += hist_browser__show_callchain(browser, entry,
1559 hist_browser__show_callchain_entry, &carg,
1560 hist_browser__check_output_full);
1566 static int hist_browser__show_no_entry(struct hist_browser *browser,
1567 unsigned short row, int level)
1569 int width = browser->b.width;
1570 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1574 struct perf_hpp_fmt *fmt;
1575 struct perf_hpp_list_node *fmt_node;
1576 int indent = browser->hists->nr_hpp_node - 2;
1578 if (current_entry) {
1579 browser->he_selection = NULL;
1580 browser->selection = NULL;
1583 ui_browser__gotorc(&browser->b, row, 0);
1585 if (current_entry && browser->b.navkeypressed)
1586 ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1588 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1590 ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1591 width -= level * HIERARCHY_INDENT;
1593 /* the first hpp_list_node is for overhead columns */
1594 fmt_node = list_first_entry(&browser->hists->hpp_formats,
1595 struct perf_hpp_list_node, list);
1596 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1597 if (perf_hpp__should_skip(fmt, browser->hists) ||
1598 column++ < browser->b.horiz_scroll)
1601 ret = fmt->width(fmt, NULL, browser->hists);
1604 /* for folded sign */
1608 /* space between columns */
1612 ui_browser__write_nstring(&browser->b, "", ret);
1616 ui_browser__write_nstring(&browser->b, "", indent * HIERARCHY_INDENT);
1617 width -= indent * HIERARCHY_INDENT;
1619 if (column >= browser->b.horiz_scroll) {
1622 ret = snprintf(buf, sizeof(buf), "no entry >= %.2f%%", browser->min_pcnt);
1623 ui_browser__printf(&browser->b, " %s", buf);
1627 /* The scroll bar isn't being used */
1628 if (!browser->b.navkeypressed)
1631 ui_browser__write_nstring(&browser->b, "", width);
1635 static int advance_hpp_check(struct perf_hpp *hpp, int inc)
1637 advance_hpp(hpp, inc);
1638 return hpp->size <= 0;
1642 hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf,
1643 size_t size, int line)
1645 struct hists *hists = browser->hists;
1646 struct perf_hpp dummy_hpp = {
1650 struct perf_hpp_fmt *fmt;
1655 if (hists__has_callchains(hists) && symbol_conf.use_callchain) {
1656 ret = scnprintf(buf, size, " ");
1657 if (advance_hpp_check(&dummy_hpp, ret))
1661 hists__for_each_format(browser->hists, fmt) {
1662 if (perf_hpp__should_skip(fmt, hists) || column++ < browser->b.horiz_scroll)
1665 ret = fmt->header(fmt, &dummy_hpp, hists, line, &span);
1666 if (advance_hpp_check(&dummy_hpp, ret))
1672 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
1673 if (advance_hpp_check(&dummy_hpp, ret))
1680 static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser, char *buf, size_t size)
1682 struct hists *hists = browser->hists;
1683 struct perf_hpp dummy_hpp = {
1687 struct perf_hpp_fmt *fmt;
1688 struct perf_hpp_list_node *fmt_node;
1691 int indent = hists->nr_hpp_node - 2;
1692 bool first_node, first_col;
1694 ret = scnprintf(buf, size, " ");
1695 if (advance_hpp_check(&dummy_hpp, ret))
1699 /* the first hpp_list_node is for overhead columns */
1700 fmt_node = list_first_entry(&hists->hpp_formats,
1701 struct perf_hpp_list_node, list);
1702 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1703 if (column++ < browser->b.horiz_scroll)
1706 ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1707 if (advance_hpp_check(&dummy_hpp, ret))
1710 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
1711 if (advance_hpp_check(&dummy_hpp, ret))
1718 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "%*s",
1719 indent * HIERARCHY_INDENT, "");
1720 if (advance_hpp_check(&dummy_hpp, ret))
1725 list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
1727 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " / ");
1728 if (advance_hpp_check(&dummy_hpp, ret))
1734 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1737 if (perf_hpp__should_skip(fmt, hists))
1741 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "+");
1742 if (advance_hpp_check(&dummy_hpp, ret))
1747 ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1748 dummy_hpp.buf[ret] = '\0';
1750 start = strim(dummy_hpp.buf);
1751 ret = strlen(start);
1753 if (start != dummy_hpp.buf)
1754 memmove(dummy_hpp.buf, start, ret + 1);
1756 if (advance_hpp_check(&dummy_hpp, ret))
1764 static void hists_browser__hierarchy_headers(struct hist_browser *browser)
1768 hists_browser__scnprintf_hierarchy_headers(browser, headers,
1771 ui_browser__gotorc_title(&browser->b, 0, 0);
1772 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1773 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1776 static void hists_browser__headers(struct hist_browser *browser)
1778 struct hists *hists = browser->hists;
1779 struct perf_hpp_list *hpp_list = hists->hpp_list;
1783 for (line = 0; line < hpp_list->nr_header_lines; line++) {
1786 hists_browser__scnprintf_headers(browser, headers,
1787 sizeof(headers), line);
1789 ui_browser__gotorc_title(&browser->b, line, 0);
1790 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1791 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1795 static void hist_browser__show_headers(struct hist_browser *browser)
1797 if (symbol_conf.report_hierarchy)
1798 hists_browser__hierarchy_headers(browser);
1800 hists_browser__headers(browser);
1803 static void ui_browser__hists_init_top(struct ui_browser *browser)
1805 if (browser->top == NULL) {
1806 struct hist_browser *hb;
1808 hb = container_of(browser, struct hist_browser, b);
1809 browser->top = rb_first_cached(&hb->hists->entries);
1813 static unsigned int hist_browser__refresh(struct ui_browser *browser)
1817 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
1819 if (hb->show_headers)
1820 hist_browser__show_headers(hb);
1822 ui_browser__hists_init_top(browser);
1823 hb->he_selection = NULL;
1824 hb->selection = NULL;
1826 for (nd = browser->top; nd; nd = rb_hierarchy_next(nd)) {
1827 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1831 /* let it move to sibling */
1832 h->unfolded = false;
1836 if (symbol_conf.report_individual_block)
1837 percent = block_info__total_cycles_percent(h);
1839 percent = hist_entry__get_percent_limit(h);
1841 if (percent < hb->min_pcnt)
1844 if (symbol_conf.report_hierarchy) {
1845 row += hist_browser__show_hierarchy_entry(hb, h, row,
1847 if (row == browser->rows)
1850 if (h->has_no_entry) {
1851 hist_browser__show_no_entry(hb, row, h->depth + 1);
1855 row += hist_browser__show_entry(hb, h, row);
1858 if (row == browser->rows)
1865 static struct rb_node *hists__filter_entries(struct rb_node *nd,
1868 while (nd != NULL) {
1869 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1870 float percent = hist_entry__get_percent_limit(h);
1872 if (!h->filtered && percent >= min_pcnt)
1876 * If it's filtered, its all children also were filtered.
1877 * So move to sibling node.
1882 nd = rb_hierarchy_next(nd);
1888 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
1891 while (nd != NULL) {
1892 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1893 float percent = hist_entry__get_percent_limit(h);
1895 if (!h->filtered && percent >= min_pcnt)
1898 nd = rb_hierarchy_prev(nd);
1904 static void ui_browser__hists_seek(struct ui_browser *browser,
1905 off_t offset, int whence)
1907 struct hist_entry *h;
1910 struct hist_browser *hb;
1912 hb = container_of(browser, struct hist_browser, b);
1914 if (browser->nr_entries == 0)
1917 ui_browser__hists_init_top(browser);
1921 nd = hists__filter_entries(rb_first(browser->entries),
1928 nd = rb_hierarchy_last(rb_last(browser->entries));
1929 nd = hists__filter_prev_entries(nd, hb->min_pcnt);
1937 * Moves not relative to the first visible entry invalidates its
1940 h = rb_entry(browser->top, struct hist_entry, rb_node);
1944 * Here we have to check if nd is expanded (+), if it is we can't go
1945 * the next top level hist_entry, instead we must compute an offset of
1946 * what _not_ to show and not change the first visible entry.
1948 * This offset increments when we are going from top to bottom and
1949 * decreases when we're going from bottom to top.
1951 * As we don't have backpointers to the top level in the callchains
1952 * structure, we need to always print the whole hist_entry callchain,
1953 * skipping the first ones that are before the first visible entry
1954 * and stop when we printed enough lines to fill the screen.
1962 h = rb_entry(nd, struct hist_entry, rb_node);
1963 if (h->unfolded && h->leaf) {
1964 u16 remaining = h->nr_rows - h->row_offset;
1965 if (offset > remaining) {
1966 offset -= remaining;
1969 h->row_offset += offset;
1975 nd = hists__filter_entries(rb_hierarchy_next(nd),
1981 } while (offset != 0);
1982 } else if (offset < 0) {
1984 h = rb_entry(nd, struct hist_entry, rb_node);
1985 if (h->unfolded && h->leaf) {
1987 if (-offset > h->row_offset) {
1988 offset += h->row_offset;
1991 h->row_offset += offset;
1997 if (-offset > h->nr_rows) {
1998 offset += h->nr_rows;
2001 h->row_offset = h->nr_rows + offset;
2009 nd = hists__filter_prev_entries(rb_hierarchy_prev(nd),
2017 * Last unfiltered hist_entry, check if it is
2018 * unfolded, if it is then we should have
2019 * row_offset at its last entry.
2021 h = rb_entry(nd, struct hist_entry, rb_node);
2022 if (h->unfolded && h->leaf)
2023 h->row_offset = h->nr_rows;
2030 h = rb_entry(nd, struct hist_entry, rb_node);
2035 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
2036 struct hist_entry *he, FILE *fp,
2039 struct callchain_print_arg arg = {
2043 hist_browser__show_callchain(browser, he, level, 0,
2044 hist_browser__fprintf_callchain_entry, &arg,
2045 hist_browser__check_dump_full);
2049 static int hist_browser__fprintf_entry(struct hist_browser *browser,
2050 struct hist_entry *he, FILE *fp)
2054 char folded_sign = ' ';
2055 struct perf_hpp hpp = {
2059 struct perf_hpp_fmt *fmt;
2063 if (hist_entry__has_callchains(he) && symbol_conf.use_callchain) {
2064 folded_sign = hist_entry__folded(he);
2065 printed += fprintf(fp, "%c ", folded_sign);
2068 hists__for_each_format(browser->hists, fmt) {
2069 if (perf_hpp__should_skip(fmt, he->hists))
2073 ret = scnprintf(hpp.buf, hpp.size, " ");
2074 advance_hpp(&hpp, ret);
2078 ret = fmt->entry(fmt, &hpp, he);
2079 ret = hist_entry__snprintf_alignment(he, &hpp, fmt, ret);
2080 advance_hpp(&hpp, ret);
2082 printed += fprintf(fp, "%s\n", s);
2084 if (folded_sign == '-')
2085 printed += hist_browser__fprintf_callchain(browser, he, fp, 1);
2091 static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,
2092 struct hist_entry *he,
2093 FILE *fp, int level)
2097 char folded_sign = ' ';
2098 struct perf_hpp hpp = {
2102 struct perf_hpp_fmt *fmt;
2103 struct perf_hpp_list_node *fmt_node;
2106 int hierarchy_indent = (he->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
2108 printed = fprintf(fp, "%*s", level * HIERARCHY_INDENT, "");
2110 folded_sign = hist_entry__folded(he);
2111 printed += fprintf(fp, "%c", folded_sign);
2113 /* the first hpp_list_node is for overhead columns */
2114 fmt_node = list_first_entry(&he->hists->hpp_formats,
2115 struct perf_hpp_list_node, list);
2116 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
2118 ret = scnprintf(hpp.buf, hpp.size, " ");
2119 advance_hpp(&hpp, ret);
2123 ret = fmt->entry(fmt, &hpp, he);
2124 advance_hpp(&hpp, ret);
2127 ret = scnprintf(hpp.buf, hpp.size, "%*s", hierarchy_indent, "");
2128 advance_hpp(&hpp, ret);
2130 perf_hpp_list__for_each_format(he->hpp_list, fmt) {
2131 ret = scnprintf(hpp.buf, hpp.size, " ");
2132 advance_hpp(&hpp, ret);
2134 ret = fmt->entry(fmt, &hpp, he);
2135 advance_hpp(&hpp, ret);
2139 printed += fprintf(fp, "%s\n", s);
2141 if (he->leaf && folded_sign == '-') {
2142 printed += hist_browser__fprintf_callchain(browser, he, fp,
2149 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
2151 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
2156 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
2158 if (symbol_conf.report_hierarchy) {
2159 printed += hist_browser__fprintf_hierarchy_entry(browser,
2163 printed += hist_browser__fprintf_entry(browser, h, fp);
2166 nd = hists__filter_entries(rb_hierarchy_next(nd),
2173 static int hist_browser__dump(struct hist_browser *browser)
2179 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
2180 if (access(filename, F_OK))
2183 * XXX: Just an arbitrary lazy upper limit
2185 if (++browser->print_seq == 8192) {
2186 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
2191 fp = fopen(filename, "w");
2194 const char *err = str_error_r(errno, bf, sizeof(bf));
2195 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
2199 ++browser->print_seq;
2200 hist_browser__fprintf(browser, fp);
2202 ui_helpline__fpush("%s written!", filename);
2207 void hist_browser__init(struct hist_browser *browser,
2208 struct hists *hists)
2210 struct perf_hpp_fmt *fmt;
2212 browser->hists = hists;
2213 browser->b.refresh = hist_browser__refresh;
2214 browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
2215 browser->b.seek = ui_browser__hists_seek;
2216 browser->b.use_navkeypressed = true;
2217 browser->show_headers = symbol_conf.show_hist_headers;
2218 hist_browser__set_title_space(browser);
2220 if (symbol_conf.report_hierarchy) {
2221 struct perf_hpp_list_node *fmt_node;
2223 /* count overhead columns (in the first node) */
2224 fmt_node = list_first_entry(&hists->hpp_formats,
2225 struct perf_hpp_list_node, list);
2226 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt)
2227 ++browser->b.columns;
2229 /* add a single column for whole hierarchy sort keys*/
2230 ++browser->b.columns;
2232 hists__for_each_format(hists, fmt)
2233 ++browser->b.columns;
2236 hists__reset_column_width(hists);
2239 struct hist_browser *hist_browser__new(struct hists *hists)
2241 struct hist_browser *browser = zalloc(sizeof(*browser));
2244 hist_browser__init(browser, hists);
2249 static struct hist_browser *
2250 perf_evsel_browser__new(struct evsel *evsel,
2251 struct hist_browser_timer *hbt,
2252 struct perf_env *env,
2253 struct annotation_options *annotation_opts)
2255 struct hist_browser *browser = hist_browser__new(evsel__hists(evsel));
2260 browser->title = hists_browser__scnprintf_title;
2261 browser->annotation_opts = annotation_opts;
2266 void hist_browser__delete(struct hist_browser *browser)
2271 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
2273 return browser->he_selection;
2276 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
2278 return browser->he_selection->thread;
2281 static struct res_sample *hist_browser__selected_res_sample(struct hist_browser *browser)
2283 return browser->he_selection ? browser->he_selection->res_samples : NULL;
2286 /* Check whether the browser is for 'top' or 'report' */
2287 static inline bool is_report_browser(void *timer)
2289 return timer == NULL;
2292 static int hists_browser__scnprintf_title(struct hist_browser *browser, char *bf, size_t size)
2294 struct hist_browser_timer *hbt = browser->hbt;
2295 int printed = __hists__scnprintf_title(browser->hists, bf, size, !is_report_browser(hbt));
2297 if (!is_report_browser(hbt)) {
2298 struct perf_top *top = hbt->arg;
2300 printed += scnprintf(bf + printed, size - printed,
2301 " lost: %" PRIu64 "/%" PRIu64,
2302 top->lost, top->lost_total);
2304 printed += scnprintf(bf + printed, size - printed,
2305 " drop: %" PRIu64 "/%" PRIu64,
2306 top->drop, top->drop_total);
2309 printed += scnprintf(bf + printed, size - printed, " [z]");
2311 perf_top__reset_sample_counters(top);
2318 static inline void free_popup_options(char **options, int n)
2322 for (i = 0; i < n; ++i)
2327 * Only runtime switching of perf data file will make "input_name" point
2328 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
2329 * whether we need to call free() for current "input_name" during the switch.
2331 static bool is_input_name_malloced = false;
2333 static int switch_data_file(void)
2335 char *pwd, *options[32], *abs_path[32], *tmp;
2337 int nr_options = 0, choice = -1, ret = -1;
2338 struct dirent *dent;
2340 pwd = getenv("PWD");
2344 pwd_dir = opendir(pwd);
2348 memset(options, 0, sizeof(options));
2349 memset(abs_path, 0, sizeof(abs_path));
2351 while ((dent = readdir(pwd_dir))) {
2352 char path[PATH_MAX];
2354 char *name = dent->d_name;
2357 if (!(dent->d_type == DT_REG))
2360 snprintf(path, sizeof(path), "%s/%s", pwd, name);
2362 file = fopen(path, "r");
2366 if (fread(&magic, 1, 8, file) < 8)
2367 goto close_file_and_continue;
2369 if (is_perf_magic(magic)) {
2370 options[nr_options] = strdup(name);
2371 if (!options[nr_options])
2372 goto close_file_and_continue;
2374 abs_path[nr_options] = strdup(path);
2375 if (!abs_path[nr_options]) {
2376 zfree(&options[nr_options]);
2377 ui__warning("Can't search all data files due to memory shortage.\n");
2385 close_file_and_continue:
2387 if (nr_options >= 32) {
2388 ui__warning("Too many perf data files in PWD!\n"
2389 "Only the first 32 files will be listed.\n");
2396 choice = ui__popup_menu(nr_options, options, NULL);
2397 if (choice < nr_options && choice >= 0) {
2398 tmp = strdup(abs_path[choice]);
2400 if (is_input_name_malloced)
2401 free((void *)input_name);
2403 is_input_name_malloced = true;
2406 ui__warning("Data switch failed due to memory shortage!\n");
2410 free_popup_options(options, nr_options);
2411 free_popup_options(abs_path, nr_options);
2415 struct popup_action {
2417 struct thread *thread;
2418 struct map_symbol ms;
2420 struct evsel *evsel;
2423 int (*fn)(struct hist_browser *browser, struct popup_action *act);
2427 do_annotate(struct hist_browser *browser, struct popup_action *act)
2429 struct evsel *evsel;
2430 struct annotation *notes;
2431 struct hist_entry *he;
2434 if (!browser->annotation_opts->objdump_path &&
2435 perf_env__lookup_objdump(browser->env, &browser->annotation_opts->objdump_path))
2438 notes = symbol__annotation(act->ms.sym);
2442 if (browser->block_evsel)
2443 evsel = browser->block_evsel;
2445 evsel = hists_to_evsel(browser->hists);
2447 err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt,
2448 browser->annotation_opts);
2449 he = hist_browser__selected_entry(browser);
2451 * offer option to annotate the other branch source or target
2452 * (if they exists) when returning from annotate
2454 if ((err == 'q' || err == CTRL('c')) && he->branch_info)
2457 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
2459 ui_browser__handle_resize(&browser->b);
2463 static struct symbol *symbol__new_unresolved(u64 addr, struct map *map)
2465 struct annotated_source *src;
2469 snprintf(name, sizeof(name), "%.*" PRIx64, BITS_PER_LONG / 4, addr);
2471 sym = symbol__new(addr, ANNOTATION_DUMMY_LEN, 0, 0, name);
2473 src = symbol__hists(sym, 1);
2475 symbol__delete(sym);
2479 dso__insert_symbol(map->dso, sym);
2486 add_annotate_opt(struct hist_browser *browser __maybe_unused,
2487 struct popup_action *act, char **optstr,
2488 struct map_symbol *ms,
2491 if (!ms->map || !ms->map->dso || ms->map->dso->annotate_warned)
2495 ms->sym = symbol__new_unresolved(addr, ms->map);
2497 if (ms->sym == NULL || symbol__annotation(ms->sym)->src == NULL)
2500 if (asprintf(optstr, "Annotate %s", ms->sym->name) < 0)
2504 act->fn = do_annotate;
2509 do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
2511 struct thread *thread = act->thread;
2513 if ((!hists__has(browser->hists, thread) &&
2514 !hists__has(browser->hists, comm)) || thread == NULL)
2517 if (browser->hists->thread_filter) {
2518 pstack__remove(browser->pstack, &browser->hists->thread_filter);
2519 perf_hpp__set_elide(HISTC_THREAD, false);
2520 thread__zput(browser->hists->thread_filter);
2523 if (hists__has(browser->hists, thread)) {
2524 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
2525 thread->comm_set ? thread__comm_str(thread) : "",
2528 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
2529 thread->comm_set ? thread__comm_str(thread) : "");
2532 browser->hists->thread_filter = thread__get(thread);
2533 perf_hpp__set_elide(HISTC_THREAD, false);
2534 pstack__push(browser->pstack, &browser->hists->thread_filter);
2537 hists__filter_by_thread(browser->hists);
2538 hist_browser__reset(browser);
2543 add_thread_opt(struct hist_browser *browser, struct popup_action *act,
2544 char **optstr, struct thread *thread)
2548 if ((!hists__has(browser->hists, thread) &&
2549 !hists__has(browser->hists, comm)) || thread == NULL)
2552 if (hists__has(browser->hists, thread)) {
2553 ret = asprintf(optstr, "Zoom %s %s(%d) thread",
2554 browser->hists->thread_filter ? "out of" : "into",
2555 thread->comm_set ? thread__comm_str(thread) : "",
2558 ret = asprintf(optstr, "Zoom %s %s thread",
2559 browser->hists->thread_filter ? "out of" : "into",
2560 thread->comm_set ? thread__comm_str(thread) : "");
2565 act->thread = thread;
2566 act->fn = do_zoom_thread;
2570 static int hists_browser__zoom_map(struct hist_browser *browser, struct map *map)
2572 if (!hists__has(browser->hists, dso) || map == NULL)
2575 if (browser->hists->dso_filter) {
2576 pstack__remove(browser->pstack, &browser->hists->dso_filter);
2577 perf_hpp__set_elide(HISTC_DSO, false);
2578 browser->hists->dso_filter = NULL;
2581 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
2582 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
2583 browser->hists->dso_filter = map->dso;
2584 perf_hpp__set_elide(HISTC_DSO, true);
2585 pstack__push(browser->pstack, &browser->hists->dso_filter);
2588 hists__filter_by_dso(browser->hists);
2589 hist_browser__reset(browser);
2594 do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
2596 return hists_browser__zoom_map(browser, act->ms.map);
2600 add_dso_opt(struct hist_browser *browser, struct popup_action *act,
2601 char **optstr, struct map *map)
2603 if (!hists__has(browser->hists, dso) || map == NULL)
2606 if (asprintf(optstr, "Zoom %s %s DSO (use the 'k' hotkey to zoom directly into the kernel)",
2607 browser->hists->dso_filter ? "out of" : "into",
2608 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
2612 act->fn = do_zoom_dso;
2616 static int do_toggle_callchain(struct hist_browser *browser, struct popup_action *act __maybe_unused)
2618 hist_browser__toggle_fold(browser);
2622 static int add_callchain_toggle_opt(struct hist_browser *browser, struct popup_action *act, char **optstr)
2626 if (!hist_browser__selection_has_children(browser))
2629 if (asprintf(optstr, "%s [%s] callchain (one level, same as '+' hotkey, use 'e'/'c' for the whole main level entry)",
2630 hist_browser__selection_unfolded(browser) ? "Collapse" : "Expand",
2631 hist_browser__selection_sym_name(browser, sym_name, sizeof(sym_name))) < 0)
2634 act->fn = do_toggle_callchain;
2639 do_browse_map(struct hist_browser *browser __maybe_unused,
2640 struct popup_action *act)
2642 map__browse(act->ms.map);
2647 add_map_opt(struct hist_browser *browser,
2648 struct popup_action *act, char **optstr, struct map *map)
2650 if (!hists__has(browser->hists, dso) || map == NULL)
2653 if (asprintf(optstr, "Browse map details") < 0)
2657 act->fn = do_browse_map;
2662 do_run_script(struct hist_browser *browser __maybe_unused,
2663 struct popup_action *act)
2671 len += strlen(thread__comm_str(act->thread));
2672 else if (act->ms.sym)
2673 len += strlen(act->ms.sym->name);
2674 script_opt = malloc(len);
2680 n = scnprintf(script_opt, len, " -c %s ",
2681 thread__comm_str(act->thread));
2682 } else if (act->ms.sym) {
2683 n = scnprintf(script_opt, len, " -S %s ",
2688 char start[32], end[32];
2689 unsigned long starttime = act->time;
2690 unsigned long endtime = act->time + symbol_conf.time_quantum;
2692 if (starttime == endtime) { /* Display 1ms as fallback */
2693 starttime -= 1*NSEC_PER_MSEC;
2694 endtime += 1*NSEC_PER_MSEC;
2696 timestamp__scnprintf_usec(starttime, start, sizeof start);
2697 timestamp__scnprintf_usec(endtime, end, sizeof end);
2698 n += snprintf(script_opt + n, len - n, " --time %s,%s", start, end);
2701 script_browse(script_opt, act->evsel);
2707 do_res_sample_script(struct hist_browser *browser __maybe_unused,
2708 struct popup_action *act)
2710 struct hist_entry *he;
2712 he = hist_browser__selected_entry(browser);
2713 res_sample_browse(he->res_samples, he->num_res, act->evsel, act->rstype);
2718 add_script_opt_2(struct hist_browser *browser __maybe_unused,
2719 struct popup_action *act, char **optstr,
2720 struct thread *thread, struct symbol *sym,
2721 struct evsel *evsel, const char *tstr)
2725 if (asprintf(optstr, "Run scripts for samples of thread [%s]%s",
2726 thread__comm_str(thread), tstr) < 0)
2729 if (asprintf(optstr, "Run scripts for samples of symbol [%s]%s",
2730 sym->name, tstr) < 0)
2733 if (asprintf(optstr, "Run scripts for all samples%s", tstr) < 0)
2737 act->thread = thread;
2740 act->fn = do_run_script;
2745 add_script_opt(struct hist_browser *browser,
2746 struct popup_action *act, char **optstr,
2747 struct thread *thread, struct symbol *sym,
2748 struct evsel *evsel)
2751 struct hist_entry *he;
2753 n = add_script_opt_2(browser, act, optstr, thread, sym, evsel, "");
2755 he = hist_browser__selected_entry(browser);
2756 if (sort_order && strstr(sort_order, "time")) {
2761 j = sprintf(tstr, " in ");
2762 j += timestamp__scnprintf_usec(he->time, tstr + j,
2764 j += sprintf(tstr + j, "-");
2765 timestamp__scnprintf_usec(he->time + symbol_conf.time_quantum,
2766 tstr + j, sizeof tstr - j);
2767 n += add_script_opt_2(browser, act, optstr, thread, sym,
2769 act->time = he->time;
2775 add_res_sample_opt(struct hist_browser *browser __maybe_unused,
2776 struct popup_action *act, char **optstr,
2777 struct res_sample *res_sample,
2778 struct evsel *evsel,
2784 if (asprintf(optstr, "Show context for individual samples %s",
2785 type == A_ASM ? "with assembler" :
2786 type == A_SOURCE ? "with source" : "") < 0)
2789 act->fn = do_res_sample_script;
2796 do_switch_data(struct hist_browser *browser __maybe_unused,
2797 struct popup_action *act __maybe_unused)
2799 if (switch_data_file()) {
2800 ui__warning("Won't switch the data files due to\n"
2801 "no valid data file get selected!\n");
2805 return K_SWITCH_INPUT_DATA;
2809 add_switch_opt(struct hist_browser *browser,
2810 struct popup_action *act, char **optstr)
2812 if (!is_report_browser(browser->hbt))
2815 if (asprintf(optstr, "Switch to another data file in PWD") < 0)
2818 act->fn = do_switch_data;
2823 do_exit_browser(struct hist_browser *browser __maybe_unused,
2824 struct popup_action *act __maybe_unused)
2830 add_exit_opt(struct hist_browser *browser __maybe_unused,
2831 struct popup_action *act, char **optstr)
2833 if (asprintf(optstr, "Exit") < 0)
2836 act->fn = do_exit_browser;
2841 do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
2843 if (!hists__has(browser->hists, socket) || act->socket < 0)
2846 if (browser->hists->socket_filter > -1) {
2847 pstack__remove(browser->pstack, &browser->hists->socket_filter);
2848 browser->hists->socket_filter = -1;
2849 perf_hpp__set_elide(HISTC_SOCKET, false);
2851 browser->hists->socket_filter = act->socket;
2852 perf_hpp__set_elide(HISTC_SOCKET, true);
2853 pstack__push(browser->pstack, &browser->hists->socket_filter);
2856 hists__filter_by_socket(browser->hists);
2857 hist_browser__reset(browser);
2862 add_socket_opt(struct hist_browser *browser, struct popup_action *act,
2863 char **optstr, int socket_id)
2865 if (!hists__has(browser->hists, socket) || socket_id < 0)
2868 if (asprintf(optstr, "Zoom %s Processor Socket %d",
2869 (browser->hists->socket_filter > -1) ? "out of" : "into",
2873 act->socket = socket_id;
2874 act->fn = do_zoom_socket;
2878 static void hist_browser__update_nr_entries(struct hist_browser *hb)
2881 struct rb_node *nd = rb_first_cached(&hb->hists->entries);
2883 if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
2884 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
2888 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2890 nd = rb_hierarchy_next(nd);
2893 hb->nr_non_filtered_entries = nr_entries;
2894 hb->nr_hierarchy_entries = nr_entries;
2897 static void hist_browser__update_percent_limit(struct hist_browser *hb,
2900 struct hist_entry *he;
2901 struct rb_node *nd = rb_first_cached(&hb->hists->entries);
2902 u64 total = hists__total_period(hb->hists);
2903 u64 min_callchain_hits = total * (percent / 100);
2905 hb->min_pcnt = callchain_param.min_percent = percent;
2907 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2908 he = rb_entry(nd, struct hist_entry, rb_node);
2910 if (he->has_no_entry) {
2911 he->has_no_entry = false;
2915 if (!he->leaf || !hist_entry__has_callchains(he) || !symbol_conf.use_callchain)
2918 if (callchain_param.mode == CHAIN_GRAPH_REL) {
2919 total = he->stat.period;
2921 if (symbol_conf.cumulate_callchain)
2922 total = he->stat_acc->period;
2924 min_callchain_hits = total * (percent / 100);
2927 callchain_param.sort(&he->sorted_chain, he->callchain,
2928 min_callchain_hits, &callchain_param);
2931 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
2933 /* force to re-evaluate folding state of callchains */
2934 he->init_have_children = false;
2935 hist_entry__set_folding(he, hb, false);
2939 static int perf_evsel__hists_browse(struct evsel *evsel, int nr_events,
2940 const char *helpline,
2942 struct hist_browser_timer *hbt,
2944 struct perf_env *env,
2945 bool warn_lost_event,
2946 struct annotation_options *annotation_opts)
2948 struct hists *hists = evsel__hists(evsel);
2949 struct hist_browser *browser = perf_evsel_browser__new(evsel, hbt, env, annotation_opts);
2950 struct branch_info *bi = NULL;
2951 #define MAX_OPTIONS 16
2952 char *options[MAX_OPTIONS];
2953 struct popup_action actions[MAX_OPTIONS];
2957 int delay_secs = hbt ? hbt->refresh : 0;
2959 #define HIST_BROWSER_HELP_COMMON \
2960 "h/?/F1 Show this window\n" \
2962 "PGDN/SPACE Navigate\n" \
2963 "q/ESC/CTRL+C Exit browser or go back to previous screen\n\n" \
2964 "For multiple event sessions:\n\n" \
2965 "TAB/UNTAB Switch events\n\n" \
2966 "For symbolic views (--sort has sym):\n\n" \
2967 "ENTER Zoom into DSO/Threads & Annotate current symbol\n" \
2969 "+ Expand/Collapse one callchain level\n" \
2970 "a Annotate current symbol\n" \
2971 "C Collapse all callchains\n" \
2972 "d Zoom into current DSO\n" \
2973 "e Expand/Collapse main entry callchains\n" \
2974 "E Expand all callchains\n" \
2975 "F Toggle percentage of filtered entries\n" \
2976 "H Display column headers\n" \
2977 "k Zoom into the kernel map\n" \
2978 "L Change percent limit\n" \
2979 "m Display context menu\n" \
2980 "S Zoom into current Processor Socket\n" \
2982 /* help messages are sorted by lexical order of the hotkey */
2983 static const char report_help[] = HIST_BROWSER_HELP_COMMON
2984 "i Show header information\n"
2985 "P Print histograms to perf.hist.N\n"
2986 "r Run available scripts\n"
2987 "s Switch to another data file in PWD\n"
2988 "t Zoom into current Thread\n"
2989 "V Verbose (DSO names in callchains, etc)\n"
2990 "/ Filter symbol by name\n"
2991 "0-9 Sort by event n in group";
2992 static const char top_help[] = HIST_BROWSER_HELP_COMMON
2993 "P Print histograms to perf.hist.N\n"
2994 "t Zoom into current Thread\n"
2995 "V Verbose (DSO names in callchains, etc)\n"
2996 "z Toggle zeroing of samples\n"
2997 "f Enable/Disable events\n"
2998 "/ Filter symbol by name";
3000 if (browser == NULL)
3003 /* reset abort key so that it can get Ctrl-C as a key */
3005 SLang_init_tty(0, 0, 0);
3008 browser->min_pcnt = min_pcnt;
3009 hist_browser__update_nr_entries(browser);
3011 browser->pstack = pstack__new(3);
3012 if (browser->pstack == NULL)
3015 ui_helpline__push(helpline);
3017 memset(options, 0, sizeof(options));
3018 memset(actions, 0, sizeof(actions));
3020 if (symbol_conf.col_width_list_str)
3021 perf_hpp__set_user_width(symbol_conf.col_width_list_str);
3023 if (!is_report_browser(hbt))
3024 browser->b.no_samples_msg = "Collecting samples...";
3027 struct thread *thread = NULL;
3028 struct map *map = NULL;
3032 key = 0; // reset key
3033 do_hotkey: // key came straight from options ui__popup_menu()
3034 choice = nr_options = 0;
3035 key = hist_browser__run(browser, helpline, warn_lost_event, key);
3037 if (browser->he_selection != NULL) {
3038 thread = hist_browser__selected_thread(browser);
3039 map = browser->selection->map;
3040 socked_id = browser->he_selection->socket;
3048 * Exit the browser, let hists__browser_tree
3049 * go to the next or previous
3051 goto out_free_stack;
3053 if (!symbol_conf.event_group ||
3054 evsel->core.nr_members < 2) {
3055 snprintf(buf, sizeof(buf),
3056 "Sort by index only available with group events!");
3061 if (key - '0' == symbol_conf.group_sort_idx)
3064 symbol_conf.group_sort_idx = key - '0';
3066 if (symbol_conf.group_sort_idx >= evsel->core.nr_members) {
3067 snprintf(buf, sizeof(buf),
3068 "Max event group index to sort is %d (index from 0 to %d)",
3069 evsel->core.nr_members - 1,
3070 evsel->core.nr_members - 1);
3076 goto out_free_stack;
3078 if (!hists__has(hists, sym)) {
3079 ui_browser__warning(&browser->b, delay_secs * 2,
3080 "Annotation is only available for symbolic views, "
3081 "include \"sym*\" in --sort to use it.");
3085 if (!browser->selection ||
3086 !browser->selection->map ||
3087 !browser->selection->map->dso ||
3088 browser->selection->map->dso->annotate_warned) {
3092 if (!browser->selection->sym) {
3093 if (!browser->he_selection)
3096 if (sort__mode == SORT_MODE__BRANCH) {
3097 bi = browser->he_selection->branch_info;
3098 if (!bi || !bi->to.ms.map)
3101 actions->ms.sym = symbol__new_unresolved(bi->to.al_addr, bi->to.ms.map);
3102 actions->ms.map = bi->to.ms.map;
3104 actions->ms.sym = symbol__new_unresolved(browser->he_selection->ip,
3105 browser->selection->map);
3106 actions->ms.map = browser->selection->map;
3109 if (!actions->ms.sym)
3112 if (symbol__annotation(browser->selection->sym)->src == NULL) {
3113 ui_browser__warning(&browser->b, delay_secs * 2,
3114 "No samples for the \"%s\" symbol.\n\n"
3115 "Probably appeared just in a callchain",
3116 browser->selection->sym->name);
3120 actions->ms.map = browser->selection->map;
3121 actions->ms.sym = browser->selection->sym;
3124 do_annotate(browser, actions);
3127 hist_browser__dump(browser);
3130 actions->ms.map = map;
3131 do_zoom_dso(browser, actions);
3134 if (browser->selection != NULL)
3135 hists_browser__zoom_map(browser, browser->selection->maps->machine->vmlinux_map);
3138 verbose = (verbose + 1) % 4;
3139 browser->show_dso = verbose > 0;
3140 ui_helpline__fpush("Verbosity level set to %d\n",
3144 actions->thread = thread;
3145 do_zoom_thread(browser, actions);
3148 actions->socket = socked_id;
3149 do_zoom_socket(browser, actions);
3152 if (ui_browser__input_window("Symbol to show",
3153 "Please enter the name of symbol you want to see.\n"
3154 "To remove the filter later, press / + ENTER.",
3155 buf, "ENTER: OK, ESC: Cancel",
3156 delay_secs * 2) == K_ENTER) {
3157 hists->symbol_filter_str = *buf ? buf : NULL;
3158 hists__filter_by_symbol(hists);
3159 hist_browser__reset(browser);
3163 if (is_report_browser(hbt)) {
3164 actions->thread = NULL;
3165 actions->ms.sym = NULL;
3166 do_run_script(browser, actions);
3170 if (is_report_browser(hbt)) {
3171 key = do_switch_data(browser, actions);
3172 if (key == K_SWITCH_INPUT_DATA)
3173 goto out_free_stack;
3177 /* env->arch is NULL for live-mode (i.e. perf top) */
3179 tui__header_window(env);
3182 symbol_conf.filter_relative ^= 1;
3185 if (!is_report_browser(hbt)) {
3186 struct perf_top *top = hbt->arg;
3188 top->zero = !top->zero;
3192 if (ui_browser__input_window("Percent Limit",
3193 "Please enter the value you want to hide entries under that percent.",
3194 buf, "ENTER: OK, ESC: Cancel",
3195 delay_secs * 2) == K_ENTER) {
3197 double new_percent = strtod(buf, &end);
3199 if (new_percent < 0 || new_percent > 100) {
3200 ui_browser__warning(&browser->b, delay_secs * 2,
3201 "Invalid percent: %.2f", new_percent);
3205 hist_browser__update_percent_limit(browser, new_percent);
3206 hist_browser__reset(browser);
3212 ui_browser__help_window(&browser->b,
3213 is_report_browser(hbt) ? report_help : top_help);
3224 if (pstack__empty(browser->pstack)) {
3226 * Go back to the perf_evsel_menu__run or other user
3229 goto out_free_stack;
3232 ui_browser__dialog_yesno(&browser->b,
3233 "Do you really want to exit?"))
3234 goto out_free_stack;
3238 actions->ms.map = map;
3239 top = pstack__peek(browser->pstack);
3240 if (top == &browser->hists->dso_filter) {
3242 * No need to set actions->dso here since
3243 * it's just to remove the current filter.
3244 * Ditto for thread below.
3246 do_zoom_dso(browser, actions);
3247 } else if (top == &browser->hists->thread_filter) {
3248 do_zoom_thread(browser, actions);
3249 } else if (top == &browser->hists->socket_filter) {
3250 do_zoom_socket(browser, actions);
3256 goto out_free_stack;
3258 if (!is_report_browser(hbt)) {
3259 struct perf_top *top = hbt->arg;
3261 perf_evlist__toggle_enable(top->evlist);
3263 * No need to refresh, resort/decay histogram
3264 * entries if we are not collecting samples:
3266 if (top->evlist->enabled) {
3267 helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
3268 hbt->refresh = delay_secs;
3270 helpline = "Press 'f' again to re-enable the events";
3277 helpline = "Press '?' for help on key bindings";
3281 if (!hists__has(hists, sym) || browser->selection == NULL)
3282 goto skip_annotation;
3284 if (sort__mode == SORT_MODE__BRANCH) {
3286 if (browser->he_selection)
3287 bi = browser->he_selection->branch_info;
3290 goto skip_annotation;
3292 nr_options += add_annotate_opt(browser,
3293 &actions[nr_options],
3294 &options[nr_options],
3297 if (bi->to.ms.sym != bi->from.ms.sym)
3298 nr_options += add_annotate_opt(browser,
3299 &actions[nr_options],
3300 &options[nr_options],
3304 nr_options += add_annotate_opt(browser,
3305 &actions[nr_options],
3306 &options[nr_options],
3308 browser->he_selection->ip);
3311 nr_options += add_thread_opt(browser, &actions[nr_options],
3312 &options[nr_options], thread);
3313 nr_options += add_dso_opt(browser, &actions[nr_options],
3314 &options[nr_options], map);
3315 nr_options += add_callchain_toggle_opt(browser, &actions[nr_options], &options[nr_options]);
3316 nr_options += add_map_opt(browser, &actions[nr_options],
3317 &options[nr_options],
3318 browser->selection ?
3319 browser->selection->map : NULL);
3320 nr_options += add_socket_opt(browser, &actions[nr_options],
3321 &options[nr_options],
3323 /* perf script support */
3324 if (!is_report_browser(hbt))
3325 goto skip_scripting;
3327 if (browser->he_selection) {
3328 if (hists__has(hists, thread) && thread) {
3329 nr_options += add_script_opt(browser,
3330 &actions[nr_options],
3331 &options[nr_options],
3332 thread, NULL, evsel);
3335 * Note that browser->selection != NULL
3336 * when browser->he_selection is not NULL,
3337 * so we don't need to check browser->selection
3338 * before fetching browser->selection->sym like what
3339 * we do before fetching browser->selection->map.
3341 * See hist_browser__show_entry.
3343 if (hists__has(hists, sym) && browser->selection->sym) {
3344 nr_options += add_script_opt(browser,
3345 &actions[nr_options],
3346 &options[nr_options],
3347 NULL, browser->selection->sym,
3351 nr_options += add_script_opt(browser, &actions[nr_options],
3352 &options[nr_options], NULL, NULL, evsel);
3353 nr_options += add_res_sample_opt(browser, &actions[nr_options],
3354 &options[nr_options],
3355 hist_browser__selected_res_sample(browser),
3357 nr_options += add_res_sample_opt(browser, &actions[nr_options],
3358 &options[nr_options],
3359 hist_browser__selected_res_sample(browser),
3361 nr_options += add_res_sample_opt(browser, &actions[nr_options],
3362 &options[nr_options],
3363 hist_browser__selected_res_sample(browser),
3365 nr_options += add_switch_opt(browser, &actions[nr_options],
3366 &options[nr_options]);
3368 nr_options += add_exit_opt(browser, &actions[nr_options],
3369 &options[nr_options]);
3372 struct popup_action *act;
3374 choice = ui__popup_menu(nr_options, options, &key);
3378 if (choice == nr_options)
3381 act = &actions[choice];
3382 key = act->fn(browser, act);
3385 if (key == K_SWITCH_INPUT_DATA)
3389 pstack__delete(browser->pstack);
3391 hist_browser__delete(browser);
3392 free_popup_options(options, MAX_OPTIONS);
3397 struct ui_browser b;
3398 struct evsel *selection;
3399 struct annotation_options *annotation_opts;
3400 bool lost_events, lost_events_warned;
3402 struct perf_env *env;
3405 static void perf_evsel_menu__write(struct ui_browser *browser,
3406 void *entry, int row)
3408 struct evsel_menu *menu = container_of(browser,
3409 struct evsel_menu, b);
3410 struct evsel *evsel = list_entry(entry, struct evsel, core.node);
3411 struct hists *hists = evsel__hists(evsel);
3412 bool current_entry = ui_browser__is_current_entry(browser, row);
3413 unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
3414 const char *ev_name = evsel__name(evsel);
3416 const char *warn = " ";
3419 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
3420 HE_COLORSET_NORMAL);
3422 if (evsel__is_group_event(evsel)) {
3425 ev_name = evsel__group_name(evsel);
3427 for_each_group_member(pos, evsel) {
3428 struct hists *pos_hists = evsel__hists(pos);
3429 nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
3433 nr_events = convert_unit(nr_events, &unit);
3434 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
3435 unit, unit == ' ' ? "" : " ", ev_name);
3436 ui_browser__printf(browser, "%s", bf);
3438 nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
3439 if (nr_events != 0) {
3440 menu->lost_events = true;
3442 ui_browser__set_color(browser, HE_COLORSET_TOP);
3443 nr_events = convert_unit(nr_events, &unit);
3444 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
3445 nr_events, unit, unit == ' ' ? "" : " ");
3449 ui_browser__write_nstring(browser, warn, browser->width - printed);
3452 menu->selection = evsel;
3455 static int perf_evsel_menu__run(struct evsel_menu *menu,
3456 int nr_events, const char *help,
3457 struct hist_browser_timer *hbt,
3458 bool warn_lost_event)
3460 struct evlist *evlist = menu->b.priv;
3462 const char *title = "Available samples";
3463 int delay_secs = hbt ? hbt->refresh : 0;
3466 if (ui_browser__show(&menu->b, title,
3467 "ESC: exit, ENTER|->: Browse histograms") < 0)
3471 key = ui_browser__run(&menu->b, delay_secs);
3476 hbt->timer(hbt->arg);
3478 if (!menu->lost_events_warned &&
3479 menu->lost_events &&
3481 ui_browser__warn_lost_events(&menu->b);
3482 menu->lost_events_warned = true;
3487 if (!menu->selection)
3489 pos = menu->selection;
3491 perf_evlist__set_selected(evlist, pos);
3493 * Give the calling tool a chance to populate the non
3494 * default evsel resorted hists tree.
3497 hbt->timer(hbt->arg);
3498 key = perf_evsel__hists_browse(pos, nr_events, help,
3503 menu->annotation_opts);
3504 ui_browser__show_title(&menu->b, title);
3507 if (pos->core.node.next == &evlist->core.entries)
3508 pos = evlist__first(evlist);
3510 pos = evsel__next(pos);
3513 if (pos->core.node.prev == &evlist->core.entries)
3514 pos = evlist__last(evlist);
3516 pos = evsel__prev(pos);
3518 case K_SWITCH_INPUT_DATA:
3530 if (!ui_browser__dialog_yesno(&menu->b,
3531 "Do you really want to exit?"))
3543 ui_browser__hide(&menu->b);
3547 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
3550 struct evsel *evsel = list_entry(entry, struct evsel, core.node);
3552 if (symbol_conf.event_group && !evsel__is_group_leader(evsel))
3558 static int __perf_evlist__tui_browse_hists(struct evlist *evlist,
3559 int nr_entries, const char *help,
3560 struct hist_browser_timer *hbt,
3562 struct perf_env *env,
3563 bool warn_lost_event,
3564 struct annotation_options *annotation_opts)
3567 struct evsel_menu menu = {
3569 .entries = &evlist->core.entries,
3570 .refresh = ui_browser__list_head_refresh,
3571 .seek = ui_browser__list_head_seek,
3572 .write = perf_evsel_menu__write,
3573 .filter = filter_group_entries,
3574 .nr_entries = nr_entries,
3577 .min_pcnt = min_pcnt,
3579 .annotation_opts = annotation_opts,
3582 ui_helpline__push("Press ESC to exit");
3584 evlist__for_each_entry(evlist, pos) {
3585 const char *ev_name = evsel__name(pos);
3586 size_t line_len = strlen(ev_name) + 7;
3588 if (menu.b.width < line_len)
3589 menu.b.width = line_len;
3592 return perf_evsel_menu__run(&menu, nr_entries, help,
3593 hbt, warn_lost_event);
3596 static bool perf_evlist__single_entry(struct evlist *evlist)
3598 int nr_entries = evlist->core.nr_entries;
3600 if (nr_entries == 1)
3603 if (nr_entries == 2) {
3604 struct evsel *last = evlist__last(evlist);
3606 if (evsel__is_dummy_event(last))
3613 int perf_evlist__tui_browse_hists(struct evlist *evlist, const char *help,
3614 struct hist_browser_timer *hbt,
3616 struct perf_env *env,
3617 bool warn_lost_event,
3618 struct annotation_options *annotation_opts)
3620 int nr_entries = evlist->core.nr_entries;
3622 if (perf_evlist__single_entry(evlist)) {
3624 struct evsel *first = evlist__first(evlist);
3626 return perf_evsel__hists_browse(first, nr_entries, help,
3627 false, hbt, min_pcnt,
3628 env, warn_lost_event,
3633 if (symbol_conf.event_group) {
3637 evlist__for_each_entry(evlist, pos) {
3638 if (evsel__is_group_leader(pos))
3642 if (nr_entries == 1)
3646 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
3652 static int block_hists_browser__title(struct hist_browser *browser, char *bf,
3655 struct hists *hists = evsel__hists(browser->block_evsel);
3656 const char *evname = evsel__name(browser->block_evsel);
3657 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
3660 ret = scnprintf(bf, size, "# Samples: %lu", nr_samples);
3662 scnprintf(bf + ret, size - ret, " of event '%s'", evname);
3667 int block_hists_tui_browse(struct block_hist *bh, struct evsel *evsel,
3668 float min_percent, struct perf_env *env,
3669 struct annotation_options *annotation_opts)
3671 struct hists *hists = &bh->block_hists;
3672 struct hist_browser *browser;
3674 struct popup_action action;
3675 static const char help[] =
3678 browser = hist_browser__new(hists);
3682 browser->block_evsel = evsel;
3683 browser->title = block_hists_browser__title;
3684 browser->min_pcnt = min_percent;
3686 browser->annotation_opts = annotation_opts;
3688 /* reset abort key so that it can get Ctrl-C as a key */
3690 SLang_init_tty(0, 0, 0);
3692 memset(&action, 0, sizeof(action));
3695 key = hist_browser__run(browser, "? - help", true, 0);
3701 ui_browser__help_window(&browser->b, help);
3705 if (!browser->selection ||
3706 !browser->selection->sym) {
3710 action.ms.map = browser->selection->map;
3711 action.ms.sym = browser->selection->sym;
3712 do_annotate(browser, &action);
3720 hist_browser__delete(browser);