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/map.h"
22 #include "../../util/symbol.h"
23 #include "../../util/map_symbol.h"
24 #include "../../util/branch.h"
25 #include "../../util/pstack.h"
26 #include "../../util/sort.h"
27 #include "../../util/top.h"
28 #include "../../util/thread.h"
29 #include "../../util/block-info.h"
30 #include "../../arch/common.h"
31 #include "../../perf.h"
33 #include "../browsers/hists.h"
34 #include "../helpline.h"
42 #include "time-utils.h"
44 #include <linux/ctype.h>
46 extern void hist_browser__init_hpp(void);
48 static int hists_browser__scnprintf_title(struct hist_browser *browser, char *bf, size_t size);
49 static void hist_browser__update_nr_entries(struct hist_browser *hb);
51 static struct rb_node *hists__filter_entries(struct rb_node *nd,
54 static bool hist_browser__has_filter(struct hist_browser *hb)
56 return hists__has_filter(hb->hists) || hb->min_pcnt || symbol_conf.has_filter || hb->c2c_filter;
59 static int hist_browser__get_folding(struct hist_browser *browser)
62 struct hists *hists = browser->hists;
63 int unfolded_rows = 0;
65 for (nd = rb_first_cached(&hists->entries);
66 (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
67 nd = rb_hierarchy_next(nd)) {
68 struct hist_entry *he =
69 rb_entry(nd, struct hist_entry, rb_node);
71 if (he->leaf && he->unfolded)
72 unfolded_rows += he->nr_rows;
77 static void hist_browser__set_title_space(struct hist_browser *hb)
79 struct ui_browser *browser = &hb->b;
80 struct hists *hists = hb->hists;
81 struct perf_hpp_list *hpp_list = hists->hpp_list;
83 browser->extra_title_lines = hb->show_headers ? hpp_list->nr_header_lines : 0;
86 static u32 hist_browser__nr_entries(struct hist_browser *hb)
90 if (symbol_conf.report_hierarchy)
91 nr_entries = hb->nr_hierarchy_entries;
92 else if (hist_browser__has_filter(hb))
93 nr_entries = hb->nr_non_filtered_entries;
95 nr_entries = hb->hists->nr_entries;
97 hb->nr_callchain_rows = hist_browser__get_folding(hb);
98 return nr_entries + hb->nr_callchain_rows;
101 static void hist_browser__update_rows(struct hist_browser *hb)
103 struct ui_browser *browser = &hb->b;
104 struct hists *hists = hb->hists;
105 struct perf_hpp_list *hpp_list = hists->hpp_list;
108 if (!hb->show_headers) {
109 browser->rows += browser->extra_title_lines;
110 browser->extra_title_lines = 0;
114 browser->extra_title_lines = hpp_list->nr_header_lines;
115 browser->rows -= browser->extra_title_lines;
117 * Verify if we were at the last line and that line isn't
118 * visibe because we now show the header line(s).
120 index_row = browser->index - browser->top_idx;
121 if (index_row >= browser->rows)
122 browser->index -= index_row - browser->rows + 1;
125 static void hist_browser__refresh_dimensions(struct ui_browser *browser)
127 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
129 /* 3 == +/- toggle symbol before actual hist_entry rendering */
130 browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]"));
132 * FIXME: Just keeping existing behaviour, but this really should be
133 * before updating browser->width, as it will invalidate the
134 * calculation above. Fix this and the fallout in another
137 ui_browser__refresh_dimensions(browser);
140 static void hist_browser__reset(struct hist_browser *browser)
143 * The hists__remove_entry_filter() already folds non-filtered
144 * entries so we can assume it has 0 callchain rows.
146 browser->nr_callchain_rows = 0;
148 hist_browser__update_nr_entries(browser);
149 browser->b.nr_entries = hist_browser__nr_entries(browser);
150 hist_browser__refresh_dimensions(&browser->b);
151 ui_browser__reset_index(&browser->b);
154 static char tree__folded_sign(bool unfolded)
156 return unfolded ? '-' : '+';
159 static char hist_entry__folded(const struct hist_entry *he)
161 return he->has_children ? tree__folded_sign(he->unfolded) : ' ';
164 static char callchain_list__folded(const struct callchain_list *cl)
166 return cl->has_children ? tree__folded_sign(cl->unfolded) : ' ';
169 static void callchain_list__set_folding(struct callchain_list *cl, bool unfold)
171 cl->unfolded = unfold ? cl->has_children : false;
174 static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
179 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
180 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
181 struct callchain_list *chain;
182 char folded_sign = ' '; /* No children */
184 list_for_each_entry(chain, &child->val, list) {
187 /* We need this because we may not have children */
188 folded_sign = callchain_list__folded(chain);
189 if (folded_sign == '+')
193 if (folded_sign == '-') /* Have children and they're unfolded */
194 n += callchain_node__count_rows_rb_tree(child);
200 static int callchain_node__count_flat_rows(struct callchain_node *node)
202 struct callchain_list *chain;
203 char folded_sign = 0;
206 list_for_each_entry(chain, &node->parent_val, list) {
208 /* only check first chain list entry */
209 folded_sign = callchain_list__folded(chain);
210 if (folded_sign == '+')
216 list_for_each_entry(chain, &node->val, list) {
218 /* node->parent_val list might be empty */
219 folded_sign = callchain_list__folded(chain);
220 if (folded_sign == '+')
229 static int callchain_node__count_folded_rows(struct callchain_node *node __maybe_unused)
234 static int callchain_node__count_rows(struct callchain_node *node)
236 struct callchain_list *chain;
237 bool unfolded = false;
240 if (callchain_param.mode == CHAIN_FLAT)
241 return callchain_node__count_flat_rows(node);
242 else if (callchain_param.mode == CHAIN_FOLDED)
243 return callchain_node__count_folded_rows(node);
245 list_for_each_entry(chain, &node->val, list) {
248 unfolded = chain->unfolded;
252 n += callchain_node__count_rows_rb_tree(node);
257 static int callchain__count_rows(struct rb_root *chain)
262 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
263 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
264 n += callchain_node__count_rows(node);
270 static int hierarchy_count_rows(struct hist_browser *hb, struct hist_entry *he,
271 bool include_children)
274 struct rb_node *node;
275 struct hist_entry *child;
278 return callchain__count_rows(&he->sorted_chain);
280 if (he->has_no_entry)
283 node = rb_first_cached(&he->hroot_out);
287 child = rb_entry(node, struct hist_entry, rb_node);
288 percent = hist_entry__get_percent_limit(child);
290 if (!child->filtered && percent >= hb->min_pcnt) {
293 if (include_children && child->unfolded)
294 count += hierarchy_count_rows(hb, child, true);
297 node = rb_next(node);
302 static bool hist_entry__toggle_fold(struct hist_entry *he)
307 if (!he->has_children)
310 he->unfolded = !he->unfolded;
314 static bool callchain_list__toggle_fold(struct callchain_list *cl)
319 if (!cl->has_children)
322 cl->unfolded = !cl->unfolded;
326 static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
328 struct rb_node *nd = rb_first(&node->rb_root);
330 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
331 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
332 struct callchain_list *chain;
335 list_for_each_entry(chain, &child->val, list) {
338 chain->has_children = chain->list.next != &child->val ||
339 !RB_EMPTY_ROOT(&child->rb_root);
341 chain->has_children = chain->list.next == &child->val &&
342 !RB_EMPTY_ROOT(&child->rb_root);
345 callchain_node__init_have_children_rb_tree(child);
349 static void callchain_node__init_have_children(struct callchain_node *node,
352 struct callchain_list *chain;
354 chain = list_entry(node->val.next, struct callchain_list, list);
355 chain->has_children = has_sibling;
357 if (!list_empty(&node->val)) {
358 chain = list_entry(node->val.prev, struct callchain_list, list);
359 chain->has_children = !RB_EMPTY_ROOT(&node->rb_root);
362 callchain_node__init_have_children_rb_tree(node);
365 static void callchain__init_have_children(struct rb_root *root)
367 struct rb_node *nd = rb_first(root);
368 bool has_sibling = nd && rb_next(nd);
370 for (nd = rb_first(root); nd; nd = rb_next(nd)) {
371 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
372 callchain_node__init_have_children(node, has_sibling);
373 if (callchain_param.mode == CHAIN_FLAT ||
374 callchain_param.mode == CHAIN_FOLDED)
375 callchain_node__make_parent_list(node);
379 static void hist_entry__init_have_children(struct hist_entry *he)
381 if (he->init_have_children)
385 he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
386 callchain__init_have_children(&he->sorted_chain);
388 he->has_children = !RB_EMPTY_ROOT(&he->hroot_out.rb_root);
391 he->init_have_children = true;
394 static bool hist_browser__toggle_fold(struct hist_browser *browser)
396 struct hist_entry *he = browser->he_selection;
397 struct map_symbol *ms = browser->selection;
398 struct callchain_list *cl = container_of(ms, struct callchain_list, ms);
405 has_children = hist_entry__toggle_fold(he);
407 has_children = callchain_list__toggle_fold(cl);
412 hist_entry__init_have_children(he);
413 browser->b.nr_entries -= he->nr_rows;
416 browser->nr_callchain_rows -= he->nr_rows;
418 browser->nr_hierarchy_entries -= he->nr_rows;
420 if (symbol_conf.report_hierarchy)
421 child_rows = hierarchy_count_rows(browser, he, true);
425 he->nr_rows = callchain__count_rows(
428 he->nr_rows = hierarchy_count_rows(browser, he, false);
430 /* account grand children */
431 if (symbol_conf.report_hierarchy)
432 browser->b.nr_entries += child_rows - he->nr_rows;
434 if (!he->leaf && he->nr_rows == 0) {
435 he->has_no_entry = true;
439 if (symbol_conf.report_hierarchy)
440 browser->b.nr_entries -= child_rows - he->nr_rows;
442 if (he->has_no_entry)
443 he->has_no_entry = false;
448 browser->b.nr_entries += he->nr_rows;
451 browser->nr_callchain_rows += he->nr_rows;
453 browser->nr_hierarchy_entries += he->nr_rows;
458 /* If it doesn't have children, no toggling performed */
462 static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
467 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
468 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
469 struct callchain_list *chain;
470 bool has_children = false;
472 list_for_each_entry(chain, &child->val, list) {
474 callchain_list__set_folding(chain, unfold);
475 has_children = chain->has_children;
479 n += callchain_node__set_folding_rb_tree(child, unfold);
485 static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
487 struct callchain_list *chain;
488 bool has_children = false;
491 list_for_each_entry(chain, &node->val, list) {
493 callchain_list__set_folding(chain, unfold);
494 has_children = chain->has_children;
498 n += callchain_node__set_folding_rb_tree(node, unfold);
503 static int callchain__set_folding(struct rb_root *chain, bool unfold)
508 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
509 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
510 n += callchain_node__set_folding(node, unfold);
516 static int hierarchy_set_folding(struct hist_browser *hb, struct hist_entry *he,
517 bool unfold __maybe_unused)
521 struct hist_entry *child;
524 for (nd = rb_first_cached(&he->hroot_out); nd; nd = rb_next(nd)) {
525 child = rb_entry(nd, struct hist_entry, rb_node);
526 percent = hist_entry__get_percent_limit(child);
527 if (!child->filtered && percent >= hb->min_pcnt)
534 static void __hist_entry__set_folding(struct hist_entry *he,
535 struct hist_browser *hb, bool unfold)
537 hist_entry__init_have_children(he);
538 he->unfolded = unfold ? he->has_children : false;
540 if (he->has_children) {
544 n = callchain__set_folding(&he->sorted_chain, unfold);
546 n = hierarchy_set_folding(hb, he, unfold);
548 he->nr_rows = unfold ? n : 0;
553 static void hist_entry__set_folding(struct hist_entry *he,
554 struct hist_browser *browser, bool unfold)
558 percent = hist_entry__get_percent_limit(he);
559 if (he->filtered || percent < browser->min_pcnt)
562 __hist_entry__set_folding(he, browser, unfold);
564 if (!he->depth || unfold)
565 browser->nr_hierarchy_entries++;
567 browser->nr_callchain_rows += he->nr_rows;
568 else if (unfold && !hist_entry__has_hierarchy_children(he, browser->min_pcnt)) {
569 browser->nr_hierarchy_entries++;
570 he->has_no_entry = true;
573 he->has_no_entry = false;
577 __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
580 struct hist_entry *he;
582 nd = rb_first_cached(&browser->hists->entries);
584 he = rb_entry(nd, struct hist_entry, rb_node);
586 /* set folding state even if it's currently folded */
587 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
589 hist_entry__set_folding(he, browser, unfold);
593 static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
595 browser->nr_hierarchy_entries = 0;
596 browser->nr_callchain_rows = 0;
597 __hist_browser__set_folding(browser, unfold);
599 browser->b.nr_entries = hist_browser__nr_entries(browser);
600 /* Go to the start, we may be way after valid entries after a collapse */
601 ui_browser__reset_index(&browser->b);
604 static void hist_browser__set_folding_selected(struct hist_browser *browser, bool unfold)
606 if (!browser->he_selection)
609 hist_entry__set_folding(browser->he_selection, browser, unfold);
610 browser->b.nr_entries = hist_browser__nr_entries(browser);
613 static void ui_browser__warn_lost_events(struct ui_browser *browser)
615 ui_browser__warning(browser, 4,
616 "Events are being lost, check IO/CPU overload!\n\n"
617 "You may want to run 'perf' using a RT scheduler policy:\n\n"
618 " perf top -r 80\n\n"
619 "Or reduce the sampling frequency.");
622 static int hist_browser__title(struct hist_browser *browser, char *bf, size_t size)
624 return browser->title ? browser->title(browser, bf, size) : 0;
627 int hist_browser__run(struct hist_browser *browser, const char *help,
628 bool warn_lost_event)
632 struct hist_browser_timer *hbt = browser->hbt;
633 int delay_secs = hbt ? hbt->refresh : 0;
635 browser->b.entries = &browser->hists->entries;
636 browser->b.nr_entries = hist_browser__nr_entries(browser);
638 hist_browser__title(browser, title, sizeof(title));
640 if (ui_browser__show(&browser->b, title, "%s", help) < 0)
644 key = ui_browser__run(&browser->b, delay_secs);
653 hbt->timer(hbt->arg);
655 if (hist_browser__has_filter(browser) ||
656 symbol_conf.report_hierarchy)
657 hist_browser__update_nr_entries(browser);
659 nr_entries = hist_browser__nr_entries(browser);
660 ui_browser__update_nr_entries(&browser->b, nr_entries);
662 if (warn_lost_event &&
663 (browser->hists->stats.nr_lost_warned !=
664 browser->hists->stats.nr_events[PERF_RECORD_LOST])) {
665 browser->hists->stats.nr_lost_warned =
666 browser->hists->stats.nr_events[PERF_RECORD_LOST];
667 ui_browser__warn_lost_events(&browser->b);
670 hist_browser__title(browser, title, sizeof(title));
671 ui_browser__show_title(&browser->b, title);
674 case 'D': { /* Debug */
676 struct hist_entry *h = rb_entry(browser->b.top,
677 struct hist_entry, rb_node);
679 ui_helpline__fpush("%d: nr_ent=(%d,%d), etl: %d, rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
680 seq++, browser->b.nr_entries,
681 browser->hists->nr_entries,
682 browser->b.extra_title_lines,
686 h->row_offset, h->nr_rows);
690 /* Collapse the whole world. */
691 hist_browser__set_folding(browser, false);
694 /* Collapse the selected entry. */
695 hist_browser__set_folding_selected(browser, false);
698 /* Expand the whole world. */
699 hist_browser__set_folding(browser, true);
702 /* Expand the selected entry. */
703 hist_browser__set_folding_selected(browser, true);
706 browser->show_headers = !browser->show_headers;
707 hist_browser__update_rows(browser);
710 if (hist_browser__toggle_fold(browser))
718 ui_browser__hide(&browser->b);
722 struct callchain_print_arg {
723 /* for hists browser */
725 bool is_current_entry;
732 typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
733 struct callchain_list *chain,
734 const char *str, int offset,
736 struct callchain_print_arg *arg);
738 static void hist_browser__show_callchain_entry(struct hist_browser *browser,
739 struct callchain_list *chain,
740 const char *str, int offset,
742 struct callchain_print_arg *arg)
745 char folded_sign = callchain_list__folded(chain);
746 bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
748 color = HE_COLORSET_NORMAL;
749 width = browser->b.width - (offset + 2);
750 if (ui_browser__is_current_entry(&browser->b, row)) {
751 browser->selection = &chain->ms;
752 color = HE_COLORSET_SELECTED;
753 arg->is_current_entry = true;
756 ui_browser__set_color(&browser->b, color);
757 ui_browser__gotorc(&browser->b, row, 0);
758 ui_browser__write_nstring(&browser->b, " ", offset);
759 ui_browser__printf(&browser->b, "%c", folded_sign);
760 ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
761 ui_browser__write_nstring(&browser->b, str, width);
764 static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
765 struct callchain_list *chain,
766 const char *str, int offset,
767 unsigned short row __maybe_unused,
768 struct callchain_print_arg *arg)
770 char folded_sign = callchain_list__folded(chain);
772 arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
776 typedef bool (*check_output_full_fn)(struct hist_browser *browser,
779 static bool hist_browser__check_output_full(struct hist_browser *browser,
782 return browser->b.rows == row;
785 static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
786 unsigned short row __maybe_unused)
791 #define LEVEL_OFFSET_STEP 3
793 static int hist_browser__show_callchain_list(struct hist_browser *browser,
794 struct callchain_node *node,
795 struct callchain_list *chain,
796 unsigned short row, u64 total,
797 bool need_percent, int offset,
798 print_callchain_entry_fn print,
799 struct callchain_print_arg *arg)
801 char bf[1024], *alloc_str;
802 char buf[64], *alloc_str2;
806 if (arg->row_offset != 0) {
814 str = callchain_list__sym_name(chain, bf, sizeof(bf),
817 if (symbol_conf.show_branchflag_count) {
818 callchain_list_counts__printf_value(chain, NULL,
821 if (asprintf(&alloc_str2, "%s%s", str, buf) < 0)
822 str = "Not enough memory!";
828 callchain_node__scnprintf_value(node, buf, sizeof(buf),
831 if (asprintf(&alloc_str, "%s %s", buf, str) < 0)
832 str = "Not enough memory!";
837 print(browser, chain, str, offset, row, arg);
844 static bool check_percent_display(struct rb_node *node, u64 parent_total)
846 struct callchain_node *child;
854 child = rb_entry(node, struct callchain_node, rb_node);
855 return callchain_cumul_hits(child) != parent_total;
858 static int hist_browser__show_callchain_flat(struct hist_browser *browser,
859 struct rb_root *root,
860 unsigned short row, u64 total,
862 print_callchain_entry_fn print,
863 struct callchain_print_arg *arg,
864 check_output_full_fn is_output_full)
866 struct rb_node *node;
867 int first_row = row, offset = LEVEL_OFFSET_STEP;
870 node = rb_first(root);
871 need_percent = check_percent_display(node, parent_total);
874 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
875 struct rb_node *next = rb_next(node);
876 struct callchain_list *chain;
877 char folded_sign = ' ';
879 int extra_offset = 0;
881 list_for_each_entry(chain, &child->parent_val, list) {
882 bool was_first = first;
886 else if (need_percent)
887 extra_offset = LEVEL_OFFSET_STEP;
889 folded_sign = callchain_list__folded(chain);
891 row += hist_browser__show_callchain_list(browser, child,
893 was_first && need_percent,
894 offset + extra_offset,
897 if (is_output_full(browser, row))
900 if (folded_sign == '+')
904 list_for_each_entry(chain, &child->val, list) {
905 bool was_first = first;
909 else if (need_percent)
910 extra_offset = LEVEL_OFFSET_STEP;
912 folded_sign = callchain_list__folded(chain);
914 row += hist_browser__show_callchain_list(browser, child,
916 was_first && need_percent,
917 offset + extra_offset,
920 if (is_output_full(browser, row))
923 if (folded_sign == '+')
928 if (is_output_full(browser, row))
933 return row - first_row;
936 static char *hist_browser__folded_callchain_str(struct hist_browser *browser,
937 struct callchain_list *chain,
938 char *value_str, char *old_str)
944 str = callchain_list__sym_name(chain, bf, sizeof(bf),
947 if (asprintf(&new, "%s%s%s", old_str,
948 symbol_conf.field_sep ?: ";", str) < 0)
952 if (asprintf(&new, "%s %s", value_str, str) < 0)
955 if (asprintf(&new, "%s", str) < 0)
962 static int hist_browser__show_callchain_folded(struct hist_browser *browser,
963 struct rb_root *root,
964 unsigned short row, u64 total,
966 print_callchain_entry_fn print,
967 struct callchain_print_arg *arg,
968 check_output_full_fn is_output_full)
970 struct rb_node *node;
971 int first_row = row, offset = LEVEL_OFFSET_STEP;
974 node = rb_first(root);
975 need_percent = check_percent_display(node, parent_total);
978 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
979 struct rb_node *next = rb_next(node);
980 struct callchain_list *chain, *first_chain = NULL;
982 char *value_str = NULL, *value_str_alloc = NULL;
983 char *chain_str = NULL, *chain_str_alloc = NULL;
985 if (arg->row_offset != 0) {
993 callchain_node__scnprintf_value(child, buf, sizeof(buf), total);
994 if (asprintf(&value_str, "%s", buf) < 0) {
995 value_str = (char *)"<...>";
998 value_str_alloc = value_str;
1001 list_for_each_entry(chain, &child->parent_val, list) {
1002 chain_str = hist_browser__folded_callchain_str(browser,
1003 chain, value_str, chain_str);
1006 first_chain = chain;
1009 if (chain_str == NULL) {
1010 chain_str = (char *)"Not enough memory!";
1014 chain_str_alloc = chain_str;
1017 list_for_each_entry(chain, &child->val, list) {
1018 chain_str = hist_browser__folded_callchain_str(browser,
1019 chain, value_str, chain_str);
1022 first_chain = chain;
1025 if (chain_str == NULL) {
1026 chain_str = (char *)"Not enough memory!";
1030 chain_str_alloc = chain_str;
1034 print(browser, first_chain, chain_str, offset, row++, arg);
1035 free(value_str_alloc);
1036 free(chain_str_alloc);
1039 if (is_output_full(browser, row))
1044 return row - first_row;
1047 static int hist_browser__show_callchain_graph(struct hist_browser *browser,
1048 struct rb_root *root, int level,
1049 unsigned short row, u64 total,
1051 print_callchain_entry_fn print,
1052 struct callchain_print_arg *arg,
1053 check_output_full_fn is_output_full)
1055 struct rb_node *node;
1056 int first_row = row, offset = level * LEVEL_OFFSET_STEP;
1058 u64 percent_total = total;
1060 if (callchain_param.mode == CHAIN_GRAPH_REL)
1061 percent_total = parent_total;
1063 node = rb_first(root);
1064 need_percent = check_percent_display(node, parent_total);
1067 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1068 struct rb_node *next = rb_next(node);
1069 struct callchain_list *chain;
1070 char folded_sign = ' ';
1072 int extra_offset = 0;
1074 list_for_each_entry(chain, &child->val, list) {
1075 bool was_first = first;
1079 else if (need_percent)
1080 extra_offset = LEVEL_OFFSET_STEP;
1082 folded_sign = callchain_list__folded(chain);
1084 row += hist_browser__show_callchain_list(browser, child,
1085 chain, row, percent_total,
1086 was_first && need_percent,
1087 offset + extra_offset,
1090 if (is_output_full(browser, row))
1093 if (folded_sign == '+')
1097 if (folded_sign == '-') {
1098 const int new_level = level + (extra_offset ? 2 : 1);
1100 row += hist_browser__show_callchain_graph(browser, &child->rb_root,
1101 new_level, row, total,
1102 child->children_hit,
1103 print, arg, is_output_full);
1105 if (is_output_full(browser, row))
1110 return row - first_row;
1113 static int hist_browser__show_callchain(struct hist_browser *browser,
1114 struct hist_entry *entry, int level,
1116 print_callchain_entry_fn print,
1117 struct callchain_print_arg *arg,
1118 check_output_full_fn is_output_full)
1120 u64 total = hists__total_period(entry->hists);
1124 if (symbol_conf.cumulate_callchain)
1125 parent_total = entry->stat_acc->period;
1127 parent_total = entry->stat.period;
1129 if (callchain_param.mode == CHAIN_FLAT) {
1130 printed = hist_browser__show_callchain_flat(browser,
1131 &entry->sorted_chain, row,
1132 total, parent_total, print, arg,
1134 } else if (callchain_param.mode == CHAIN_FOLDED) {
1135 printed = hist_browser__show_callchain_folded(browser,
1136 &entry->sorted_chain, row,
1137 total, parent_total, print, arg,
1140 printed = hist_browser__show_callchain_graph(browser,
1141 &entry->sorted_chain, level, row,
1142 total, parent_total, print, arg,
1146 if (arg->is_current_entry)
1147 browser->he_selection = entry;
1153 struct ui_browser *b;
1158 int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
1160 struct hpp_arg *arg = hpp->ptr;
1165 va_start(args, fmt);
1166 len = va_arg(args, int);
1167 percent = va_arg(args, double);
1170 ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
1172 ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
1173 ui_browser__printf(arg->b, "%s", hpp->buf);
1178 #define __HPP_COLOR_PERCENT_FN(_type, _field) \
1179 static u64 __hpp_get_##_field(struct hist_entry *he) \
1181 return he->stat._field; \
1185 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
1186 struct perf_hpp *hpp, \
1187 struct hist_entry *he) \
1189 return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%", \
1190 __hpp__slsmg_color_printf, true); \
1193 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
1194 static u64 __hpp_get_acc_##_field(struct hist_entry *he) \
1196 return he->stat_acc->_field; \
1200 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
1201 struct perf_hpp *hpp, \
1202 struct hist_entry *he) \
1204 if (!symbol_conf.cumulate_callchain) { \
1205 struct hpp_arg *arg = hpp->ptr; \
1206 int len = fmt->user_len ?: fmt->len; \
1207 int ret = scnprintf(hpp->buf, hpp->size, \
1208 "%*s", len, "N/A"); \
1209 ui_browser__printf(arg->b, "%s", hpp->buf); \
1213 return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field, \
1214 " %*.2f%%", __hpp__slsmg_color_printf, true); \
1217 __HPP_COLOR_PERCENT_FN(overhead, period)
1218 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
1219 __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
1220 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
1221 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
1222 __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
1224 #undef __HPP_COLOR_PERCENT_FN
1225 #undef __HPP_COLOR_ACC_PERCENT_FN
1227 void hist_browser__init_hpp(void)
1229 perf_hpp__format[PERF_HPP__OVERHEAD].color =
1230 hist_browser__hpp_color_overhead;
1231 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
1232 hist_browser__hpp_color_overhead_sys;
1233 perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
1234 hist_browser__hpp_color_overhead_us;
1235 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
1236 hist_browser__hpp_color_overhead_guest_sys;
1237 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
1238 hist_browser__hpp_color_overhead_guest_us;
1239 perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
1240 hist_browser__hpp_color_overhead_acc;
1245 static int hist_browser__show_entry(struct hist_browser *browser,
1246 struct hist_entry *entry,
1250 int width = browser->b.width;
1251 char folded_sign = ' ';
1252 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1253 bool use_callchain = hist_entry__has_callchains(entry) && symbol_conf.use_callchain;
1254 off_t row_offset = entry->row_offset;
1256 struct perf_hpp_fmt *fmt;
1258 if (current_entry) {
1259 browser->he_selection = entry;
1260 browser->selection = &entry->ms;
1263 if (use_callchain) {
1264 hist_entry__init_have_children(entry);
1265 folded_sign = hist_entry__folded(entry);
1268 if (row_offset == 0) {
1269 struct hpp_arg arg = {
1271 .folded_sign = folded_sign,
1272 .current_entry = current_entry,
1276 ui_browser__gotorc(&browser->b, row, 0);
1278 hists__for_each_format(browser->hists, fmt) {
1280 struct perf_hpp hpp = {
1286 if (perf_hpp__should_skip(fmt, entry->hists) ||
1287 column++ < browser->b.horiz_scroll)
1290 if (current_entry && browser->b.navkeypressed) {
1291 ui_browser__set_color(&browser->b,
1292 HE_COLORSET_SELECTED);
1294 ui_browser__set_color(&browser->b,
1295 HE_COLORSET_NORMAL);
1299 if (use_callchain) {
1300 ui_browser__printf(&browser->b, "%c ", folded_sign);
1305 ui_browser__printf(&browser->b, " ");
1310 int ret = fmt->color(fmt, &hpp, entry);
1311 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1313 * fmt->color() already used ui_browser to
1314 * print the non alignment bits, skip it (+ret):
1316 ui_browser__printf(&browser->b, "%s", s + ret);
1318 hist_entry__snprintf_alignment(entry, &hpp, fmt, fmt->entry(fmt, &hpp, entry));
1319 ui_browser__printf(&browser->b, "%s", s);
1321 width -= hpp.buf - s;
1324 /* The scroll bar isn't being used */
1325 if (!browser->b.navkeypressed)
1328 ui_browser__write_nstring(&browser->b, "", width);
1335 if (folded_sign == '-' && row != browser->b.rows) {
1336 struct callchain_print_arg arg = {
1337 .row_offset = row_offset,
1338 .is_current_entry = current_entry,
1341 printed += hist_browser__show_callchain(browser,
1343 hist_browser__show_callchain_entry,
1345 hist_browser__check_output_full);
1351 static int hist_browser__show_hierarchy_entry(struct hist_browser *browser,
1352 struct hist_entry *entry,
1357 int width = browser->b.width;
1358 char folded_sign = ' ';
1359 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1360 off_t row_offset = entry->row_offset;
1362 struct perf_hpp_fmt *fmt;
1363 struct perf_hpp_list_node *fmt_node;
1364 struct hpp_arg arg = {
1366 .current_entry = current_entry,
1369 int hierarchy_indent = (entry->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
1371 if (current_entry) {
1372 browser->he_selection = entry;
1373 browser->selection = &entry->ms;
1376 hist_entry__init_have_children(entry);
1377 folded_sign = hist_entry__folded(entry);
1378 arg.folded_sign = folded_sign;
1380 if (entry->leaf && row_offset) {
1382 goto show_callchain;
1385 ui_browser__gotorc(&browser->b, row, 0);
1387 if (current_entry && browser->b.navkeypressed)
1388 ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1390 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1392 ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1393 width -= level * HIERARCHY_INDENT;
1395 /* the first hpp_list_node is for overhead columns */
1396 fmt_node = list_first_entry(&entry->hists->hpp_formats,
1397 struct perf_hpp_list_node, list);
1398 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1400 struct perf_hpp hpp = {
1406 if (perf_hpp__should_skip(fmt, entry->hists) ||
1407 column++ < browser->b.horiz_scroll)
1410 if (current_entry && browser->b.navkeypressed) {
1411 ui_browser__set_color(&browser->b,
1412 HE_COLORSET_SELECTED);
1414 ui_browser__set_color(&browser->b,
1415 HE_COLORSET_NORMAL);
1419 ui_browser__printf(&browser->b, "%c ", folded_sign);
1423 ui_browser__printf(&browser->b, " ");
1428 int ret = fmt->color(fmt, &hpp, entry);
1429 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1431 * fmt->color() already used ui_browser to
1432 * print the non alignment bits, skip it (+ret):
1434 ui_browser__printf(&browser->b, "%s", s + ret);
1436 int ret = fmt->entry(fmt, &hpp, entry);
1437 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1438 ui_browser__printf(&browser->b, "%s", s);
1440 width -= hpp.buf - s;
1444 ui_browser__write_nstring(&browser->b, "", hierarchy_indent);
1445 width -= hierarchy_indent;
1448 if (column >= browser->b.horiz_scroll) {
1450 struct perf_hpp hpp = {
1456 if (current_entry && browser->b.navkeypressed) {
1457 ui_browser__set_color(&browser->b,
1458 HE_COLORSET_SELECTED);
1460 ui_browser__set_color(&browser->b,
1461 HE_COLORSET_NORMAL);
1464 perf_hpp_list__for_each_format(entry->hpp_list, fmt) {
1466 ui_browser__printf(&browser->b, "%c ", folded_sign);
1469 ui_browser__write_nstring(&browser->b, "", 2);
1475 * No need to call hist_entry__snprintf_alignment()
1476 * since this fmt is always the last column in the
1480 width -= fmt->color(fmt, &hpp, entry);
1484 width -= fmt->entry(fmt, &hpp, entry);
1485 ui_browser__printf(&browser->b, "%s", skip_spaces(s));
1487 while (isspace(s[i++]))
1493 /* The scroll bar isn't being used */
1494 if (!browser->b.navkeypressed)
1497 ui_browser__write_nstring(&browser->b, "", width);
1503 if (entry->leaf && folded_sign == '-' && row != browser->b.rows) {
1504 struct callchain_print_arg carg = {
1505 .row_offset = row_offset,
1508 printed += hist_browser__show_callchain(browser, entry,
1510 hist_browser__show_callchain_entry, &carg,
1511 hist_browser__check_output_full);
1517 static int hist_browser__show_no_entry(struct hist_browser *browser,
1518 unsigned short row, int level)
1520 int width = browser->b.width;
1521 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1525 struct perf_hpp_fmt *fmt;
1526 struct perf_hpp_list_node *fmt_node;
1527 int indent = browser->hists->nr_hpp_node - 2;
1529 if (current_entry) {
1530 browser->he_selection = NULL;
1531 browser->selection = NULL;
1534 ui_browser__gotorc(&browser->b, row, 0);
1536 if (current_entry && browser->b.navkeypressed)
1537 ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1539 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1541 ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1542 width -= level * HIERARCHY_INDENT;
1544 /* the first hpp_list_node is for overhead columns */
1545 fmt_node = list_first_entry(&browser->hists->hpp_formats,
1546 struct perf_hpp_list_node, list);
1547 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1548 if (perf_hpp__should_skip(fmt, browser->hists) ||
1549 column++ < browser->b.horiz_scroll)
1552 ret = fmt->width(fmt, NULL, browser->hists);
1555 /* for folded sign */
1559 /* space between columns */
1563 ui_browser__write_nstring(&browser->b, "", ret);
1567 ui_browser__write_nstring(&browser->b, "", indent * HIERARCHY_INDENT);
1568 width -= indent * HIERARCHY_INDENT;
1570 if (column >= browser->b.horiz_scroll) {
1573 ret = snprintf(buf, sizeof(buf), "no entry >= %.2f%%", browser->min_pcnt);
1574 ui_browser__printf(&browser->b, " %s", buf);
1578 /* The scroll bar isn't being used */
1579 if (!browser->b.navkeypressed)
1582 ui_browser__write_nstring(&browser->b, "", width);
1586 static int advance_hpp_check(struct perf_hpp *hpp, int inc)
1588 advance_hpp(hpp, inc);
1589 return hpp->size <= 0;
1593 hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf,
1594 size_t size, int line)
1596 struct hists *hists = browser->hists;
1597 struct perf_hpp dummy_hpp = {
1601 struct perf_hpp_fmt *fmt;
1606 if (hists__has_callchains(hists) && symbol_conf.use_callchain) {
1607 ret = scnprintf(buf, size, " ");
1608 if (advance_hpp_check(&dummy_hpp, ret))
1612 hists__for_each_format(browser->hists, fmt) {
1613 if (perf_hpp__should_skip(fmt, hists) || column++ < browser->b.horiz_scroll)
1616 ret = fmt->header(fmt, &dummy_hpp, hists, line, &span);
1617 if (advance_hpp_check(&dummy_hpp, ret))
1623 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
1624 if (advance_hpp_check(&dummy_hpp, ret))
1631 static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser, char *buf, size_t size)
1633 struct hists *hists = browser->hists;
1634 struct perf_hpp dummy_hpp = {
1638 struct perf_hpp_fmt *fmt;
1639 struct perf_hpp_list_node *fmt_node;
1642 int indent = hists->nr_hpp_node - 2;
1643 bool first_node, first_col;
1645 ret = scnprintf(buf, size, " ");
1646 if (advance_hpp_check(&dummy_hpp, ret))
1650 /* the first hpp_list_node is for overhead columns */
1651 fmt_node = list_first_entry(&hists->hpp_formats,
1652 struct perf_hpp_list_node, list);
1653 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1654 if (column++ < browser->b.horiz_scroll)
1657 ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1658 if (advance_hpp_check(&dummy_hpp, ret))
1661 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
1662 if (advance_hpp_check(&dummy_hpp, ret))
1669 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "%*s",
1670 indent * HIERARCHY_INDENT, "");
1671 if (advance_hpp_check(&dummy_hpp, ret))
1676 list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
1678 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " / ");
1679 if (advance_hpp_check(&dummy_hpp, ret))
1685 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1688 if (perf_hpp__should_skip(fmt, hists))
1692 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "+");
1693 if (advance_hpp_check(&dummy_hpp, ret))
1698 ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1699 dummy_hpp.buf[ret] = '\0';
1701 start = strim(dummy_hpp.buf);
1702 ret = strlen(start);
1704 if (start != dummy_hpp.buf)
1705 memmove(dummy_hpp.buf, start, ret + 1);
1707 if (advance_hpp_check(&dummy_hpp, ret))
1715 static void hists_browser__hierarchy_headers(struct hist_browser *browser)
1719 hists_browser__scnprintf_hierarchy_headers(browser, headers,
1722 ui_browser__gotorc(&browser->b, 0, 0);
1723 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1724 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1727 static void hists_browser__headers(struct hist_browser *browser)
1729 struct hists *hists = browser->hists;
1730 struct perf_hpp_list *hpp_list = hists->hpp_list;
1734 for (line = 0; line < hpp_list->nr_header_lines; line++) {
1737 hists_browser__scnprintf_headers(browser, headers,
1738 sizeof(headers), line);
1740 ui_browser__gotorc_title(&browser->b, line, 0);
1741 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1742 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1746 static void hist_browser__show_headers(struct hist_browser *browser)
1748 if (symbol_conf.report_hierarchy)
1749 hists_browser__hierarchy_headers(browser);
1751 hists_browser__headers(browser);
1754 static void ui_browser__hists_init_top(struct ui_browser *browser)
1756 if (browser->top == NULL) {
1757 struct hist_browser *hb;
1759 hb = container_of(browser, struct hist_browser, b);
1760 browser->top = rb_first_cached(&hb->hists->entries);
1764 static unsigned int hist_browser__refresh(struct ui_browser *browser)
1768 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
1770 if (hb->show_headers)
1771 hist_browser__show_headers(hb);
1773 ui_browser__hists_init_top(browser);
1774 hb->he_selection = NULL;
1775 hb->selection = NULL;
1777 for (nd = browser->top; nd; nd = rb_hierarchy_next(nd)) {
1778 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1782 /* let it move to sibling */
1783 h->unfolded = false;
1787 if (symbol_conf.report_individual_block)
1788 percent = block_info__total_cycles_percent(h);
1790 percent = hist_entry__get_percent_limit(h);
1792 if (percent < hb->min_pcnt)
1795 if (symbol_conf.report_hierarchy) {
1796 row += hist_browser__show_hierarchy_entry(hb, h, row,
1798 if (row == browser->rows)
1801 if (h->has_no_entry) {
1802 hist_browser__show_no_entry(hb, row, h->depth + 1);
1806 row += hist_browser__show_entry(hb, h, row);
1809 if (row == browser->rows)
1816 static struct rb_node *hists__filter_entries(struct rb_node *nd,
1819 while (nd != NULL) {
1820 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1821 float percent = hist_entry__get_percent_limit(h);
1823 if (!h->filtered && percent >= min_pcnt)
1827 * If it's filtered, its all children also were filtered.
1828 * So move to sibling node.
1833 nd = rb_hierarchy_next(nd);
1839 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
1842 while (nd != NULL) {
1843 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1844 float percent = hist_entry__get_percent_limit(h);
1846 if (!h->filtered && percent >= min_pcnt)
1849 nd = rb_hierarchy_prev(nd);
1855 static void ui_browser__hists_seek(struct ui_browser *browser,
1856 off_t offset, int whence)
1858 struct hist_entry *h;
1861 struct hist_browser *hb;
1863 hb = container_of(browser, struct hist_browser, b);
1865 if (browser->nr_entries == 0)
1868 ui_browser__hists_init_top(browser);
1872 nd = hists__filter_entries(rb_first(browser->entries),
1879 nd = rb_hierarchy_last(rb_last(browser->entries));
1880 nd = hists__filter_prev_entries(nd, hb->min_pcnt);
1888 * Moves not relative to the first visible entry invalidates its
1891 h = rb_entry(browser->top, struct hist_entry, rb_node);
1895 * Here we have to check if nd is expanded (+), if it is we can't go
1896 * the next top level hist_entry, instead we must compute an offset of
1897 * what _not_ to show and not change the first visible entry.
1899 * This offset increments when we are going from top to bottom and
1900 * decreases when we're going from bottom to top.
1902 * As we don't have backpointers to the top level in the callchains
1903 * structure, we need to always print the whole hist_entry callchain,
1904 * skipping the first ones that are before the first visible entry
1905 * and stop when we printed enough lines to fill the screen.
1913 h = rb_entry(nd, struct hist_entry, rb_node);
1914 if (h->unfolded && h->leaf) {
1915 u16 remaining = h->nr_rows - h->row_offset;
1916 if (offset > remaining) {
1917 offset -= remaining;
1920 h->row_offset += offset;
1926 nd = hists__filter_entries(rb_hierarchy_next(nd),
1932 } while (offset != 0);
1933 } else if (offset < 0) {
1935 h = rb_entry(nd, struct hist_entry, rb_node);
1936 if (h->unfolded && h->leaf) {
1938 if (-offset > h->row_offset) {
1939 offset += h->row_offset;
1942 h->row_offset += offset;
1948 if (-offset > h->nr_rows) {
1949 offset += h->nr_rows;
1952 h->row_offset = h->nr_rows + offset;
1960 nd = hists__filter_prev_entries(rb_hierarchy_prev(nd),
1968 * Last unfiltered hist_entry, check if it is
1969 * unfolded, if it is then we should have
1970 * row_offset at its last entry.
1972 h = rb_entry(nd, struct hist_entry, rb_node);
1973 if (h->unfolded && h->leaf)
1974 h->row_offset = h->nr_rows;
1981 h = rb_entry(nd, struct hist_entry, rb_node);
1986 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1987 struct hist_entry *he, FILE *fp,
1990 struct callchain_print_arg arg = {
1994 hist_browser__show_callchain(browser, he, level, 0,
1995 hist_browser__fprintf_callchain_entry, &arg,
1996 hist_browser__check_dump_full);
2000 static int hist_browser__fprintf_entry(struct hist_browser *browser,
2001 struct hist_entry *he, FILE *fp)
2005 char folded_sign = ' ';
2006 struct perf_hpp hpp = {
2010 struct perf_hpp_fmt *fmt;
2014 if (hist_entry__has_callchains(he) && symbol_conf.use_callchain) {
2015 folded_sign = hist_entry__folded(he);
2016 printed += fprintf(fp, "%c ", folded_sign);
2019 hists__for_each_format(browser->hists, fmt) {
2020 if (perf_hpp__should_skip(fmt, he->hists))
2024 ret = scnprintf(hpp.buf, hpp.size, " ");
2025 advance_hpp(&hpp, ret);
2029 ret = fmt->entry(fmt, &hpp, he);
2030 ret = hist_entry__snprintf_alignment(he, &hpp, fmt, ret);
2031 advance_hpp(&hpp, ret);
2033 printed += fprintf(fp, "%s\n", s);
2035 if (folded_sign == '-')
2036 printed += hist_browser__fprintf_callchain(browser, he, fp, 1);
2042 static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,
2043 struct hist_entry *he,
2044 FILE *fp, int level)
2048 char folded_sign = ' ';
2049 struct perf_hpp hpp = {
2053 struct perf_hpp_fmt *fmt;
2054 struct perf_hpp_list_node *fmt_node;
2057 int hierarchy_indent = (he->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
2059 printed = fprintf(fp, "%*s", level * HIERARCHY_INDENT, "");
2061 folded_sign = hist_entry__folded(he);
2062 printed += fprintf(fp, "%c", folded_sign);
2064 /* the first hpp_list_node is for overhead columns */
2065 fmt_node = list_first_entry(&he->hists->hpp_formats,
2066 struct perf_hpp_list_node, list);
2067 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
2069 ret = scnprintf(hpp.buf, hpp.size, " ");
2070 advance_hpp(&hpp, ret);
2074 ret = fmt->entry(fmt, &hpp, he);
2075 advance_hpp(&hpp, ret);
2078 ret = scnprintf(hpp.buf, hpp.size, "%*s", hierarchy_indent, "");
2079 advance_hpp(&hpp, ret);
2081 perf_hpp_list__for_each_format(he->hpp_list, fmt) {
2082 ret = scnprintf(hpp.buf, hpp.size, " ");
2083 advance_hpp(&hpp, ret);
2085 ret = fmt->entry(fmt, &hpp, he);
2086 advance_hpp(&hpp, ret);
2090 printed += fprintf(fp, "%s\n", s);
2092 if (he->leaf && folded_sign == '-') {
2093 printed += hist_browser__fprintf_callchain(browser, he, fp,
2100 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
2102 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
2107 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
2109 if (symbol_conf.report_hierarchy) {
2110 printed += hist_browser__fprintf_hierarchy_entry(browser,
2114 printed += hist_browser__fprintf_entry(browser, h, fp);
2117 nd = hists__filter_entries(rb_hierarchy_next(nd),
2124 static int hist_browser__dump(struct hist_browser *browser)
2130 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
2131 if (access(filename, F_OK))
2134 * XXX: Just an arbitrary lazy upper limit
2136 if (++browser->print_seq == 8192) {
2137 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
2142 fp = fopen(filename, "w");
2145 const char *err = str_error_r(errno, bf, sizeof(bf));
2146 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
2150 ++browser->print_seq;
2151 hist_browser__fprintf(browser, fp);
2153 ui_helpline__fpush("%s written!", filename);
2158 void hist_browser__init(struct hist_browser *browser,
2159 struct hists *hists)
2161 struct perf_hpp_fmt *fmt;
2163 browser->hists = hists;
2164 browser->b.refresh = hist_browser__refresh;
2165 browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
2166 browser->b.seek = ui_browser__hists_seek;
2167 browser->b.use_navkeypressed = true;
2168 browser->show_headers = symbol_conf.show_hist_headers;
2169 hist_browser__set_title_space(browser);
2171 if (symbol_conf.report_hierarchy) {
2172 struct perf_hpp_list_node *fmt_node;
2174 /* count overhead columns (in the first node) */
2175 fmt_node = list_first_entry(&hists->hpp_formats,
2176 struct perf_hpp_list_node, list);
2177 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt)
2178 ++browser->b.columns;
2180 /* add a single column for whole hierarchy sort keys*/
2181 ++browser->b.columns;
2183 hists__for_each_format(hists, fmt)
2184 ++browser->b.columns;
2187 hists__reset_column_width(hists);
2190 struct hist_browser *hist_browser__new(struct hists *hists)
2192 struct hist_browser *browser = zalloc(sizeof(*browser));
2195 hist_browser__init(browser, hists);
2200 static struct hist_browser *
2201 perf_evsel_browser__new(struct evsel *evsel,
2202 struct hist_browser_timer *hbt,
2203 struct perf_env *env,
2204 struct annotation_options *annotation_opts)
2206 struct hist_browser *browser = hist_browser__new(evsel__hists(evsel));
2211 browser->title = hists_browser__scnprintf_title;
2212 browser->annotation_opts = annotation_opts;
2217 void hist_browser__delete(struct hist_browser *browser)
2222 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
2224 return browser->he_selection;
2227 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
2229 return browser->he_selection->thread;
2232 /* Check whether the browser is for 'top' or 'report' */
2233 static inline bool is_report_browser(void *timer)
2235 return timer == NULL;
2238 static int hists_browser__scnprintf_title(struct hist_browser *browser, char *bf, size_t size)
2240 struct hist_browser_timer *hbt = browser->hbt;
2241 int printed = __hists__scnprintf_title(browser->hists, bf, size, !is_report_browser(hbt));
2243 if (!is_report_browser(hbt)) {
2244 struct perf_top *top = hbt->arg;
2246 printed += scnprintf(bf + printed, size - printed,
2247 " lost: %" PRIu64 "/%" PRIu64,
2248 top->lost, top->lost_total);
2250 printed += scnprintf(bf + printed, size - printed,
2251 " drop: %" PRIu64 "/%" PRIu64,
2252 top->drop, top->drop_total);
2255 printed += scnprintf(bf + printed, size - printed, " [z]");
2257 perf_top__reset_sample_counters(top);
2264 static inline void free_popup_options(char **options, int n)
2268 for (i = 0; i < n; ++i)
2273 * Only runtime switching of perf data file will make "input_name" point
2274 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
2275 * whether we need to call free() for current "input_name" during the switch.
2277 static bool is_input_name_malloced = false;
2279 static int switch_data_file(void)
2281 char *pwd, *options[32], *abs_path[32], *tmp;
2283 int nr_options = 0, choice = -1, ret = -1;
2284 struct dirent *dent;
2286 pwd = getenv("PWD");
2290 pwd_dir = opendir(pwd);
2294 memset(options, 0, sizeof(options));
2295 memset(abs_path, 0, sizeof(abs_path));
2297 while ((dent = readdir(pwd_dir))) {
2298 char path[PATH_MAX];
2300 char *name = dent->d_name;
2303 if (!(dent->d_type == DT_REG))
2306 snprintf(path, sizeof(path), "%s/%s", pwd, name);
2308 file = fopen(path, "r");
2312 if (fread(&magic, 1, 8, file) < 8)
2313 goto close_file_and_continue;
2315 if (is_perf_magic(magic)) {
2316 options[nr_options] = strdup(name);
2317 if (!options[nr_options])
2318 goto close_file_and_continue;
2320 abs_path[nr_options] = strdup(path);
2321 if (!abs_path[nr_options]) {
2322 zfree(&options[nr_options]);
2323 ui__warning("Can't search all data files due to memory shortage.\n");
2331 close_file_and_continue:
2333 if (nr_options >= 32) {
2334 ui__warning("Too many perf data files in PWD!\n"
2335 "Only the first 32 files will be listed.\n");
2342 choice = ui__popup_menu(nr_options, options);
2343 if (choice < nr_options && choice >= 0) {
2344 tmp = strdup(abs_path[choice]);
2346 if (is_input_name_malloced)
2347 free((void *)input_name);
2349 is_input_name_malloced = true;
2352 ui__warning("Data switch failed due to memory shortage!\n");
2356 free_popup_options(options, nr_options);
2357 free_popup_options(abs_path, nr_options);
2361 struct popup_action {
2363 struct thread *thread;
2364 struct map_symbol ms;
2366 struct evsel *evsel;
2369 int (*fn)(struct hist_browser *browser, struct popup_action *act);
2373 do_annotate(struct hist_browser *browser, struct popup_action *act)
2375 struct evsel *evsel;
2376 struct annotation *notes;
2377 struct hist_entry *he;
2380 if (!browser->annotation_opts->objdump_path &&
2381 perf_env__lookup_objdump(browser->env, &browser->annotation_opts->objdump_path))
2384 notes = symbol__annotation(act->ms.sym);
2388 evsel = hists_to_evsel(browser->hists);
2389 err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt,
2390 browser->annotation_opts);
2391 he = hist_browser__selected_entry(browser);
2393 * offer option to annotate the other branch source or target
2394 * (if they exists) when returning from annotate
2396 if ((err == 'q' || err == CTRL('c')) && he->branch_info)
2399 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
2401 ui_browser__handle_resize(&browser->b);
2406 add_annotate_opt(struct hist_browser *browser __maybe_unused,
2407 struct popup_action *act, char **optstr,
2408 struct map *map, struct symbol *sym)
2410 if (sym == NULL || map->dso->annotate_warned)
2413 if (asprintf(optstr, "Annotate %s", sym->name) < 0)
2418 act->fn = do_annotate;
2423 do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
2425 struct thread *thread = act->thread;
2427 if ((!hists__has(browser->hists, thread) &&
2428 !hists__has(browser->hists, comm)) || thread == NULL)
2431 if (browser->hists->thread_filter) {
2432 pstack__remove(browser->pstack, &browser->hists->thread_filter);
2433 perf_hpp__set_elide(HISTC_THREAD, false);
2434 thread__zput(browser->hists->thread_filter);
2437 if (hists__has(browser->hists, thread)) {
2438 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
2439 thread->comm_set ? thread__comm_str(thread) : "",
2442 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
2443 thread->comm_set ? thread__comm_str(thread) : "");
2446 browser->hists->thread_filter = thread__get(thread);
2447 perf_hpp__set_elide(HISTC_THREAD, false);
2448 pstack__push(browser->pstack, &browser->hists->thread_filter);
2451 hists__filter_by_thread(browser->hists);
2452 hist_browser__reset(browser);
2457 add_thread_opt(struct hist_browser *browser, struct popup_action *act,
2458 char **optstr, struct thread *thread)
2462 if ((!hists__has(browser->hists, thread) &&
2463 !hists__has(browser->hists, comm)) || thread == NULL)
2466 if (hists__has(browser->hists, thread)) {
2467 ret = asprintf(optstr, "Zoom %s %s(%d) thread",
2468 browser->hists->thread_filter ? "out of" : "into",
2469 thread->comm_set ? thread__comm_str(thread) : "",
2472 ret = asprintf(optstr, "Zoom %s %s thread",
2473 browser->hists->thread_filter ? "out of" : "into",
2474 thread->comm_set ? thread__comm_str(thread) : "");
2479 act->thread = thread;
2480 act->fn = do_zoom_thread;
2485 do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
2487 struct map *map = act->ms.map;
2489 if (!hists__has(browser->hists, dso) || map == NULL)
2492 if (browser->hists->dso_filter) {
2493 pstack__remove(browser->pstack, &browser->hists->dso_filter);
2494 perf_hpp__set_elide(HISTC_DSO, false);
2495 browser->hists->dso_filter = NULL;
2498 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
2499 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
2500 browser->hists->dso_filter = map->dso;
2501 perf_hpp__set_elide(HISTC_DSO, true);
2502 pstack__push(browser->pstack, &browser->hists->dso_filter);
2505 hists__filter_by_dso(browser->hists);
2506 hist_browser__reset(browser);
2511 add_dso_opt(struct hist_browser *browser, struct popup_action *act,
2512 char **optstr, struct map *map)
2514 if (!hists__has(browser->hists, dso) || map == NULL)
2517 if (asprintf(optstr, "Zoom %s %s DSO",
2518 browser->hists->dso_filter ? "out of" : "into",
2519 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
2523 act->fn = do_zoom_dso;
2528 do_browse_map(struct hist_browser *browser __maybe_unused,
2529 struct popup_action *act)
2531 map__browse(act->ms.map);
2536 add_map_opt(struct hist_browser *browser,
2537 struct popup_action *act, char **optstr, struct map *map)
2539 if (!hists__has(browser->hists, dso) || map == NULL)
2542 if (asprintf(optstr, "Browse map details") < 0)
2546 act->fn = do_browse_map;
2551 do_run_script(struct hist_browser *browser __maybe_unused,
2552 struct popup_action *act)
2560 len += strlen(thread__comm_str(act->thread));
2561 else if (act->ms.sym)
2562 len += strlen(act->ms.sym->name);
2563 script_opt = malloc(len);
2569 n = scnprintf(script_opt, len, " -c %s ",
2570 thread__comm_str(act->thread));
2571 } else if (act->ms.sym) {
2572 n = scnprintf(script_opt, len, " -S %s ",
2577 char start[32], end[32];
2578 unsigned long starttime = act->time;
2579 unsigned long endtime = act->time + symbol_conf.time_quantum;
2581 if (starttime == endtime) { /* Display 1ms as fallback */
2582 starttime -= 1*NSEC_PER_MSEC;
2583 endtime += 1*NSEC_PER_MSEC;
2585 timestamp__scnprintf_usec(starttime, start, sizeof start);
2586 timestamp__scnprintf_usec(endtime, end, sizeof end);
2587 n += snprintf(script_opt + n, len - n, " --time %s,%s", start, end);
2590 script_browse(script_opt, act->evsel);
2596 do_res_sample_script(struct hist_browser *browser __maybe_unused,
2597 struct popup_action *act)
2599 struct hist_entry *he;
2601 he = hist_browser__selected_entry(browser);
2602 res_sample_browse(he->res_samples, he->num_res, act->evsel, act->rstype);
2607 add_script_opt_2(struct hist_browser *browser __maybe_unused,
2608 struct popup_action *act, char **optstr,
2609 struct thread *thread, struct symbol *sym,
2610 struct evsel *evsel, const char *tstr)
2614 if (asprintf(optstr, "Run scripts for samples of thread [%s]%s",
2615 thread__comm_str(thread), tstr) < 0)
2618 if (asprintf(optstr, "Run scripts for samples of symbol [%s]%s",
2619 sym->name, tstr) < 0)
2622 if (asprintf(optstr, "Run scripts for all samples%s", tstr) < 0)
2626 act->thread = thread;
2629 act->fn = do_run_script;
2634 add_script_opt(struct hist_browser *browser,
2635 struct popup_action *act, char **optstr,
2636 struct thread *thread, struct symbol *sym,
2637 struct evsel *evsel)
2640 struct hist_entry *he;
2642 n = add_script_opt_2(browser, act, optstr, thread, sym, evsel, "");
2644 he = hist_browser__selected_entry(browser);
2645 if (sort_order && strstr(sort_order, "time")) {
2650 j = sprintf(tstr, " in ");
2651 j += timestamp__scnprintf_usec(he->time, tstr + j,
2653 j += sprintf(tstr + j, "-");
2654 timestamp__scnprintf_usec(he->time + symbol_conf.time_quantum,
2655 tstr + j, sizeof tstr - j);
2656 n += add_script_opt_2(browser, act, optstr, thread, sym,
2658 act->time = he->time;
2664 add_res_sample_opt(struct hist_browser *browser __maybe_unused,
2665 struct popup_action *act, char **optstr,
2666 struct res_sample *res_sample,
2667 struct evsel *evsel,
2673 if (asprintf(optstr, "Show context for individual samples %s",
2674 type == A_ASM ? "with assembler" :
2675 type == A_SOURCE ? "with source" : "") < 0)
2678 act->fn = do_res_sample_script;
2685 do_switch_data(struct hist_browser *browser __maybe_unused,
2686 struct popup_action *act __maybe_unused)
2688 if (switch_data_file()) {
2689 ui__warning("Won't switch the data files due to\n"
2690 "no valid data file get selected!\n");
2694 return K_SWITCH_INPUT_DATA;
2698 add_switch_opt(struct hist_browser *browser,
2699 struct popup_action *act, char **optstr)
2701 if (!is_report_browser(browser->hbt))
2704 if (asprintf(optstr, "Switch to another data file in PWD") < 0)
2707 act->fn = do_switch_data;
2712 do_exit_browser(struct hist_browser *browser __maybe_unused,
2713 struct popup_action *act __maybe_unused)
2719 add_exit_opt(struct hist_browser *browser __maybe_unused,
2720 struct popup_action *act, char **optstr)
2722 if (asprintf(optstr, "Exit") < 0)
2725 act->fn = do_exit_browser;
2730 do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
2732 if (!hists__has(browser->hists, socket) || act->socket < 0)
2735 if (browser->hists->socket_filter > -1) {
2736 pstack__remove(browser->pstack, &browser->hists->socket_filter);
2737 browser->hists->socket_filter = -1;
2738 perf_hpp__set_elide(HISTC_SOCKET, false);
2740 browser->hists->socket_filter = act->socket;
2741 perf_hpp__set_elide(HISTC_SOCKET, true);
2742 pstack__push(browser->pstack, &browser->hists->socket_filter);
2745 hists__filter_by_socket(browser->hists);
2746 hist_browser__reset(browser);
2751 add_socket_opt(struct hist_browser *browser, struct popup_action *act,
2752 char **optstr, int socket_id)
2754 if (!hists__has(browser->hists, socket) || socket_id < 0)
2757 if (asprintf(optstr, "Zoom %s Processor Socket %d",
2758 (browser->hists->socket_filter > -1) ? "out of" : "into",
2762 act->socket = socket_id;
2763 act->fn = do_zoom_socket;
2767 static void hist_browser__update_nr_entries(struct hist_browser *hb)
2770 struct rb_node *nd = rb_first_cached(&hb->hists->entries);
2772 if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
2773 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
2777 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2779 nd = rb_hierarchy_next(nd);
2782 hb->nr_non_filtered_entries = nr_entries;
2783 hb->nr_hierarchy_entries = nr_entries;
2786 static void hist_browser__update_percent_limit(struct hist_browser *hb,
2789 struct hist_entry *he;
2790 struct rb_node *nd = rb_first_cached(&hb->hists->entries);
2791 u64 total = hists__total_period(hb->hists);
2792 u64 min_callchain_hits = total * (percent / 100);
2794 hb->min_pcnt = callchain_param.min_percent = percent;
2796 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2797 he = rb_entry(nd, struct hist_entry, rb_node);
2799 if (he->has_no_entry) {
2800 he->has_no_entry = false;
2804 if (!he->leaf || !hist_entry__has_callchains(he) || !symbol_conf.use_callchain)
2807 if (callchain_param.mode == CHAIN_GRAPH_REL) {
2808 total = he->stat.period;
2810 if (symbol_conf.cumulate_callchain)
2811 total = he->stat_acc->period;
2813 min_callchain_hits = total * (percent / 100);
2816 callchain_param.sort(&he->sorted_chain, he->callchain,
2817 min_callchain_hits, &callchain_param);
2820 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
2822 /* force to re-evaluate folding state of callchains */
2823 he->init_have_children = false;
2824 hist_entry__set_folding(he, hb, false);
2828 static int perf_evsel__hists_browse(struct evsel *evsel, int nr_events,
2829 const char *helpline,
2831 struct hist_browser_timer *hbt,
2833 struct perf_env *env,
2834 bool warn_lost_event,
2835 struct annotation_options *annotation_opts)
2837 struct hists *hists = evsel__hists(evsel);
2838 struct hist_browser *browser = perf_evsel_browser__new(evsel, hbt, env, annotation_opts);
2839 struct branch_info *bi = NULL;
2840 #define MAX_OPTIONS 16
2841 char *options[MAX_OPTIONS];
2842 struct popup_action actions[MAX_OPTIONS];
2846 int delay_secs = hbt ? hbt->refresh : 0;
2848 #define HIST_BROWSER_HELP_COMMON \
2849 "h/?/F1 Show this window\n" \
2851 "PGDN/SPACE Navigate\n" \
2852 "q/ESC/CTRL+C Exit browser or go back to previous screen\n\n" \
2853 "For multiple event sessions:\n\n" \
2854 "TAB/UNTAB Switch events\n\n" \
2855 "For symbolic views (--sort has sym):\n\n" \
2856 "ENTER Zoom into DSO/Threads & Annotate current symbol\n" \
2858 "a Annotate current symbol\n" \
2859 "C Collapse all callchains\n" \
2860 "d Zoom into current DSO\n" \
2861 "E Expand all callchains\n" \
2862 "F Toggle percentage of filtered entries\n" \
2863 "H Display column headers\n" \
2864 "L Change percent limit\n" \
2865 "m Display context menu\n" \
2866 "S Zoom into current Processor Socket\n" \
2868 /* help messages are sorted by lexical order of the hotkey */
2869 static const char report_help[] = HIST_BROWSER_HELP_COMMON
2870 "i Show header information\n"
2871 "P Print histograms to perf.hist.N\n"
2872 "r Run available scripts\n"
2873 "s Switch to another data file in PWD\n"
2874 "t Zoom into current Thread\n"
2875 "V Verbose (DSO names in callchains, etc)\n"
2876 "/ Filter symbol by name";
2877 static const char top_help[] = HIST_BROWSER_HELP_COMMON
2878 "P Print histograms to perf.hist.N\n"
2879 "t Zoom into current Thread\n"
2880 "V Verbose (DSO names in callchains, etc)\n"
2881 "z Toggle zeroing of samples\n"
2882 "f Enable/Disable events\n"
2883 "/ Filter symbol by name";
2885 if (browser == NULL)
2888 /* reset abort key so that it can get Ctrl-C as a key */
2890 SLang_init_tty(0, 0, 0);
2893 browser->min_pcnt = min_pcnt;
2894 hist_browser__update_nr_entries(browser);
2896 browser->pstack = pstack__new(3);
2897 if (browser->pstack == NULL)
2900 ui_helpline__push(helpline);
2902 memset(options, 0, sizeof(options));
2903 memset(actions, 0, sizeof(actions));
2905 if (symbol_conf.col_width_list_str)
2906 perf_hpp__set_user_width(symbol_conf.col_width_list_str);
2908 if (!is_report_browser(hbt))
2909 browser->b.no_samples_msg = "Collecting samples...";
2912 struct thread *thread = NULL;
2913 struct map *map = NULL;
2919 key = hist_browser__run(browser, helpline,
2922 if (browser->he_selection != NULL) {
2923 thread = hist_browser__selected_thread(browser);
2924 map = browser->selection->map;
2925 socked_id = browser->he_selection->socket;
2933 * Exit the browser, let hists__browser_tree
2934 * go to the next or previous
2936 goto out_free_stack;
2938 if (!hists__has(hists, sym)) {
2939 ui_browser__warning(&browser->b, delay_secs * 2,
2940 "Annotation is only available for symbolic views, "
2941 "include \"sym*\" in --sort to use it.");
2945 if (browser->selection == NULL ||
2946 browser->selection->sym == NULL ||
2947 browser->selection->map->dso->annotate_warned)
2950 actions->ms.map = browser->selection->map;
2951 actions->ms.sym = browser->selection->sym;
2952 do_annotate(browser, actions);
2955 hist_browser__dump(browser);
2958 actions->ms.map = map;
2959 do_zoom_dso(browser, actions);
2962 verbose = (verbose + 1) % 4;
2963 browser->show_dso = verbose > 0;
2964 ui_helpline__fpush("Verbosity level set to %d\n",
2968 actions->thread = thread;
2969 do_zoom_thread(browser, actions);
2972 actions->socket = socked_id;
2973 do_zoom_socket(browser, actions);
2976 if (ui_browser__input_window("Symbol to show",
2977 "Please enter the name of symbol you want to see.\n"
2978 "To remove the filter later, press / + ENTER.",
2979 buf, "ENTER: OK, ESC: Cancel",
2980 delay_secs * 2) == K_ENTER) {
2981 hists->symbol_filter_str = *buf ? buf : NULL;
2982 hists__filter_by_symbol(hists);
2983 hist_browser__reset(browser);
2987 if (is_report_browser(hbt)) {
2988 actions->thread = NULL;
2989 actions->ms.sym = NULL;
2990 do_run_script(browser, actions);
2994 if (is_report_browser(hbt)) {
2995 key = do_switch_data(browser, actions);
2996 if (key == K_SWITCH_INPUT_DATA)
2997 goto out_free_stack;
3001 /* env->arch is NULL for live-mode (i.e. perf top) */
3003 tui__header_window(env);
3006 symbol_conf.filter_relative ^= 1;
3009 if (!is_report_browser(hbt)) {
3010 struct perf_top *top = hbt->arg;
3012 top->zero = !top->zero;
3016 if (ui_browser__input_window("Percent Limit",
3017 "Please enter the value you want to hide entries under that percent.",
3018 buf, "ENTER: OK, ESC: Cancel",
3019 delay_secs * 2) == K_ENTER) {
3021 double new_percent = strtod(buf, &end);
3023 if (new_percent < 0 || new_percent > 100) {
3024 ui_browser__warning(&browser->b, delay_secs * 2,
3025 "Invalid percent: %.2f", new_percent);
3029 hist_browser__update_percent_limit(browser, new_percent);
3030 hist_browser__reset(browser);
3036 ui_browser__help_window(&browser->b,
3037 is_report_browser(hbt) ? report_help : top_help);
3048 if (pstack__empty(browser->pstack)) {
3050 * Go back to the perf_evsel_menu__run or other user
3053 goto out_free_stack;
3056 ui_browser__dialog_yesno(&browser->b,
3057 "Do you really want to exit?"))
3058 goto out_free_stack;
3062 top = pstack__peek(browser->pstack);
3063 if (top == &browser->hists->dso_filter) {
3065 * No need to set actions->dso here since
3066 * it's just to remove the current filter.
3067 * Ditto for thread below.
3069 do_zoom_dso(browser, actions);
3070 } else if (top == &browser->hists->thread_filter) {
3071 do_zoom_thread(browser, actions);
3072 } else if (top == &browser->hists->socket_filter) {
3073 do_zoom_socket(browser, actions);
3079 goto out_free_stack;
3081 if (!is_report_browser(hbt)) {
3082 struct perf_top *top = hbt->arg;
3084 perf_evlist__toggle_enable(top->evlist);
3086 * No need to refresh, resort/decay histogram
3087 * entries if we are not collecting samples:
3089 if (top->evlist->enabled) {
3090 helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
3091 hbt->refresh = delay_secs;
3093 helpline = "Press 'f' again to re-enable the events";
3100 helpline = "Press '?' for help on key bindings";
3104 if (!hists__has(hists, sym) || browser->selection == NULL)
3105 goto skip_annotation;
3107 if (sort__mode == SORT_MODE__BRANCH) {
3109 if (browser->he_selection)
3110 bi = browser->he_selection->branch_info;
3113 goto skip_annotation;
3115 nr_options += add_annotate_opt(browser,
3116 &actions[nr_options],
3117 &options[nr_options],
3120 if (bi->to.sym != bi->from.sym)
3121 nr_options += add_annotate_opt(browser,
3122 &actions[nr_options],
3123 &options[nr_options],
3127 nr_options += add_annotate_opt(browser,
3128 &actions[nr_options],
3129 &options[nr_options],
3130 browser->selection->map,
3131 browser->selection->sym);
3134 nr_options += add_thread_opt(browser, &actions[nr_options],
3135 &options[nr_options], thread);
3136 nr_options += add_dso_opt(browser, &actions[nr_options],
3137 &options[nr_options], map);
3138 nr_options += add_map_opt(browser, &actions[nr_options],
3139 &options[nr_options],
3140 browser->selection ?
3141 browser->selection->map : NULL);
3142 nr_options += add_socket_opt(browser, &actions[nr_options],
3143 &options[nr_options],
3145 /* perf script support */
3146 if (!is_report_browser(hbt))
3147 goto skip_scripting;
3149 if (browser->he_selection) {
3150 if (hists__has(hists, thread) && thread) {
3151 nr_options += add_script_opt(browser,
3152 &actions[nr_options],
3153 &options[nr_options],
3154 thread, NULL, evsel);
3157 * Note that browser->selection != NULL
3158 * when browser->he_selection is not NULL,
3159 * so we don't need to check browser->selection
3160 * before fetching browser->selection->sym like what
3161 * we do before fetching browser->selection->map.
3163 * See hist_browser__show_entry.
3165 if (hists__has(hists, sym) && browser->selection->sym) {
3166 nr_options += add_script_opt(browser,
3167 &actions[nr_options],
3168 &options[nr_options],
3169 NULL, browser->selection->sym,
3173 nr_options += add_script_opt(browser, &actions[nr_options],
3174 &options[nr_options], NULL, NULL, evsel);
3175 nr_options += add_res_sample_opt(browser, &actions[nr_options],
3176 &options[nr_options],
3177 hist_browser__selected_entry(browser)->res_samples,
3179 nr_options += add_res_sample_opt(browser, &actions[nr_options],
3180 &options[nr_options],
3181 hist_browser__selected_entry(browser)->res_samples,
3183 nr_options += add_res_sample_opt(browser, &actions[nr_options],
3184 &options[nr_options],
3185 hist_browser__selected_entry(browser)->res_samples,
3187 nr_options += add_switch_opt(browser, &actions[nr_options],
3188 &options[nr_options]);
3190 nr_options += add_exit_opt(browser, &actions[nr_options],
3191 &options[nr_options]);
3194 struct popup_action *act;
3196 choice = ui__popup_menu(nr_options, options);
3197 if (choice == -1 || choice >= nr_options)
3200 act = &actions[choice];
3201 key = act->fn(browser, act);
3204 if (key == K_SWITCH_INPUT_DATA)
3208 pstack__delete(browser->pstack);
3210 hist_browser__delete(browser);
3211 free_popup_options(options, MAX_OPTIONS);
3216 struct ui_browser b;
3217 struct evsel *selection;
3218 struct annotation_options *annotation_opts;
3219 bool lost_events, lost_events_warned;
3221 struct perf_env *env;
3224 static void perf_evsel_menu__write(struct ui_browser *browser,
3225 void *entry, int row)
3227 struct evsel_menu *menu = container_of(browser,
3228 struct evsel_menu, b);
3229 struct evsel *evsel = list_entry(entry, struct evsel, core.node);
3230 struct hists *hists = evsel__hists(evsel);
3231 bool current_entry = ui_browser__is_current_entry(browser, row);
3232 unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
3233 const char *ev_name = perf_evsel__name(evsel);
3235 const char *warn = " ";
3238 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
3239 HE_COLORSET_NORMAL);
3241 if (perf_evsel__is_group_event(evsel)) {
3244 ev_name = perf_evsel__group_name(evsel);
3246 for_each_group_member(pos, evsel) {
3247 struct hists *pos_hists = evsel__hists(pos);
3248 nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
3252 nr_events = convert_unit(nr_events, &unit);
3253 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
3254 unit, unit == ' ' ? "" : " ", ev_name);
3255 ui_browser__printf(browser, "%s", bf);
3257 nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
3258 if (nr_events != 0) {
3259 menu->lost_events = true;
3261 ui_browser__set_color(browser, HE_COLORSET_TOP);
3262 nr_events = convert_unit(nr_events, &unit);
3263 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
3264 nr_events, unit, unit == ' ' ? "" : " ");
3268 ui_browser__write_nstring(browser, warn, browser->width - printed);
3271 menu->selection = evsel;
3274 static int perf_evsel_menu__run(struct evsel_menu *menu,
3275 int nr_events, const char *help,
3276 struct hist_browser_timer *hbt,
3277 bool warn_lost_event)
3279 struct evlist *evlist = menu->b.priv;
3281 const char *title = "Available samples";
3282 int delay_secs = hbt ? hbt->refresh : 0;
3285 if (ui_browser__show(&menu->b, title,
3286 "ESC: exit, ENTER|->: Browse histograms") < 0)
3290 key = ui_browser__run(&menu->b, delay_secs);
3295 hbt->timer(hbt->arg);
3297 if (!menu->lost_events_warned &&
3298 menu->lost_events &&
3300 ui_browser__warn_lost_events(&menu->b);
3301 menu->lost_events_warned = true;
3306 if (!menu->selection)
3308 pos = menu->selection;
3310 perf_evlist__set_selected(evlist, pos);
3312 * Give the calling tool a chance to populate the non
3313 * default evsel resorted hists tree.
3316 hbt->timer(hbt->arg);
3317 key = perf_evsel__hists_browse(pos, nr_events, help,
3322 menu->annotation_opts);
3323 ui_browser__show_title(&menu->b, title);
3326 if (pos->core.node.next == &evlist->core.entries)
3327 pos = evlist__first(evlist);
3329 pos = perf_evsel__next(pos);
3332 if (pos->core.node.prev == &evlist->core.entries)
3333 pos = evlist__last(evlist);
3335 pos = perf_evsel__prev(pos);
3337 case K_SWITCH_INPUT_DATA:
3348 if (!ui_browser__dialog_yesno(&menu->b,
3349 "Do you really want to exit?"))
3361 ui_browser__hide(&menu->b);
3365 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
3368 struct evsel *evsel = list_entry(entry, struct evsel, core.node);
3370 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
3376 static int __perf_evlist__tui_browse_hists(struct evlist *evlist,
3377 int nr_entries, const char *help,
3378 struct hist_browser_timer *hbt,
3380 struct perf_env *env,
3381 bool warn_lost_event,
3382 struct annotation_options *annotation_opts)
3385 struct evsel_menu menu = {
3387 .entries = &evlist->core.entries,
3388 .refresh = ui_browser__list_head_refresh,
3389 .seek = ui_browser__list_head_seek,
3390 .write = perf_evsel_menu__write,
3391 .filter = filter_group_entries,
3392 .nr_entries = nr_entries,
3395 .min_pcnt = min_pcnt,
3397 .annotation_opts = annotation_opts,
3400 ui_helpline__push("Press ESC to exit");
3402 evlist__for_each_entry(evlist, pos) {
3403 const char *ev_name = perf_evsel__name(pos);
3404 size_t line_len = strlen(ev_name) + 7;
3406 if (menu.b.width < line_len)
3407 menu.b.width = line_len;
3410 return perf_evsel_menu__run(&menu, nr_entries, help,
3411 hbt, warn_lost_event);
3414 int perf_evlist__tui_browse_hists(struct evlist *evlist, const char *help,
3415 struct hist_browser_timer *hbt,
3417 struct perf_env *env,
3418 bool warn_lost_event,
3419 struct annotation_options *annotation_opts)
3421 int nr_entries = evlist->core.nr_entries;
3424 if (nr_entries == 1) {
3425 struct evsel *first = evlist__first(evlist);
3427 return perf_evsel__hists_browse(first, nr_entries, help,
3428 false, hbt, min_pcnt,
3429 env, warn_lost_event,
3433 if (symbol_conf.event_group) {
3437 evlist__for_each_entry(evlist, pos) {
3438 if (perf_evsel__is_group_leader(pos))
3442 if (nr_entries == 1)
3446 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,