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_symbol *ms)
2410 if (ms->sym == NULL || ms->map->dso->annotate_warned)
2413 if (asprintf(optstr, "Annotate %s", ms->sym->name) < 0)
2417 act->fn = do_annotate;
2422 do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
2424 struct thread *thread = act->thread;
2426 if ((!hists__has(browser->hists, thread) &&
2427 !hists__has(browser->hists, comm)) || thread == NULL)
2430 if (browser->hists->thread_filter) {
2431 pstack__remove(browser->pstack, &browser->hists->thread_filter);
2432 perf_hpp__set_elide(HISTC_THREAD, false);
2433 thread__zput(browser->hists->thread_filter);
2436 if (hists__has(browser->hists, thread)) {
2437 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
2438 thread->comm_set ? thread__comm_str(thread) : "",
2441 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
2442 thread->comm_set ? thread__comm_str(thread) : "");
2445 browser->hists->thread_filter = thread__get(thread);
2446 perf_hpp__set_elide(HISTC_THREAD, false);
2447 pstack__push(browser->pstack, &browser->hists->thread_filter);
2450 hists__filter_by_thread(browser->hists);
2451 hist_browser__reset(browser);
2456 add_thread_opt(struct hist_browser *browser, struct popup_action *act,
2457 char **optstr, struct thread *thread)
2461 if ((!hists__has(browser->hists, thread) &&
2462 !hists__has(browser->hists, comm)) || thread == NULL)
2465 if (hists__has(browser->hists, thread)) {
2466 ret = asprintf(optstr, "Zoom %s %s(%d) thread",
2467 browser->hists->thread_filter ? "out of" : "into",
2468 thread->comm_set ? thread__comm_str(thread) : "",
2471 ret = asprintf(optstr, "Zoom %s %s thread",
2472 browser->hists->thread_filter ? "out of" : "into",
2473 thread->comm_set ? thread__comm_str(thread) : "");
2478 act->thread = thread;
2479 act->fn = do_zoom_thread;
2484 do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
2486 struct map *map = act->ms.map;
2488 if (!hists__has(browser->hists, dso) || map == NULL)
2491 if (browser->hists->dso_filter) {
2492 pstack__remove(browser->pstack, &browser->hists->dso_filter);
2493 perf_hpp__set_elide(HISTC_DSO, false);
2494 browser->hists->dso_filter = NULL;
2497 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
2498 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
2499 browser->hists->dso_filter = map->dso;
2500 perf_hpp__set_elide(HISTC_DSO, true);
2501 pstack__push(browser->pstack, &browser->hists->dso_filter);
2504 hists__filter_by_dso(browser->hists);
2505 hist_browser__reset(browser);
2510 add_dso_opt(struct hist_browser *browser, struct popup_action *act,
2511 char **optstr, struct map *map)
2513 if (!hists__has(browser->hists, dso) || map == NULL)
2516 if (asprintf(optstr, "Zoom %s %s DSO",
2517 browser->hists->dso_filter ? "out of" : "into",
2518 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
2522 act->fn = do_zoom_dso;
2527 do_browse_map(struct hist_browser *browser __maybe_unused,
2528 struct popup_action *act)
2530 map__browse(act->ms.map);
2535 add_map_opt(struct hist_browser *browser,
2536 struct popup_action *act, char **optstr, struct map *map)
2538 if (!hists__has(browser->hists, dso) || map == NULL)
2541 if (asprintf(optstr, "Browse map details") < 0)
2545 act->fn = do_browse_map;
2550 do_run_script(struct hist_browser *browser __maybe_unused,
2551 struct popup_action *act)
2559 len += strlen(thread__comm_str(act->thread));
2560 else if (act->ms.sym)
2561 len += strlen(act->ms.sym->name);
2562 script_opt = malloc(len);
2568 n = scnprintf(script_opt, len, " -c %s ",
2569 thread__comm_str(act->thread));
2570 } else if (act->ms.sym) {
2571 n = scnprintf(script_opt, len, " -S %s ",
2576 char start[32], end[32];
2577 unsigned long starttime = act->time;
2578 unsigned long endtime = act->time + symbol_conf.time_quantum;
2580 if (starttime == endtime) { /* Display 1ms as fallback */
2581 starttime -= 1*NSEC_PER_MSEC;
2582 endtime += 1*NSEC_PER_MSEC;
2584 timestamp__scnprintf_usec(starttime, start, sizeof start);
2585 timestamp__scnprintf_usec(endtime, end, sizeof end);
2586 n += snprintf(script_opt + n, len - n, " --time %s,%s", start, end);
2589 script_browse(script_opt, act->evsel);
2595 do_res_sample_script(struct hist_browser *browser __maybe_unused,
2596 struct popup_action *act)
2598 struct hist_entry *he;
2600 he = hist_browser__selected_entry(browser);
2601 res_sample_browse(he->res_samples, he->num_res, act->evsel, act->rstype);
2606 add_script_opt_2(struct hist_browser *browser __maybe_unused,
2607 struct popup_action *act, char **optstr,
2608 struct thread *thread, struct symbol *sym,
2609 struct evsel *evsel, const char *tstr)
2613 if (asprintf(optstr, "Run scripts for samples of thread [%s]%s",
2614 thread__comm_str(thread), tstr) < 0)
2617 if (asprintf(optstr, "Run scripts for samples of symbol [%s]%s",
2618 sym->name, tstr) < 0)
2621 if (asprintf(optstr, "Run scripts for all samples%s", tstr) < 0)
2625 act->thread = thread;
2628 act->fn = do_run_script;
2633 add_script_opt(struct hist_browser *browser,
2634 struct popup_action *act, char **optstr,
2635 struct thread *thread, struct symbol *sym,
2636 struct evsel *evsel)
2639 struct hist_entry *he;
2641 n = add_script_opt_2(browser, act, optstr, thread, sym, evsel, "");
2643 he = hist_browser__selected_entry(browser);
2644 if (sort_order && strstr(sort_order, "time")) {
2649 j = sprintf(tstr, " in ");
2650 j += timestamp__scnprintf_usec(he->time, tstr + j,
2652 j += sprintf(tstr + j, "-");
2653 timestamp__scnprintf_usec(he->time + symbol_conf.time_quantum,
2654 tstr + j, sizeof tstr - j);
2655 n += add_script_opt_2(browser, act, optstr, thread, sym,
2657 act->time = he->time;
2663 add_res_sample_opt(struct hist_browser *browser __maybe_unused,
2664 struct popup_action *act, char **optstr,
2665 struct res_sample *res_sample,
2666 struct evsel *evsel,
2672 if (asprintf(optstr, "Show context for individual samples %s",
2673 type == A_ASM ? "with assembler" :
2674 type == A_SOURCE ? "with source" : "") < 0)
2677 act->fn = do_res_sample_script;
2684 do_switch_data(struct hist_browser *browser __maybe_unused,
2685 struct popup_action *act __maybe_unused)
2687 if (switch_data_file()) {
2688 ui__warning("Won't switch the data files due to\n"
2689 "no valid data file get selected!\n");
2693 return K_SWITCH_INPUT_DATA;
2697 add_switch_opt(struct hist_browser *browser,
2698 struct popup_action *act, char **optstr)
2700 if (!is_report_browser(browser->hbt))
2703 if (asprintf(optstr, "Switch to another data file in PWD") < 0)
2706 act->fn = do_switch_data;
2711 do_exit_browser(struct hist_browser *browser __maybe_unused,
2712 struct popup_action *act __maybe_unused)
2718 add_exit_opt(struct hist_browser *browser __maybe_unused,
2719 struct popup_action *act, char **optstr)
2721 if (asprintf(optstr, "Exit") < 0)
2724 act->fn = do_exit_browser;
2729 do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
2731 if (!hists__has(browser->hists, socket) || act->socket < 0)
2734 if (browser->hists->socket_filter > -1) {
2735 pstack__remove(browser->pstack, &browser->hists->socket_filter);
2736 browser->hists->socket_filter = -1;
2737 perf_hpp__set_elide(HISTC_SOCKET, false);
2739 browser->hists->socket_filter = act->socket;
2740 perf_hpp__set_elide(HISTC_SOCKET, true);
2741 pstack__push(browser->pstack, &browser->hists->socket_filter);
2744 hists__filter_by_socket(browser->hists);
2745 hist_browser__reset(browser);
2750 add_socket_opt(struct hist_browser *browser, struct popup_action *act,
2751 char **optstr, int socket_id)
2753 if (!hists__has(browser->hists, socket) || socket_id < 0)
2756 if (asprintf(optstr, "Zoom %s Processor Socket %d",
2757 (browser->hists->socket_filter > -1) ? "out of" : "into",
2761 act->socket = socket_id;
2762 act->fn = do_zoom_socket;
2766 static void hist_browser__update_nr_entries(struct hist_browser *hb)
2769 struct rb_node *nd = rb_first_cached(&hb->hists->entries);
2771 if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
2772 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
2776 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2778 nd = rb_hierarchy_next(nd);
2781 hb->nr_non_filtered_entries = nr_entries;
2782 hb->nr_hierarchy_entries = nr_entries;
2785 static void hist_browser__update_percent_limit(struct hist_browser *hb,
2788 struct hist_entry *he;
2789 struct rb_node *nd = rb_first_cached(&hb->hists->entries);
2790 u64 total = hists__total_period(hb->hists);
2791 u64 min_callchain_hits = total * (percent / 100);
2793 hb->min_pcnt = callchain_param.min_percent = percent;
2795 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2796 he = rb_entry(nd, struct hist_entry, rb_node);
2798 if (he->has_no_entry) {
2799 he->has_no_entry = false;
2803 if (!he->leaf || !hist_entry__has_callchains(he) || !symbol_conf.use_callchain)
2806 if (callchain_param.mode == CHAIN_GRAPH_REL) {
2807 total = he->stat.period;
2809 if (symbol_conf.cumulate_callchain)
2810 total = he->stat_acc->period;
2812 min_callchain_hits = total * (percent / 100);
2815 callchain_param.sort(&he->sorted_chain, he->callchain,
2816 min_callchain_hits, &callchain_param);
2819 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
2821 /* force to re-evaluate folding state of callchains */
2822 he->init_have_children = false;
2823 hist_entry__set_folding(he, hb, false);
2827 static int perf_evsel__hists_browse(struct evsel *evsel, int nr_events,
2828 const char *helpline,
2830 struct hist_browser_timer *hbt,
2832 struct perf_env *env,
2833 bool warn_lost_event,
2834 struct annotation_options *annotation_opts)
2836 struct hists *hists = evsel__hists(evsel);
2837 struct hist_browser *browser = perf_evsel_browser__new(evsel, hbt, env, annotation_opts);
2838 struct branch_info *bi = NULL;
2839 #define MAX_OPTIONS 16
2840 char *options[MAX_OPTIONS];
2841 struct popup_action actions[MAX_OPTIONS];
2845 int delay_secs = hbt ? hbt->refresh : 0;
2847 #define HIST_BROWSER_HELP_COMMON \
2848 "h/?/F1 Show this window\n" \
2850 "PGDN/SPACE Navigate\n" \
2851 "q/ESC/CTRL+C Exit browser or go back to previous screen\n\n" \
2852 "For multiple event sessions:\n\n" \
2853 "TAB/UNTAB Switch events\n\n" \
2854 "For symbolic views (--sort has sym):\n\n" \
2855 "ENTER Zoom into DSO/Threads & Annotate current symbol\n" \
2857 "a Annotate current symbol\n" \
2858 "C Collapse all callchains\n" \
2859 "d Zoom into current DSO\n" \
2860 "E Expand all callchains\n" \
2861 "F Toggle percentage of filtered entries\n" \
2862 "H Display column headers\n" \
2863 "L Change percent limit\n" \
2864 "m Display context menu\n" \
2865 "S Zoom into current Processor Socket\n" \
2867 /* help messages are sorted by lexical order of the hotkey */
2868 static const char report_help[] = HIST_BROWSER_HELP_COMMON
2869 "i Show header information\n"
2870 "P Print histograms to perf.hist.N\n"
2871 "r Run available scripts\n"
2872 "s Switch to another data file in PWD\n"
2873 "t Zoom into current Thread\n"
2874 "V Verbose (DSO names in callchains, etc)\n"
2875 "/ Filter symbol by name";
2876 static const char top_help[] = HIST_BROWSER_HELP_COMMON
2877 "P Print histograms to perf.hist.N\n"
2878 "t Zoom into current Thread\n"
2879 "V Verbose (DSO names in callchains, etc)\n"
2880 "z Toggle zeroing of samples\n"
2881 "f Enable/Disable events\n"
2882 "/ Filter symbol by name";
2884 if (browser == NULL)
2887 /* reset abort key so that it can get Ctrl-C as a key */
2889 SLang_init_tty(0, 0, 0);
2892 browser->min_pcnt = min_pcnt;
2893 hist_browser__update_nr_entries(browser);
2895 browser->pstack = pstack__new(3);
2896 if (browser->pstack == NULL)
2899 ui_helpline__push(helpline);
2901 memset(options, 0, sizeof(options));
2902 memset(actions, 0, sizeof(actions));
2904 if (symbol_conf.col_width_list_str)
2905 perf_hpp__set_user_width(symbol_conf.col_width_list_str);
2907 if (!is_report_browser(hbt))
2908 browser->b.no_samples_msg = "Collecting samples...";
2911 struct thread *thread = NULL;
2912 struct map *map = NULL;
2918 key = hist_browser__run(browser, helpline,
2921 if (browser->he_selection != NULL) {
2922 thread = hist_browser__selected_thread(browser);
2923 map = browser->selection->map;
2924 socked_id = browser->he_selection->socket;
2932 * Exit the browser, let hists__browser_tree
2933 * go to the next or previous
2935 goto out_free_stack;
2937 if (!hists__has(hists, sym)) {
2938 ui_browser__warning(&browser->b, delay_secs * 2,
2939 "Annotation is only available for symbolic views, "
2940 "include \"sym*\" in --sort to use it.");
2944 if (browser->selection == NULL ||
2945 browser->selection->sym == NULL ||
2946 browser->selection->map->dso->annotate_warned)
2949 actions->ms.map = browser->selection->map;
2950 actions->ms.sym = browser->selection->sym;
2951 do_annotate(browser, actions);
2954 hist_browser__dump(browser);
2957 actions->ms.map = map;
2958 do_zoom_dso(browser, actions);
2961 verbose = (verbose + 1) % 4;
2962 browser->show_dso = verbose > 0;
2963 ui_helpline__fpush("Verbosity level set to %d\n",
2967 actions->thread = thread;
2968 do_zoom_thread(browser, actions);
2971 actions->socket = socked_id;
2972 do_zoom_socket(browser, actions);
2975 if (ui_browser__input_window("Symbol to show",
2976 "Please enter the name of symbol you want to see.\n"
2977 "To remove the filter later, press / + ENTER.",
2978 buf, "ENTER: OK, ESC: Cancel",
2979 delay_secs * 2) == K_ENTER) {
2980 hists->symbol_filter_str = *buf ? buf : NULL;
2981 hists__filter_by_symbol(hists);
2982 hist_browser__reset(browser);
2986 if (is_report_browser(hbt)) {
2987 actions->thread = NULL;
2988 actions->ms.sym = NULL;
2989 do_run_script(browser, actions);
2993 if (is_report_browser(hbt)) {
2994 key = do_switch_data(browser, actions);
2995 if (key == K_SWITCH_INPUT_DATA)
2996 goto out_free_stack;
3000 /* env->arch is NULL for live-mode (i.e. perf top) */
3002 tui__header_window(env);
3005 symbol_conf.filter_relative ^= 1;
3008 if (!is_report_browser(hbt)) {
3009 struct perf_top *top = hbt->arg;
3011 top->zero = !top->zero;
3015 if (ui_browser__input_window("Percent Limit",
3016 "Please enter the value you want to hide entries under that percent.",
3017 buf, "ENTER: OK, ESC: Cancel",
3018 delay_secs * 2) == K_ENTER) {
3020 double new_percent = strtod(buf, &end);
3022 if (new_percent < 0 || new_percent > 100) {
3023 ui_browser__warning(&browser->b, delay_secs * 2,
3024 "Invalid percent: %.2f", new_percent);
3028 hist_browser__update_percent_limit(browser, new_percent);
3029 hist_browser__reset(browser);
3035 ui_browser__help_window(&browser->b,
3036 is_report_browser(hbt) ? report_help : top_help);
3047 if (pstack__empty(browser->pstack)) {
3049 * Go back to the perf_evsel_menu__run or other user
3052 goto out_free_stack;
3055 ui_browser__dialog_yesno(&browser->b,
3056 "Do you really want to exit?"))
3057 goto out_free_stack;
3061 top = pstack__peek(browser->pstack);
3062 if (top == &browser->hists->dso_filter) {
3064 * No need to set actions->dso here since
3065 * it's just to remove the current filter.
3066 * Ditto for thread below.
3068 do_zoom_dso(browser, actions);
3069 } else if (top == &browser->hists->thread_filter) {
3070 do_zoom_thread(browser, actions);
3071 } else if (top == &browser->hists->socket_filter) {
3072 do_zoom_socket(browser, actions);
3078 goto out_free_stack;
3080 if (!is_report_browser(hbt)) {
3081 struct perf_top *top = hbt->arg;
3083 perf_evlist__toggle_enable(top->evlist);
3085 * No need to refresh, resort/decay histogram
3086 * entries if we are not collecting samples:
3088 if (top->evlist->enabled) {
3089 helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
3090 hbt->refresh = delay_secs;
3092 helpline = "Press 'f' again to re-enable the events";
3099 helpline = "Press '?' for help on key bindings";
3103 if (!hists__has(hists, sym) || browser->selection == NULL)
3104 goto skip_annotation;
3106 if (sort__mode == SORT_MODE__BRANCH) {
3108 if (browser->he_selection)
3109 bi = browser->he_selection->branch_info;
3112 goto skip_annotation;
3114 nr_options += add_annotate_opt(browser,
3115 &actions[nr_options],
3116 &options[nr_options],
3118 if (bi->to.ms.sym != bi->from.ms.sym)
3119 nr_options += add_annotate_opt(browser,
3120 &actions[nr_options],
3121 &options[nr_options],
3124 nr_options += add_annotate_opt(browser,
3125 &actions[nr_options],
3126 &options[nr_options],
3127 browser->selection);
3130 nr_options += add_thread_opt(browser, &actions[nr_options],
3131 &options[nr_options], thread);
3132 nr_options += add_dso_opt(browser, &actions[nr_options],
3133 &options[nr_options], map);
3134 nr_options += add_map_opt(browser, &actions[nr_options],
3135 &options[nr_options],
3136 browser->selection ?
3137 browser->selection->map : NULL);
3138 nr_options += add_socket_opt(browser, &actions[nr_options],
3139 &options[nr_options],
3141 /* perf script support */
3142 if (!is_report_browser(hbt))
3143 goto skip_scripting;
3145 if (browser->he_selection) {
3146 if (hists__has(hists, thread) && thread) {
3147 nr_options += add_script_opt(browser,
3148 &actions[nr_options],
3149 &options[nr_options],
3150 thread, NULL, evsel);
3153 * Note that browser->selection != NULL
3154 * when browser->he_selection is not NULL,
3155 * so we don't need to check browser->selection
3156 * before fetching browser->selection->sym like what
3157 * we do before fetching browser->selection->map.
3159 * See hist_browser__show_entry.
3161 if (hists__has(hists, sym) && browser->selection->sym) {
3162 nr_options += add_script_opt(browser,
3163 &actions[nr_options],
3164 &options[nr_options],
3165 NULL, browser->selection->sym,
3169 nr_options += add_script_opt(browser, &actions[nr_options],
3170 &options[nr_options], NULL, NULL, evsel);
3171 nr_options += add_res_sample_opt(browser, &actions[nr_options],
3172 &options[nr_options],
3173 hist_browser__selected_entry(browser)->res_samples,
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_switch_opt(browser, &actions[nr_options],
3184 &options[nr_options]);
3186 nr_options += add_exit_opt(browser, &actions[nr_options],
3187 &options[nr_options]);
3190 struct popup_action *act;
3192 choice = ui__popup_menu(nr_options, options);
3193 if (choice == -1 || choice >= nr_options)
3196 act = &actions[choice];
3197 key = act->fn(browser, act);
3200 if (key == K_SWITCH_INPUT_DATA)
3204 pstack__delete(browser->pstack);
3206 hist_browser__delete(browser);
3207 free_popup_options(options, MAX_OPTIONS);
3212 struct ui_browser b;
3213 struct evsel *selection;
3214 struct annotation_options *annotation_opts;
3215 bool lost_events, lost_events_warned;
3217 struct perf_env *env;
3220 static void perf_evsel_menu__write(struct ui_browser *browser,
3221 void *entry, int row)
3223 struct evsel_menu *menu = container_of(browser,
3224 struct evsel_menu, b);
3225 struct evsel *evsel = list_entry(entry, struct evsel, core.node);
3226 struct hists *hists = evsel__hists(evsel);
3227 bool current_entry = ui_browser__is_current_entry(browser, row);
3228 unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
3229 const char *ev_name = perf_evsel__name(evsel);
3231 const char *warn = " ";
3234 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
3235 HE_COLORSET_NORMAL);
3237 if (perf_evsel__is_group_event(evsel)) {
3240 ev_name = perf_evsel__group_name(evsel);
3242 for_each_group_member(pos, evsel) {
3243 struct hists *pos_hists = evsel__hists(pos);
3244 nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
3248 nr_events = convert_unit(nr_events, &unit);
3249 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
3250 unit, unit == ' ' ? "" : " ", ev_name);
3251 ui_browser__printf(browser, "%s", bf);
3253 nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
3254 if (nr_events != 0) {
3255 menu->lost_events = true;
3257 ui_browser__set_color(browser, HE_COLORSET_TOP);
3258 nr_events = convert_unit(nr_events, &unit);
3259 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
3260 nr_events, unit, unit == ' ' ? "" : " ");
3264 ui_browser__write_nstring(browser, warn, browser->width - printed);
3267 menu->selection = evsel;
3270 static int perf_evsel_menu__run(struct evsel_menu *menu,
3271 int nr_events, const char *help,
3272 struct hist_browser_timer *hbt,
3273 bool warn_lost_event)
3275 struct evlist *evlist = menu->b.priv;
3277 const char *title = "Available samples";
3278 int delay_secs = hbt ? hbt->refresh : 0;
3281 if (ui_browser__show(&menu->b, title,
3282 "ESC: exit, ENTER|->: Browse histograms") < 0)
3286 key = ui_browser__run(&menu->b, delay_secs);
3291 hbt->timer(hbt->arg);
3293 if (!menu->lost_events_warned &&
3294 menu->lost_events &&
3296 ui_browser__warn_lost_events(&menu->b);
3297 menu->lost_events_warned = true;
3302 if (!menu->selection)
3304 pos = menu->selection;
3306 perf_evlist__set_selected(evlist, pos);
3308 * Give the calling tool a chance to populate the non
3309 * default evsel resorted hists tree.
3312 hbt->timer(hbt->arg);
3313 key = perf_evsel__hists_browse(pos, nr_events, help,
3318 menu->annotation_opts);
3319 ui_browser__show_title(&menu->b, title);
3322 if (pos->core.node.next == &evlist->core.entries)
3323 pos = evlist__first(evlist);
3325 pos = perf_evsel__next(pos);
3328 if (pos->core.node.prev == &evlist->core.entries)
3329 pos = evlist__last(evlist);
3331 pos = perf_evsel__prev(pos);
3333 case K_SWITCH_INPUT_DATA:
3344 if (!ui_browser__dialog_yesno(&menu->b,
3345 "Do you really want to exit?"))
3357 ui_browser__hide(&menu->b);
3361 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
3364 struct evsel *evsel = list_entry(entry, struct evsel, core.node);
3366 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
3372 static int __perf_evlist__tui_browse_hists(struct evlist *evlist,
3373 int nr_entries, const char *help,
3374 struct hist_browser_timer *hbt,
3376 struct perf_env *env,
3377 bool warn_lost_event,
3378 struct annotation_options *annotation_opts)
3381 struct evsel_menu menu = {
3383 .entries = &evlist->core.entries,
3384 .refresh = ui_browser__list_head_refresh,
3385 .seek = ui_browser__list_head_seek,
3386 .write = perf_evsel_menu__write,
3387 .filter = filter_group_entries,
3388 .nr_entries = nr_entries,
3391 .min_pcnt = min_pcnt,
3393 .annotation_opts = annotation_opts,
3396 ui_helpline__push("Press ESC to exit");
3398 evlist__for_each_entry(evlist, pos) {
3399 const char *ev_name = perf_evsel__name(pos);
3400 size_t line_len = strlen(ev_name) + 7;
3402 if (menu.b.width < line_len)
3403 menu.b.width = line_len;
3406 return perf_evsel_menu__run(&menu, nr_entries, help,
3407 hbt, warn_lost_event);
3410 int perf_evlist__tui_browse_hists(struct evlist *evlist, const char *help,
3411 struct hist_browser_timer *hbt,
3413 struct perf_env *env,
3414 bool warn_lost_event,
3415 struct annotation_options *annotation_opts)
3417 int nr_entries = evlist->core.nr_entries;
3420 if (nr_entries == 1) {
3421 struct evsel *first = evlist__first(evlist);
3423 return perf_evsel__hists_browse(first, nr_entries, help,
3424 false, hbt, min_pcnt,
3425 env, warn_lost_event,
3429 if (symbol_conf.event_group) {
3433 evlist__for_each_entry(evlist, pos) {
3434 if (perf_evsel__is_group_leader(pos))
3438 if (nr_entries == 1)
3442 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
3448 static int block_hists_browser__title(struct hist_browser *browser, char *bf,
3451 struct hists *hists = evsel__hists(browser->block_evsel);
3452 const char *evname = perf_evsel__name(browser->block_evsel);
3453 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
3456 ret = scnprintf(bf, size, "# Samples: %lu", nr_samples);
3458 scnprintf(bf + ret, size - ret, " of event '%s'", evname);
3463 int block_hists_tui_browse(struct block_hist *bh, struct evsel *evsel,
3466 struct hists *hists = &bh->block_hists;
3467 struct hist_browser *browser;
3469 static const char help[] =
3472 browser = hist_browser__new(hists);
3476 browser->block_evsel = evsel;
3477 browser->title = block_hists_browser__title;
3478 browser->min_pcnt = min_percent;
3480 /* reset abort key so that it can get Ctrl-C as a key */
3482 SLang_init_tty(0, 0, 0);
3485 key = hist_browser__run(browser, "? - help", true);
3491 ui_browser__help_window(&browser->b, help);
3499 hist_browser__delete(browser);