2 #include "../libslang.h"
5 #include <linux/rbtree.h>
7 #include "../../util/evsel.h"
8 #include "../../util/evlist.h"
9 #include "../../util/hist.h"
10 #include "../../util/pstack.h"
11 #include "../../util/sort.h"
12 #include "../../util/util.h"
13 #include "../../arch/common.h"
15 #include "../browser.h"
16 #include "../helpline.h"
24 struct hist_entry *he_selection;
25 struct map_symbol *selection;
32 extern void hist_browser__init_hpp(void);
34 static int hists__browser_title(struct hists *hists, char *bf, size_t size,
37 static void hist_browser__refresh_dimensions(struct hist_browser *browser)
39 /* 3 == +/- toggle symbol before actual hist_entry rendering */
40 browser->b.width = 3 + (hists__sort_list_width(browser->hists) +
44 static void hist_browser__reset(struct hist_browser *browser)
46 browser->b.nr_entries = browser->hists->nr_entries;
47 hist_browser__refresh_dimensions(browser);
48 ui_browser__reset_index(&browser->b);
51 static char tree__folded_sign(bool unfolded)
53 return unfolded ? '-' : '+';
56 static char map_symbol__folded(const struct map_symbol *ms)
58 return ms->has_children ? tree__folded_sign(ms->unfolded) : ' ';
61 static char hist_entry__folded(const struct hist_entry *he)
63 return map_symbol__folded(&he->ms);
66 static char callchain_list__folded(const struct callchain_list *cl)
68 return map_symbol__folded(&cl->ms);
71 static void map_symbol__set_folding(struct map_symbol *ms, bool unfold)
73 ms->unfolded = unfold ? ms->has_children : false;
76 static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
81 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
82 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
83 struct callchain_list *chain;
84 char folded_sign = ' '; /* No children */
86 list_for_each_entry(chain, &child->val, list) {
88 /* We need this because we may not have children */
89 folded_sign = callchain_list__folded(chain);
90 if (folded_sign == '+')
94 if (folded_sign == '-') /* Have children and they're unfolded */
95 n += callchain_node__count_rows_rb_tree(child);
101 static int callchain_node__count_rows(struct callchain_node *node)
103 struct callchain_list *chain;
104 bool unfolded = false;
107 list_for_each_entry(chain, &node->val, list) {
109 unfolded = chain->ms.unfolded;
113 n += callchain_node__count_rows_rb_tree(node);
118 static int callchain__count_rows(struct rb_root *chain)
123 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
124 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
125 n += callchain_node__count_rows(node);
131 static bool map_symbol__toggle_fold(struct map_symbol *ms)
136 if (!ms->has_children)
139 ms->unfolded = !ms->unfolded;
143 static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
145 struct rb_node *nd = rb_first(&node->rb_root);
147 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
148 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
149 struct callchain_list *chain;
152 list_for_each_entry(chain, &child->val, list) {
155 chain->ms.has_children = chain->list.next != &child->val ||
156 !RB_EMPTY_ROOT(&child->rb_root);
158 chain->ms.has_children = chain->list.next == &child->val &&
159 !RB_EMPTY_ROOT(&child->rb_root);
162 callchain_node__init_have_children_rb_tree(child);
166 static void callchain_node__init_have_children(struct callchain_node *node)
168 struct callchain_list *chain;
170 list_for_each_entry(chain, &node->val, list)
171 chain->ms.has_children = !RB_EMPTY_ROOT(&node->rb_root);
173 callchain_node__init_have_children_rb_tree(node);
176 static void callchain__init_have_children(struct rb_root *root)
180 for (nd = rb_first(root); nd; nd = rb_next(nd)) {
181 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
182 callchain_node__init_have_children(node);
186 static void hist_entry__init_have_children(struct hist_entry *he)
188 if (!he->init_have_children) {
189 he->ms.has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
190 callchain__init_have_children(&he->sorted_chain);
191 he->init_have_children = true;
195 static bool hist_browser__toggle_fold(struct hist_browser *browser)
197 if (map_symbol__toggle_fold(browser->selection)) {
198 struct hist_entry *he = browser->he_selection;
200 hist_entry__init_have_children(he);
201 browser->hists->nr_entries -= he->nr_rows;
204 he->nr_rows = callchain__count_rows(&he->sorted_chain);
207 browser->hists->nr_entries += he->nr_rows;
208 browser->b.nr_entries = browser->hists->nr_entries;
213 /* If it doesn't have children, no toggling performed */
217 static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
222 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
223 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
224 struct callchain_list *chain;
225 bool has_children = false;
227 list_for_each_entry(chain, &child->val, list) {
229 map_symbol__set_folding(&chain->ms, unfold);
230 has_children = chain->ms.has_children;
234 n += callchain_node__set_folding_rb_tree(child, unfold);
240 static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
242 struct callchain_list *chain;
243 bool has_children = false;
246 list_for_each_entry(chain, &node->val, list) {
248 map_symbol__set_folding(&chain->ms, unfold);
249 has_children = chain->ms.has_children;
253 n += callchain_node__set_folding_rb_tree(node, unfold);
258 static int callchain__set_folding(struct rb_root *chain, bool unfold)
263 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
264 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
265 n += callchain_node__set_folding(node, unfold);
271 static void hist_entry__set_folding(struct hist_entry *he, bool unfold)
273 hist_entry__init_have_children(he);
274 map_symbol__set_folding(&he->ms, unfold);
276 if (he->ms.has_children) {
277 int n = callchain__set_folding(&he->sorted_chain, unfold);
278 he->nr_rows = unfold ? n : 0;
283 static void hists__set_folding(struct hists *hists, bool unfold)
287 hists->nr_entries = 0;
289 for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
290 struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
291 hist_entry__set_folding(he, unfold);
292 hists->nr_entries += 1 + he->nr_rows;
296 static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
298 hists__set_folding(browser->hists, unfold);
299 browser->b.nr_entries = browser->hists->nr_entries;
300 /* Go to the start, we may be way after valid entries after a collapse */
301 ui_browser__reset_index(&browser->b);
304 static void ui_browser__warn_lost_events(struct ui_browser *browser)
306 ui_browser__warning(browser, 4,
307 "Events are being lost, check IO/CPU overload!\n\n"
308 "You may want to run 'perf' using a RT scheduler policy:\n\n"
309 " perf top -r 80\n\n"
310 "Or reduce the sampling frequency.");
313 static void hist_browser__update_pcnt_entries(struct hist_browser *hb);
315 static int hist_browser__run(struct hist_browser *browser, const char *ev_name,
316 struct hist_browser_timer *hbt)
320 int delay_secs = hbt ? hbt->refresh : 0;
322 browser->b.entries = &browser->hists->entries;
323 browser->b.nr_entries = browser->hists->nr_entries;
324 if (browser->min_pcnt)
325 browser->b.nr_entries = browser->nr_pcnt_entries;
327 hist_browser__refresh_dimensions(browser);
328 hists__browser_title(browser->hists, title, sizeof(title), ev_name);
330 if (ui_browser__show(&browser->b, title,
331 "Press '?' for help on key bindings") < 0)
335 key = ui_browser__run(&browser->b, delay_secs);
340 hbt->timer(hbt->arg);
342 if (browser->min_pcnt) {
343 hist_browser__update_pcnt_entries(browser);
344 nr_entries = browser->nr_pcnt_entries;
346 nr_entries = browser->hists->nr_entries;
349 ui_browser__update_nr_entries(&browser->b, nr_entries);
351 if (browser->hists->stats.nr_lost_warned !=
352 browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
353 browser->hists->stats.nr_lost_warned =
354 browser->hists->stats.nr_events[PERF_RECORD_LOST];
355 ui_browser__warn_lost_events(&browser->b);
358 hists__browser_title(browser->hists, title, sizeof(title), ev_name);
359 ui_browser__show_title(&browser->b, title);
362 case 'D': { /* Debug */
364 struct hist_entry *h = rb_entry(browser->b.top,
365 struct hist_entry, rb_node);
367 ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
368 seq++, browser->b.nr_entries,
369 browser->hists->nr_entries,
373 h->row_offset, h->nr_rows);
377 /* Collapse the whole world. */
378 hist_browser__set_folding(browser, false);
381 /* Expand the whole world. */
382 hist_browser__set_folding(browser, true);
385 if (hist_browser__toggle_fold(browser))
393 ui_browser__hide(&browser->b);
397 static char *callchain_list__sym_name(struct callchain_list *cl,
398 char *bf, size_t bfsize, bool show_dso)
403 printed = scnprintf(bf, bfsize, "%s", cl->ms.sym->name);
405 printed = scnprintf(bf, bfsize, "%#" PRIx64, cl->ip);
408 scnprintf(bf + printed, bfsize - printed, " %s",
409 cl->ms.map ? cl->ms.map->dso->short_name : "unknown");
414 #define LEVEL_OFFSET_STEP 3
416 static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browser,
417 struct callchain_node *chain_node,
418 u64 total, int level,
421 bool *is_current_entry)
423 struct rb_node *node;
424 int first_row = row, width, offset = level * LEVEL_OFFSET_STEP;
425 u64 new_total, remaining;
427 if (callchain_param.mode == CHAIN_GRAPH_REL)
428 new_total = chain_node->children_hit;
432 remaining = new_total;
433 node = rb_first(&chain_node->rb_root);
435 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
436 struct rb_node *next = rb_next(node);
437 u64 cumul = callchain_cumul_hits(child);
438 struct callchain_list *chain;
439 char folded_sign = ' ';
441 int extra_offset = 0;
445 list_for_each_entry(chain, &child->val, list) {
446 char bf[1024], *alloc_str;
449 bool was_first = first;
454 extra_offset = LEVEL_OFFSET_STEP;
456 folded_sign = callchain_list__folded(chain);
457 if (*row_offset != 0) {
463 str = callchain_list__sym_name(chain, bf, sizeof(bf),
466 double percent = cumul * 100.0 / new_total;
468 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
469 str = "Not enough memory!";
474 color = HE_COLORSET_NORMAL;
475 width = browser->b.width - (offset + extra_offset + 2);
476 if (ui_browser__is_current_entry(&browser->b, row)) {
477 browser->selection = &chain->ms;
478 color = HE_COLORSET_SELECTED;
479 *is_current_entry = true;
482 ui_browser__set_color(&browser->b, color);
483 ui_browser__gotorc(&browser->b, row, 0);
484 slsmg_write_nstring(" ", offset + extra_offset);
485 slsmg_printf("%c ", folded_sign);
486 slsmg_write_nstring(str, width);
489 if (++row == browser->b.height)
492 if (folded_sign == '+')
496 if (folded_sign == '-') {
497 const int new_level = level + (extra_offset ? 2 : 1);
498 row += hist_browser__show_callchain_node_rb_tree(browser, child, new_total,
499 new_level, row, row_offset,
502 if (row == browser->b.height)
507 return row - first_row;
510 static int hist_browser__show_callchain_node(struct hist_browser *browser,
511 struct callchain_node *node,
512 int level, unsigned short row,
514 bool *is_current_entry)
516 struct callchain_list *chain;
518 offset = level * LEVEL_OFFSET_STEP,
519 width = browser->b.width - offset;
520 char folded_sign = ' ';
522 list_for_each_entry(chain, &node->val, list) {
526 folded_sign = callchain_list__folded(chain);
528 if (*row_offset != 0) {
533 color = HE_COLORSET_NORMAL;
534 if (ui_browser__is_current_entry(&browser->b, row)) {
535 browser->selection = &chain->ms;
536 color = HE_COLORSET_SELECTED;
537 *is_current_entry = true;
540 s = callchain_list__sym_name(chain, bf, sizeof(bf),
542 ui_browser__gotorc(&browser->b, row, 0);
543 ui_browser__set_color(&browser->b, color);
544 slsmg_write_nstring(" ", offset);
545 slsmg_printf("%c ", folded_sign);
546 slsmg_write_nstring(s, width - 2);
548 if (++row == browser->b.height)
552 if (folded_sign == '-')
553 row += hist_browser__show_callchain_node_rb_tree(browser, node,
554 browser->hists->stats.total_period,
559 return row - first_row;
562 static int hist_browser__show_callchain(struct hist_browser *browser,
563 struct rb_root *chain,
564 int level, unsigned short row,
566 bool *is_current_entry)
571 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
572 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
574 row += hist_browser__show_callchain_node(browser, node, level,
577 if (row == browser->b.height)
581 return row - first_row;
585 struct ui_browser *b;
590 static int __hpp__overhead_callback(struct perf_hpp *hpp, bool front)
592 struct hpp_arg *arg = hpp->ptr;
594 if (arg->current_entry && arg->b->navkeypressed)
595 ui_browser__set_color(arg->b, HE_COLORSET_SELECTED);
597 ui_browser__set_color(arg->b, HE_COLORSET_NORMAL);
600 if (!symbol_conf.use_callchain)
603 slsmg_printf("%c ", arg->folded_sign);
610 static int __hpp__color_callback(struct perf_hpp *hpp, bool front __maybe_unused)
612 struct hpp_arg *arg = hpp->ptr;
614 if (!arg->current_entry || !arg->b->navkeypressed)
615 ui_browser__set_color(arg->b, HE_COLORSET_NORMAL);
619 static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
621 struct hpp_arg *arg = hpp->ptr;
627 percent = va_arg(args, double);
630 ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
632 ret = scnprintf(hpp->buf, hpp->size, fmt, percent);
633 slsmg_printf("%s", hpp->buf);
635 advance_hpp(hpp, ret);
639 #define __HPP_COLOR_PERCENT_FN(_type, _field, _cb) \
640 static u64 __hpp_get_##_field(struct hist_entry *he) \
642 return he->stat._field; \
646 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,\
647 struct perf_hpp *hpp, \
648 struct hist_entry *he) \
650 return __hpp__fmt(hpp, he, __hpp_get_##_field, _cb, " %6.2f%%", \
651 __hpp__slsmg_color_printf, true); \
654 __HPP_COLOR_PERCENT_FN(overhead, period, __hpp__overhead_callback)
655 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys, __hpp__color_callback)
656 __HPP_COLOR_PERCENT_FN(overhead_us, period_us, __hpp__color_callback)
657 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys, __hpp__color_callback)
658 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us, __hpp__color_callback)
660 #undef __HPP_COLOR_PERCENT_FN
662 void hist_browser__init_hpp(void)
666 perf_hpp__format[PERF_HPP__OVERHEAD].color =
667 hist_browser__hpp_color_overhead;
668 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
669 hist_browser__hpp_color_overhead_sys;
670 perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
671 hist_browser__hpp_color_overhead_us;
672 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
673 hist_browser__hpp_color_overhead_guest_sys;
674 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
675 hist_browser__hpp_color_overhead_guest_us;
678 static int hist_browser__show_entry(struct hist_browser *browser,
679 struct hist_entry *entry,
684 int width = browser->b.width;
685 char folded_sign = ' ';
686 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
687 off_t row_offset = entry->row_offset;
689 struct perf_hpp_fmt *fmt;
692 browser->he_selection = entry;
693 browser->selection = &entry->ms;
696 if (symbol_conf.use_callchain) {
697 hist_entry__init_have_children(entry);
698 folded_sign = hist_entry__folded(entry);
701 if (row_offset == 0) {
702 struct hpp_arg arg = {
704 .folded_sign = folded_sign,
705 .current_entry = current_entry,
707 struct perf_hpp hpp = {
713 ui_browser__gotorc(&browser->b, row, 0);
715 perf_hpp__for_each_format(fmt) {
723 width -= fmt->color(fmt, &hpp, entry);
725 width -= fmt->entry(fmt, &hpp, entry);
726 slsmg_printf("%s", s);
730 /* The scroll bar isn't being used */
731 if (!browser->b.navkeypressed)
734 hist_entry__sort_snprintf(entry, s, sizeof(s), browser->hists);
735 slsmg_write_nstring(s, width);
741 if (folded_sign == '-' && row != browser->b.height) {
742 printed += hist_browser__show_callchain(browser, &entry->sorted_chain,
746 browser->he_selection = entry;
752 static void ui_browser__hists_init_top(struct ui_browser *browser)
754 if (browser->top == NULL) {
755 struct hist_browser *hb;
757 hb = container_of(browser, struct hist_browser, b);
758 browser->top = rb_first(&hb->hists->entries);
762 static unsigned int hist_browser__refresh(struct ui_browser *browser)
766 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
768 ui_browser__hists_init_top(browser);
770 for (nd = browser->top; nd; nd = rb_next(nd)) {
771 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
772 u64 total = hists__total_period(h->hists);
779 percent = h->stat.period * 100.0 / total;
781 if (percent < hb->min_pcnt)
784 row += hist_browser__show_entry(hb, h, row);
785 if (row == browser->height)
792 static struct rb_node *hists__filter_entries(struct rb_node *nd,
797 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
798 u64 total = hists__total_period(hists);
802 percent = h->stat.period * 100.0 / total;
804 if (percent < min_pcnt)
816 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
821 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
822 u64 total = hists__total_period(hists);
826 percent = h->stat.period * 100.0 / total;
828 if (!h->filtered && percent >= min_pcnt)
837 static void ui_browser__hists_seek(struct ui_browser *browser,
838 off_t offset, int whence)
840 struct hist_entry *h;
843 struct hist_browser *hb;
845 hb = container_of(browser, struct hist_browser, b);
847 if (browser->nr_entries == 0)
850 ui_browser__hists_init_top(browser);
854 nd = hists__filter_entries(rb_first(browser->entries),
855 hb->hists, hb->min_pcnt);
861 nd = hists__filter_prev_entries(rb_last(browser->entries),
862 hb->hists, hb->min_pcnt);
870 * Moves not relative to the first visible entry invalidates its
873 h = rb_entry(browser->top, struct hist_entry, rb_node);
877 * Here we have to check if nd is expanded (+), if it is we can't go
878 * the next top level hist_entry, instead we must compute an offset of
879 * what _not_ to show and not change the first visible entry.
881 * This offset increments when we are going from top to bottom and
882 * decreases when we're going from bottom to top.
884 * As we don't have backpointers to the top level in the callchains
885 * structure, we need to always print the whole hist_entry callchain,
886 * skipping the first ones that are before the first visible entry
887 * and stop when we printed enough lines to fill the screen.
892 h = rb_entry(nd, struct hist_entry, rb_node);
893 if (h->ms.unfolded) {
894 u16 remaining = h->nr_rows - h->row_offset;
895 if (offset > remaining) {
899 h->row_offset += offset;
905 nd = hists__filter_entries(rb_next(nd), hb->hists,
911 } while (offset != 0);
912 } else if (offset < 0) {
914 h = rb_entry(nd, struct hist_entry, rb_node);
915 if (h->ms.unfolded) {
917 if (-offset > h->row_offset) {
918 offset += h->row_offset;
921 h->row_offset += offset;
927 if (-offset > h->nr_rows) {
928 offset += h->nr_rows;
931 h->row_offset = h->nr_rows + offset;
939 nd = hists__filter_prev_entries(rb_prev(nd), hb->hists,
947 * Last unfiltered hist_entry, check if it is
948 * unfolded, if it is then we should have
949 * row_offset at its last entry.
951 h = rb_entry(nd, struct hist_entry, rb_node);
953 h->row_offset = h->nr_rows;
960 h = rb_entry(nd, struct hist_entry, rb_node);
965 static int hist_browser__fprintf_callchain_node_rb_tree(struct hist_browser *browser,
966 struct callchain_node *chain_node,
967 u64 total, int level,
970 struct rb_node *node;
971 int offset = level * LEVEL_OFFSET_STEP;
972 u64 new_total, remaining;
975 if (callchain_param.mode == CHAIN_GRAPH_REL)
976 new_total = chain_node->children_hit;
980 remaining = new_total;
981 node = rb_first(&chain_node->rb_root);
983 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
984 struct rb_node *next = rb_next(node);
985 u64 cumul = callchain_cumul_hits(child);
986 struct callchain_list *chain;
987 char folded_sign = ' ';
989 int extra_offset = 0;
993 list_for_each_entry(chain, &child->val, list) {
994 char bf[1024], *alloc_str;
996 bool was_first = first;
1001 extra_offset = LEVEL_OFFSET_STEP;
1003 folded_sign = callchain_list__folded(chain);
1006 str = callchain_list__sym_name(chain, bf, sizeof(bf),
1009 double percent = cumul * 100.0 / new_total;
1011 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
1012 str = "Not enough memory!";
1017 printed += fprintf(fp, "%*s%c %s\n", offset + extra_offset, " ", folded_sign, str);
1019 if (folded_sign == '+')
1023 if (folded_sign == '-') {
1024 const int new_level = level + (extra_offset ? 2 : 1);
1025 printed += hist_browser__fprintf_callchain_node_rb_tree(browser, child, new_total,
1035 static int hist_browser__fprintf_callchain_node(struct hist_browser *browser,
1036 struct callchain_node *node,
1037 int level, FILE *fp)
1039 struct callchain_list *chain;
1040 int offset = level * LEVEL_OFFSET_STEP;
1041 char folded_sign = ' ';
1044 list_for_each_entry(chain, &node->val, list) {
1047 folded_sign = callchain_list__folded(chain);
1048 s = callchain_list__sym_name(chain, bf, sizeof(bf), browser->show_dso);
1049 printed += fprintf(fp, "%*s%c %s\n", offset, " ", folded_sign, s);
1052 if (folded_sign == '-')
1053 printed += hist_browser__fprintf_callchain_node_rb_tree(browser, node,
1054 browser->hists->stats.total_period,
1059 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1060 struct rb_root *chain, int level, FILE *fp)
1065 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
1066 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
1068 printed += hist_browser__fprintf_callchain_node(browser, node, level, fp);
1074 static int hist_browser__fprintf_entry(struct hist_browser *browser,
1075 struct hist_entry *he, FILE *fp)
1080 char folded_sign = ' ';
1082 if (symbol_conf.use_callchain)
1083 folded_sign = hist_entry__folded(he);
1085 hist_entry__sort_snprintf(he, s, sizeof(s), browser->hists);
1086 percent = (he->stat.period * 100.0) / browser->hists->stats.total_period;
1088 if (symbol_conf.use_callchain)
1089 printed += fprintf(fp, "%c ", folded_sign);
1091 printed += fprintf(fp, " %5.2f%%", percent);
1093 if (symbol_conf.show_nr_samples)
1094 printed += fprintf(fp, " %11u", he->stat.nr_events);
1096 if (symbol_conf.show_total_period)
1097 printed += fprintf(fp, " %12" PRIu64, he->stat.period);
1099 printed += fprintf(fp, "%s\n", rtrim(s));
1101 if (folded_sign == '-')
1102 printed += hist_browser__fprintf_callchain(browser, &he->sorted_chain, 1, fp);
1107 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1109 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
1115 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1117 printed += hist_browser__fprintf_entry(browser, h, fp);
1118 nd = hists__filter_entries(rb_next(nd), browser->hists,
1125 static int hist_browser__dump(struct hist_browser *browser)
1131 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
1132 if (access(filename, F_OK))
1135 * XXX: Just an arbitrary lazy upper limit
1137 if (++browser->print_seq == 8192) {
1138 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1143 fp = fopen(filename, "w");
1146 const char *err = strerror_r(errno, bf, sizeof(bf));
1147 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
1151 ++browser->print_seq;
1152 hist_browser__fprintf(browser, fp);
1154 ui_helpline__fpush("%s written!", filename);
1159 static struct hist_browser *hist_browser__new(struct hists *hists)
1161 struct hist_browser *browser = zalloc(sizeof(*browser));
1164 browser->hists = hists;
1165 browser->b.refresh = hist_browser__refresh;
1166 browser->b.seek = ui_browser__hists_seek;
1167 browser->b.use_navkeypressed = true;
1173 static void hist_browser__delete(struct hist_browser *browser)
1178 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
1180 return browser->he_selection;
1183 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
1185 return browser->he_selection->thread;
1188 static int hists__browser_title(struct hists *hists, char *bf, size_t size,
1189 const char *ev_name)
1193 const struct dso *dso = hists->dso_filter;
1194 const struct thread *thread = hists->thread_filter;
1195 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1196 u64 nr_events = hists->stats.total_period;
1197 struct perf_evsel *evsel = hists_to_evsel(hists);
1199 size_t buflen = sizeof(buf);
1201 if (symbol_conf.filter_relative) {
1202 nr_samples = hists->stats.nr_non_filtered_samples;
1203 nr_events = hists->stats.total_non_filtered_period;
1206 if (perf_evsel__is_group_event(evsel)) {
1207 struct perf_evsel *pos;
1209 perf_evsel__group_desc(evsel, buf, buflen);
1212 for_each_group_member(pos, evsel) {
1213 if (symbol_conf.filter_relative) {
1214 nr_samples += pos->hists.stats.nr_non_filtered_samples;
1215 nr_events += pos->hists.stats.total_non_filtered_period;
1217 nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1218 nr_events += pos->hists.stats.total_period;
1223 nr_samples = convert_unit(nr_samples, &unit);
1224 printed = scnprintf(bf, size,
1225 "Samples: %lu%c of event '%s', Event count (approx.): %lu",
1226 nr_samples, unit, ev_name, nr_events);
1229 if (hists->uid_filter_str)
1230 printed += snprintf(bf + printed, size - printed,
1231 ", UID: %s", hists->uid_filter_str);
1233 printed += scnprintf(bf + printed, size - printed,
1235 (thread->comm_set ? thread__comm_str(thread) : ""),
1238 printed += scnprintf(bf + printed, size - printed,
1239 ", DSO: %s", dso->short_name);
1243 static inline void free_popup_options(char **options, int n)
1247 for (i = 0; i < n; ++i)
1251 /* Check whether the browser is for 'top' or 'report' */
1252 static inline bool is_report_browser(void *timer)
1254 return timer == NULL;
1258 * Only runtime switching of perf data file will make "input_name" point
1259 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1260 * whether we need to call free() for current "input_name" during the switch.
1262 static bool is_input_name_malloced = false;
1264 static int switch_data_file(void)
1266 char *pwd, *options[32], *abs_path[32], *tmp;
1268 int nr_options = 0, choice = -1, ret = -1;
1269 struct dirent *dent;
1271 pwd = getenv("PWD");
1275 pwd_dir = opendir(pwd);
1279 memset(options, 0, sizeof(options));
1280 memset(options, 0, sizeof(abs_path));
1282 while ((dent = readdir(pwd_dir))) {
1283 char path[PATH_MAX];
1285 char *name = dent->d_name;
1288 if (!(dent->d_type == DT_REG))
1291 snprintf(path, sizeof(path), "%s/%s", pwd, name);
1293 file = fopen(path, "r");
1297 if (fread(&magic, 1, 8, file) < 8)
1298 goto close_file_and_continue;
1300 if (is_perf_magic(magic)) {
1301 options[nr_options] = strdup(name);
1302 if (!options[nr_options])
1303 goto close_file_and_continue;
1305 abs_path[nr_options] = strdup(path);
1306 if (!abs_path[nr_options]) {
1307 zfree(&options[nr_options]);
1308 ui__warning("Can't search all data files due to memory shortage.\n");
1316 close_file_and_continue:
1318 if (nr_options >= 32) {
1319 ui__warning("Too many perf data files in PWD!\n"
1320 "Only the first 32 files will be listed.\n");
1327 choice = ui__popup_menu(nr_options, options);
1328 if (choice < nr_options && choice >= 0) {
1329 tmp = strdup(abs_path[choice]);
1331 if (is_input_name_malloced)
1332 free((void *)input_name);
1334 is_input_name_malloced = true;
1337 ui__warning("Data switch failed due to memory shortage!\n");
1341 free_popup_options(options, nr_options);
1342 free_popup_options(abs_path, nr_options);
1346 static void hist_browser__update_pcnt_entries(struct hist_browser *hb)
1349 struct rb_node *nd = rb_first(&hb->hists->entries);
1353 nd = hists__filter_entries(rb_next(nd), hb->hists,
1357 hb->nr_pcnt_entries = nr_entries;
1360 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
1361 const char *helpline, const char *ev_name,
1363 struct hist_browser_timer *hbt,
1365 struct perf_session_env *env)
1367 struct hists *hists = &evsel->hists;
1368 struct hist_browser *browser = hist_browser__new(hists);
1369 struct branch_info *bi;
1370 struct pstack *fstack;
1375 char script_opt[64];
1376 int delay_secs = hbt ? hbt->refresh : 0;
1378 #define HIST_BROWSER_HELP_COMMON \
1379 "h/?/F1 Show this window\n" \
1381 "PGDN/SPACE Navigate\n" \
1382 "q/ESC/CTRL+C Exit browser\n\n" \
1383 "For multiple event sessions:\n\n" \
1384 "TAB/UNTAB Switch events\n\n" \
1385 "For symbolic views (--sort has sym):\n\n" \
1386 "-> Zoom into DSO/Threads & Annotate current symbol\n" \
1388 "a Annotate current symbol\n" \
1389 "C Collapse all callchains\n" \
1390 "d Zoom into current DSO\n" \
1391 "E Expand all callchains\n" \
1393 /* help messages are sorted by lexical order of the hotkey */
1394 const char report_help[] = HIST_BROWSER_HELP_COMMON
1395 "i Show header information\n"
1396 "P Print histograms to perf.hist.N\n"
1397 "r Run available scripts\n"
1398 "s Switch to another data file in PWD\n"
1399 "t Zoom into current Thread\n"
1400 "V Verbose (DSO names in callchains, etc)\n"
1401 "/ Filter symbol by name";
1402 const char top_help[] = HIST_BROWSER_HELP_COMMON
1403 "P Print histograms to perf.hist.N\n"
1404 "t Zoom into current Thread\n"
1405 "V Verbose (DSO names in callchains, etc)\n"
1406 "/ Filter symbol by name";
1408 if (browser == NULL)
1412 browser->min_pcnt = min_pcnt;
1413 hist_browser__update_pcnt_entries(browser);
1416 fstack = pstack__new(2);
1420 ui_helpline__push(helpline);
1422 memset(options, 0, sizeof(options));
1425 const struct thread *thread = NULL;
1426 const struct dso *dso = NULL;
1428 annotate = -2, zoom_dso = -2, zoom_thread = -2,
1429 annotate_f = -2, annotate_t = -2, browse_map = -2;
1430 int scripts_comm = -2, scripts_symbol = -2,
1431 scripts_all = -2, switch_data = -2;
1435 key = hist_browser__run(browser, ev_name, hbt);
1437 if (browser->he_selection != NULL) {
1438 thread = hist_browser__selected_thread(browser);
1439 dso = browser->selection->map ? browser->selection->map->dso : NULL;
1447 * Exit the browser, let hists__browser_tree
1448 * go to the next or previous
1450 goto out_free_stack;
1452 if (!sort__has_sym) {
1453 ui_browser__warning(&browser->b, delay_secs * 2,
1454 "Annotation is only available for symbolic views, "
1455 "include \"sym*\" in --sort to use it.");
1459 if (browser->selection == NULL ||
1460 browser->selection->sym == NULL ||
1461 browser->selection->map->dso->annotate_warned)
1465 hist_browser__dump(browser);
1470 browser->show_dso = !browser->show_dso;
1475 if (ui_browser__input_window("Symbol to show",
1476 "Please enter the name of symbol you want to see",
1477 buf, "ENTER: OK, ESC: Cancel",
1478 delay_secs * 2) == K_ENTER) {
1479 hists->symbol_filter_str = *buf ? buf : NULL;
1480 hists__filter_by_symbol(hists);
1481 hist_browser__reset(browser);
1485 if (is_report_browser(hbt))
1489 if (is_report_browser(hbt))
1490 goto do_data_switch;
1493 /* env->arch is NULL for live-mode (i.e. perf top) */
1495 tui__header_window(env);
1500 ui_browser__help_window(&browser->b,
1501 is_report_browser(hbt) ? report_help : top_help);
1510 if (pstack__empty(fstack)) {
1512 * Go back to the perf_evsel_menu__run or other user
1515 goto out_free_stack;
1518 top = pstack__pop(fstack);
1519 if (top == &browser->hists->dso_filter)
1521 if (top == &browser->hists->thread_filter)
1522 goto zoom_out_thread;
1527 !ui_browser__dialog_yesno(&browser->b,
1528 "Do you really want to exit?"))
1533 goto out_free_stack;
1539 goto add_exit_option;
1541 if (sort__mode == SORT_MODE__BRANCH) {
1542 bi = browser->he_selection->branch_info;
1543 if (browser->selection != NULL &&
1545 bi->from.sym != NULL &&
1546 !bi->from.map->dso->annotate_warned &&
1547 asprintf(&options[nr_options], "Annotate %s",
1548 bi->from.sym->name) > 0)
1549 annotate_f = nr_options++;
1551 if (browser->selection != NULL &&
1553 bi->to.sym != NULL &&
1554 !bi->to.map->dso->annotate_warned &&
1555 (bi->to.sym != bi->from.sym ||
1556 bi->to.map->dso != bi->from.map->dso) &&
1557 asprintf(&options[nr_options], "Annotate %s",
1558 bi->to.sym->name) > 0)
1559 annotate_t = nr_options++;
1562 if (browser->selection != NULL &&
1563 browser->selection->sym != NULL &&
1564 !browser->selection->map->dso->annotate_warned &&
1565 asprintf(&options[nr_options], "Annotate %s",
1566 browser->selection->sym->name) > 0)
1567 annotate = nr_options++;
1570 if (thread != NULL &&
1571 asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
1572 (browser->hists->thread_filter ? "out of" : "into"),
1573 (thread->comm_set ? thread__comm_str(thread) : ""),
1575 zoom_thread = nr_options++;
1578 asprintf(&options[nr_options], "Zoom %s %s DSO",
1579 (browser->hists->dso_filter ? "out of" : "into"),
1580 (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
1581 zoom_dso = nr_options++;
1583 if (browser->selection != NULL &&
1584 browser->selection->map != NULL &&
1585 asprintf(&options[nr_options], "Browse map details") > 0)
1586 browse_map = nr_options++;
1588 /* perf script support */
1589 if (browser->he_selection) {
1592 if (asprintf(&options[nr_options], "Run scripts for samples of thread [%s]",
1593 thread__comm_str(browser->he_selection->thread)) > 0)
1594 scripts_comm = nr_options++;
1596 sym = browser->he_selection->ms.sym;
1597 if (sym && sym->namelen &&
1598 asprintf(&options[nr_options], "Run scripts for samples of symbol [%s]",
1600 scripts_symbol = nr_options++;
1603 if (asprintf(&options[nr_options], "Run scripts for all samples") > 0)
1604 scripts_all = nr_options++;
1606 if (is_report_browser(hbt) && asprintf(&options[nr_options],
1607 "Switch to another data file in PWD") > 0)
1608 switch_data = nr_options++;
1610 options[nr_options++] = (char *)"Exit";
1612 choice = ui__popup_menu(nr_options, options);
1614 if (choice == nr_options - 1)
1618 free_popup_options(options, nr_options - 1);
1622 if (choice == annotate || choice == annotate_t || choice == annotate_f) {
1623 struct hist_entry *he;
1626 if (!objdump_path && perf_session_env__lookup_objdump(env))
1629 he = hist_browser__selected_entry(browser);
1634 * we stash the branch_info symbol + map into the
1635 * the ms so we don't have to rewrite all the annotation
1636 * code to use branch_info.
1637 * in branch mode, the ms struct is not used
1639 if (choice == annotate_f) {
1640 he->ms.sym = he->branch_info->from.sym;
1641 he->ms.map = he->branch_info->from.map;
1642 } else if (choice == annotate_t) {
1643 he->ms.sym = he->branch_info->to.sym;
1644 he->ms.map = he->branch_info->to.map;
1648 * Don't let this be freed, say, by hists__decay_entry.
1651 err = hist_entry__tui_annotate(he, evsel, hbt);
1654 * offer option to annotate the other branch source or target
1655 * (if they exists) when returning from annotate
1657 if ((err == 'q' || err == CTRL('c'))
1658 && annotate_t != -2 && annotate_f != -2)
1659 goto retry_popup_menu;
1661 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1663 ui_browser__handle_resize(&browser->b);
1665 } else if (choice == browse_map)
1666 map__browse(browser->selection->map);
1667 else if (choice == zoom_dso) {
1669 if (browser->hists->dso_filter) {
1670 pstack__remove(fstack, &browser->hists->dso_filter);
1673 browser->hists->dso_filter = NULL;
1674 sort_dso.elide = false;
1678 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1679 dso->kernel ? "the Kernel" : dso->short_name);
1680 browser->hists->dso_filter = dso;
1681 sort_dso.elide = true;
1682 pstack__push(fstack, &browser->hists->dso_filter);
1684 hists__filter_by_dso(hists);
1685 hist_browser__reset(browser);
1686 } else if (choice == zoom_thread) {
1688 if (browser->hists->thread_filter) {
1689 pstack__remove(fstack, &browser->hists->thread_filter);
1692 browser->hists->thread_filter = NULL;
1693 sort_thread.elide = false;
1695 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1696 thread->comm_set ? thread__comm_str(thread) : "",
1698 browser->hists->thread_filter = thread;
1699 sort_thread.elide = true;
1700 pstack__push(fstack, &browser->hists->thread_filter);
1702 hists__filter_by_thread(hists);
1703 hist_browser__reset(browser);
1705 /* perf scripts support */
1706 else if (choice == scripts_all || choice == scripts_comm ||
1707 choice == scripts_symbol) {
1709 memset(script_opt, 0, 64);
1711 if (choice == scripts_comm)
1712 sprintf(script_opt, " -c %s ", thread__comm_str(browser->he_selection->thread));
1714 if (choice == scripts_symbol)
1715 sprintf(script_opt, " -S %s ", browser->he_selection->ms.sym->name);
1717 script_browse(script_opt);
1719 /* Switch to another data file */
1720 else if (choice == switch_data) {
1722 if (!switch_data_file()) {
1723 key = K_SWITCH_INPUT_DATA;
1726 ui__warning("Won't switch the data files due to\n"
1727 "no valid data file get selected!\n");
1731 pstack__delete(fstack);
1733 hist_browser__delete(browser);
1734 free_popup_options(options, nr_options - 1);
1738 struct perf_evsel_menu {
1739 struct ui_browser b;
1740 struct perf_evsel *selection;
1741 bool lost_events, lost_events_warned;
1743 struct perf_session_env *env;
1746 static void perf_evsel_menu__write(struct ui_browser *browser,
1747 void *entry, int row)
1749 struct perf_evsel_menu *menu = container_of(browser,
1750 struct perf_evsel_menu, b);
1751 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1752 bool current_entry = ui_browser__is_current_entry(browser, row);
1753 unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1754 const char *ev_name = perf_evsel__name(evsel);
1756 const char *warn = " ";
1759 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
1760 HE_COLORSET_NORMAL);
1762 if (perf_evsel__is_group_event(evsel)) {
1763 struct perf_evsel *pos;
1765 ev_name = perf_evsel__group_name(evsel);
1767 for_each_group_member(pos, evsel) {
1768 nr_events += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1772 nr_events = convert_unit(nr_events, &unit);
1773 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
1774 unit, unit == ' ' ? "" : " ", ev_name);
1775 slsmg_printf("%s", bf);
1777 nr_events = evsel->hists.stats.nr_events[PERF_RECORD_LOST];
1778 if (nr_events != 0) {
1779 menu->lost_events = true;
1781 ui_browser__set_color(browser, HE_COLORSET_TOP);
1782 nr_events = convert_unit(nr_events, &unit);
1783 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
1784 nr_events, unit, unit == ' ' ? "" : " ");
1788 slsmg_write_nstring(warn, browser->width - printed);
1791 menu->selection = evsel;
1794 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
1795 int nr_events, const char *help,
1796 struct hist_browser_timer *hbt)
1798 struct perf_evlist *evlist = menu->b.priv;
1799 struct perf_evsel *pos;
1800 const char *ev_name, *title = "Available samples";
1801 int delay_secs = hbt ? hbt->refresh : 0;
1804 if (ui_browser__show(&menu->b, title,
1805 "ESC: exit, ENTER|->: Browse histograms") < 0)
1809 key = ui_browser__run(&menu->b, delay_secs);
1813 hbt->timer(hbt->arg);
1815 if (!menu->lost_events_warned && menu->lost_events) {
1816 ui_browser__warn_lost_events(&menu->b);
1817 menu->lost_events_warned = true;
1822 if (!menu->selection)
1824 pos = menu->selection;
1826 perf_evlist__set_selected(evlist, pos);
1828 * Give the calling tool a chance to populate the non
1829 * default evsel resorted hists tree.
1832 hbt->timer(hbt->arg);
1833 ev_name = perf_evsel__name(pos);
1834 key = perf_evsel__hists_browse(pos, nr_events, help,
1838 ui_browser__show_title(&menu->b, title);
1841 if (pos->node.next == &evlist->entries)
1842 pos = perf_evlist__first(evlist);
1844 pos = perf_evsel__next(pos);
1847 if (pos->node.prev == &evlist->entries)
1848 pos = perf_evlist__last(evlist);
1850 pos = perf_evsel__prev(pos);
1853 if (!ui_browser__dialog_yesno(&menu->b,
1854 "Do you really want to exit?"))
1857 case K_SWITCH_INPUT_DATA:
1867 if (!ui_browser__dialog_yesno(&menu->b,
1868 "Do you really want to exit?"))
1880 ui_browser__hide(&menu->b);
1884 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
1887 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1889 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
1895 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
1896 int nr_entries, const char *help,
1897 struct hist_browser_timer *hbt,
1899 struct perf_session_env *env)
1901 struct perf_evsel *pos;
1902 struct perf_evsel_menu menu = {
1904 .entries = &evlist->entries,
1905 .refresh = ui_browser__list_head_refresh,
1906 .seek = ui_browser__list_head_seek,
1907 .write = perf_evsel_menu__write,
1908 .filter = filter_group_entries,
1909 .nr_entries = nr_entries,
1912 .min_pcnt = min_pcnt,
1916 ui_helpline__push("Press ESC to exit");
1918 evlist__for_each(evlist, pos) {
1919 const char *ev_name = perf_evsel__name(pos);
1920 size_t line_len = strlen(ev_name) + 7;
1922 if (menu.b.width < line_len)
1923 menu.b.width = line_len;
1926 return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
1929 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
1930 struct hist_browser_timer *hbt,
1932 struct perf_session_env *env)
1934 int nr_entries = evlist->nr_entries;
1937 if (nr_entries == 1) {
1938 struct perf_evsel *first = perf_evlist__first(evlist);
1939 const char *ev_name = perf_evsel__name(first);
1941 return perf_evsel__hists_browse(first, nr_entries, help,
1942 ev_name, false, hbt, min_pcnt,
1946 if (symbol_conf.event_group) {
1947 struct perf_evsel *pos;
1950 evlist__for_each(evlist, pos) {
1951 if (perf_evsel__is_group_leader(pos))
1955 if (nr_entries == 1)
1959 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
1960 hbt, min_pcnt, env);