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 static int hist_browser__handle_hotkey(struct hist_browser *browser, bool warn_lost_event, char *title, int key)
679 struct hist_browser_timer *hbt = browser->hbt;
685 hbt->timer(hbt->arg);
687 if (hist_browser__has_filter(browser) || symbol_conf.report_hierarchy)
688 hist_browser__update_nr_entries(browser);
690 nr_entries = hist_browser__nr_entries(browser);
691 ui_browser__update_nr_entries(&browser->b, nr_entries);
693 if (warn_lost_event &&
694 (browser->hists->stats.nr_lost_warned !=
695 browser->hists->stats.nr_events[PERF_RECORD_LOST])) {
696 browser->hists->stats.nr_lost_warned =
697 browser->hists->stats.nr_events[PERF_RECORD_LOST];
698 ui_browser__warn_lost_events(&browser->b);
701 hist_browser__title(browser, title, sizeof(title));
702 ui_browser__show_title(&browser->b, title);
705 case 'D': { /* Debug */
706 struct hist_entry *h = rb_entry(browser->b.top, struct hist_entry, rb_node);
710 ui_helpline__fpush("%d: nr_ent=(%d,%d), etl: %d, rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
711 seq++, browser->b.nr_entries, browser->hists->nr_entries,
712 browser->b.extra_title_lines, browser->b.rows,
713 browser->b.index, browser->b.top_idx, h->row_offset, h->nr_rows);
717 /* Collapse the whole world. */
718 hist_browser__set_folding(browser, false);
721 /* Collapse the selected entry. */
722 hist_browser__set_folding_selected(browser, false);
725 /* Expand the whole world. */
726 hist_browser__set_folding(browser, true);
729 /* Expand the selected entry. */
730 hist_browser__set_folding_selected(browser, true);
733 browser->show_headers = !browser->show_headers;
734 hist_browser__update_rows(browser);
737 if (hist_browser__toggle_fold(browser))
747 int hist_browser__run(struct hist_browser *browser, const char *help,
748 bool warn_lost_event, int key)
751 struct hist_browser_timer *hbt = browser->hbt;
752 int delay_secs = hbt ? hbt->refresh : 0;
754 browser->b.entries = &browser->hists->entries;
755 browser->b.nr_entries = hist_browser__nr_entries(browser);
757 hist_browser__title(browser, title, sizeof(title));
759 if (ui_browser__show(&browser->b, title, "%s", help) < 0)
762 if (key && hist_browser__handle_hotkey(browser, warn_lost_event, title, key))
766 key = ui_browser__run(&browser->b, delay_secs);
768 if (hist_browser__handle_hotkey(browser, warn_lost_event, title, key))
772 ui_browser__hide(&browser->b);
776 struct callchain_print_arg {
777 /* for hists browser */
779 bool is_current_entry;
786 typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
787 struct callchain_list *chain,
788 const char *str, int offset,
790 struct callchain_print_arg *arg);
792 static void hist_browser__show_callchain_entry(struct hist_browser *browser,
793 struct callchain_list *chain,
794 const char *str, int offset,
796 struct callchain_print_arg *arg)
799 char folded_sign = callchain_list__folded(chain);
800 bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
802 color = HE_COLORSET_NORMAL;
803 width = browser->b.width - (offset + 2);
804 if (ui_browser__is_current_entry(&browser->b, row)) {
805 browser->selection = &chain->ms;
806 color = HE_COLORSET_SELECTED;
807 arg->is_current_entry = true;
810 ui_browser__set_color(&browser->b, color);
811 ui_browser__gotorc(&browser->b, row, 0);
812 ui_browser__write_nstring(&browser->b, " ", offset);
813 ui_browser__printf(&browser->b, "%c", folded_sign);
814 ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
815 ui_browser__write_nstring(&browser->b, str, width);
818 static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
819 struct callchain_list *chain,
820 const char *str, int offset,
821 unsigned short row __maybe_unused,
822 struct callchain_print_arg *arg)
824 char folded_sign = callchain_list__folded(chain);
826 arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
830 typedef bool (*check_output_full_fn)(struct hist_browser *browser,
833 static bool hist_browser__check_output_full(struct hist_browser *browser,
836 return browser->b.rows == row;
839 static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
840 unsigned short row __maybe_unused)
845 #define LEVEL_OFFSET_STEP 3
847 static int hist_browser__show_callchain_list(struct hist_browser *browser,
848 struct callchain_node *node,
849 struct callchain_list *chain,
850 unsigned short row, u64 total,
851 bool need_percent, int offset,
852 print_callchain_entry_fn print,
853 struct callchain_print_arg *arg)
855 char bf[1024], *alloc_str;
856 char buf[64], *alloc_str2;
860 if (arg->row_offset != 0) {
868 str = callchain_list__sym_name(chain, bf, sizeof(bf),
871 if (symbol_conf.show_branchflag_count) {
872 callchain_list_counts__printf_value(chain, NULL,
875 if (asprintf(&alloc_str2, "%s%s", str, buf) < 0)
876 str = "Not enough memory!";
882 callchain_node__scnprintf_value(node, buf, sizeof(buf),
885 if (asprintf(&alloc_str, "%s %s", buf, str) < 0)
886 str = "Not enough memory!";
891 print(browser, chain, str, offset, row, arg);
898 static bool check_percent_display(struct rb_node *node, u64 parent_total)
900 struct callchain_node *child;
908 child = rb_entry(node, struct callchain_node, rb_node);
909 return callchain_cumul_hits(child) != parent_total;
912 static int hist_browser__show_callchain_flat(struct hist_browser *browser,
913 struct rb_root *root,
914 unsigned short row, u64 total,
916 print_callchain_entry_fn print,
917 struct callchain_print_arg *arg,
918 check_output_full_fn is_output_full)
920 struct rb_node *node;
921 int first_row = row, offset = LEVEL_OFFSET_STEP;
924 node = rb_first(root);
925 need_percent = check_percent_display(node, parent_total);
928 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
929 struct rb_node *next = rb_next(node);
930 struct callchain_list *chain;
931 char folded_sign = ' ';
933 int extra_offset = 0;
935 list_for_each_entry(chain, &child->parent_val, list) {
936 bool was_first = first;
940 else if (need_percent)
941 extra_offset = LEVEL_OFFSET_STEP;
943 folded_sign = callchain_list__folded(chain);
945 row += hist_browser__show_callchain_list(browser, child,
947 was_first && need_percent,
948 offset + extra_offset,
951 if (is_output_full(browser, row))
954 if (folded_sign == '+')
958 list_for_each_entry(chain, &child->val, list) {
959 bool was_first = first;
963 else if (need_percent)
964 extra_offset = LEVEL_OFFSET_STEP;
966 folded_sign = callchain_list__folded(chain);
968 row += hist_browser__show_callchain_list(browser, child,
970 was_first && need_percent,
971 offset + extra_offset,
974 if (is_output_full(browser, row))
977 if (folded_sign == '+')
982 if (is_output_full(browser, row))
987 return row - first_row;
990 static char *hist_browser__folded_callchain_str(struct hist_browser *browser,
991 struct callchain_list *chain,
992 char *value_str, char *old_str)
998 str = callchain_list__sym_name(chain, bf, sizeof(bf),
1001 if (asprintf(&new, "%s%s%s", old_str,
1002 symbol_conf.field_sep ?: ";", str) < 0)
1006 if (asprintf(&new, "%s %s", value_str, str) < 0)
1009 if (asprintf(&new, "%s", str) < 0)
1016 static int hist_browser__show_callchain_folded(struct hist_browser *browser,
1017 struct rb_root *root,
1018 unsigned short row, u64 total,
1020 print_callchain_entry_fn print,
1021 struct callchain_print_arg *arg,
1022 check_output_full_fn is_output_full)
1024 struct rb_node *node;
1025 int first_row = row, offset = LEVEL_OFFSET_STEP;
1028 node = rb_first(root);
1029 need_percent = check_percent_display(node, parent_total);
1032 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1033 struct rb_node *next = rb_next(node);
1034 struct callchain_list *chain, *first_chain = NULL;
1036 char *value_str = NULL, *value_str_alloc = NULL;
1037 char *chain_str = NULL, *chain_str_alloc = NULL;
1039 if (arg->row_offset != 0) {
1047 callchain_node__scnprintf_value(child, buf, sizeof(buf), total);
1048 if (asprintf(&value_str, "%s", buf) < 0) {
1049 value_str = (char *)"<...>";
1052 value_str_alloc = value_str;
1055 list_for_each_entry(chain, &child->parent_val, list) {
1056 chain_str = hist_browser__folded_callchain_str(browser,
1057 chain, value_str, chain_str);
1060 first_chain = chain;
1063 if (chain_str == NULL) {
1064 chain_str = (char *)"Not enough memory!";
1068 chain_str_alloc = chain_str;
1071 list_for_each_entry(chain, &child->val, list) {
1072 chain_str = hist_browser__folded_callchain_str(browser,
1073 chain, value_str, chain_str);
1076 first_chain = chain;
1079 if (chain_str == NULL) {
1080 chain_str = (char *)"Not enough memory!";
1084 chain_str_alloc = chain_str;
1088 print(browser, first_chain, chain_str, offset, row++, arg);
1089 free(value_str_alloc);
1090 free(chain_str_alloc);
1093 if (is_output_full(browser, row))
1098 return row - first_row;
1101 static int hist_browser__show_callchain_graph(struct hist_browser *browser,
1102 struct rb_root *root, int level,
1103 unsigned short row, u64 total,
1105 print_callchain_entry_fn print,
1106 struct callchain_print_arg *arg,
1107 check_output_full_fn is_output_full)
1109 struct rb_node *node;
1110 int first_row = row, offset = level * LEVEL_OFFSET_STEP;
1112 u64 percent_total = total;
1114 if (callchain_param.mode == CHAIN_GRAPH_REL)
1115 percent_total = parent_total;
1117 node = rb_first(root);
1118 need_percent = check_percent_display(node, parent_total);
1121 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1122 struct rb_node *next = rb_next(node);
1123 struct callchain_list *chain;
1124 char folded_sign = ' ';
1126 int extra_offset = 0;
1128 list_for_each_entry(chain, &child->val, list) {
1129 bool was_first = first;
1133 else if (need_percent)
1134 extra_offset = LEVEL_OFFSET_STEP;
1136 folded_sign = callchain_list__folded(chain);
1138 row += hist_browser__show_callchain_list(browser, child,
1139 chain, row, percent_total,
1140 was_first && need_percent,
1141 offset + extra_offset,
1144 if (is_output_full(browser, row))
1147 if (folded_sign == '+')
1151 if (folded_sign == '-') {
1152 const int new_level = level + (extra_offset ? 2 : 1);
1154 row += hist_browser__show_callchain_graph(browser, &child->rb_root,
1155 new_level, row, total,
1156 child->children_hit,
1157 print, arg, is_output_full);
1159 if (is_output_full(browser, row))
1164 return row - first_row;
1167 static int hist_browser__show_callchain(struct hist_browser *browser,
1168 struct hist_entry *entry, int level,
1170 print_callchain_entry_fn print,
1171 struct callchain_print_arg *arg,
1172 check_output_full_fn is_output_full)
1174 u64 total = hists__total_period(entry->hists);
1178 if (symbol_conf.cumulate_callchain)
1179 parent_total = entry->stat_acc->period;
1181 parent_total = entry->stat.period;
1183 if (callchain_param.mode == CHAIN_FLAT) {
1184 printed = hist_browser__show_callchain_flat(browser,
1185 &entry->sorted_chain, row,
1186 total, parent_total, print, arg,
1188 } else if (callchain_param.mode == CHAIN_FOLDED) {
1189 printed = hist_browser__show_callchain_folded(browser,
1190 &entry->sorted_chain, row,
1191 total, parent_total, print, arg,
1194 printed = hist_browser__show_callchain_graph(browser,
1195 &entry->sorted_chain, level, row,
1196 total, parent_total, print, arg,
1200 if (arg->is_current_entry)
1201 browser->he_selection = entry;
1207 struct ui_browser *b;
1212 int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
1214 struct hpp_arg *arg = hpp->ptr;
1219 va_start(args, fmt);
1220 len = va_arg(args, int);
1221 percent = va_arg(args, double);
1224 ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
1226 ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
1227 ui_browser__printf(arg->b, "%s", hpp->buf);
1232 #define __HPP_COLOR_PERCENT_FN(_type, _field) \
1233 static u64 __hpp_get_##_field(struct hist_entry *he) \
1235 return he->stat._field; \
1239 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
1240 struct perf_hpp *hpp, \
1241 struct hist_entry *he) \
1243 return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%", \
1244 __hpp__slsmg_color_printf, true); \
1247 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
1248 static u64 __hpp_get_acc_##_field(struct hist_entry *he) \
1250 return he->stat_acc->_field; \
1254 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
1255 struct perf_hpp *hpp, \
1256 struct hist_entry *he) \
1258 if (!symbol_conf.cumulate_callchain) { \
1259 struct hpp_arg *arg = hpp->ptr; \
1260 int len = fmt->user_len ?: fmt->len; \
1261 int ret = scnprintf(hpp->buf, hpp->size, \
1262 "%*s", len, "N/A"); \
1263 ui_browser__printf(arg->b, "%s", hpp->buf); \
1267 return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field, \
1268 " %*.2f%%", __hpp__slsmg_color_printf, true); \
1271 __HPP_COLOR_PERCENT_FN(overhead, period)
1272 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
1273 __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
1274 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
1275 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
1276 __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
1278 #undef __HPP_COLOR_PERCENT_FN
1279 #undef __HPP_COLOR_ACC_PERCENT_FN
1281 void hist_browser__init_hpp(void)
1283 perf_hpp__format[PERF_HPP__OVERHEAD].color =
1284 hist_browser__hpp_color_overhead;
1285 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
1286 hist_browser__hpp_color_overhead_sys;
1287 perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
1288 hist_browser__hpp_color_overhead_us;
1289 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
1290 hist_browser__hpp_color_overhead_guest_sys;
1291 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
1292 hist_browser__hpp_color_overhead_guest_us;
1293 perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
1294 hist_browser__hpp_color_overhead_acc;
1299 static int hist_browser__show_entry(struct hist_browser *browser,
1300 struct hist_entry *entry,
1304 int width = browser->b.width;
1305 char folded_sign = ' ';
1306 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1307 bool use_callchain = hist_entry__has_callchains(entry) && symbol_conf.use_callchain;
1308 off_t row_offset = entry->row_offset;
1310 struct perf_hpp_fmt *fmt;
1312 if (current_entry) {
1313 browser->he_selection = entry;
1314 browser->selection = &entry->ms;
1317 if (use_callchain) {
1318 hist_entry__init_have_children(entry);
1319 folded_sign = hist_entry__folded(entry);
1322 if (row_offset == 0) {
1323 struct hpp_arg arg = {
1325 .folded_sign = folded_sign,
1326 .current_entry = current_entry,
1330 ui_browser__gotorc(&browser->b, row, 0);
1332 hists__for_each_format(browser->hists, fmt) {
1334 struct perf_hpp hpp = {
1340 if (perf_hpp__should_skip(fmt, entry->hists) ||
1341 column++ < browser->b.horiz_scroll)
1344 if (current_entry && browser->b.navkeypressed) {
1345 ui_browser__set_color(&browser->b,
1346 HE_COLORSET_SELECTED);
1348 ui_browser__set_color(&browser->b,
1349 HE_COLORSET_NORMAL);
1353 if (use_callchain) {
1354 ui_browser__printf(&browser->b, "%c ", folded_sign);
1359 ui_browser__printf(&browser->b, " ");
1364 int ret = fmt->color(fmt, &hpp, entry);
1365 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1367 * fmt->color() already used ui_browser to
1368 * print the non alignment bits, skip it (+ret):
1370 ui_browser__printf(&browser->b, "%s", s + ret);
1372 hist_entry__snprintf_alignment(entry, &hpp, fmt, fmt->entry(fmt, &hpp, entry));
1373 ui_browser__printf(&browser->b, "%s", s);
1375 width -= hpp.buf - s;
1378 /* The scroll bar isn't being used */
1379 if (!browser->b.navkeypressed)
1382 ui_browser__write_nstring(&browser->b, "", width);
1389 if (folded_sign == '-' && row != browser->b.rows) {
1390 struct callchain_print_arg arg = {
1391 .row_offset = row_offset,
1392 .is_current_entry = current_entry,
1395 printed += hist_browser__show_callchain(browser,
1397 hist_browser__show_callchain_entry,
1399 hist_browser__check_output_full);
1405 static int hist_browser__show_hierarchy_entry(struct hist_browser *browser,
1406 struct hist_entry *entry,
1411 int width = browser->b.width;
1412 char folded_sign = ' ';
1413 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1414 off_t row_offset = entry->row_offset;
1416 struct perf_hpp_fmt *fmt;
1417 struct perf_hpp_list_node *fmt_node;
1418 struct hpp_arg arg = {
1420 .current_entry = current_entry,
1423 int hierarchy_indent = (entry->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
1425 if (current_entry) {
1426 browser->he_selection = entry;
1427 browser->selection = &entry->ms;
1430 hist_entry__init_have_children(entry);
1431 folded_sign = hist_entry__folded(entry);
1432 arg.folded_sign = folded_sign;
1434 if (entry->leaf && row_offset) {
1436 goto show_callchain;
1439 ui_browser__gotorc(&browser->b, row, 0);
1441 if (current_entry && browser->b.navkeypressed)
1442 ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1444 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1446 ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1447 width -= level * HIERARCHY_INDENT;
1449 /* the first hpp_list_node is for overhead columns */
1450 fmt_node = list_first_entry(&entry->hists->hpp_formats,
1451 struct perf_hpp_list_node, list);
1452 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1454 struct perf_hpp hpp = {
1460 if (perf_hpp__should_skip(fmt, entry->hists) ||
1461 column++ < browser->b.horiz_scroll)
1464 if (current_entry && browser->b.navkeypressed) {
1465 ui_browser__set_color(&browser->b,
1466 HE_COLORSET_SELECTED);
1468 ui_browser__set_color(&browser->b,
1469 HE_COLORSET_NORMAL);
1473 ui_browser__printf(&browser->b, "%c ", folded_sign);
1477 ui_browser__printf(&browser->b, " ");
1482 int ret = fmt->color(fmt, &hpp, entry);
1483 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1485 * fmt->color() already used ui_browser to
1486 * print the non alignment bits, skip it (+ret):
1488 ui_browser__printf(&browser->b, "%s", s + ret);
1490 int ret = fmt->entry(fmt, &hpp, entry);
1491 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1492 ui_browser__printf(&browser->b, "%s", s);
1494 width -= hpp.buf - s;
1498 ui_browser__write_nstring(&browser->b, "", hierarchy_indent);
1499 width -= hierarchy_indent;
1502 if (column >= browser->b.horiz_scroll) {
1504 struct perf_hpp hpp = {
1510 if (current_entry && browser->b.navkeypressed) {
1511 ui_browser__set_color(&browser->b,
1512 HE_COLORSET_SELECTED);
1514 ui_browser__set_color(&browser->b,
1515 HE_COLORSET_NORMAL);
1518 perf_hpp_list__for_each_format(entry->hpp_list, fmt) {
1520 ui_browser__printf(&browser->b, "%c ", folded_sign);
1523 ui_browser__write_nstring(&browser->b, "", 2);
1529 * No need to call hist_entry__snprintf_alignment()
1530 * since this fmt is always the last column in the
1534 width -= fmt->color(fmt, &hpp, entry);
1538 width -= fmt->entry(fmt, &hpp, entry);
1539 ui_browser__printf(&browser->b, "%s", skip_spaces(s));
1541 while (isspace(s[i++]))
1547 /* The scroll bar isn't being used */
1548 if (!browser->b.navkeypressed)
1551 ui_browser__write_nstring(&browser->b, "", width);
1557 if (entry->leaf && folded_sign == '-' && row != browser->b.rows) {
1558 struct callchain_print_arg carg = {
1559 .row_offset = row_offset,
1562 printed += hist_browser__show_callchain(browser, entry,
1564 hist_browser__show_callchain_entry, &carg,
1565 hist_browser__check_output_full);
1571 static int hist_browser__show_no_entry(struct hist_browser *browser,
1572 unsigned short row, int level)
1574 int width = browser->b.width;
1575 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1579 struct perf_hpp_fmt *fmt;
1580 struct perf_hpp_list_node *fmt_node;
1581 int indent = browser->hists->nr_hpp_node - 2;
1583 if (current_entry) {
1584 browser->he_selection = NULL;
1585 browser->selection = NULL;
1588 ui_browser__gotorc(&browser->b, row, 0);
1590 if (current_entry && browser->b.navkeypressed)
1591 ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1593 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1595 ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1596 width -= level * HIERARCHY_INDENT;
1598 /* the first hpp_list_node is for overhead columns */
1599 fmt_node = list_first_entry(&browser->hists->hpp_formats,
1600 struct perf_hpp_list_node, list);
1601 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1602 if (perf_hpp__should_skip(fmt, browser->hists) ||
1603 column++ < browser->b.horiz_scroll)
1606 ret = fmt->width(fmt, NULL, browser->hists);
1609 /* for folded sign */
1613 /* space between columns */
1617 ui_browser__write_nstring(&browser->b, "", ret);
1621 ui_browser__write_nstring(&browser->b, "", indent * HIERARCHY_INDENT);
1622 width -= indent * HIERARCHY_INDENT;
1624 if (column >= browser->b.horiz_scroll) {
1627 ret = snprintf(buf, sizeof(buf), "no entry >= %.2f%%", browser->min_pcnt);
1628 ui_browser__printf(&browser->b, " %s", buf);
1632 /* The scroll bar isn't being used */
1633 if (!browser->b.navkeypressed)
1636 ui_browser__write_nstring(&browser->b, "", width);
1640 static int advance_hpp_check(struct perf_hpp *hpp, int inc)
1642 advance_hpp(hpp, inc);
1643 return hpp->size <= 0;
1647 hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf,
1648 size_t size, int line)
1650 struct hists *hists = browser->hists;
1651 struct perf_hpp dummy_hpp = {
1655 struct perf_hpp_fmt *fmt;
1660 if (hists__has_callchains(hists) && symbol_conf.use_callchain) {
1661 ret = scnprintf(buf, size, " ");
1662 if (advance_hpp_check(&dummy_hpp, ret))
1666 hists__for_each_format(browser->hists, fmt) {
1667 if (perf_hpp__should_skip(fmt, hists) || column++ < browser->b.horiz_scroll)
1670 ret = fmt->header(fmt, &dummy_hpp, hists, line, &span);
1671 if (advance_hpp_check(&dummy_hpp, ret))
1677 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
1678 if (advance_hpp_check(&dummy_hpp, ret))
1685 static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser, char *buf, size_t size)
1687 struct hists *hists = browser->hists;
1688 struct perf_hpp dummy_hpp = {
1692 struct perf_hpp_fmt *fmt;
1693 struct perf_hpp_list_node *fmt_node;
1696 int indent = hists->nr_hpp_node - 2;
1697 bool first_node, first_col;
1699 ret = scnprintf(buf, size, " ");
1700 if (advance_hpp_check(&dummy_hpp, ret))
1704 /* the first hpp_list_node is for overhead columns */
1705 fmt_node = list_first_entry(&hists->hpp_formats,
1706 struct perf_hpp_list_node, list);
1707 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1708 if (column++ < browser->b.horiz_scroll)
1711 ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1712 if (advance_hpp_check(&dummy_hpp, ret))
1715 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
1716 if (advance_hpp_check(&dummy_hpp, ret))
1723 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "%*s",
1724 indent * HIERARCHY_INDENT, "");
1725 if (advance_hpp_check(&dummy_hpp, ret))
1730 list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
1732 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " / ");
1733 if (advance_hpp_check(&dummy_hpp, ret))
1739 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1742 if (perf_hpp__should_skip(fmt, hists))
1746 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "+");
1747 if (advance_hpp_check(&dummy_hpp, ret))
1752 ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1753 dummy_hpp.buf[ret] = '\0';
1755 start = strim(dummy_hpp.buf);
1756 ret = strlen(start);
1758 if (start != dummy_hpp.buf)
1759 memmove(dummy_hpp.buf, start, ret + 1);
1761 if (advance_hpp_check(&dummy_hpp, ret))
1769 static void hists_browser__hierarchy_headers(struct hist_browser *browser)
1773 hists_browser__scnprintf_hierarchy_headers(browser, headers,
1776 ui_browser__gotorc(&browser->b, 0, 0);
1777 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1778 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1781 static void hists_browser__headers(struct hist_browser *browser)
1783 struct hists *hists = browser->hists;
1784 struct perf_hpp_list *hpp_list = hists->hpp_list;
1788 for (line = 0; line < hpp_list->nr_header_lines; line++) {
1791 hists_browser__scnprintf_headers(browser, headers,
1792 sizeof(headers), line);
1794 ui_browser__gotorc_title(&browser->b, line, 0);
1795 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1796 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1800 static void hist_browser__show_headers(struct hist_browser *browser)
1802 if (symbol_conf.report_hierarchy)
1803 hists_browser__hierarchy_headers(browser);
1805 hists_browser__headers(browser);
1808 static void ui_browser__hists_init_top(struct ui_browser *browser)
1810 if (browser->top == NULL) {
1811 struct hist_browser *hb;
1813 hb = container_of(browser, struct hist_browser, b);
1814 browser->top = rb_first_cached(&hb->hists->entries);
1818 static unsigned int hist_browser__refresh(struct ui_browser *browser)
1822 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
1824 if (hb->show_headers)
1825 hist_browser__show_headers(hb);
1827 ui_browser__hists_init_top(browser);
1828 hb->he_selection = NULL;
1829 hb->selection = NULL;
1831 for (nd = browser->top; nd; nd = rb_hierarchy_next(nd)) {
1832 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1836 /* let it move to sibling */
1837 h->unfolded = false;
1841 if (symbol_conf.report_individual_block)
1842 percent = block_info__total_cycles_percent(h);
1844 percent = hist_entry__get_percent_limit(h);
1846 if (percent < hb->min_pcnt)
1849 if (symbol_conf.report_hierarchy) {
1850 row += hist_browser__show_hierarchy_entry(hb, h, row,
1852 if (row == browser->rows)
1855 if (h->has_no_entry) {
1856 hist_browser__show_no_entry(hb, row, h->depth + 1);
1860 row += hist_browser__show_entry(hb, h, row);
1863 if (row == browser->rows)
1870 static struct rb_node *hists__filter_entries(struct rb_node *nd,
1873 while (nd != NULL) {
1874 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1875 float percent = hist_entry__get_percent_limit(h);
1877 if (!h->filtered && percent >= min_pcnt)
1881 * If it's filtered, its all children also were filtered.
1882 * So move to sibling node.
1887 nd = rb_hierarchy_next(nd);
1893 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
1896 while (nd != NULL) {
1897 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1898 float percent = hist_entry__get_percent_limit(h);
1900 if (!h->filtered && percent >= min_pcnt)
1903 nd = rb_hierarchy_prev(nd);
1909 static void ui_browser__hists_seek(struct ui_browser *browser,
1910 off_t offset, int whence)
1912 struct hist_entry *h;
1915 struct hist_browser *hb;
1917 hb = container_of(browser, struct hist_browser, b);
1919 if (browser->nr_entries == 0)
1922 ui_browser__hists_init_top(browser);
1926 nd = hists__filter_entries(rb_first(browser->entries),
1933 nd = rb_hierarchy_last(rb_last(browser->entries));
1934 nd = hists__filter_prev_entries(nd, hb->min_pcnt);
1942 * Moves not relative to the first visible entry invalidates its
1945 h = rb_entry(browser->top, struct hist_entry, rb_node);
1949 * Here we have to check if nd is expanded (+), if it is we can't go
1950 * the next top level hist_entry, instead we must compute an offset of
1951 * what _not_ to show and not change the first visible entry.
1953 * This offset increments when we are going from top to bottom and
1954 * decreases when we're going from bottom to top.
1956 * As we don't have backpointers to the top level in the callchains
1957 * structure, we need to always print the whole hist_entry callchain,
1958 * skipping the first ones that are before the first visible entry
1959 * and stop when we printed enough lines to fill the screen.
1967 h = rb_entry(nd, struct hist_entry, rb_node);
1968 if (h->unfolded && h->leaf) {
1969 u16 remaining = h->nr_rows - h->row_offset;
1970 if (offset > remaining) {
1971 offset -= remaining;
1974 h->row_offset += offset;
1980 nd = hists__filter_entries(rb_hierarchy_next(nd),
1986 } while (offset != 0);
1987 } else if (offset < 0) {
1989 h = rb_entry(nd, struct hist_entry, rb_node);
1990 if (h->unfolded && h->leaf) {
1992 if (-offset > h->row_offset) {
1993 offset += h->row_offset;
1996 h->row_offset += offset;
2002 if (-offset > h->nr_rows) {
2003 offset += h->nr_rows;
2006 h->row_offset = h->nr_rows + offset;
2014 nd = hists__filter_prev_entries(rb_hierarchy_prev(nd),
2022 * Last unfiltered hist_entry, check if it is
2023 * unfolded, if it is then we should have
2024 * row_offset at its last entry.
2026 h = rb_entry(nd, struct hist_entry, rb_node);
2027 if (h->unfolded && h->leaf)
2028 h->row_offset = h->nr_rows;
2035 h = rb_entry(nd, struct hist_entry, rb_node);
2040 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
2041 struct hist_entry *he, FILE *fp,
2044 struct callchain_print_arg arg = {
2048 hist_browser__show_callchain(browser, he, level, 0,
2049 hist_browser__fprintf_callchain_entry, &arg,
2050 hist_browser__check_dump_full);
2054 static int hist_browser__fprintf_entry(struct hist_browser *browser,
2055 struct hist_entry *he, FILE *fp)
2059 char folded_sign = ' ';
2060 struct perf_hpp hpp = {
2064 struct perf_hpp_fmt *fmt;
2068 if (hist_entry__has_callchains(he) && symbol_conf.use_callchain) {
2069 folded_sign = hist_entry__folded(he);
2070 printed += fprintf(fp, "%c ", folded_sign);
2073 hists__for_each_format(browser->hists, fmt) {
2074 if (perf_hpp__should_skip(fmt, he->hists))
2078 ret = scnprintf(hpp.buf, hpp.size, " ");
2079 advance_hpp(&hpp, ret);
2083 ret = fmt->entry(fmt, &hpp, he);
2084 ret = hist_entry__snprintf_alignment(he, &hpp, fmt, ret);
2085 advance_hpp(&hpp, ret);
2087 printed += fprintf(fp, "%s\n", s);
2089 if (folded_sign == '-')
2090 printed += hist_browser__fprintf_callchain(browser, he, fp, 1);
2096 static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,
2097 struct hist_entry *he,
2098 FILE *fp, int level)
2102 char folded_sign = ' ';
2103 struct perf_hpp hpp = {
2107 struct perf_hpp_fmt *fmt;
2108 struct perf_hpp_list_node *fmt_node;
2111 int hierarchy_indent = (he->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
2113 printed = fprintf(fp, "%*s", level * HIERARCHY_INDENT, "");
2115 folded_sign = hist_entry__folded(he);
2116 printed += fprintf(fp, "%c", folded_sign);
2118 /* the first hpp_list_node is for overhead columns */
2119 fmt_node = list_first_entry(&he->hists->hpp_formats,
2120 struct perf_hpp_list_node, list);
2121 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
2123 ret = scnprintf(hpp.buf, hpp.size, " ");
2124 advance_hpp(&hpp, ret);
2128 ret = fmt->entry(fmt, &hpp, he);
2129 advance_hpp(&hpp, ret);
2132 ret = scnprintf(hpp.buf, hpp.size, "%*s", hierarchy_indent, "");
2133 advance_hpp(&hpp, ret);
2135 perf_hpp_list__for_each_format(he->hpp_list, fmt) {
2136 ret = scnprintf(hpp.buf, hpp.size, " ");
2137 advance_hpp(&hpp, ret);
2139 ret = fmt->entry(fmt, &hpp, he);
2140 advance_hpp(&hpp, ret);
2144 printed += fprintf(fp, "%s\n", s);
2146 if (he->leaf && folded_sign == '-') {
2147 printed += hist_browser__fprintf_callchain(browser, he, fp,
2154 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
2156 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
2161 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
2163 if (symbol_conf.report_hierarchy) {
2164 printed += hist_browser__fprintf_hierarchy_entry(browser,
2168 printed += hist_browser__fprintf_entry(browser, h, fp);
2171 nd = hists__filter_entries(rb_hierarchy_next(nd),
2178 static int hist_browser__dump(struct hist_browser *browser)
2184 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
2185 if (access(filename, F_OK))
2188 * XXX: Just an arbitrary lazy upper limit
2190 if (++browser->print_seq == 8192) {
2191 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
2196 fp = fopen(filename, "w");
2199 const char *err = str_error_r(errno, bf, sizeof(bf));
2200 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
2204 ++browser->print_seq;
2205 hist_browser__fprintf(browser, fp);
2207 ui_helpline__fpush("%s written!", filename);
2212 void hist_browser__init(struct hist_browser *browser,
2213 struct hists *hists)
2215 struct perf_hpp_fmt *fmt;
2217 browser->hists = hists;
2218 browser->b.refresh = hist_browser__refresh;
2219 browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
2220 browser->b.seek = ui_browser__hists_seek;
2221 browser->b.use_navkeypressed = true;
2222 browser->show_headers = symbol_conf.show_hist_headers;
2223 hist_browser__set_title_space(browser);
2225 if (symbol_conf.report_hierarchy) {
2226 struct perf_hpp_list_node *fmt_node;
2228 /* count overhead columns (in the first node) */
2229 fmt_node = list_first_entry(&hists->hpp_formats,
2230 struct perf_hpp_list_node, list);
2231 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt)
2232 ++browser->b.columns;
2234 /* add a single column for whole hierarchy sort keys*/
2235 ++browser->b.columns;
2237 hists__for_each_format(hists, fmt)
2238 ++browser->b.columns;
2241 hists__reset_column_width(hists);
2244 struct hist_browser *hist_browser__new(struct hists *hists)
2246 struct hist_browser *browser = zalloc(sizeof(*browser));
2249 hist_browser__init(browser, hists);
2254 static struct hist_browser *
2255 perf_evsel_browser__new(struct evsel *evsel,
2256 struct hist_browser_timer *hbt,
2257 struct perf_env *env,
2258 struct annotation_options *annotation_opts)
2260 struct hist_browser *browser = hist_browser__new(evsel__hists(evsel));
2265 browser->title = hists_browser__scnprintf_title;
2266 browser->annotation_opts = annotation_opts;
2271 void hist_browser__delete(struct hist_browser *browser)
2276 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
2278 return browser->he_selection;
2281 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
2283 return browser->he_selection->thread;
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);
2464 add_annotate_opt(struct hist_browser *browser __maybe_unused,
2465 struct popup_action *act, char **optstr,
2466 struct map_symbol *ms)
2468 if (ms->sym == NULL || ms->map->dso->annotate_warned)
2471 if (asprintf(optstr, "Annotate %s", ms->sym->name) < 0)
2475 act->fn = do_annotate;
2480 do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
2482 struct thread *thread = act->thread;
2484 if ((!hists__has(browser->hists, thread) &&
2485 !hists__has(browser->hists, comm)) || thread == NULL)
2488 if (browser->hists->thread_filter) {
2489 pstack__remove(browser->pstack, &browser->hists->thread_filter);
2490 perf_hpp__set_elide(HISTC_THREAD, false);
2491 thread__zput(browser->hists->thread_filter);
2494 if (hists__has(browser->hists, thread)) {
2495 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
2496 thread->comm_set ? thread__comm_str(thread) : "",
2499 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
2500 thread->comm_set ? thread__comm_str(thread) : "");
2503 browser->hists->thread_filter = thread__get(thread);
2504 perf_hpp__set_elide(HISTC_THREAD, false);
2505 pstack__push(browser->pstack, &browser->hists->thread_filter);
2508 hists__filter_by_thread(browser->hists);
2509 hist_browser__reset(browser);
2514 add_thread_opt(struct hist_browser *browser, struct popup_action *act,
2515 char **optstr, struct thread *thread)
2519 if ((!hists__has(browser->hists, thread) &&
2520 !hists__has(browser->hists, comm)) || thread == NULL)
2523 if (hists__has(browser->hists, thread)) {
2524 ret = asprintf(optstr, "Zoom %s %s(%d) thread",
2525 browser->hists->thread_filter ? "out of" : "into",
2526 thread->comm_set ? thread__comm_str(thread) : "",
2529 ret = asprintf(optstr, "Zoom %s %s thread",
2530 browser->hists->thread_filter ? "out of" : "into",
2531 thread->comm_set ? thread__comm_str(thread) : "");
2536 act->thread = thread;
2537 act->fn = do_zoom_thread;
2541 static int hists_browser__zoom_map(struct hist_browser *browser, struct map *map)
2543 if (!hists__has(browser->hists, dso) || map == NULL)
2546 if (browser->hists->dso_filter) {
2547 pstack__remove(browser->pstack, &browser->hists->dso_filter);
2548 perf_hpp__set_elide(HISTC_DSO, false);
2549 browser->hists->dso_filter = NULL;
2552 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
2553 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
2554 browser->hists->dso_filter = map->dso;
2555 perf_hpp__set_elide(HISTC_DSO, true);
2556 pstack__push(browser->pstack, &browser->hists->dso_filter);
2559 hists__filter_by_dso(browser->hists);
2560 hist_browser__reset(browser);
2565 do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
2567 return hists_browser__zoom_map(browser, act->ms.map);
2571 add_dso_opt(struct hist_browser *browser, struct popup_action *act,
2572 char **optstr, struct map *map)
2574 if (!hists__has(browser->hists, dso) || map == NULL)
2577 if (asprintf(optstr, "Zoom %s %s DSO (use the 'k' hotkey to zoom directly into the kernel)",
2578 browser->hists->dso_filter ? "out of" : "into",
2579 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
2583 act->fn = do_zoom_dso;
2587 static int do_toggle_callchain(struct hist_browser *browser, struct popup_action *act __maybe_unused)
2589 hist_browser__toggle_fold(browser);
2593 static int add_callchain_toggle_opt(struct hist_browser *browser, struct popup_action *act, char **optstr)
2597 if (!hist_browser__selection_has_children(browser))
2600 if (asprintf(optstr, "%s [%s] callchain (one level, same as '+' hotkey, use 'e'/'c' for the whole main level entry)",
2601 hist_browser__selection_unfolded(browser) ? "Collapse" : "Expand",
2602 hist_browser__selection_sym_name(browser, sym_name, sizeof(sym_name))) < 0)
2605 act->fn = do_toggle_callchain;
2610 do_browse_map(struct hist_browser *browser __maybe_unused,
2611 struct popup_action *act)
2613 map__browse(act->ms.map);
2618 add_map_opt(struct hist_browser *browser,
2619 struct popup_action *act, char **optstr, struct map *map)
2621 if (!hists__has(browser->hists, dso) || map == NULL)
2624 if (asprintf(optstr, "Browse map details") < 0)
2628 act->fn = do_browse_map;
2633 do_run_script(struct hist_browser *browser __maybe_unused,
2634 struct popup_action *act)
2642 len += strlen(thread__comm_str(act->thread));
2643 else if (act->ms.sym)
2644 len += strlen(act->ms.sym->name);
2645 script_opt = malloc(len);
2651 n = scnprintf(script_opt, len, " -c %s ",
2652 thread__comm_str(act->thread));
2653 } else if (act->ms.sym) {
2654 n = scnprintf(script_opt, len, " -S %s ",
2659 char start[32], end[32];
2660 unsigned long starttime = act->time;
2661 unsigned long endtime = act->time + symbol_conf.time_quantum;
2663 if (starttime == endtime) { /* Display 1ms as fallback */
2664 starttime -= 1*NSEC_PER_MSEC;
2665 endtime += 1*NSEC_PER_MSEC;
2667 timestamp__scnprintf_usec(starttime, start, sizeof start);
2668 timestamp__scnprintf_usec(endtime, end, sizeof end);
2669 n += snprintf(script_opt + n, len - n, " --time %s,%s", start, end);
2672 script_browse(script_opt, act->evsel);
2678 do_res_sample_script(struct hist_browser *browser __maybe_unused,
2679 struct popup_action *act)
2681 struct hist_entry *he;
2683 he = hist_browser__selected_entry(browser);
2684 res_sample_browse(he->res_samples, he->num_res, act->evsel, act->rstype);
2689 add_script_opt_2(struct hist_browser *browser __maybe_unused,
2690 struct popup_action *act, char **optstr,
2691 struct thread *thread, struct symbol *sym,
2692 struct evsel *evsel, const char *tstr)
2696 if (asprintf(optstr, "Run scripts for samples of thread [%s]%s",
2697 thread__comm_str(thread), tstr) < 0)
2700 if (asprintf(optstr, "Run scripts for samples of symbol [%s]%s",
2701 sym->name, tstr) < 0)
2704 if (asprintf(optstr, "Run scripts for all samples%s", tstr) < 0)
2708 act->thread = thread;
2711 act->fn = do_run_script;
2716 add_script_opt(struct hist_browser *browser,
2717 struct popup_action *act, char **optstr,
2718 struct thread *thread, struct symbol *sym,
2719 struct evsel *evsel)
2722 struct hist_entry *he;
2724 n = add_script_opt_2(browser, act, optstr, thread, sym, evsel, "");
2726 he = hist_browser__selected_entry(browser);
2727 if (sort_order && strstr(sort_order, "time")) {
2732 j = sprintf(tstr, " in ");
2733 j += timestamp__scnprintf_usec(he->time, tstr + j,
2735 j += sprintf(tstr + j, "-");
2736 timestamp__scnprintf_usec(he->time + symbol_conf.time_quantum,
2737 tstr + j, sizeof tstr - j);
2738 n += add_script_opt_2(browser, act, optstr, thread, sym,
2740 act->time = he->time;
2746 add_res_sample_opt(struct hist_browser *browser __maybe_unused,
2747 struct popup_action *act, char **optstr,
2748 struct res_sample *res_sample,
2749 struct evsel *evsel,
2755 if (asprintf(optstr, "Show context for individual samples %s",
2756 type == A_ASM ? "with assembler" :
2757 type == A_SOURCE ? "with source" : "") < 0)
2760 act->fn = do_res_sample_script;
2767 do_switch_data(struct hist_browser *browser __maybe_unused,
2768 struct popup_action *act __maybe_unused)
2770 if (switch_data_file()) {
2771 ui__warning("Won't switch the data files due to\n"
2772 "no valid data file get selected!\n");
2776 return K_SWITCH_INPUT_DATA;
2780 add_switch_opt(struct hist_browser *browser,
2781 struct popup_action *act, char **optstr)
2783 if (!is_report_browser(browser->hbt))
2786 if (asprintf(optstr, "Switch to another data file in PWD") < 0)
2789 act->fn = do_switch_data;
2794 do_exit_browser(struct hist_browser *browser __maybe_unused,
2795 struct popup_action *act __maybe_unused)
2801 add_exit_opt(struct hist_browser *browser __maybe_unused,
2802 struct popup_action *act, char **optstr)
2804 if (asprintf(optstr, "Exit") < 0)
2807 act->fn = do_exit_browser;
2812 do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
2814 if (!hists__has(browser->hists, socket) || act->socket < 0)
2817 if (browser->hists->socket_filter > -1) {
2818 pstack__remove(browser->pstack, &browser->hists->socket_filter);
2819 browser->hists->socket_filter = -1;
2820 perf_hpp__set_elide(HISTC_SOCKET, false);
2822 browser->hists->socket_filter = act->socket;
2823 perf_hpp__set_elide(HISTC_SOCKET, true);
2824 pstack__push(browser->pstack, &browser->hists->socket_filter);
2827 hists__filter_by_socket(browser->hists);
2828 hist_browser__reset(browser);
2833 add_socket_opt(struct hist_browser *browser, struct popup_action *act,
2834 char **optstr, int socket_id)
2836 if (!hists__has(browser->hists, socket) || socket_id < 0)
2839 if (asprintf(optstr, "Zoom %s Processor Socket %d",
2840 (browser->hists->socket_filter > -1) ? "out of" : "into",
2844 act->socket = socket_id;
2845 act->fn = do_zoom_socket;
2849 static void hist_browser__update_nr_entries(struct hist_browser *hb)
2852 struct rb_node *nd = rb_first_cached(&hb->hists->entries);
2854 if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
2855 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
2859 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2861 nd = rb_hierarchy_next(nd);
2864 hb->nr_non_filtered_entries = nr_entries;
2865 hb->nr_hierarchy_entries = nr_entries;
2868 static void hist_browser__update_percent_limit(struct hist_browser *hb,
2871 struct hist_entry *he;
2872 struct rb_node *nd = rb_first_cached(&hb->hists->entries);
2873 u64 total = hists__total_period(hb->hists);
2874 u64 min_callchain_hits = total * (percent / 100);
2876 hb->min_pcnt = callchain_param.min_percent = percent;
2878 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2879 he = rb_entry(nd, struct hist_entry, rb_node);
2881 if (he->has_no_entry) {
2882 he->has_no_entry = false;
2886 if (!he->leaf || !hist_entry__has_callchains(he) || !symbol_conf.use_callchain)
2889 if (callchain_param.mode == CHAIN_GRAPH_REL) {
2890 total = he->stat.period;
2892 if (symbol_conf.cumulate_callchain)
2893 total = he->stat_acc->period;
2895 min_callchain_hits = total * (percent / 100);
2898 callchain_param.sort(&he->sorted_chain, he->callchain,
2899 min_callchain_hits, &callchain_param);
2902 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
2904 /* force to re-evaluate folding state of callchains */
2905 he->init_have_children = false;
2906 hist_entry__set_folding(he, hb, false);
2910 static int perf_evsel__hists_browse(struct evsel *evsel, int nr_events,
2911 const char *helpline,
2913 struct hist_browser_timer *hbt,
2915 struct perf_env *env,
2916 bool warn_lost_event,
2917 struct annotation_options *annotation_opts)
2919 struct hists *hists = evsel__hists(evsel);
2920 struct hist_browser *browser = perf_evsel_browser__new(evsel, hbt, env, annotation_opts);
2921 struct branch_info *bi = NULL;
2922 #define MAX_OPTIONS 16
2923 char *options[MAX_OPTIONS];
2924 struct popup_action actions[MAX_OPTIONS];
2928 int delay_secs = hbt ? hbt->refresh : 0;
2930 #define HIST_BROWSER_HELP_COMMON \
2931 "h/?/F1 Show this window\n" \
2933 "PGDN/SPACE Navigate\n" \
2934 "q/ESC/CTRL+C Exit browser or go back to previous screen\n\n" \
2935 "For multiple event sessions:\n\n" \
2936 "TAB/UNTAB Switch events\n\n" \
2937 "For symbolic views (--sort has sym):\n\n" \
2938 "ENTER Zoom into DSO/Threads & Annotate current symbol\n" \
2940 "+ Expand/Collapse one callchain level\n" \
2941 "a Annotate current symbol\n" \
2942 "C Collapse all callchains\n" \
2943 "d Zoom into current DSO\n" \
2944 "E Expand all callchains\n" \
2945 "F Toggle percentage of filtered entries\n" \
2946 "H Display column headers\n" \
2947 "k Zoom into the kernel map\n" \
2948 "L Change percent limit\n" \
2949 "m Display context menu\n" \
2950 "S Zoom into current Processor Socket\n" \
2952 /* help messages are sorted by lexical order of the hotkey */
2953 static const char report_help[] = HIST_BROWSER_HELP_COMMON
2954 "i Show header information\n"
2955 "P Print histograms to perf.hist.N\n"
2956 "r Run available scripts\n"
2957 "s Switch to another data file in PWD\n"
2958 "t Zoom into current Thread\n"
2959 "V Verbose (DSO names in callchains, etc)\n"
2960 "/ Filter symbol by name";
2961 static const char top_help[] = HIST_BROWSER_HELP_COMMON
2962 "P Print histograms to perf.hist.N\n"
2963 "t Zoom into current Thread\n"
2964 "V Verbose (DSO names in callchains, etc)\n"
2965 "z Toggle zeroing of samples\n"
2966 "f Enable/Disable events\n"
2967 "/ Filter symbol by name";
2969 if (browser == NULL)
2972 /* reset abort key so that it can get Ctrl-C as a key */
2974 SLang_init_tty(0, 0, 0);
2977 browser->min_pcnt = min_pcnt;
2978 hist_browser__update_nr_entries(browser);
2980 browser->pstack = pstack__new(3);
2981 if (browser->pstack == NULL)
2984 ui_helpline__push(helpline);
2986 memset(options, 0, sizeof(options));
2987 memset(actions, 0, sizeof(actions));
2989 if (symbol_conf.col_width_list_str)
2990 perf_hpp__set_user_width(symbol_conf.col_width_list_str);
2992 if (!is_report_browser(hbt))
2993 browser->b.no_samples_msg = "Collecting samples...";
2996 struct thread *thread = NULL;
2997 struct map *map = NULL;
3003 key = hist_browser__run(browser, helpline, warn_lost_event, 0);
3005 if (browser->he_selection != NULL) {
3006 thread = hist_browser__selected_thread(browser);
3007 map = browser->selection->map;
3008 socked_id = browser->he_selection->socket;
3016 * Exit the browser, let hists__browser_tree
3017 * go to the next or previous
3019 goto out_free_stack;
3021 if (!hists__has(hists, sym)) {
3022 ui_browser__warning(&browser->b, delay_secs * 2,
3023 "Annotation is only available for symbolic views, "
3024 "include \"sym*\" in --sort to use it.");
3028 if (browser->selection == NULL ||
3029 browser->selection->sym == NULL ||
3030 browser->selection->map->dso->annotate_warned)
3033 actions->ms.map = browser->selection->map;
3034 actions->ms.sym = browser->selection->sym;
3035 do_annotate(browser, actions);
3038 hist_browser__dump(browser);
3041 actions->ms.map = map;
3042 do_zoom_dso(browser, actions);
3045 if (browser->selection != NULL)
3046 hists_browser__zoom_map(browser, browser->selection->maps->machine->vmlinux_map);
3049 verbose = (verbose + 1) % 4;
3050 browser->show_dso = verbose > 0;
3051 ui_helpline__fpush("Verbosity level set to %d\n",
3055 actions->thread = thread;
3056 do_zoom_thread(browser, actions);
3059 actions->socket = socked_id;
3060 do_zoom_socket(browser, actions);
3063 if (ui_browser__input_window("Symbol to show",
3064 "Please enter the name of symbol you want to see.\n"
3065 "To remove the filter later, press / + ENTER.",
3066 buf, "ENTER: OK, ESC: Cancel",
3067 delay_secs * 2) == K_ENTER) {
3068 hists->symbol_filter_str = *buf ? buf : NULL;
3069 hists__filter_by_symbol(hists);
3070 hist_browser__reset(browser);
3074 if (is_report_browser(hbt)) {
3075 actions->thread = NULL;
3076 actions->ms.sym = NULL;
3077 do_run_script(browser, actions);
3081 if (is_report_browser(hbt)) {
3082 key = do_switch_data(browser, actions);
3083 if (key == K_SWITCH_INPUT_DATA)
3084 goto out_free_stack;
3088 /* env->arch is NULL for live-mode (i.e. perf top) */
3090 tui__header_window(env);
3093 symbol_conf.filter_relative ^= 1;
3096 if (!is_report_browser(hbt)) {
3097 struct perf_top *top = hbt->arg;
3099 top->zero = !top->zero;
3103 if (ui_browser__input_window("Percent Limit",
3104 "Please enter the value you want to hide entries under that percent.",
3105 buf, "ENTER: OK, ESC: Cancel",
3106 delay_secs * 2) == K_ENTER) {
3108 double new_percent = strtod(buf, &end);
3110 if (new_percent < 0 || new_percent > 100) {
3111 ui_browser__warning(&browser->b, delay_secs * 2,
3112 "Invalid percent: %.2f", new_percent);
3116 hist_browser__update_percent_limit(browser, new_percent);
3117 hist_browser__reset(browser);
3123 ui_browser__help_window(&browser->b,
3124 is_report_browser(hbt) ? report_help : top_help);
3135 if (pstack__empty(browser->pstack)) {
3137 * Go back to the perf_evsel_menu__run or other user
3140 goto out_free_stack;
3143 ui_browser__dialog_yesno(&browser->b,
3144 "Do you really want to exit?"))
3145 goto out_free_stack;
3149 actions->ms.map = map;
3150 top = pstack__peek(browser->pstack);
3151 if (top == &browser->hists->dso_filter) {
3153 * No need to set actions->dso here since
3154 * it's just to remove the current filter.
3155 * Ditto for thread below.
3157 do_zoom_dso(browser, actions);
3158 } else if (top == &browser->hists->thread_filter) {
3159 do_zoom_thread(browser, actions);
3160 } else if (top == &browser->hists->socket_filter) {
3161 do_zoom_socket(browser, actions);
3167 goto out_free_stack;
3169 if (!is_report_browser(hbt)) {
3170 struct perf_top *top = hbt->arg;
3172 perf_evlist__toggle_enable(top->evlist);
3174 * No need to refresh, resort/decay histogram
3175 * entries if we are not collecting samples:
3177 if (top->evlist->enabled) {
3178 helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
3179 hbt->refresh = delay_secs;
3181 helpline = "Press 'f' again to re-enable the events";
3188 helpline = "Press '?' for help on key bindings";
3192 if (!hists__has(hists, sym) || browser->selection == NULL)
3193 goto skip_annotation;
3195 if (sort__mode == SORT_MODE__BRANCH) {
3197 if (browser->he_selection)
3198 bi = browser->he_selection->branch_info;
3201 goto skip_annotation;
3203 nr_options += add_annotate_opt(browser,
3204 &actions[nr_options],
3205 &options[nr_options],
3207 if (bi->to.ms.sym != bi->from.ms.sym)
3208 nr_options += add_annotate_opt(browser,
3209 &actions[nr_options],
3210 &options[nr_options],
3213 nr_options += add_annotate_opt(browser,
3214 &actions[nr_options],
3215 &options[nr_options],
3216 browser->selection);
3219 nr_options += add_thread_opt(browser, &actions[nr_options],
3220 &options[nr_options], thread);
3221 nr_options += add_dso_opt(browser, &actions[nr_options],
3222 &options[nr_options], map);
3223 nr_options += add_callchain_toggle_opt(browser, &actions[nr_options], &options[nr_options]);
3224 nr_options += add_map_opt(browser, &actions[nr_options],
3225 &options[nr_options],
3226 browser->selection ?
3227 browser->selection->map : NULL);
3228 nr_options += add_socket_opt(browser, &actions[nr_options],
3229 &options[nr_options],
3231 /* perf script support */
3232 if (!is_report_browser(hbt))
3233 goto skip_scripting;
3235 if (browser->he_selection) {
3236 if (hists__has(hists, thread) && thread) {
3237 nr_options += add_script_opt(browser,
3238 &actions[nr_options],
3239 &options[nr_options],
3240 thread, NULL, evsel);
3243 * Note that browser->selection != NULL
3244 * when browser->he_selection is not NULL,
3245 * so we don't need to check browser->selection
3246 * before fetching browser->selection->sym like what
3247 * we do before fetching browser->selection->map.
3249 * See hist_browser__show_entry.
3251 if (hists__has(hists, sym) && browser->selection->sym) {
3252 nr_options += add_script_opt(browser,
3253 &actions[nr_options],
3254 &options[nr_options],
3255 NULL, browser->selection->sym,
3259 nr_options += add_script_opt(browser, &actions[nr_options],
3260 &options[nr_options], NULL, NULL, evsel);
3261 nr_options += add_res_sample_opt(browser, &actions[nr_options],
3262 &options[nr_options],
3263 hist_browser__selected_entry(browser)->res_samples,
3265 nr_options += add_res_sample_opt(browser, &actions[nr_options],
3266 &options[nr_options],
3267 hist_browser__selected_entry(browser)->res_samples,
3269 nr_options += add_res_sample_opt(browser, &actions[nr_options],
3270 &options[nr_options],
3271 hist_browser__selected_entry(browser)->res_samples,
3273 nr_options += add_switch_opt(browser, &actions[nr_options],
3274 &options[nr_options]);
3276 nr_options += add_exit_opt(browser, &actions[nr_options],
3277 &options[nr_options]);
3280 struct popup_action *act;
3282 choice = ui__popup_menu(nr_options, options, NULL);
3283 if (choice == -1 || choice >= nr_options)
3286 act = &actions[choice];
3287 key = act->fn(browser, act);
3290 if (key == K_SWITCH_INPUT_DATA)
3294 pstack__delete(browser->pstack);
3296 hist_browser__delete(browser);
3297 free_popup_options(options, MAX_OPTIONS);
3302 struct ui_browser b;
3303 struct evsel *selection;
3304 struct annotation_options *annotation_opts;
3305 bool lost_events, lost_events_warned;
3307 struct perf_env *env;
3310 static void perf_evsel_menu__write(struct ui_browser *browser,
3311 void *entry, int row)
3313 struct evsel_menu *menu = container_of(browser,
3314 struct evsel_menu, b);
3315 struct evsel *evsel = list_entry(entry, struct evsel, core.node);
3316 struct hists *hists = evsel__hists(evsel);
3317 bool current_entry = ui_browser__is_current_entry(browser, row);
3318 unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
3319 const char *ev_name = perf_evsel__name(evsel);
3321 const char *warn = " ";
3324 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
3325 HE_COLORSET_NORMAL);
3327 if (perf_evsel__is_group_event(evsel)) {
3330 ev_name = perf_evsel__group_name(evsel);
3332 for_each_group_member(pos, evsel) {
3333 struct hists *pos_hists = evsel__hists(pos);
3334 nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
3338 nr_events = convert_unit(nr_events, &unit);
3339 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
3340 unit, unit == ' ' ? "" : " ", ev_name);
3341 ui_browser__printf(browser, "%s", bf);
3343 nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
3344 if (nr_events != 0) {
3345 menu->lost_events = true;
3347 ui_browser__set_color(browser, HE_COLORSET_TOP);
3348 nr_events = convert_unit(nr_events, &unit);
3349 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
3350 nr_events, unit, unit == ' ' ? "" : " ");
3354 ui_browser__write_nstring(browser, warn, browser->width - printed);
3357 menu->selection = evsel;
3360 static int perf_evsel_menu__run(struct evsel_menu *menu,
3361 int nr_events, const char *help,
3362 struct hist_browser_timer *hbt,
3363 bool warn_lost_event)
3365 struct evlist *evlist = menu->b.priv;
3367 const char *title = "Available samples";
3368 int delay_secs = hbt ? hbt->refresh : 0;
3371 if (ui_browser__show(&menu->b, title,
3372 "ESC: exit, ENTER|->: Browse histograms") < 0)
3376 key = ui_browser__run(&menu->b, delay_secs);
3381 hbt->timer(hbt->arg);
3383 if (!menu->lost_events_warned &&
3384 menu->lost_events &&
3386 ui_browser__warn_lost_events(&menu->b);
3387 menu->lost_events_warned = true;
3392 if (!menu->selection)
3394 pos = menu->selection;
3396 perf_evlist__set_selected(evlist, pos);
3398 * Give the calling tool a chance to populate the non
3399 * default evsel resorted hists tree.
3402 hbt->timer(hbt->arg);
3403 key = perf_evsel__hists_browse(pos, nr_events, help,
3408 menu->annotation_opts);
3409 ui_browser__show_title(&menu->b, title);
3412 if (pos->core.node.next == &evlist->core.entries)
3413 pos = evlist__first(evlist);
3415 pos = perf_evsel__next(pos);
3418 if (pos->core.node.prev == &evlist->core.entries)
3419 pos = evlist__last(evlist);
3421 pos = perf_evsel__prev(pos);
3423 case K_SWITCH_INPUT_DATA:
3434 if (!ui_browser__dialog_yesno(&menu->b,
3435 "Do you really want to exit?"))
3447 ui_browser__hide(&menu->b);
3451 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
3454 struct evsel *evsel = list_entry(entry, struct evsel, core.node);
3456 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
3462 static int __perf_evlist__tui_browse_hists(struct evlist *evlist,
3463 int nr_entries, const char *help,
3464 struct hist_browser_timer *hbt,
3466 struct perf_env *env,
3467 bool warn_lost_event,
3468 struct annotation_options *annotation_opts)
3471 struct evsel_menu menu = {
3473 .entries = &evlist->core.entries,
3474 .refresh = ui_browser__list_head_refresh,
3475 .seek = ui_browser__list_head_seek,
3476 .write = perf_evsel_menu__write,
3477 .filter = filter_group_entries,
3478 .nr_entries = nr_entries,
3481 .min_pcnt = min_pcnt,
3483 .annotation_opts = annotation_opts,
3486 ui_helpline__push("Press ESC to exit");
3488 evlist__for_each_entry(evlist, pos) {
3489 const char *ev_name = perf_evsel__name(pos);
3490 size_t line_len = strlen(ev_name) + 7;
3492 if (menu.b.width < line_len)
3493 menu.b.width = line_len;
3496 return perf_evsel_menu__run(&menu, nr_entries, help,
3497 hbt, warn_lost_event);
3500 int perf_evlist__tui_browse_hists(struct evlist *evlist, const char *help,
3501 struct hist_browser_timer *hbt,
3503 struct perf_env *env,
3504 bool warn_lost_event,
3505 struct annotation_options *annotation_opts)
3507 int nr_entries = evlist->core.nr_entries;
3510 if (nr_entries == 1) {
3511 struct evsel *first = evlist__first(evlist);
3513 return perf_evsel__hists_browse(first, nr_entries, help,
3514 false, hbt, min_pcnt,
3515 env, warn_lost_event,
3519 if (symbol_conf.event_group) {
3523 evlist__for_each_entry(evlist, pos) {
3524 if (perf_evsel__is_group_leader(pos))
3528 if (nr_entries == 1)
3532 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
3538 static int block_hists_browser__title(struct hist_browser *browser, char *bf,
3541 struct hists *hists = evsel__hists(browser->block_evsel);
3542 const char *evname = perf_evsel__name(browser->block_evsel);
3543 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
3546 ret = scnprintf(bf, size, "# Samples: %lu", nr_samples);
3548 scnprintf(bf + ret, size - ret, " of event '%s'", evname);
3553 int block_hists_tui_browse(struct block_hist *bh, struct evsel *evsel,
3554 float min_percent, struct perf_env *env,
3555 struct annotation_options *annotation_opts)
3557 struct hists *hists = &bh->block_hists;
3558 struct hist_browser *browser;
3560 struct popup_action action;
3561 static const char help[] =
3564 browser = hist_browser__new(hists);
3568 browser->block_evsel = evsel;
3569 browser->title = block_hists_browser__title;
3570 browser->min_pcnt = min_percent;
3572 browser->annotation_opts = annotation_opts;
3574 /* reset abort key so that it can get Ctrl-C as a key */
3576 SLang_init_tty(0, 0, 0);
3578 memset(&action, 0, sizeof(action));
3581 key = hist_browser__run(browser, "? - help", true, 0);
3587 ui_browser__help_window(&browser->b, help);
3591 if (!browser->selection ||
3592 !browser->selection->sym) {
3596 action.ms.map = browser->selection->map;
3597 action.ms.sym = browser->selection->sym;
3598 do_annotate(browser, &action);
3606 hist_browser__delete(browser);