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/pstack.h"
24 #include "../../util/sort.h"
25 #include "../../util/top.h"
26 #include "../../util/thread.h"
27 #include "../../arch/common.h"
28 #include "../../perf.h"
30 #include "../browsers/hists.h"
31 #include "../helpline.h"
39 #include "time-utils.h"
41 #include <linux/ctype.h>
43 extern void hist_browser__init_hpp(void);
45 static int hists_browser__scnprintf_title(struct hist_browser *browser, char *bf, size_t size);
46 static void hist_browser__update_nr_entries(struct hist_browser *hb);
48 static struct rb_node *hists__filter_entries(struct rb_node *nd,
51 static bool hist_browser__has_filter(struct hist_browser *hb)
53 return hists__has_filter(hb->hists) || hb->min_pcnt || symbol_conf.has_filter || hb->c2c_filter;
56 static int hist_browser__get_folding(struct hist_browser *browser)
59 struct hists *hists = browser->hists;
60 int unfolded_rows = 0;
62 for (nd = rb_first_cached(&hists->entries);
63 (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
64 nd = rb_hierarchy_next(nd)) {
65 struct hist_entry *he =
66 rb_entry(nd, struct hist_entry, rb_node);
68 if (he->leaf && he->unfolded)
69 unfolded_rows += he->nr_rows;
74 static void hist_browser__set_title_space(struct hist_browser *hb)
76 struct ui_browser *browser = &hb->b;
77 struct hists *hists = hb->hists;
78 struct perf_hpp_list *hpp_list = hists->hpp_list;
80 browser->extra_title_lines = hb->show_headers ? hpp_list->nr_header_lines : 0;
83 static u32 hist_browser__nr_entries(struct hist_browser *hb)
87 if (symbol_conf.report_hierarchy)
88 nr_entries = hb->nr_hierarchy_entries;
89 else if (hist_browser__has_filter(hb))
90 nr_entries = hb->nr_non_filtered_entries;
92 nr_entries = hb->hists->nr_entries;
94 hb->nr_callchain_rows = hist_browser__get_folding(hb);
95 return nr_entries + hb->nr_callchain_rows;
98 static void hist_browser__update_rows(struct hist_browser *hb)
100 struct ui_browser *browser = &hb->b;
101 struct hists *hists = hb->hists;
102 struct perf_hpp_list *hpp_list = hists->hpp_list;
105 if (!hb->show_headers) {
106 browser->rows += browser->extra_title_lines;
107 browser->extra_title_lines = 0;
111 browser->extra_title_lines = hpp_list->nr_header_lines;
112 browser->rows -= browser->extra_title_lines;
114 * Verify if we were at the last line and that line isn't
115 * visibe because we now show the header line(s).
117 index_row = browser->index - browser->top_idx;
118 if (index_row >= browser->rows)
119 browser->index -= index_row - browser->rows + 1;
122 static void hist_browser__refresh_dimensions(struct ui_browser *browser)
124 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
126 /* 3 == +/- toggle symbol before actual hist_entry rendering */
127 browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]"));
129 * FIXME: Just keeping existing behaviour, but this really should be
130 * before updating browser->width, as it will invalidate the
131 * calculation above. Fix this and the fallout in another
134 ui_browser__refresh_dimensions(browser);
137 static void hist_browser__reset(struct hist_browser *browser)
140 * The hists__remove_entry_filter() already folds non-filtered
141 * entries so we can assume it has 0 callchain rows.
143 browser->nr_callchain_rows = 0;
145 hist_browser__update_nr_entries(browser);
146 browser->b.nr_entries = hist_browser__nr_entries(browser);
147 hist_browser__refresh_dimensions(&browser->b);
148 ui_browser__reset_index(&browser->b);
151 static char tree__folded_sign(bool unfolded)
153 return unfolded ? '-' : '+';
156 static char hist_entry__folded(const struct hist_entry *he)
158 return he->has_children ? tree__folded_sign(he->unfolded) : ' ';
161 static char callchain_list__folded(const struct callchain_list *cl)
163 return cl->has_children ? tree__folded_sign(cl->unfolded) : ' ';
166 static void callchain_list__set_folding(struct callchain_list *cl, bool unfold)
168 cl->unfolded = unfold ? cl->has_children : false;
171 static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
176 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
177 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
178 struct callchain_list *chain;
179 char folded_sign = ' '; /* No children */
181 list_for_each_entry(chain, &child->val, list) {
184 /* We need this because we may not have children */
185 folded_sign = callchain_list__folded(chain);
186 if (folded_sign == '+')
190 if (folded_sign == '-') /* Have children and they're unfolded */
191 n += callchain_node__count_rows_rb_tree(child);
197 static int callchain_node__count_flat_rows(struct callchain_node *node)
199 struct callchain_list *chain;
200 char folded_sign = 0;
203 list_for_each_entry(chain, &node->parent_val, list) {
205 /* only check first chain list entry */
206 folded_sign = callchain_list__folded(chain);
207 if (folded_sign == '+')
213 list_for_each_entry(chain, &node->val, list) {
215 /* node->parent_val list might be empty */
216 folded_sign = callchain_list__folded(chain);
217 if (folded_sign == '+')
226 static int callchain_node__count_folded_rows(struct callchain_node *node __maybe_unused)
231 static int callchain_node__count_rows(struct callchain_node *node)
233 struct callchain_list *chain;
234 bool unfolded = false;
237 if (callchain_param.mode == CHAIN_FLAT)
238 return callchain_node__count_flat_rows(node);
239 else if (callchain_param.mode == CHAIN_FOLDED)
240 return callchain_node__count_folded_rows(node);
242 list_for_each_entry(chain, &node->val, list) {
245 unfolded = chain->unfolded;
249 n += callchain_node__count_rows_rb_tree(node);
254 static int callchain__count_rows(struct rb_root *chain)
259 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
260 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
261 n += callchain_node__count_rows(node);
267 static int hierarchy_count_rows(struct hist_browser *hb, struct hist_entry *he,
268 bool include_children)
271 struct rb_node *node;
272 struct hist_entry *child;
275 return callchain__count_rows(&he->sorted_chain);
277 if (he->has_no_entry)
280 node = rb_first_cached(&he->hroot_out);
284 child = rb_entry(node, struct hist_entry, rb_node);
285 percent = hist_entry__get_percent_limit(child);
287 if (!child->filtered && percent >= hb->min_pcnt) {
290 if (include_children && child->unfolded)
291 count += hierarchy_count_rows(hb, child, true);
294 node = rb_next(node);
299 static bool hist_entry__toggle_fold(struct hist_entry *he)
304 if (!he->has_children)
307 he->unfolded = !he->unfolded;
311 static bool callchain_list__toggle_fold(struct callchain_list *cl)
316 if (!cl->has_children)
319 cl->unfolded = !cl->unfolded;
323 static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
325 struct rb_node *nd = rb_first(&node->rb_root);
327 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
328 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
329 struct callchain_list *chain;
332 list_for_each_entry(chain, &child->val, list) {
335 chain->has_children = chain->list.next != &child->val ||
336 !RB_EMPTY_ROOT(&child->rb_root);
338 chain->has_children = chain->list.next == &child->val &&
339 !RB_EMPTY_ROOT(&child->rb_root);
342 callchain_node__init_have_children_rb_tree(child);
346 static void callchain_node__init_have_children(struct callchain_node *node,
349 struct callchain_list *chain;
351 chain = list_entry(node->val.next, struct callchain_list, list);
352 chain->has_children = has_sibling;
354 if (!list_empty(&node->val)) {
355 chain = list_entry(node->val.prev, struct callchain_list, list);
356 chain->has_children = !RB_EMPTY_ROOT(&node->rb_root);
359 callchain_node__init_have_children_rb_tree(node);
362 static void callchain__init_have_children(struct rb_root *root)
364 struct rb_node *nd = rb_first(root);
365 bool has_sibling = nd && rb_next(nd);
367 for (nd = rb_first(root); nd; nd = rb_next(nd)) {
368 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
369 callchain_node__init_have_children(node, has_sibling);
370 if (callchain_param.mode == CHAIN_FLAT ||
371 callchain_param.mode == CHAIN_FOLDED)
372 callchain_node__make_parent_list(node);
376 static void hist_entry__init_have_children(struct hist_entry *he)
378 if (he->init_have_children)
382 he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
383 callchain__init_have_children(&he->sorted_chain);
385 he->has_children = !RB_EMPTY_ROOT(&he->hroot_out.rb_root);
388 he->init_have_children = true;
391 static bool hist_browser__toggle_fold(struct hist_browser *browser)
393 struct hist_entry *he = browser->he_selection;
394 struct map_symbol *ms = browser->selection;
395 struct callchain_list *cl = container_of(ms, struct callchain_list, ms);
402 has_children = hist_entry__toggle_fold(he);
404 has_children = callchain_list__toggle_fold(cl);
409 hist_entry__init_have_children(he);
410 browser->b.nr_entries -= he->nr_rows;
413 browser->nr_callchain_rows -= he->nr_rows;
415 browser->nr_hierarchy_entries -= he->nr_rows;
417 if (symbol_conf.report_hierarchy)
418 child_rows = hierarchy_count_rows(browser, he, true);
422 he->nr_rows = callchain__count_rows(
425 he->nr_rows = hierarchy_count_rows(browser, he, false);
427 /* account grand children */
428 if (symbol_conf.report_hierarchy)
429 browser->b.nr_entries += child_rows - he->nr_rows;
431 if (!he->leaf && he->nr_rows == 0) {
432 he->has_no_entry = true;
436 if (symbol_conf.report_hierarchy)
437 browser->b.nr_entries -= child_rows - he->nr_rows;
439 if (he->has_no_entry)
440 he->has_no_entry = false;
445 browser->b.nr_entries += he->nr_rows;
448 browser->nr_callchain_rows += he->nr_rows;
450 browser->nr_hierarchy_entries += he->nr_rows;
455 /* If it doesn't have children, no toggling performed */
459 static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
464 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
465 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
466 struct callchain_list *chain;
467 bool has_children = false;
469 list_for_each_entry(chain, &child->val, list) {
471 callchain_list__set_folding(chain, unfold);
472 has_children = chain->has_children;
476 n += callchain_node__set_folding_rb_tree(child, unfold);
482 static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
484 struct callchain_list *chain;
485 bool has_children = false;
488 list_for_each_entry(chain, &node->val, list) {
490 callchain_list__set_folding(chain, unfold);
491 has_children = chain->has_children;
495 n += callchain_node__set_folding_rb_tree(node, unfold);
500 static int callchain__set_folding(struct rb_root *chain, bool unfold)
505 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
506 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
507 n += callchain_node__set_folding(node, unfold);
513 static int hierarchy_set_folding(struct hist_browser *hb, struct hist_entry *he,
514 bool unfold __maybe_unused)
518 struct hist_entry *child;
521 for (nd = rb_first_cached(&he->hroot_out); nd; nd = rb_next(nd)) {
522 child = rb_entry(nd, struct hist_entry, rb_node);
523 percent = hist_entry__get_percent_limit(child);
524 if (!child->filtered && percent >= hb->min_pcnt)
531 static void __hist_entry__set_folding(struct hist_entry *he,
532 struct hist_browser *hb, bool unfold)
534 hist_entry__init_have_children(he);
535 he->unfolded = unfold ? he->has_children : false;
537 if (he->has_children) {
541 n = callchain__set_folding(&he->sorted_chain, unfold);
543 n = hierarchy_set_folding(hb, he, unfold);
545 he->nr_rows = unfold ? n : 0;
550 static void hist_entry__set_folding(struct hist_entry *he,
551 struct hist_browser *browser, bool unfold)
555 percent = hist_entry__get_percent_limit(he);
556 if (he->filtered || percent < browser->min_pcnt)
559 __hist_entry__set_folding(he, browser, unfold);
561 if (!he->depth || unfold)
562 browser->nr_hierarchy_entries++;
564 browser->nr_callchain_rows += he->nr_rows;
565 else if (unfold && !hist_entry__has_hierarchy_children(he, browser->min_pcnt)) {
566 browser->nr_hierarchy_entries++;
567 he->has_no_entry = true;
570 he->has_no_entry = false;
574 __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
577 struct hist_entry *he;
579 nd = rb_first_cached(&browser->hists->entries);
581 he = rb_entry(nd, struct hist_entry, rb_node);
583 /* set folding state even if it's currently folded */
584 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
586 hist_entry__set_folding(he, browser, unfold);
590 static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
592 browser->nr_hierarchy_entries = 0;
593 browser->nr_callchain_rows = 0;
594 __hist_browser__set_folding(browser, unfold);
596 browser->b.nr_entries = hist_browser__nr_entries(browser);
597 /* Go to the start, we may be way after valid entries after a collapse */
598 ui_browser__reset_index(&browser->b);
601 static void hist_browser__set_folding_selected(struct hist_browser *browser, bool unfold)
603 if (!browser->he_selection)
606 hist_entry__set_folding(browser->he_selection, browser, unfold);
607 browser->b.nr_entries = hist_browser__nr_entries(browser);
610 static void ui_browser__warn_lost_events(struct ui_browser *browser)
612 ui_browser__warning(browser, 4,
613 "Events are being lost, check IO/CPU overload!\n\n"
614 "You may want to run 'perf' using a RT scheduler policy:\n\n"
615 " perf top -r 80\n\n"
616 "Or reduce the sampling frequency.");
619 static int hist_browser__title(struct hist_browser *browser, char *bf, size_t size)
621 return browser->title ? browser->title(browser, bf, size) : 0;
624 int hist_browser__run(struct hist_browser *browser, const char *help,
625 bool warn_lost_event)
629 struct hist_browser_timer *hbt = browser->hbt;
630 int delay_secs = hbt ? hbt->refresh : 0;
632 browser->b.entries = &browser->hists->entries;
633 browser->b.nr_entries = hist_browser__nr_entries(browser);
635 hist_browser__title(browser, title, sizeof(title));
637 if (ui_browser__show(&browser->b, title, "%s", help) < 0)
641 key = ui_browser__run(&browser->b, delay_secs);
650 hbt->timer(hbt->arg);
652 if (hist_browser__has_filter(browser) ||
653 symbol_conf.report_hierarchy)
654 hist_browser__update_nr_entries(browser);
656 nr_entries = hist_browser__nr_entries(browser);
657 ui_browser__update_nr_entries(&browser->b, nr_entries);
659 if (warn_lost_event &&
660 (browser->hists->stats.nr_lost_warned !=
661 browser->hists->stats.nr_events[PERF_RECORD_LOST])) {
662 browser->hists->stats.nr_lost_warned =
663 browser->hists->stats.nr_events[PERF_RECORD_LOST];
664 ui_browser__warn_lost_events(&browser->b);
667 hist_browser__title(browser, title, sizeof(title));
668 ui_browser__show_title(&browser->b, title);
671 case 'D': { /* Debug */
673 struct hist_entry *h = rb_entry(browser->b.top,
674 struct hist_entry, rb_node);
676 ui_helpline__fpush("%d: nr_ent=(%d,%d), etl: %d, rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
677 seq++, browser->b.nr_entries,
678 browser->hists->nr_entries,
679 browser->b.extra_title_lines,
683 h->row_offset, h->nr_rows);
687 /* Collapse the whole world. */
688 hist_browser__set_folding(browser, false);
691 /* Collapse the selected entry. */
692 hist_browser__set_folding_selected(browser, false);
695 /* Expand the whole world. */
696 hist_browser__set_folding(browser, true);
699 /* Expand the selected entry. */
700 hist_browser__set_folding_selected(browser, true);
703 browser->show_headers = !browser->show_headers;
704 hist_browser__update_rows(browser);
707 if (hist_browser__toggle_fold(browser))
715 ui_browser__hide(&browser->b);
719 struct callchain_print_arg {
720 /* for hists browser */
722 bool is_current_entry;
729 typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
730 struct callchain_list *chain,
731 const char *str, int offset,
733 struct callchain_print_arg *arg);
735 static void hist_browser__show_callchain_entry(struct hist_browser *browser,
736 struct callchain_list *chain,
737 const char *str, int offset,
739 struct callchain_print_arg *arg)
742 char folded_sign = callchain_list__folded(chain);
743 bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
745 color = HE_COLORSET_NORMAL;
746 width = browser->b.width - (offset + 2);
747 if (ui_browser__is_current_entry(&browser->b, row)) {
748 browser->selection = &chain->ms;
749 color = HE_COLORSET_SELECTED;
750 arg->is_current_entry = true;
753 ui_browser__set_color(&browser->b, color);
754 ui_browser__gotorc(&browser->b, row, 0);
755 ui_browser__write_nstring(&browser->b, " ", offset);
756 ui_browser__printf(&browser->b, "%c", folded_sign);
757 ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
758 ui_browser__write_nstring(&browser->b, str, width);
761 static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
762 struct callchain_list *chain,
763 const char *str, int offset,
764 unsigned short row __maybe_unused,
765 struct callchain_print_arg *arg)
767 char folded_sign = callchain_list__folded(chain);
769 arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
773 typedef bool (*check_output_full_fn)(struct hist_browser *browser,
776 static bool hist_browser__check_output_full(struct hist_browser *browser,
779 return browser->b.rows == row;
782 static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
783 unsigned short row __maybe_unused)
788 #define LEVEL_OFFSET_STEP 3
790 static int hist_browser__show_callchain_list(struct hist_browser *browser,
791 struct callchain_node *node,
792 struct callchain_list *chain,
793 unsigned short row, u64 total,
794 bool need_percent, int offset,
795 print_callchain_entry_fn print,
796 struct callchain_print_arg *arg)
798 char bf[1024], *alloc_str;
799 char buf[64], *alloc_str2;
803 if (arg->row_offset != 0) {
811 str = callchain_list__sym_name(chain, bf, sizeof(bf),
814 if (symbol_conf.show_branchflag_count) {
815 callchain_list_counts__printf_value(chain, NULL,
818 if (asprintf(&alloc_str2, "%s%s", str, buf) < 0)
819 str = "Not enough memory!";
825 callchain_node__scnprintf_value(node, buf, sizeof(buf),
828 if (asprintf(&alloc_str, "%s %s", buf, str) < 0)
829 str = "Not enough memory!";
834 print(browser, chain, str, offset, row, arg);
841 static bool check_percent_display(struct rb_node *node, u64 parent_total)
843 struct callchain_node *child;
851 child = rb_entry(node, struct callchain_node, rb_node);
852 return callchain_cumul_hits(child) != parent_total;
855 static int hist_browser__show_callchain_flat(struct hist_browser *browser,
856 struct rb_root *root,
857 unsigned short row, u64 total,
859 print_callchain_entry_fn print,
860 struct callchain_print_arg *arg,
861 check_output_full_fn is_output_full)
863 struct rb_node *node;
864 int first_row = row, offset = LEVEL_OFFSET_STEP;
867 node = rb_first(root);
868 need_percent = check_percent_display(node, parent_total);
871 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
872 struct rb_node *next = rb_next(node);
873 struct callchain_list *chain;
874 char folded_sign = ' ';
876 int extra_offset = 0;
878 list_for_each_entry(chain, &child->parent_val, list) {
879 bool was_first = first;
883 else if (need_percent)
884 extra_offset = LEVEL_OFFSET_STEP;
886 folded_sign = callchain_list__folded(chain);
888 row += hist_browser__show_callchain_list(browser, child,
890 was_first && need_percent,
891 offset + extra_offset,
894 if (is_output_full(browser, row))
897 if (folded_sign == '+')
901 list_for_each_entry(chain, &child->val, list) {
902 bool was_first = first;
906 else if (need_percent)
907 extra_offset = LEVEL_OFFSET_STEP;
909 folded_sign = callchain_list__folded(chain);
911 row += hist_browser__show_callchain_list(browser, child,
913 was_first && need_percent,
914 offset + extra_offset,
917 if (is_output_full(browser, row))
920 if (folded_sign == '+')
925 if (is_output_full(browser, row))
930 return row - first_row;
933 static char *hist_browser__folded_callchain_str(struct hist_browser *browser,
934 struct callchain_list *chain,
935 char *value_str, char *old_str)
941 str = callchain_list__sym_name(chain, bf, sizeof(bf),
944 if (asprintf(&new, "%s%s%s", old_str,
945 symbol_conf.field_sep ?: ";", str) < 0)
949 if (asprintf(&new, "%s %s", value_str, str) < 0)
952 if (asprintf(&new, "%s", str) < 0)
959 static int hist_browser__show_callchain_folded(struct hist_browser *browser,
960 struct rb_root *root,
961 unsigned short row, u64 total,
963 print_callchain_entry_fn print,
964 struct callchain_print_arg *arg,
965 check_output_full_fn is_output_full)
967 struct rb_node *node;
968 int first_row = row, offset = LEVEL_OFFSET_STEP;
971 node = rb_first(root);
972 need_percent = check_percent_display(node, parent_total);
975 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
976 struct rb_node *next = rb_next(node);
977 struct callchain_list *chain, *first_chain = NULL;
979 char *value_str = NULL, *value_str_alloc = NULL;
980 char *chain_str = NULL, *chain_str_alloc = NULL;
982 if (arg->row_offset != 0) {
990 callchain_node__scnprintf_value(child, buf, sizeof(buf), total);
991 if (asprintf(&value_str, "%s", buf) < 0) {
992 value_str = (char *)"<...>";
995 value_str_alloc = value_str;
998 list_for_each_entry(chain, &child->parent_val, list) {
999 chain_str = hist_browser__folded_callchain_str(browser,
1000 chain, value_str, chain_str);
1003 first_chain = chain;
1006 if (chain_str == NULL) {
1007 chain_str = (char *)"Not enough memory!";
1011 chain_str_alloc = chain_str;
1014 list_for_each_entry(chain, &child->val, list) {
1015 chain_str = hist_browser__folded_callchain_str(browser,
1016 chain, value_str, chain_str);
1019 first_chain = chain;
1022 if (chain_str == NULL) {
1023 chain_str = (char *)"Not enough memory!";
1027 chain_str_alloc = chain_str;
1031 print(browser, first_chain, chain_str, offset, row++, arg);
1032 free(value_str_alloc);
1033 free(chain_str_alloc);
1036 if (is_output_full(browser, row))
1041 return row - first_row;
1044 static int hist_browser__show_callchain_graph(struct hist_browser *browser,
1045 struct rb_root *root, int level,
1046 unsigned short row, u64 total,
1048 print_callchain_entry_fn print,
1049 struct callchain_print_arg *arg,
1050 check_output_full_fn is_output_full)
1052 struct rb_node *node;
1053 int first_row = row, offset = level * LEVEL_OFFSET_STEP;
1055 u64 percent_total = total;
1057 if (callchain_param.mode == CHAIN_GRAPH_REL)
1058 percent_total = parent_total;
1060 node = rb_first(root);
1061 need_percent = check_percent_display(node, parent_total);
1064 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1065 struct rb_node *next = rb_next(node);
1066 struct callchain_list *chain;
1067 char folded_sign = ' ';
1069 int extra_offset = 0;
1071 list_for_each_entry(chain, &child->val, list) {
1072 bool was_first = first;
1076 else if (need_percent)
1077 extra_offset = LEVEL_OFFSET_STEP;
1079 folded_sign = callchain_list__folded(chain);
1081 row += hist_browser__show_callchain_list(browser, child,
1082 chain, row, percent_total,
1083 was_first && need_percent,
1084 offset + extra_offset,
1087 if (is_output_full(browser, row))
1090 if (folded_sign == '+')
1094 if (folded_sign == '-') {
1095 const int new_level = level + (extra_offset ? 2 : 1);
1097 row += hist_browser__show_callchain_graph(browser, &child->rb_root,
1098 new_level, row, total,
1099 child->children_hit,
1100 print, arg, is_output_full);
1102 if (is_output_full(browser, row))
1107 return row - first_row;
1110 static int hist_browser__show_callchain(struct hist_browser *browser,
1111 struct hist_entry *entry, int level,
1113 print_callchain_entry_fn print,
1114 struct callchain_print_arg *arg,
1115 check_output_full_fn is_output_full)
1117 u64 total = hists__total_period(entry->hists);
1121 if (symbol_conf.cumulate_callchain)
1122 parent_total = entry->stat_acc->period;
1124 parent_total = entry->stat.period;
1126 if (callchain_param.mode == CHAIN_FLAT) {
1127 printed = hist_browser__show_callchain_flat(browser,
1128 &entry->sorted_chain, row,
1129 total, parent_total, print, arg,
1131 } else if (callchain_param.mode == CHAIN_FOLDED) {
1132 printed = hist_browser__show_callchain_folded(browser,
1133 &entry->sorted_chain, row,
1134 total, parent_total, print, arg,
1137 printed = hist_browser__show_callchain_graph(browser,
1138 &entry->sorted_chain, level, row,
1139 total, parent_total, print, arg,
1143 if (arg->is_current_entry)
1144 browser->he_selection = entry;
1150 struct ui_browser *b;
1155 int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
1157 struct hpp_arg *arg = hpp->ptr;
1162 va_start(args, fmt);
1163 len = va_arg(args, int);
1164 percent = va_arg(args, double);
1167 ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
1169 ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
1170 ui_browser__printf(arg->b, "%s", hpp->buf);
1175 #define __HPP_COLOR_PERCENT_FN(_type, _field) \
1176 static u64 __hpp_get_##_field(struct hist_entry *he) \
1178 return he->stat._field; \
1182 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
1183 struct perf_hpp *hpp, \
1184 struct hist_entry *he) \
1186 return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%", \
1187 __hpp__slsmg_color_printf, true); \
1190 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
1191 static u64 __hpp_get_acc_##_field(struct hist_entry *he) \
1193 return he->stat_acc->_field; \
1197 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
1198 struct perf_hpp *hpp, \
1199 struct hist_entry *he) \
1201 if (!symbol_conf.cumulate_callchain) { \
1202 struct hpp_arg *arg = hpp->ptr; \
1203 int len = fmt->user_len ?: fmt->len; \
1204 int ret = scnprintf(hpp->buf, hpp->size, \
1205 "%*s", len, "N/A"); \
1206 ui_browser__printf(arg->b, "%s", hpp->buf); \
1210 return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field, \
1211 " %*.2f%%", __hpp__slsmg_color_printf, true); \
1214 __HPP_COLOR_PERCENT_FN(overhead, period)
1215 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
1216 __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
1217 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
1218 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
1219 __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
1221 #undef __HPP_COLOR_PERCENT_FN
1222 #undef __HPP_COLOR_ACC_PERCENT_FN
1224 void hist_browser__init_hpp(void)
1226 perf_hpp__format[PERF_HPP__OVERHEAD].color =
1227 hist_browser__hpp_color_overhead;
1228 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
1229 hist_browser__hpp_color_overhead_sys;
1230 perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
1231 hist_browser__hpp_color_overhead_us;
1232 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
1233 hist_browser__hpp_color_overhead_guest_sys;
1234 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
1235 hist_browser__hpp_color_overhead_guest_us;
1236 perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
1237 hist_browser__hpp_color_overhead_acc;
1242 static int hist_browser__show_entry(struct hist_browser *browser,
1243 struct hist_entry *entry,
1247 int width = browser->b.width;
1248 char folded_sign = ' ';
1249 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1250 bool use_callchain = hist_entry__has_callchains(entry) && symbol_conf.use_callchain;
1251 off_t row_offset = entry->row_offset;
1253 struct perf_hpp_fmt *fmt;
1255 if (current_entry) {
1256 browser->he_selection = entry;
1257 browser->selection = &entry->ms;
1260 if (use_callchain) {
1261 hist_entry__init_have_children(entry);
1262 folded_sign = hist_entry__folded(entry);
1265 if (row_offset == 0) {
1266 struct hpp_arg arg = {
1268 .folded_sign = folded_sign,
1269 .current_entry = current_entry,
1273 ui_browser__gotorc(&browser->b, row, 0);
1275 hists__for_each_format(browser->hists, fmt) {
1277 struct perf_hpp hpp = {
1283 if (perf_hpp__should_skip(fmt, entry->hists) ||
1284 column++ < browser->b.horiz_scroll)
1287 if (current_entry && browser->b.navkeypressed) {
1288 ui_browser__set_color(&browser->b,
1289 HE_COLORSET_SELECTED);
1291 ui_browser__set_color(&browser->b,
1292 HE_COLORSET_NORMAL);
1296 if (use_callchain) {
1297 ui_browser__printf(&browser->b, "%c ", folded_sign);
1302 ui_browser__printf(&browser->b, " ");
1307 int ret = fmt->color(fmt, &hpp, entry);
1308 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1310 * fmt->color() already used ui_browser to
1311 * print the non alignment bits, skip it (+ret):
1313 ui_browser__printf(&browser->b, "%s", s + ret);
1315 hist_entry__snprintf_alignment(entry, &hpp, fmt, fmt->entry(fmt, &hpp, entry));
1316 ui_browser__printf(&browser->b, "%s", s);
1318 width -= hpp.buf - s;
1321 /* The scroll bar isn't being used */
1322 if (!browser->b.navkeypressed)
1325 ui_browser__write_nstring(&browser->b, "", width);
1332 if (folded_sign == '-' && row != browser->b.rows) {
1333 struct callchain_print_arg arg = {
1334 .row_offset = row_offset,
1335 .is_current_entry = current_entry,
1338 printed += hist_browser__show_callchain(browser,
1340 hist_browser__show_callchain_entry,
1342 hist_browser__check_output_full);
1348 static int hist_browser__show_hierarchy_entry(struct hist_browser *browser,
1349 struct hist_entry *entry,
1354 int width = browser->b.width;
1355 char folded_sign = ' ';
1356 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1357 off_t row_offset = entry->row_offset;
1359 struct perf_hpp_fmt *fmt;
1360 struct perf_hpp_list_node *fmt_node;
1361 struct hpp_arg arg = {
1363 .current_entry = current_entry,
1366 int hierarchy_indent = (entry->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
1368 if (current_entry) {
1369 browser->he_selection = entry;
1370 browser->selection = &entry->ms;
1373 hist_entry__init_have_children(entry);
1374 folded_sign = hist_entry__folded(entry);
1375 arg.folded_sign = folded_sign;
1377 if (entry->leaf && row_offset) {
1379 goto show_callchain;
1382 ui_browser__gotorc(&browser->b, row, 0);
1384 if (current_entry && browser->b.navkeypressed)
1385 ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1387 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1389 ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1390 width -= level * HIERARCHY_INDENT;
1392 /* the first hpp_list_node is for overhead columns */
1393 fmt_node = list_first_entry(&entry->hists->hpp_formats,
1394 struct perf_hpp_list_node, list);
1395 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1397 struct perf_hpp hpp = {
1403 if (perf_hpp__should_skip(fmt, entry->hists) ||
1404 column++ < browser->b.horiz_scroll)
1407 if (current_entry && browser->b.navkeypressed) {
1408 ui_browser__set_color(&browser->b,
1409 HE_COLORSET_SELECTED);
1411 ui_browser__set_color(&browser->b,
1412 HE_COLORSET_NORMAL);
1416 ui_browser__printf(&browser->b, "%c ", folded_sign);
1420 ui_browser__printf(&browser->b, " ");
1425 int ret = fmt->color(fmt, &hpp, entry);
1426 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1428 * fmt->color() already used ui_browser to
1429 * print the non alignment bits, skip it (+ret):
1431 ui_browser__printf(&browser->b, "%s", s + ret);
1433 int ret = fmt->entry(fmt, &hpp, entry);
1434 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1435 ui_browser__printf(&browser->b, "%s", s);
1437 width -= hpp.buf - s;
1441 ui_browser__write_nstring(&browser->b, "", hierarchy_indent);
1442 width -= hierarchy_indent;
1445 if (column >= browser->b.horiz_scroll) {
1447 struct perf_hpp hpp = {
1453 if (current_entry && browser->b.navkeypressed) {
1454 ui_browser__set_color(&browser->b,
1455 HE_COLORSET_SELECTED);
1457 ui_browser__set_color(&browser->b,
1458 HE_COLORSET_NORMAL);
1461 perf_hpp_list__for_each_format(entry->hpp_list, fmt) {
1463 ui_browser__printf(&browser->b, "%c ", folded_sign);
1466 ui_browser__write_nstring(&browser->b, "", 2);
1472 * No need to call hist_entry__snprintf_alignment()
1473 * since this fmt is always the last column in the
1477 width -= fmt->color(fmt, &hpp, entry);
1481 width -= fmt->entry(fmt, &hpp, entry);
1482 ui_browser__printf(&browser->b, "%s", skip_spaces(s));
1484 while (isspace(s[i++]))
1490 /* The scroll bar isn't being used */
1491 if (!browser->b.navkeypressed)
1494 ui_browser__write_nstring(&browser->b, "", width);
1500 if (entry->leaf && folded_sign == '-' && row != browser->b.rows) {
1501 struct callchain_print_arg carg = {
1502 .row_offset = row_offset,
1505 printed += hist_browser__show_callchain(browser, entry,
1507 hist_browser__show_callchain_entry, &carg,
1508 hist_browser__check_output_full);
1514 static int hist_browser__show_no_entry(struct hist_browser *browser,
1515 unsigned short row, int level)
1517 int width = browser->b.width;
1518 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1522 struct perf_hpp_fmt *fmt;
1523 struct perf_hpp_list_node *fmt_node;
1524 int indent = browser->hists->nr_hpp_node - 2;
1526 if (current_entry) {
1527 browser->he_selection = NULL;
1528 browser->selection = NULL;
1531 ui_browser__gotorc(&browser->b, row, 0);
1533 if (current_entry && browser->b.navkeypressed)
1534 ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1536 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1538 ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1539 width -= level * HIERARCHY_INDENT;
1541 /* the first hpp_list_node is for overhead columns */
1542 fmt_node = list_first_entry(&browser->hists->hpp_formats,
1543 struct perf_hpp_list_node, list);
1544 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1545 if (perf_hpp__should_skip(fmt, browser->hists) ||
1546 column++ < browser->b.horiz_scroll)
1549 ret = fmt->width(fmt, NULL, browser->hists);
1552 /* for folded sign */
1556 /* space between columns */
1560 ui_browser__write_nstring(&browser->b, "", ret);
1564 ui_browser__write_nstring(&browser->b, "", indent * HIERARCHY_INDENT);
1565 width -= indent * HIERARCHY_INDENT;
1567 if (column >= browser->b.horiz_scroll) {
1570 ret = snprintf(buf, sizeof(buf), "no entry >= %.2f%%", browser->min_pcnt);
1571 ui_browser__printf(&browser->b, " %s", buf);
1575 /* The scroll bar isn't being used */
1576 if (!browser->b.navkeypressed)
1579 ui_browser__write_nstring(&browser->b, "", width);
1583 static int advance_hpp_check(struct perf_hpp *hpp, int inc)
1585 advance_hpp(hpp, inc);
1586 return hpp->size <= 0;
1590 hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf,
1591 size_t size, int line)
1593 struct hists *hists = browser->hists;
1594 struct perf_hpp dummy_hpp = {
1598 struct perf_hpp_fmt *fmt;
1603 if (hists__has_callchains(hists) && symbol_conf.use_callchain) {
1604 ret = scnprintf(buf, size, " ");
1605 if (advance_hpp_check(&dummy_hpp, ret))
1609 hists__for_each_format(browser->hists, fmt) {
1610 if (perf_hpp__should_skip(fmt, hists) || column++ < browser->b.horiz_scroll)
1613 ret = fmt->header(fmt, &dummy_hpp, hists, line, &span);
1614 if (advance_hpp_check(&dummy_hpp, ret))
1620 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
1621 if (advance_hpp_check(&dummy_hpp, ret))
1628 static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser, char *buf, size_t size)
1630 struct hists *hists = browser->hists;
1631 struct perf_hpp dummy_hpp = {
1635 struct perf_hpp_fmt *fmt;
1636 struct perf_hpp_list_node *fmt_node;
1639 int indent = hists->nr_hpp_node - 2;
1640 bool first_node, first_col;
1642 ret = scnprintf(buf, size, " ");
1643 if (advance_hpp_check(&dummy_hpp, ret))
1647 /* the first hpp_list_node is for overhead columns */
1648 fmt_node = list_first_entry(&hists->hpp_formats,
1649 struct perf_hpp_list_node, list);
1650 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1651 if (column++ < browser->b.horiz_scroll)
1654 ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1655 if (advance_hpp_check(&dummy_hpp, ret))
1658 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
1659 if (advance_hpp_check(&dummy_hpp, ret))
1666 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "%*s",
1667 indent * HIERARCHY_INDENT, "");
1668 if (advance_hpp_check(&dummy_hpp, ret))
1673 list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
1675 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " / ");
1676 if (advance_hpp_check(&dummy_hpp, ret))
1682 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1685 if (perf_hpp__should_skip(fmt, hists))
1689 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "+");
1690 if (advance_hpp_check(&dummy_hpp, ret))
1695 ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1696 dummy_hpp.buf[ret] = '\0';
1698 start = strim(dummy_hpp.buf);
1699 ret = strlen(start);
1701 if (start != dummy_hpp.buf)
1702 memmove(dummy_hpp.buf, start, ret + 1);
1704 if (advance_hpp_check(&dummy_hpp, ret))
1712 static void hists_browser__hierarchy_headers(struct hist_browser *browser)
1716 hists_browser__scnprintf_hierarchy_headers(browser, headers,
1719 ui_browser__gotorc(&browser->b, 0, 0);
1720 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1721 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1724 static void hists_browser__headers(struct hist_browser *browser)
1726 struct hists *hists = browser->hists;
1727 struct perf_hpp_list *hpp_list = hists->hpp_list;
1731 for (line = 0; line < hpp_list->nr_header_lines; line++) {
1734 hists_browser__scnprintf_headers(browser, headers,
1735 sizeof(headers), line);
1737 ui_browser__gotorc_title(&browser->b, line, 0);
1738 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1739 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1743 static void hist_browser__show_headers(struct hist_browser *browser)
1745 if (symbol_conf.report_hierarchy)
1746 hists_browser__hierarchy_headers(browser);
1748 hists_browser__headers(browser);
1751 static void ui_browser__hists_init_top(struct ui_browser *browser)
1753 if (browser->top == NULL) {
1754 struct hist_browser *hb;
1756 hb = container_of(browser, struct hist_browser, b);
1757 browser->top = rb_first_cached(&hb->hists->entries);
1761 static unsigned int hist_browser__refresh(struct ui_browser *browser)
1765 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
1767 if (hb->show_headers)
1768 hist_browser__show_headers(hb);
1770 ui_browser__hists_init_top(browser);
1771 hb->he_selection = NULL;
1772 hb->selection = NULL;
1774 for (nd = browser->top; nd; nd = rb_hierarchy_next(nd)) {
1775 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1779 /* let it move to sibling */
1780 h->unfolded = false;
1784 percent = hist_entry__get_percent_limit(h);
1785 if (percent < hb->min_pcnt)
1788 if (symbol_conf.report_hierarchy) {
1789 row += hist_browser__show_hierarchy_entry(hb, h, row,
1791 if (row == browser->rows)
1794 if (h->has_no_entry) {
1795 hist_browser__show_no_entry(hb, row, h->depth + 1);
1799 row += hist_browser__show_entry(hb, h, row);
1802 if (row == browser->rows)
1809 static struct rb_node *hists__filter_entries(struct rb_node *nd,
1812 while (nd != NULL) {
1813 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1814 float percent = hist_entry__get_percent_limit(h);
1816 if (!h->filtered && percent >= min_pcnt)
1820 * If it's filtered, its all children also were filtered.
1821 * So move to sibling node.
1826 nd = rb_hierarchy_next(nd);
1832 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
1835 while (nd != NULL) {
1836 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1837 float percent = hist_entry__get_percent_limit(h);
1839 if (!h->filtered && percent >= min_pcnt)
1842 nd = rb_hierarchy_prev(nd);
1848 static void ui_browser__hists_seek(struct ui_browser *browser,
1849 off_t offset, int whence)
1851 struct hist_entry *h;
1854 struct hist_browser *hb;
1856 hb = container_of(browser, struct hist_browser, b);
1858 if (browser->nr_entries == 0)
1861 ui_browser__hists_init_top(browser);
1865 nd = hists__filter_entries(rb_first(browser->entries),
1872 nd = rb_hierarchy_last(rb_last(browser->entries));
1873 nd = hists__filter_prev_entries(nd, hb->min_pcnt);
1881 * Moves not relative to the first visible entry invalidates its
1884 h = rb_entry(browser->top, struct hist_entry, rb_node);
1888 * Here we have to check if nd is expanded (+), if it is we can't go
1889 * the next top level hist_entry, instead we must compute an offset of
1890 * what _not_ to show and not change the first visible entry.
1892 * This offset increments when we are going from top to bottom and
1893 * decreases when we're going from bottom to top.
1895 * As we don't have backpointers to the top level in the callchains
1896 * structure, we need to always print the whole hist_entry callchain,
1897 * skipping the first ones that are before the first visible entry
1898 * and stop when we printed enough lines to fill the screen.
1906 h = rb_entry(nd, struct hist_entry, rb_node);
1907 if (h->unfolded && h->leaf) {
1908 u16 remaining = h->nr_rows - h->row_offset;
1909 if (offset > remaining) {
1910 offset -= remaining;
1913 h->row_offset += offset;
1919 nd = hists__filter_entries(rb_hierarchy_next(nd),
1925 } while (offset != 0);
1926 } else if (offset < 0) {
1928 h = rb_entry(nd, struct hist_entry, rb_node);
1929 if (h->unfolded && h->leaf) {
1931 if (-offset > h->row_offset) {
1932 offset += h->row_offset;
1935 h->row_offset += offset;
1941 if (-offset > h->nr_rows) {
1942 offset += h->nr_rows;
1945 h->row_offset = h->nr_rows + offset;
1953 nd = hists__filter_prev_entries(rb_hierarchy_prev(nd),
1961 * Last unfiltered hist_entry, check if it is
1962 * unfolded, if it is then we should have
1963 * row_offset at its last entry.
1965 h = rb_entry(nd, struct hist_entry, rb_node);
1966 if (h->unfolded && h->leaf)
1967 h->row_offset = h->nr_rows;
1974 h = rb_entry(nd, struct hist_entry, rb_node);
1979 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1980 struct hist_entry *he, FILE *fp,
1983 struct callchain_print_arg arg = {
1987 hist_browser__show_callchain(browser, he, level, 0,
1988 hist_browser__fprintf_callchain_entry, &arg,
1989 hist_browser__check_dump_full);
1993 static int hist_browser__fprintf_entry(struct hist_browser *browser,
1994 struct hist_entry *he, FILE *fp)
1998 char folded_sign = ' ';
1999 struct perf_hpp hpp = {
2003 struct perf_hpp_fmt *fmt;
2007 if (hist_entry__has_callchains(he) && symbol_conf.use_callchain) {
2008 folded_sign = hist_entry__folded(he);
2009 printed += fprintf(fp, "%c ", folded_sign);
2012 hists__for_each_format(browser->hists, fmt) {
2013 if (perf_hpp__should_skip(fmt, he->hists))
2017 ret = scnprintf(hpp.buf, hpp.size, " ");
2018 advance_hpp(&hpp, ret);
2022 ret = fmt->entry(fmt, &hpp, he);
2023 ret = hist_entry__snprintf_alignment(he, &hpp, fmt, ret);
2024 advance_hpp(&hpp, ret);
2026 printed += fprintf(fp, "%s\n", s);
2028 if (folded_sign == '-')
2029 printed += hist_browser__fprintf_callchain(browser, he, fp, 1);
2035 static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,
2036 struct hist_entry *he,
2037 FILE *fp, int level)
2041 char folded_sign = ' ';
2042 struct perf_hpp hpp = {
2046 struct perf_hpp_fmt *fmt;
2047 struct perf_hpp_list_node *fmt_node;
2050 int hierarchy_indent = (he->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
2052 printed = fprintf(fp, "%*s", level * HIERARCHY_INDENT, "");
2054 folded_sign = hist_entry__folded(he);
2055 printed += fprintf(fp, "%c", folded_sign);
2057 /* the first hpp_list_node is for overhead columns */
2058 fmt_node = list_first_entry(&he->hists->hpp_formats,
2059 struct perf_hpp_list_node, list);
2060 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
2062 ret = scnprintf(hpp.buf, hpp.size, " ");
2063 advance_hpp(&hpp, ret);
2067 ret = fmt->entry(fmt, &hpp, he);
2068 advance_hpp(&hpp, ret);
2071 ret = scnprintf(hpp.buf, hpp.size, "%*s", hierarchy_indent, "");
2072 advance_hpp(&hpp, ret);
2074 perf_hpp_list__for_each_format(he->hpp_list, fmt) {
2075 ret = scnprintf(hpp.buf, hpp.size, " ");
2076 advance_hpp(&hpp, ret);
2078 ret = fmt->entry(fmt, &hpp, he);
2079 advance_hpp(&hpp, ret);
2083 printed += fprintf(fp, "%s\n", s);
2085 if (he->leaf && folded_sign == '-') {
2086 printed += hist_browser__fprintf_callchain(browser, he, fp,
2093 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
2095 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
2100 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
2102 if (symbol_conf.report_hierarchy) {
2103 printed += hist_browser__fprintf_hierarchy_entry(browser,
2107 printed += hist_browser__fprintf_entry(browser, h, fp);
2110 nd = hists__filter_entries(rb_hierarchy_next(nd),
2117 static int hist_browser__dump(struct hist_browser *browser)
2123 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
2124 if (access(filename, F_OK))
2127 * XXX: Just an arbitrary lazy upper limit
2129 if (++browser->print_seq == 8192) {
2130 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
2135 fp = fopen(filename, "w");
2138 const char *err = str_error_r(errno, bf, sizeof(bf));
2139 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
2143 ++browser->print_seq;
2144 hist_browser__fprintf(browser, fp);
2146 ui_helpline__fpush("%s written!", filename);
2151 void hist_browser__init(struct hist_browser *browser,
2152 struct hists *hists)
2154 struct perf_hpp_fmt *fmt;
2156 browser->hists = hists;
2157 browser->b.refresh = hist_browser__refresh;
2158 browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
2159 browser->b.seek = ui_browser__hists_seek;
2160 browser->b.use_navkeypressed = true;
2161 browser->show_headers = symbol_conf.show_hist_headers;
2162 hist_browser__set_title_space(browser);
2164 if (symbol_conf.report_hierarchy) {
2165 struct perf_hpp_list_node *fmt_node;
2167 /* count overhead columns (in the first node) */
2168 fmt_node = list_first_entry(&hists->hpp_formats,
2169 struct perf_hpp_list_node, list);
2170 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt)
2171 ++browser->b.columns;
2173 /* add a single column for whole hierarchy sort keys*/
2174 ++browser->b.columns;
2176 hists__for_each_format(hists, fmt)
2177 ++browser->b.columns;
2180 hists__reset_column_width(hists);
2183 struct hist_browser *hist_browser__new(struct hists *hists)
2185 struct hist_browser *browser = zalloc(sizeof(*browser));
2188 hist_browser__init(browser, hists);
2193 static struct hist_browser *
2194 perf_evsel_browser__new(struct evsel *evsel,
2195 struct hist_browser_timer *hbt,
2196 struct perf_env *env,
2197 struct annotation_options *annotation_opts)
2199 struct hist_browser *browser = hist_browser__new(evsel__hists(evsel));
2204 browser->title = hists_browser__scnprintf_title;
2205 browser->annotation_opts = annotation_opts;
2210 void hist_browser__delete(struct hist_browser *browser)
2215 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
2217 return browser->he_selection;
2220 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
2222 return browser->he_selection->thread;
2225 /* Check whether the browser is for 'top' or 'report' */
2226 static inline bool is_report_browser(void *timer)
2228 return timer == NULL;
2231 static int hists_browser__scnprintf_title(struct hist_browser *browser, char *bf, size_t size)
2233 struct hist_browser_timer *hbt = browser->hbt;
2234 int printed = __hists__scnprintf_title(browser->hists, bf, size, !is_report_browser(hbt));
2236 if (!is_report_browser(hbt)) {
2237 struct perf_top *top = hbt->arg;
2239 printed += scnprintf(bf + printed, size - printed,
2240 " lost: %" PRIu64 "/%" PRIu64,
2241 top->lost, top->lost_total);
2243 printed += scnprintf(bf + printed, size - printed,
2244 " drop: %" PRIu64 "/%" PRIu64,
2245 top->drop, top->drop_total);
2248 printed += scnprintf(bf + printed, size - printed, " [z]");
2250 perf_top__reset_sample_counters(top);
2257 static inline void free_popup_options(char **options, int n)
2261 for (i = 0; i < n; ++i)
2266 * Only runtime switching of perf data file will make "input_name" point
2267 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
2268 * whether we need to call free() for current "input_name" during the switch.
2270 static bool is_input_name_malloced = false;
2272 static int switch_data_file(void)
2274 char *pwd, *options[32], *abs_path[32], *tmp;
2276 int nr_options = 0, choice = -1, ret = -1;
2277 struct dirent *dent;
2279 pwd = getenv("PWD");
2283 pwd_dir = opendir(pwd);
2287 memset(options, 0, sizeof(options));
2288 memset(abs_path, 0, sizeof(abs_path));
2290 while ((dent = readdir(pwd_dir))) {
2291 char path[PATH_MAX];
2293 char *name = dent->d_name;
2296 if (!(dent->d_type == DT_REG))
2299 snprintf(path, sizeof(path), "%s/%s", pwd, name);
2301 file = fopen(path, "r");
2305 if (fread(&magic, 1, 8, file) < 8)
2306 goto close_file_and_continue;
2308 if (is_perf_magic(magic)) {
2309 options[nr_options] = strdup(name);
2310 if (!options[nr_options])
2311 goto close_file_and_continue;
2313 abs_path[nr_options] = strdup(path);
2314 if (!abs_path[nr_options]) {
2315 zfree(&options[nr_options]);
2316 ui__warning("Can't search all data files due to memory shortage.\n");
2324 close_file_and_continue:
2326 if (nr_options >= 32) {
2327 ui__warning("Too many perf data files in PWD!\n"
2328 "Only the first 32 files will be listed.\n");
2335 choice = ui__popup_menu(nr_options, options);
2336 if (choice < nr_options && choice >= 0) {
2337 tmp = strdup(abs_path[choice]);
2339 if (is_input_name_malloced)
2340 free((void *)input_name);
2342 is_input_name_malloced = true;
2345 ui__warning("Data switch failed due to memory shortage!\n");
2349 free_popup_options(options, nr_options);
2350 free_popup_options(abs_path, nr_options);
2354 struct popup_action {
2356 struct thread *thread;
2357 struct map_symbol ms;
2359 struct evsel *evsel;
2362 int (*fn)(struct hist_browser *browser, struct popup_action *act);
2366 do_annotate(struct hist_browser *browser, struct popup_action *act)
2368 struct evsel *evsel;
2369 struct annotation *notes;
2370 struct hist_entry *he;
2373 if (!browser->annotation_opts->objdump_path &&
2374 perf_env__lookup_objdump(browser->env, &browser->annotation_opts->objdump_path))
2377 notes = symbol__annotation(act->ms.sym);
2381 evsel = hists_to_evsel(browser->hists);
2382 err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt,
2383 browser->annotation_opts);
2384 he = hist_browser__selected_entry(browser);
2386 * offer option to annotate the other branch source or target
2387 * (if they exists) when returning from annotate
2389 if ((err == 'q' || err == CTRL('c')) && he->branch_info)
2392 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
2394 ui_browser__handle_resize(&browser->b);
2399 add_annotate_opt(struct hist_browser *browser __maybe_unused,
2400 struct popup_action *act, char **optstr,
2401 struct map *map, struct symbol *sym)
2403 if (sym == NULL || map->dso->annotate_warned)
2406 if (asprintf(optstr, "Annotate %s", sym->name) < 0)
2411 act->fn = do_annotate;
2416 do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
2418 struct thread *thread = act->thread;
2420 if ((!hists__has(browser->hists, thread) &&
2421 !hists__has(browser->hists, comm)) || thread == NULL)
2424 if (browser->hists->thread_filter) {
2425 pstack__remove(browser->pstack, &browser->hists->thread_filter);
2426 perf_hpp__set_elide(HISTC_THREAD, false);
2427 thread__zput(browser->hists->thread_filter);
2430 if (hists__has(browser->hists, thread)) {
2431 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
2432 thread->comm_set ? thread__comm_str(thread) : "",
2435 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
2436 thread->comm_set ? thread__comm_str(thread) : "");
2439 browser->hists->thread_filter = thread__get(thread);
2440 perf_hpp__set_elide(HISTC_THREAD, false);
2441 pstack__push(browser->pstack, &browser->hists->thread_filter);
2444 hists__filter_by_thread(browser->hists);
2445 hist_browser__reset(browser);
2450 add_thread_opt(struct hist_browser *browser, struct popup_action *act,
2451 char **optstr, struct thread *thread)
2455 if ((!hists__has(browser->hists, thread) &&
2456 !hists__has(browser->hists, comm)) || thread == NULL)
2459 if (hists__has(browser->hists, thread)) {
2460 ret = asprintf(optstr, "Zoom %s %s(%d) thread",
2461 browser->hists->thread_filter ? "out of" : "into",
2462 thread->comm_set ? thread__comm_str(thread) : "",
2465 ret = asprintf(optstr, "Zoom %s %s thread",
2466 browser->hists->thread_filter ? "out of" : "into",
2467 thread->comm_set ? thread__comm_str(thread) : "");
2472 act->thread = thread;
2473 act->fn = do_zoom_thread;
2478 do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
2480 struct map *map = act->ms.map;
2482 if (!hists__has(browser->hists, dso) || map == NULL)
2485 if (browser->hists->dso_filter) {
2486 pstack__remove(browser->pstack, &browser->hists->dso_filter);
2487 perf_hpp__set_elide(HISTC_DSO, false);
2488 browser->hists->dso_filter = NULL;
2491 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
2492 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
2493 browser->hists->dso_filter = map->dso;
2494 perf_hpp__set_elide(HISTC_DSO, true);
2495 pstack__push(browser->pstack, &browser->hists->dso_filter);
2498 hists__filter_by_dso(browser->hists);
2499 hist_browser__reset(browser);
2504 add_dso_opt(struct hist_browser *browser, struct popup_action *act,
2505 char **optstr, struct map *map)
2507 if (!hists__has(browser->hists, dso) || map == NULL)
2510 if (asprintf(optstr, "Zoom %s %s DSO",
2511 browser->hists->dso_filter ? "out of" : "into",
2512 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
2516 act->fn = do_zoom_dso;
2521 do_browse_map(struct hist_browser *browser __maybe_unused,
2522 struct popup_action *act)
2524 map__browse(act->ms.map);
2529 add_map_opt(struct hist_browser *browser,
2530 struct popup_action *act, char **optstr, struct map *map)
2532 if (!hists__has(browser->hists, dso) || map == NULL)
2535 if (asprintf(optstr, "Browse map details") < 0)
2539 act->fn = do_browse_map;
2544 do_run_script(struct hist_browser *browser __maybe_unused,
2545 struct popup_action *act)
2553 len += strlen(thread__comm_str(act->thread));
2554 else if (act->ms.sym)
2555 len += strlen(act->ms.sym->name);
2556 script_opt = malloc(len);
2562 n = scnprintf(script_opt, len, " -c %s ",
2563 thread__comm_str(act->thread));
2564 } else if (act->ms.sym) {
2565 n = scnprintf(script_opt, len, " -S %s ",
2570 char start[32], end[32];
2571 unsigned long starttime = act->time;
2572 unsigned long endtime = act->time + symbol_conf.time_quantum;
2574 if (starttime == endtime) { /* Display 1ms as fallback */
2575 starttime -= 1*NSEC_PER_MSEC;
2576 endtime += 1*NSEC_PER_MSEC;
2578 timestamp__scnprintf_usec(starttime, start, sizeof start);
2579 timestamp__scnprintf_usec(endtime, end, sizeof end);
2580 n += snprintf(script_opt + n, len - n, " --time %s,%s", start, end);
2583 script_browse(script_opt, act->evsel);
2589 do_res_sample_script(struct hist_browser *browser __maybe_unused,
2590 struct popup_action *act)
2592 struct hist_entry *he;
2594 he = hist_browser__selected_entry(browser);
2595 res_sample_browse(he->res_samples, he->num_res, act->evsel, act->rstype);
2600 add_script_opt_2(struct hist_browser *browser __maybe_unused,
2601 struct popup_action *act, char **optstr,
2602 struct thread *thread, struct symbol *sym,
2603 struct evsel *evsel, const char *tstr)
2607 if (asprintf(optstr, "Run scripts for samples of thread [%s]%s",
2608 thread__comm_str(thread), tstr) < 0)
2611 if (asprintf(optstr, "Run scripts for samples of symbol [%s]%s",
2612 sym->name, tstr) < 0)
2615 if (asprintf(optstr, "Run scripts for all samples%s", tstr) < 0)
2619 act->thread = thread;
2622 act->fn = do_run_script;
2627 add_script_opt(struct hist_browser *browser,
2628 struct popup_action *act, char **optstr,
2629 struct thread *thread, struct symbol *sym,
2630 struct evsel *evsel)
2633 struct hist_entry *he;
2635 n = add_script_opt_2(browser, act, optstr, thread, sym, evsel, "");
2637 he = hist_browser__selected_entry(browser);
2638 if (sort_order && strstr(sort_order, "time")) {
2643 j = sprintf(tstr, " in ");
2644 j += timestamp__scnprintf_usec(he->time, tstr + j,
2646 j += sprintf(tstr + j, "-");
2647 timestamp__scnprintf_usec(he->time + symbol_conf.time_quantum,
2648 tstr + j, sizeof tstr - j);
2649 n += add_script_opt_2(browser, act, optstr, thread, sym,
2651 act->time = he->time;
2657 add_res_sample_opt(struct hist_browser *browser __maybe_unused,
2658 struct popup_action *act, char **optstr,
2659 struct res_sample *res_sample,
2660 struct evsel *evsel,
2666 if (asprintf(optstr, "Show context for individual samples %s",
2667 type == A_ASM ? "with assembler" :
2668 type == A_SOURCE ? "with source" : "") < 0)
2671 act->fn = do_res_sample_script;
2678 do_switch_data(struct hist_browser *browser __maybe_unused,
2679 struct popup_action *act __maybe_unused)
2681 if (switch_data_file()) {
2682 ui__warning("Won't switch the data files due to\n"
2683 "no valid data file get selected!\n");
2687 return K_SWITCH_INPUT_DATA;
2691 add_switch_opt(struct hist_browser *browser,
2692 struct popup_action *act, char **optstr)
2694 if (!is_report_browser(browser->hbt))
2697 if (asprintf(optstr, "Switch to another data file in PWD") < 0)
2700 act->fn = do_switch_data;
2705 do_exit_browser(struct hist_browser *browser __maybe_unused,
2706 struct popup_action *act __maybe_unused)
2712 add_exit_opt(struct hist_browser *browser __maybe_unused,
2713 struct popup_action *act, char **optstr)
2715 if (asprintf(optstr, "Exit") < 0)
2718 act->fn = do_exit_browser;
2723 do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
2725 if (!hists__has(browser->hists, socket) || act->socket < 0)
2728 if (browser->hists->socket_filter > -1) {
2729 pstack__remove(browser->pstack, &browser->hists->socket_filter);
2730 browser->hists->socket_filter = -1;
2731 perf_hpp__set_elide(HISTC_SOCKET, false);
2733 browser->hists->socket_filter = act->socket;
2734 perf_hpp__set_elide(HISTC_SOCKET, true);
2735 pstack__push(browser->pstack, &browser->hists->socket_filter);
2738 hists__filter_by_socket(browser->hists);
2739 hist_browser__reset(browser);
2744 add_socket_opt(struct hist_browser *browser, struct popup_action *act,
2745 char **optstr, int socket_id)
2747 if (!hists__has(browser->hists, socket) || socket_id < 0)
2750 if (asprintf(optstr, "Zoom %s Processor Socket %d",
2751 (browser->hists->socket_filter > -1) ? "out of" : "into",
2755 act->socket = socket_id;
2756 act->fn = do_zoom_socket;
2760 static void hist_browser__update_nr_entries(struct hist_browser *hb)
2763 struct rb_node *nd = rb_first_cached(&hb->hists->entries);
2765 if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
2766 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
2770 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2772 nd = rb_hierarchy_next(nd);
2775 hb->nr_non_filtered_entries = nr_entries;
2776 hb->nr_hierarchy_entries = nr_entries;
2779 static void hist_browser__update_percent_limit(struct hist_browser *hb,
2782 struct hist_entry *he;
2783 struct rb_node *nd = rb_first_cached(&hb->hists->entries);
2784 u64 total = hists__total_period(hb->hists);
2785 u64 min_callchain_hits = total * (percent / 100);
2787 hb->min_pcnt = callchain_param.min_percent = percent;
2789 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2790 he = rb_entry(nd, struct hist_entry, rb_node);
2792 if (he->has_no_entry) {
2793 he->has_no_entry = false;
2797 if (!he->leaf || !hist_entry__has_callchains(he) || !symbol_conf.use_callchain)
2800 if (callchain_param.mode == CHAIN_GRAPH_REL) {
2801 total = he->stat.period;
2803 if (symbol_conf.cumulate_callchain)
2804 total = he->stat_acc->period;
2806 min_callchain_hits = total * (percent / 100);
2809 callchain_param.sort(&he->sorted_chain, he->callchain,
2810 min_callchain_hits, &callchain_param);
2813 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
2815 /* force to re-evaluate folding state of callchains */
2816 he->init_have_children = false;
2817 hist_entry__set_folding(he, hb, false);
2821 static int perf_evsel__hists_browse(struct evsel *evsel, int nr_events,
2822 const char *helpline,
2824 struct hist_browser_timer *hbt,
2826 struct perf_env *env,
2827 bool warn_lost_event,
2828 struct annotation_options *annotation_opts)
2830 struct hists *hists = evsel__hists(evsel);
2831 struct hist_browser *browser = perf_evsel_browser__new(evsel, hbt, env, annotation_opts);
2832 struct branch_info *bi = NULL;
2833 #define MAX_OPTIONS 16
2834 char *options[MAX_OPTIONS];
2835 struct popup_action actions[MAX_OPTIONS];
2839 int delay_secs = hbt ? hbt->refresh : 0;
2841 #define HIST_BROWSER_HELP_COMMON \
2842 "h/?/F1 Show this window\n" \
2844 "PGDN/SPACE Navigate\n" \
2845 "q/ESC/CTRL+C Exit browser or go back to previous screen\n\n" \
2846 "For multiple event sessions:\n\n" \
2847 "TAB/UNTAB Switch events\n\n" \
2848 "For symbolic views (--sort has sym):\n\n" \
2849 "ENTER Zoom into DSO/Threads & Annotate current symbol\n" \
2851 "a Annotate current symbol\n" \
2852 "C Collapse all callchains\n" \
2853 "d Zoom into current DSO\n" \
2854 "E Expand all callchains\n" \
2855 "F Toggle percentage of filtered entries\n" \
2856 "H Display column headers\n" \
2857 "L Change percent limit\n" \
2858 "m Display context menu\n" \
2859 "S Zoom into current Processor Socket\n" \
2861 /* help messages are sorted by lexical order of the hotkey */
2862 static const char report_help[] = HIST_BROWSER_HELP_COMMON
2863 "i Show header information\n"
2864 "P Print histograms to perf.hist.N\n"
2865 "r Run available scripts\n"
2866 "s Switch to another data file in PWD\n"
2867 "t Zoom into current Thread\n"
2868 "V Verbose (DSO names in callchains, etc)\n"
2869 "/ Filter symbol by name";
2870 static const char top_help[] = HIST_BROWSER_HELP_COMMON
2871 "P Print histograms to perf.hist.N\n"
2872 "t Zoom into current Thread\n"
2873 "V Verbose (DSO names in callchains, etc)\n"
2874 "z Toggle zeroing of samples\n"
2875 "f Enable/Disable events\n"
2876 "/ Filter symbol by name";
2878 if (browser == NULL)
2881 /* reset abort key so that it can get Ctrl-C as a key */
2883 SLang_init_tty(0, 0, 0);
2886 browser->min_pcnt = min_pcnt;
2887 hist_browser__update_nr_entries(browser);
2889 browser->pstack = pstack__new(3);
2890 if (browser->pstack == NULL)
2893 ui_helpline__push(helpline);
2895 memset(options, 0, sizeof(options));
2896 memset(actions, 0, sizeof(actions));
2898 if (symbol_conf.col_width_list_str)
2899 perf_hpp__set_user_width(symbol_conf.col_width_list_str);
2901 if (!is_report_browser(hbt))
2902 browser->b.no_samples_msg = "Collecting samples...";
2905 struct thread *thread = NULL;
2906 struct map *map = NULL;
2912 key = hist_browser__run(browser, helpline,
2915 if (browser->he_selection != NULL) {
2916 thread = hist_browser__selected_thread(browser);
2917 map = browser->selection->map;
2918 socked_id = browser->he_selection->socket;
2926 * Exit the browser, let hists__browser_tree
2927 * go to the next or previous
2929 goto out_free_stack;
2931 if (!hists__has(hists, sym)) {
2932 ui_browser__warning(&browser->b, delay_secs * 2,
2933 "Annotation is only available for symbolic views, "
2934 "include \"sym*\" in --sort to use it.");
2938 if (browser->selection == NULL ||
2939 browser->selection->sym == NULL ||
2940 browser->selection->map->dso->annotate_warned)
2943 actions->ms.map = browser->selection->map;
2944 actions->ms.sym = browser->selection->sym;
2945 do_annotate(browser, actions);
2948 hist_browser__dump(browser);
2951 actions->ms.map = map;
2952 do_zoom_dso(browser, actions);
2955 verbose = (verbose + 1) % 4;
2956 browser->show_dso = verbose > 0;
2957 ui_helpline__fpush("Verbosity level set to %d\n",
2961 actions->thread = thread;
2962 do_zoom_thread(browser, actions);
2965 actions->socket = socked_id;
2966 do_zoom_socket(browser, actions);
2969 if (ui_browser__input_window("Symbol to show",
2970 "Please enter the name of symbol you want to see.\n"
2971 "To remove the filter later, press / + ENTER.",
2972 buf, "ENTER: OK, ESC: Cancel",
2973 delay_secs * 2) == K_ENTER) {
2974 hists->symbol_filter_str = *buf ? buf : NULL;
2975 hists__filter_by_symbol(hists);
2976 hist_browser__reset(browser);
2980 if (is_report_browser(hbt)) {
2981 actions->thread = NULL;
2982 actions->ms.sym = NULL;
2983 do_run_script(browser, actions);
2987 if (is_report_browser(hbt)) {
2988 key = do_switch_data(browser, actions);
2989 if (key == K_SWITCH_INPUT_DATA)
2990 goto out_free_stack;
2994 /* env->arch is NULL for live-mode (i.e. perf top) */
2996 tui__header_window(env);
2999 symbol_conf.filter_relative ^= 1;
3002 if (!is_report_browser(hbt)) {
3003 struct perf_top *top = hbt->arg;
3005 top->zero = !top->zero;
3009 if (ui_browser__input_window("Percent Limit",
3010 "Please enter the value you want to hide entries under that percent.",
3011 buf, "ENTER: OK, ESC: Cancel",
3012 delay_secs * 2) == K_ENTER) {
3014 double new_percent = strtod(buf, &end);
3016 if (new_percent < 0 || new_percent > 100) {
3017 ui_browser__warning(&browser->b, delay_secs * 2,
3018 "Invalid percent: %.2f", new_percent);
3022 hist_browser__update_percent_limit(browser, new_percent);
3023 hist_browser__reset(browser);
3029 ui_browser__help_window(&browser->b,
3030 is_report_browser(hbt) ? report_help : top_help);
3041 if (pstack__empty(browser->pstack)) {
3043 * Go back to the perf_evsel_menu__run or other user
3046 goto out_free_stack;
3049 ui_browser__dialog_yesno(&browser->b,
3050 "Do you really want to exit?"))
3051 goto out_free_stack;
3055 top = pstack__peek(browser->pstack);
3056 if (top == &browser->hists->dso_filter) {
3058 * No need to set actions->dso here since
3059 * it's just to remove the current filter.
3060 * Ditto for thread below.
3062 do_zoom_dso(browser, actions);
3063 } else if (top == &browser->hists->thread_filter) {
3064 do_zoom_thread(browser, actions);
3065 } else if (top == &browser->hists->socket_filter) {
3066 do_zoom_socket(browser, actions);
3072 goto out_free_stack;
3074 if (!is_report_browser(hbt)) {
3075 struct perf_top *top = hbt->arg;
3077 perf_evlist__toggle_enable(top->evlist);
3079 * No need to refresh, resort/decay histogram
3080 * entries if we are not collecting samples:
3082 if (top->evlist->enabled) {
3083 helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
3084 hbt->refresh = delay_secs;
3086 helpline = "Press 'f' again to re-enable the events";
3093 helpline = "Press '?' for help on key bindings";
3097 if (!hists__has(hists, sym) || browser->selection == NULL)
3098 goto skip_annotation;
3100 if (sort__mode == SORT_MODE__BRANCH) {
3102 if (browser->he_selection)
3103 bi = browser->he_selection->branch_info;
3106 goto skip_annotation;
3108 nr_options += add_annotate_opt(browser,
3109 &actions[nr_options],
3110 &options[nr_options],
3113 if (bi->to.sym != bi->from.sym)
3114 nr_options += add_annotate_opt(browser,
3115 &actions[nr_options],
3116 &options[nr_options],
3120 nr_options += add_annotate_opt(browser,
3121 &actions[nr_options],
3122 &options[nr_options],
3123 browser->selection->map,
3124 browser->selection->sym);
3127 nr_options += add_thread_opt(browser, &actions[nr_options],
3128 &options[nr_options], thread);
3129 nr_options += add_dso_opt(browser, &actions[nr_options],
3130 &options[nr_options], map);
3131 nr_options += add_map_opt(browser, &actions[nr_options],
3132 &options[nr_options],
3133 browser->selection ?
3134 browser->selection->map : NULL);
3135 nr_options += add_socket_opt(browser, &actions[nr_options],
3136 &options[nr_options],
3138 /* perf script support */
3139 if (!is_report_browser(hbt))
3140 goto skip_scripting;
3142 if (browser->he_selection) {
3143 if (hists__has(hists, thread) && thread) {
3144 nr_options += add_script_opt(browser,
3145 &actions[nr_options],
3146 &options[nr_options],
3147 thread, NULL, evsel);
3150 * Note that browser->selection != NULL
3151 * when browser->he_selection is not NULL,
3152 * so we don't need to check browser->selection
3153 * before fetching browser->selection->sym like what
3154 * we do before fetching browser->selection->map.
3156 * See hist_browser__show_entry.
3158 if (hists__has(hists, sym) && browser->selection->sym) {
3159 nr_options += add_script_opt(browser,
3160 &actions[nr_options],
3161 &options[nr_options],
3162 NULL, browser->selection->sym,
3166 nr_options += add_script_opt(browser, &actions[nr_options],
3167 &options[nr_options], NULL, NULL, evsel);
3168 nr_options += add_res_sample_opt(browser, &actions[nr_options],
3169 &options[nr_options],
3170 hist_browser__selected_entry(browser)->res_samples,
3172 nr_options += add_res_sample_opt(browser, &actions[nr_options],
3173 &options[nr_options],
3174 hist_browser__selected_entry(browser)->res_samples,
3176 nr_options += add_res_sample_opt(browser, &actions[nr_options],
3177 &options[nr_options],
3178 hist_browser__selected_entry(browser)->res_samples,
3180 nr_options += add_switch_opt(browser, &actions[nr_options],
3181 &options[nr_options]);
3183 nr_options += add_exit_opt(browser, &actions[nr_options],
3184 &options[nr_options]);
3187 struct popup_action *act;
3189 choice = ui__popup_menu(nr_options, options);
3190 if (choice == -1 || choice >= nr_options)
3193 act = &actions[choice];
3194 key = act->fn(browser, act);
3197 if (key == K_SWITCH_INPUT_DATA)
3201 pstack__delete(browser->pstack);
3203 hist_browser__delete(browser);
3204 free_popup_options(options, MAX_OPTIONS);
3209 struct ui_browser b;
3210 struct evsel *selection;
3211 struct annotation_options *annotation_opts;
3212 bool lost_events, lost_events_warned;
3214 struct perf_env *env;
3217 static void perf_evsel_menu__write(struct ui_browser *browser,
3218 void *entry, int row)
3220 struct evsel_menu *menu = container_of(browser,
3221 struct evsel_menu, b);
3222 struct evsel *evsel = list_entry(entry, struct evsel, core.node);
3223 struct hists *hists = evsel__hists(evsel);
3224 bool current_entry = ui_browser__is_current_entry(browser, row);
3225 unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
3226 const char *ev_name = perf_evsel__name(evsel);
3228 const char *warn = " ";
3231 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
3232 HE_COLORSET_NORMAL);
3234 if (perf_evsel__is_group_event(evsel)) {
3237 ev_name = perf_evsel__group_name(evsel);
3239 for_each_group_member(pos, evsel) {
3240 struct hists *pos_hists = evsel__hists(pos);
3241 nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
3245 nr_events = convert_unit(nr_events, &unit);
3246 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
3247 unit, unit == ' ' ? "" : " ", ev_name);
3248 ui_browser__printf(browser, "%s", bf);
3250 nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
3251 if (nr_events != 0) {
3252 menu->lost_events = true;
3254 ui_browser__set_color(browser, HE_COLORSET_TOP);
3255 nr_events = convert_unit(nr_events, &unit);
3256 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
3257 nr_events, unit, unit == ' ' ? "" : " ");
3261 ui_browser__write_nstring(browser, warn, browser->width - printed);
3264 menu->selection = evsel;
3267 static int perf_evsel_menu__run(struct evsel_menu *menu,
3268 int nr_events, const char *help,
3269 struct hist_browser_timer *hbt,
3270 bool warn_lost_event)
3272 struct evlist *evlist = menu->b.priv;
3274 const char *title = "Available samples";
3275 int delay_secs = hbt ? hbt->refresh : 0;
3278 if (ui_browser__show(&menu->b, title,
3279 "ESC: exit, ENTER|->: Browse histograms") < 0)
3283 key = ui_browser__run(&menu->b, delay_secs);
3288 hbt->timer(hbt->arg);
3290 if (!menu->lost_events_warned &&
3291 menu->lost_events &&
3293 ui_browser__warn_lost_events(&menu->b);
3294 menu->lost_events_warned = true;
3299 if (!menu->selection)
3301 pos = menu->selection;
3303 perf_evlist__set_selected(evlist, pos);
3305 * Give the calling tool a chance to populate the non
3306 * default evsel resorted hists tree.
3309 hbt->timer(hbt->arg);
3310 key = perf_evsel__hists_browse(pos, nr_events, help,
3315 menu->annotation_opts);
3316 ui_browser__show_title(&menu->b, title);
3319 if (pos->core.node.next == &evlist->core.entries)
3320 pos = perf_evlist__first(evlist);
3322 pos = perf_evsel__next(pos);
3325 if (pos->core.node.prev == &evlist->core.entries)
3326 pos = perf_evlist__last(evlist);
3328 pos = perf_evsel__prev(pos);
3330 case K_SWITCH_INPUT_DATA:
3341 if (!ui_browser__dialog_yesno(&menu->b,
3342 "Do you really want to exit?"))
3354 ui_browser__hide(&menu->b);
3358 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
3361 struct evsel *evsel = list_entry(entry, struct evsel, core.node);
3363 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
3369 static int __perf_evlist__tui_browse_hists(struct evlist *evlist,
3370 int nr_entries, const char *help,
3371 struct hist_browser_timer *hbt,
3373 struct perf_env *env,
3374 bool warn_lost_event,
3375 struct annotation_options *annotation_opts)
3378 struct evsel_menu menu = {
3380 .entries = &evlist->core.entries,
3381 .refresh = ui_browser__list_head_refresh,
3382 .seek = ui_browser__list_head_seek,
3383 .write = perf_evsel_menu__write,
3384 .filter = filter_group_entries,
3385 .nr_entries = nr_entries,
3388 .min_pcnt = min_pcnt,
3390 .annotation_opts = annotation_opts,
3393 ui_helpline__push("Press ESC to exit");
3395 evlist__for_each_entry(evlist, pos) {
3396 const char *ev_name = perf_evsel__name(pos);
3397 size_t line_len = strlen(ev_name) + 7;
3399 if (menu.b.width < line_len)
3400 menu.b.width = line_len;
3403 return perf_evsel_menu__run(&menu, nr_entries, help,
3404 hbt, warn_lost_event);
3407 int perf_evlist__tui_browse_hists(struct evlist *evlist, const char *help,
3408 struct hist_browser_timer *hbt,
3410 struct perf_env *env,
3411 bool warn_lost_event,
3412 struct annotation_options *annotation_opts)
3414 int nr_entries = evlist->core.nr_entries;
3417 if (nr_entries == 1) {
3418 struct evsel *first = perf_evlist__first(evlist);
3420 return perf_evsel__hists_browse(first, nr_entries, help,
3421 false, hbt, min_pcnt,
3422 env, warn_lost_event,
3426 if (symbol_conf.event_group) {
3430 evlist__for_each_entry(evlist, pos) {
3431 if (perf_evsel__is_group_leader(pos))
3435 if (nr_entries == 1)
3439 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,