2 #include "../libslang.h"
6 #include <linux/rbtree.h>
8 #include "../../util/evsel.h"
9 #include "../../util/evlist.h"
10 #include "../../util/hist.h"
11 #include "../../util/pstack.h"
12 #include "../../util/sort.h"
13 #include "../../util/util.h"
14 #include "../../arch/common.h"
16 #include "../browser.h"
17 #include "../helpline.h"
25 struct hist_entry *he_selection;
26 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 int hist_browser__run(struct hist_browser *browser, const char *ev_name,
314 struct hist_browser_timer *hbt)
318 int delay_secs = hbt ? hbt->refresh : 0;
320 browser->b.entries = &browser->hists->entries;
321 browser->b.nr_entries = browser->hists->nr_entries;
323 hist_browser__refresh_dimensions(browser);
324 hists__browser_title(browser->hists, title, sizeof(title), ev_name);
326 if (ui_browser__show(&browser->b, title,
327 "Press '?' for help on key bindings") < 0)
331 key = ui_browser__run(&browser->b, delay_secs);
335 hbt->timer(hbt->arg);
336 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
338 if (browser->hists->stats.nr_lost_warned !=
339 browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
340 browser->hists->stats.nr_lost_warned =
341 browser->hists->stats.nr_events[PERF_RECORD_LOST];
342 ui_browser__warn_lost_events(&browser->b);
345 hists__browser_title(browser->hists, title, sizeof(title), ev_name);
346 ui_browser__show_title(&browser->b, title);
348 case 'D': { /* Debug */
350 struct hist_entry *h = rb_entry(browser->b.top,
351 struct hist_entry, rb_node);
353 ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
354 seq++, browser->b.nr_entries,
355 browser->hists->nr_entries,
359 h->row_offset, h->nr_rows);
363 /* Collapse the whole world. */
364 hist_browser__set_folding(browser, false);
367 /* Expand the whole world. */
368 hist_browser__set_folding(browser, true);
371 if (hist_browser__toggle_fold(browser))
379 ui_browser__hide(&browser->b);
383 static char *callchain_list__sym_name(struct callchain_list *cl,
384 char *bf, size_t bfsize, bool show_dso)
389 printed = scnprintf(bf, bfsize, "%s", cl->ms.sym->name);
391 printed = scnprintf(bf, bfsize, "%#" PRIx64, cl->ip);
394 scnprintf(bf + printed, bfsize - printed, " %s",
395 cl->ms.map ? cl->ms.map->dso->short_name : "unknown");
400 #define LEVEL_OFFSET_STEP 3
402 static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browser,
403 struct callchain_node *chain_node,
404 u64 total, int level,
407 bool *is_current_entry)
409 struct rb_node *node;
410 int first_row = row, width, offset = level * LEVEL_OFFSET_STEP;
411 u64 new_total, remaining;
413 if (callchain_param.mode == CHAIN_GRAPH_REL)
414 new_total = chain_node->children_hit;
418 remaining = new_total;
419 node = rb_first(&chain_node->rb_root);
421 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
422 struct rb_node *next = rb_next(node);
423 u64 cumul = callchain_cumul_hits(child);
424 struct callchain_list *chain;
425 char folded_sign = ' ';
427 int extra_offset = 0;
431 list_for_each_entry(chain, &child->val, list) {
432 char bf[1024], *alloc_str;
435 bool was_first = first;
440 extra_offset = LEVEL_OFFSET_STEP;
442 folded_sign = callchain_list__folded(chain);
443 if (*row_offset != 0) {
449 str = callchain_list__sym_name(chain, bf, sizeof(bf),
452 double percent = cumul * 100.0 / new_total;
454 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
455 str = "Not enough memory!";
460 color = HE_COLORSET_NORMAL;
461 width = browser->b.width - (offset + extra_offset + 2);
462 if (ui_browser__is_current_entry(&browser->b, row)) {
463 browser->selection = &chain->ms;
464 color = HE_COLORSET_SELECTED;
465 *is_current_entry = true;
468 ui_browser__set_color(&browser->b, color);
469 ui_browser__gotorc(&browser->b, row, 0);
470 slsmg_write_nstring(" ", offset + extra_offset);
471 slsmg_printf("%c ", folded_sign);
472 slsmg_write_nstring(str, width);
475 if (++row == browser->b.height)
478 if (folded_sign == '+')
482 if (folded_sign == '-') {
483 const int new_level = level + (extra_offset ? 2 : 1);
484 row += hist_browser__show_callchain_node_rb_tree(browser, child, new_total,
485 new_level, row, row_offset,
488 if (row == browser->b.height)
493 return row - first_row;
496 static int hist_browser__show_callchain_node(struct hist_browser *browser,
497 struct callchain_node *node,
498 int level, unsigned short row,
500 bool *is_current_entry)
502 struct callchain_list *chain;
504 offset = level * LEVEL_OFFSET_STEP,
505 width = browser->b.width - offset;
506 char folded_sign = ' ';
508 list_for_each_entry(chain, &node->val, list) {
512 folded_sign = callchain_list__folded(chain);
514 if (*row_offset != 0) {
519 color = HE_COLORSET_NORMAL;
520 if (ui_browser__is_current_entry(&browser->b, row)) {
521 browser->selection = &chain->ms;
522 color = HE_COLORSET_SELECTED;
523 *is_current_entry = true;
526 s = callchain_list__sym_name(chain, bf, sizeof(bf),
528 ui_browser__gotorc(&browser->b, row, 0);
529 ui_browser__set_color(&browser->b, color);
530 slsmg_write_nstring(" ", offset);
531 slsmg_printf("%c ", folded_sign);
532 slsmg_write_nstring(s, width - 2);
534 if (++row == browser->b.height)
538 if (folded_sign == '-')
539 row += hist_browser__show_callchain_node_rb_tree(browser, node,
540 browser->hists->stats.total_period,
545 return row - first_row;
548 static int hist_browser__show_callchain(struct hist_browser *browser,
549 struct rb_root *chain,
550 int level, unsigned short row,
552 bool *is_current_entry)
557 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
558 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
560 row += hist_browser__show_callchain_node(browser, node, level,
563 if (row == browser->b.height)
567 return row - first_row;
571 struct ui_browser *b;
576 static int __hpp__color_callchain(struct hpp_arg *arg)
578 if (!symbol_conf.use_callchain)
581 slsmg_printf("%c ", arg->folded_sign);
585 static int __hpp__color_fmt(struct perf_hpp *hpp, struct hist_entry *he,
586 u64 (*get_field)(struct hist_entry *),
587 int (*callchain_cb)(struct hpp_arg *))
590 double percent = 0.0;
591 struct hists *hists = he->hists;
592 struct hpp_arg *arg = hpp->ptr;
594 if (hists->stats.total_period)
595 percent = 100.0 * get_field(he) / hists->stats.total_period;
597 ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
600 ret += callchain_cb(arg);
602 ret += scnprintf(hpp->buf, hpp->size, "%6.2f%%", percent);
603 slsmg_printf("%s", hpp->buf);
605 if (symbol_conf.event_group) {
606 int prev_idx, idx_delta;
607 struct perf_evsel *evsel = hists_to_evsel(hists);
608 struct hist_entry *pair;
609 int nr_members = evsel->nr_members;
614 prev_idx = perf_evsel__group_idx(evsel);
616 list_for_each_entry(pair, &he->pairs.head, pairs.node) {
617 u64 period = get_field(pair);
618 u64 total = pair->hists->stats.total_period;
623 evsel = hists_to_evsel(pair->hists);
624 idx_delta = perf_evsel__group_idx(evsel) - prev_idx - 1;
626 while (idx_delta--) {
628 * zero-fill group members in the middle which
631 ui_browser__set_percent_color(arg->b, 0.0,
633 ret += scnprintf(hpp->buf, hpp->size,
635 slsmg_printf("%s", hpp->buf);
638 percent = 100.0 * period / total;
639 ui_browser__set_percent_color(arg->b, percent,
641 ret += scnprintf(hpp->buf, hpp->size,
642 " %6.2f%%", percent);
643 slsmg_printf("%s", hpp->buf);
645 prev_idx = perf_evsel__group_idx(evsel);
648 idx_delta = nr_members - prev_idx - 1;
650 while (idx_delta--) {
652 * zero-fill group members at last which have no sample
654 ui_browser__set_percent_color(arg->b, 0.0,
656 ret += scnprintf(hpp->buf, hpp->size,
658 slsmg_printf("%s", hpp->buf);
662 if (!arg->current_entry || !arg->b->navkeypressed)
663 ui_browser__set_color(arg->b, HE_COLORSET_NORMAL);
668 #define __HPP_COLOR_PERCENT_FN(_type, _field, _cb) \
669 static u64 __hpp_get_##_field(struct hist_entry *he) \
671 return he->stat._field; \
674 static int hist_browser__hpp_color_##_type(struct perf_hpp *hpp, \
675 struct hist_entry *he) \
677 return __hpp__color_fmt(hpp, he, __hpp_get_##_field, _cb); \
680 __HPP_COLOR_PERCENT_FN(overhead, period, __hpp__color_callchain)
681 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys, NULL)
682 __HPP_COLOR_PERCENT_FN(overhead_us, period_us, NULL)
683 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys, NULL)
684 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us, NULL)
686 #undef __HPP_COLOR_PERCENT_FN
688 void hist_browser__init_hpp(void)
690 perf_hpp__column_enable(PERF_HPP__OVERHEAD);
694 perf_hpp__format[PERF_HPP__OVERHEAD].color =
695 hist_browser__hpp_color_overhead;
696 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
697 hist_browser__hpp_color_overhead_sys;
698 perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
699 hist_browser__hpp_color_overhead_us;
700 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
701 hist_browser__hpp_color_overhead_guest_sys;
702 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
703 hist_browser__hpp_color_overhead_guest_us;
706 static int hist_browser__show_entry(struct hist_browser *browser,
707 struct hist_entry *entry,
712 int width = browser->b.width;
713 char folded_sign = ' ';
714 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
715 off_t row_offset = entry->row_offset;
717 struct perf_hpp_fmt *fmt;
720 browser->he_selection = entry;
721 browser->selection = &entry->ms;
724 if (symbol_conf.use_callchain) {
725 hist_entry__init_have_children(entry);
726 folded_sign = hist_entry__folded(entry);
729 if (row_offset == 0) {
730 struct hpp_arg arg = {
732 .folded_sign = folded_sign,
733 .current_entry = current_entry,
735 struct perf_hpp hpp = {
741 ui_browser__gotorc(&browser->b, row, 0);
743 perf_hpp__for_each_format(fmt) {
751 width -= fmt->color(&hpp, entry);
753 width -= fmt->entry(&hpp, entry);
754 slsmg_printf("%s", s);
758 /* The scroll bar isn't being used */
759 if (!browser->b.navkeypressed)
762 hist_entry__sort_snprintf(entry, s, sizeof(s), browser->hists);
763 slsmg_write_nstring(s, width);
769 if (folded_sign == '-' && row != browser->b.height) {
770 printed += hist_browser__show_callchain(browser, &entry->sorted_chain,
774 browser->he_selection = entry;
780 static void ui_browser__hists_init_top(struct ui_browser *browser)
782 if (browser->top == NULL) {
783 struct hist_browser *hb;
785 hb = container_of(browser, struct hist_browser, b);
786 browser->top = rb_first(&hb->hists->entries);
790 static unsigned int hist_browser__refresh(struct ui_browser *browser)
794 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
796 ui_browser__hists_init_top(browser);
798 for (nd = browser->top; nd; nd = rb_next(nd)) {
799 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
804 row += hist_browser__show_entry(hb, h, row);
805 if (row == browser->height)
812 static struct rb_node *hists__filter_entries(struct rb_node *nd)
815 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
825 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd)
828 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
838 static void ui_browser__hists_seek(struct ui_browser *browser,
839 off_t offset, int whence)
841 struct hist_entry *h;
845 if (browser->nr_entries == 0)
848 ui_browser__hists_init_top(browser);
852 nd = hists__filter_entries(rb_first(browser->entries));
858 nd = hists__filter_prev_entries(rb_last(browser->entries));
866 * Moves not relative to the first visible entry invalidates its
869 h = rb_entry(browser->top, struct hist_entry, rb_node);
873 * Here we have to check if nd is expanded (+), if it is we can't go
874 * the next top level hist_entry, instead we must compute an offset of
875 * what _not_ to show and not change the first visible entry.
877 * This offset increments when we are going from top to bottom and
878 * decreases when we're going from bottom to top.
880 * As we don't have backpointers to the top level in the callchains
881 * structure, we need to always print the whole hist_entry callchain,
882 * skipping the first ones that are before the first visible entry
883 * and stop when we printed enough lines to fill the screen.
888 h = rb_entry(nd, struct hist_entry, rb_node);
889 if (h->ms.unfolded) {
890 u16 remaining = h->nr_rows - h->row_offset;
891 if (offset > remaining) {
895 h->row_offset += offset;
901 nd = hists__filter_entries(rb_next(nd));
906 } while (offset != 0);
907 } else if (offset < 0) {
909 h = rb_entry(nd, struct hist_entry, rb_node);
910 if (h->ms.unfolded) {
912 if (-offset > h->row_offset) {
913 offset += h->row_offset;
916 h->row_offset += offset;
922 if (-offset > h->nr_rows) {
923 offset += h->nr_rows;
926 h->row_offset = h->nr_rows + offset;
934 nd = hists__filter_prev_entries(rb_prev(nd));
941 * Last unfiltered hist_entry, check if it is
942 * unfolded, if it is then we should have
943 * row_offset at its last entry.
945 h = rb_entry(nd, struct hist_entry, rb_node);
947 h->row_offset = h->nr_rows;
954 h = rb_entry(nd, struct hist_entry, rb_node);
959 static int hist_browser__fprintf_callchain_node_rb_tree(struct hist_browser *browser,
960 struct callchain_node *chain_node,
961 u64 total, int level,
964 struct rb_node *node;
965 int offset = level * LEVEL_OFFSET_STEP;
966 u64 new_total, remaining;
969 if (callchain_param.mode == CHAIN_GRAPH_REL)
970 new_total = chain_node->children_hit;
974 remaining = new_total;
975 node = rb_first(&chain_node->rb_root);
977 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
978 struct rb_node *next = rb_next(node);
979 u64 cumul = callchain_cumul_hits(child);
980 struct callchain_list *chain;
981 char folded_sign = ' ';
983 int extra_offset = 0;
987 list_for_each_entry(chain, &child->val, list) {
988 char bf[1024], *alloc_str;
990 bool was_first = first;
995 extra_offset = LEVEL_OFFSET_STEP;
997 folded_sign = callchain_list__folded(chain);
1000 str = callchain_list__sym_name(chain, bf, sizeof(bf),
1003 double percent = cumul * 100.0 / new_total;
1005 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
1006 str = "Not enough memory!";
1011 printed += fprintf(fp, "%*s%c %s\n", offset + extra_offset, " ", folded_sign, str);
1013 if (folded_sign == '+')
1017 if (folded_sign == '-') {
1018 const int new_level = level + (extra_offset ? 2 : 1);
1019 printed += hist_browser__fprintf_callchain_node_rb_tree(browser, child, new_total,
1029 static int hist_browser__fprintf_callchain_node(struct hist_browser *browser,
1030 struct callchain_node *node,
1031 int level, FILE *fp)
1033 struct callchain_list *chain;
1034 int offset = level * LEVEL_OFFSET_STEP;
1035 char folded_sign = ' ';
1038 list_for_each_entry(chain, &node->val, list) {
1041 folded_sign = callchain_list__folded(chain);
1042 s = callchain_list__sym_name(chain, bf, sizeof(bf), browser->show_dso);
1043 printed += fprintf(fp, "%*s%c %s\n", offset, " ", folded_sign, s);
1046 if (folded_sign == '-')
1047 printed += hist_browser__fprintf_callchain_node_rb_tree(browser, node,
1048 browser->hists->stats.total_period,
1053 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1054 struct rb_root *chain, int level, FILE *fp)
1059 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
1060 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
1062 printed += hist_browser__fprintf_callchain_node(browser, node, level, fp);
1068 static int hist_browser__fprintf_entry(struct hist_browser *browser,
1069 struct hist_entry *he, FILE *fp)
1074 char folded_sign = ' ';
1076 if (symbol_conf.use_callchain)
1077 folded_sign = hist_entry__folded(he);
1079 hist_entry__sort_snprintf(he, s, sizeof(s), browser->hists);
1080 percent = (he->stat.period * 100.0) / browser->hists->stats.total_period;
1082 if (symbol_conf.use_callchain)
1083 printed += fprintf(fp, "%c ", folded_sign);
1085 printed += fprintf(fp, " %5.2f%%", percent);
1087 if (symbol_conf.show_nr_samples)
1088 printed += fprintf(fp, " %11u", he->stat.nr_events);
1090 if (symbol_conf.show_total_period)
1091 printed += fprintf(fp, " %12" PRIu64, he->stat.period);
1093 printed += fprintf(fp, "%s\n", rtrim(s));
1095 if (folded_sign == '-')
1096 printed += hist_browser__fprintf_callchain(browser, &he->sorted_chain, 1, fp);
1101 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1103 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries));
1107 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1109 printed += hist_browser__fprintf_entry(browser, h, fp);
1110 nd = hists__filter_entries(rb_next(nd));
1116 static int hist_browser__dump(struct hist_browser *browser)
1122 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
1123 if (access(filename, F_OK))
1126 * XXX: Just an arbitrary lazy upper limit
1128 if (++browser->print_seq == 8192) {
1129 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1134 fp = fopen(filename, "w");
1137 const char *err = strerror_r(errno, bf, sizeof(bf));
1138 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
1142 ++browser->print_seq;
1143 hist_browser__fprintf(browser, fp);
1145 ui_helpline__fpush("%s written!", filename);
1150 static struct hist_browser *hist_browser__new(struct hists *hists)
1152 struct hist_browser *browser = zalloc(sizeof(*browser));
1155 browser->hists = hists;
1156 browser->b.refresh = hist_browser__refresh;
1157 browser->b.seek = ui_browser__hists_seek;
1158 browser->b.use_navkeypressed = true;
1159 if (sort__branch_mode == 1)
1160 browser->has_symbols = sort_sym_from.list.next != NULL;
1162 browser->has_symbols = sort_sym.list.next != NULL;
1168 static void hist_browser__delete(struct hist_browser *browser)
1173 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
1175 return browser->he_selection;
1178 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
1180 return browser->he_selection->thread;
1183 static int hists__browser_title(struct hists *hists, char *bf, size_t size,
1184 const char *ev_name)
1188 const struct dso *dso = hists->dso_filter;
1189 const struct thread *thread = hists->thread_filter;
1190 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1191 u64 nr_events = hists->stats.total_period;
1192 struct perf_evsel *evsel = hists_to_evsel(hists);
1194 size_t buflen = sizeof(buf);
1196 if (symbol_conf.event_group && evsel->nr_members > 1) {
1197 struct perf_evsel *pos;
1199 perf_evsel__group_desc(evsel, buf, buflen);
1202 for_each_group_member(pos, evsel) {
1203 nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1204 nr_events += pos->hists.stats.total_period;
1208 nr_samples = convert_unit(nr_samples, &unit);
1209 printed = scnprintf(bf, size,
1210 "Samples: %lu%c of event '%s', Event count (approx.): %lu",
1211 nr_samples, unit, ev_name, nr_events);
1214 if (hists->uid_filter_str)
1215 printed += snprintf(bf + printed, size - printed,
1216 ", UID: %s", hists->uid_filter_str);
1218 printed += scnprintf(bf + printed, size - printed,
1220 (thread->comm_set ? thread->comm : ""),
1223 printed += scnprintf(bf + printed, size - printed,
1224 ", DSO: %s", dso->short_name);
1228 static inline void free_popup_options(char **options, int n)
1232 for (i = 0; i < n; ++i) {
1238 /* Check whether the browser is for 'top' or 'report' */
1239 static inline bool is_report_browser(void *timer)
1241 return timer == NULL;
1245 * Only runtime switching of perf data file will make "input_name" point
1246 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1247 * whether we need to call free() for current "input_name" during the switch.
1249 static bool is_input_name_malloced = false;
1251 static int switch_data_file(void)
1253 char *pwd, *options[32], *abs_path[32], *tmp;
1255 int nr_options = 0, choice = -1, ret = -1;
1256 struct dirent *dent;
1258 pwd = getenv("PWD");
1262 pwd_dir = opendir(pwd);
1266 memset(options, 0, sizeof(options));
1267 memset(options, 0, sizeof(abs_path));
1269 while ((dent = readdir(pwd_dir))) {
1270 char path[PATH_MAX];
1272 char *name = dent->d_name;
1275 if (!(dent->d_type == DT_REG))
1278 snprintf(path, sizeof(path), "%s/%s", pwd, name);
1280 file = fopen(path, "r");
1284 if (fread(&magic, 1, 8, file) < 8)
1285 goto close_file_and_continue;
1287 if (is_perf_magic(magic)) {
1288 options[nr_options] = strdup(name);
1289 if (!options[nr_options])
1290 goto close_file_and_continue;
1292 abs_path[nr_options] = strdup(path);
1293 if (!abs_path[nr_options]) {
1294 free(options[nr_options]);
1295 ui__warning("Can't search all data files due to memory shortage.\n");
1303 close_file_and_continue:
1305 if (nr_options >= 32) {
1306 ui__warning("Too many perf data files in PWD!\n"
1307 "Only the first 32 files will be listed.\n");
1314 choice = ui__popup_menu(nr_options, options);
1315 if (choice < nr_options && choice >= 0) {
1316 tmp = strdup(abs_path[choice]);
1318 if (is_input_name_malloced)
1319 free((void *)input_name);
1321 is_input_name_malloced = true;
1324 ui__warning("Data switch failed due to memory shortage!\n");
1328 free_popup_options(options, nr_options);
1329 free_popup_options(abs_path, nr_options);
1334 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
1335 const char *helpline, const char *ev_name,
1337 struct hist_browser_timer *hbt,
1338 struct perf_session_env *env)
1340 struct hists *hists = &evsel->hists;
1341 struct hist_browser *browser = hist_browser__new(hists);
1342 struct branch_info *bi;
1343 struct pstack *fstack;
1348 char script_opt[64];
1349 int delay_secs = hbt ? hbt->refresh : 0;
1351 if (browser == NULL)
1354 fstack = pstack__new(2);
1358 ui_helpline__push(helpline);
1360 memset(options, 0, sizeof(options));
1363 const struct thread *thread = NULL;
1364 const struct dso *dso = NULL;
1366 annotate = -2, zoom_dso = -2, zoom_thread = -2,
1367 annotate_f = -2, annotate_t = -2, browse_map = -2;
1368 int scripts_comm = -2, scripts_symbol = -2,
1369 scripts_all = -2, switch_data = -2;
1373 key = hist_browser__run(browser, ev_name, hbt);
1375 if (browser->he_selection != NULL) {
1376 thread = hist_browser__selected_thread(browser);
1377 dso = browser->selection->map ? browser->selection->map->dso : NULL;
1385 * Exit the browser, let hists__browser_tree
1386 * go to the next or previous
1388 goto out_free_stack;
1390 if (!browser->has_symbols) {
1391 ui_browser__warning(&browser->b, delay_secs * 2,
1392 "Annotation is only available for symbolic views, "
1393 "include \"sym*\" in --sort to use it.");
1397 if (browser->selection == NULL ||
1398 browser->selection->sym == NULL ||
1399 browser->selection->map->dso->annotate_warned)
1403 hist_browser__dump(browser);
1408 browser->show_dso = !browser->show_dso;
1413 if (ui_browser__input_window("Symbol to show",
1414 "Please enter the name of symbol you want to see",
1415 buf, "ENTER: OK, ESC: Cancel",
1416 delay_secs * 2) == K_ENTER) {
1417 hists->symbol_filter_str = *buf ? buf : NULL;
1418 hists__filter_by_symbol(hists);
1419 hist_browser__reset(browser);
1423 if (is_report_browser(hbt))
1427 if (is_report_browser(hbt))
1428 goto do_data_switch;
1433 ui_browser__help_window(&browser->b,
1434 "h/?/F1 Show this window\n"
1436 "PGDN/SPACE Navigate\n"
1437 "q/ESC/CTRL+C Exit browser\n\n"
1438 "For multiple event sessions:\n\n"
1439 "TAB/UNTAB Switch events\n\n"
1440 "For symbolic views (--sort has sym):\n\n"
1441 "-> Zoom into DSO/Threads & Annotate current symbol\n"
1443 "a Annotate current symbol\n"
1444 "C Collapse all callchains\n"
1445 "E Expand all callchains\n"
1446 "d Zoom into current DSO\n"
1447 "t Zoom into current Thread\n"
1448 "r Run available scripts('perf report' only)\n"
1449 "s Switch to another data file in PWD ('perf report' only)\n"
1450 "P Print histograms to perf.hist.N\n"
1451 "V Verbose (DSO names in callchains, etc)\n"
1452 "/ Filter symbol by name");
1461 if (pstack__empty(fstack)) {
1463 * Go back to the perf_evsel_menu__run or other user
1466 goto out_free_stack;
1469 top = pstack__pop(fstack);
1470 if (top == &browser->hists->dso_filter)
1472 if (top == &browser->hists->thread_filter)
1473 goto zoom_out_thread;
1478 !ui_browser__dialog_yesno(&browser->b,
1479 "Do you really want to exit?"))
1484 goto out_free_stack;
1489 if (!browser->has_symbols)
1490 goto add_exit_option;
1492 if (sort__branch_mode == 1) {
1493 bi = browser->he_selection->branch_info;
1494 if (browser->selection != NULL &&
1496 bi->from.sym != NULL &&
1497 !bi->from.map->dso->annotate_warned &&
1498 asprintf(&options[nr_options], "Annotate %s",
1499 bi->from.sym->name) > 0)
1500 annotate_f = nr_options++;
1502 if (browser->selection != NULL &&
1504 bi->to.sym != NULL &&
1505 !bi->to.map->dso->annotate_warned &&
1506 (bi->to.sym != bi->from.sym ||
1507 bi->to.map->dso != bi->from.map->dso) &&
1508 asprintf(&options[nr_options], "Annotate %s",
1509 bi->to.sym->name) > 0)
1510 annotate_t = nr_options++;
1513 if (browser->selection != NULL &&
1514 browser->selection->sym != NULL &&
1515 !browser->selection->map->dso->annotate_warned &&
1516 asprintf(&options[nr_options], "Annotate %s",
1517 browser->selection->sym->name) > 0)
1518 annotate = nr_options++;
1521 if (thread != NULL &&
1522 asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
1523 (browser->hists->thread_filter ? "out of" : "into"),
1524 (thread->comm_set ? thread->comm : ""),
1526 zoom_thread = nr_options++;
1529 asprintf(&options[nr_options], "Zoom %s %s DSO",
1530 (browser->hists->dso_filter ? "out of" : "into"),
1531 (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
1532 zoom_dso = nr_options++;
1534 if (browser->selection != NULL &&
1535 browser->selection->map != NULL &&
1536 asprintf(&options[nr_options], "Browse map details") > 0)
1537 browse_map = nr_options++;
1539 /* perf script support */
1540 if (browser->he_selection) {
1543 if (asprintf(&options[nr_options], "Run scripts for samples of thread [%s]",
1544 browser->he_selection->thread->comm) > 0)
1545 scripts_comm = nr_options++;
1547 sym = browser->he_selection->ms.sym;
1548 if (sym && sym->namelen &&
1549 asprintf(&options[nr_options], "Run scripts for samples of symbol [%s]",
1551 scripts_symbol = nr_options++;
1554 if (asprintf(&options[nr_options], "Run scripts for all samples") > 0)
1555 scripts_all = nr_options++;
1557 if (is_report_browser(hbt) && asprintf(&options[nr_options],
1558 "Switch to another data file in PWD") > 0)
1559 switch_data = nr_options++;
1561 options[nr_options++] = (char *)"Exit";
1563 choice = ui__popup_menu(nr_options, options);
1565 if (choice == nr_options - 1)
1569 free_popup_options(options, nr_options - 1);
1573 if (choice == annotate || choice == annotate_t || choice == annotate_f) {
1574 struct hist_entry *he;
1577 if (!objdump_path && perf_session_env__lookup_objdump(env))
1580 he = hist_browser__selected_entry(browser);
1585 * we stash the branch_info symbol + map into the
1586 * the ms so we don't have to rewrite all the annotation
1587 * code to use branch_info.
1588 * in branch mode, the ms struct is not used
1590 if (choice == annotate_f) {
1591 he->ms.sym = he->branch_info->from.sym;
1592 he->ms.map = he->branch_info->from.map;
1593 } else if (choice == annotate_t) {
1594 he->ms.sym = he->branch_info->to.sym;
1595 he->ms.map = he->branch_info->to.map;
1599 * Don't let this be freed, say, by hists__decay_entry.
1602 err = hist_entry__tui_annotate(he, evsel->idx, hbt);
1605 * offer option to annotate the other branch source or target
1606 * (if they exists) when returning from annotate
1608 if ((err == 'q' || err == CTRL('c'))
1609 && annotate_t != -2 && annotate_f != -2)
1610 goto retry_popup_menu;
1612 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1614 ui_browser__handle_resize(&browser->b);
1616 } else if (choice == browse_map)
1617 map__browse(browser->selection->map);
1618 else if (choice == zoom_dso) {
1620 if (browser->hists->dso_filter) {
1621 pstack__remove(fstack, &browser->hists->dso_filter);
1624 browser->hists->dso_filter = NULL;
1625 sort_dso.elide = false;
1629 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1630 dso->kernel ? "the Kernel" : dso->short_name);
1631 browser->hists->dso_filter = dso;
1632 sort_dso.elide = true;
1633 pstack__push(fstack, &browser->hists->dso_filter);
1635 hists__filter_by_dso(hists);
1636 hist_browser__reset(browser);
1637 } else if (choice == zoom_thread) {
1639 if (browser->hists->thread_filter) {
1640 pstack__remove(fstack, &browser->hists->thread_filter);
1643 browser->hists->thread_filter = NULL;
1644 sort_thread.elide = false;
1646 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1647 thread->comm_set ? thread->comm : "",
1649 browser->hists->thread_filter = thread;
1650 sort_thread.elide = true;
1651 pstack__push(fstack, &browser->hists->thread_filter);
1653 hists__filter_by_thread(hists);
1654 hist_browser__reset(browser);
1656 /* perf scripts support */
1657 else if (choice == scripts_all || choice == scripts_comm ||
1658 choice == scripts_symbol) {
1660 memset(script_opt, 0, 64);
1662 if (choice == scripts_comm)
1663 sprintf(script_opt, " -c %s ", browser->he_selection->thread->comm);
1665 if (choice == scripts_symbol)
1666 sprintf(script_opt, " -S %s ", browser->he_selection->ms.sym->name);
1668 script_browse(script_opt);
1670 /* Switch to another data file */
1671 else if (choice == switch_data) {
1673 if (!switch_data_file()) {
1674 key = K_SWITCH_INPUT_DATA;
1677 ui__warning("Won't switch the data files due to\n"
1678 "no valid data file get selected!\n");
1682 pstack__delete(fstack);
1684 hist_browser__delete(browser);
1685 free_popup_options(options, nr_options - 1);
1689 struct perf_evsel_menu {
1690 struct ui_browser b;
1691 struct perf_evsel *selection;
1692 bool lost_events, lost_events_warned;
1693 struct perf_session_env *env;
1696 static void perf_evsel_menu__write(struct ui_browser *browser,
1697 void *entry, int row)
1699 struct perf_evsel_menu *menu = container_of(browser,
1700 struct perf_evsel_menu, b);
1701 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1702 bool current_entry = ui_browser__is_current_entry(browser, row);
1703 unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1704 const char *ev_name = perf_evsel__name(evsel);
1706 const char *warn = " ";
1709 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
1710 HE_COLORSET_NORMAL);
1712 if (symbol_conf.event_group && evsel->nr_members > 1) {
1713 struct perf_evsel *pos;
1715 ev_name = perf_evsel__group_name(evsel);
1717 for_each_group_member(pos, evsel) {
1718 nr_events += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1722 nr_events = convert_unit(nr_events, &unit);
1723 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
1724 unit, unit == ' ' ? "" : " ", ev_name);
1725 slsmg_printf("%s", bf);
1727 nr_events = evsel->hists.stats.nr_events[PERF_RECORD_LOST];
1728 if (nr_events != 0) {
1729 menu->lost_events = true;
1731 ui_browser__set_color(browser, HE_COLORSET_TOP);
1732 nr_events = convert_unit(nr_events, &unit);
1733 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
1734 nr_events, unit, unit == ' ' ? "" : " ");
1738 slsmg_write_nstring(warn, browser->width - printed);
1741 menu->selection = evsel;
1744 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
1745 int nr_events, const char *help,
1746 struct hist_browser_timer *hbt)
1748 struct perf_evlist *evlist = menu->b.priv;
1749 struct perf_evsel *pos;
1750 const char *ev_name, *title = "Available samples";
1751 int delay_secs = hbt ? hbt->refresh : 0;
1754 if (ui_browser__show(&menu->b, title,
1755 "ESC: exit, ENTER|->: Browse histograms") < 0)
1759 key = ui_browser__run(&menu->b, delay_secs);
1763 hbt->timer(hbt->arg);
1765 if (!menu->lost_events_warned && menu->lost_events) {
1766 ui_browser__warn_lost_events(&menu->b);
1767 menu->lost_events_warned = true;
1772 if (!menu->selection)
1774 pos = menu->selection;
1776 perf_evlist__set_selected(evlist, pos);
1778 * Give the calling tool a chance to populate the non
1779 * default evsel resorted hists tree.
1782 hbt->timer(hbt->arg);
1783 ev_name = perf_evsel__name(pos);
1784 key = perf_evsel__hists_browse(pos, nr_events, help,
1787 ui_browser__show_title(&menu->b, title);
1790 if (pos->node.next == &evlist->entries)
1791 pos = list_entry(evlist->entries.next, struct perf_evsel, node);
1793 pos = list_entry(pos->node.next, struct perf_evsel, node);
1796 if (pos->node.prev == &evlist->entries)
1797 pos = list_entry(evlist->entries.prev, struct perf_evsel, node);
1799 pos = list_entry(pos->node.prev, struct perf_evsel, node);
1802 if (!ui_browser__dialog_yesno(&menu->b,
1803 "Do you really want to exit?"))
1806 case K_SWITCH_INPUT_DATA:
1816 if (!ui_browser__dialog_yesno(&menu->b,
1817 "Do you really want to exit?"))
1829 ui_browser__hide(&menu->b);
1833 static bool filter_group_entries(struct ui_browser *self __maybe_unused,
1836 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1838 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
1844 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
1845 int nr_entries, const char *help,
1846 struct hist_browser_timer *hbt,
1847 struct perf_session_env *env)
1849 struct perf_evsel *pos;
1850 struct perf_evsel_menu menu = {
1852 .entries = &evlist->entries,
1853 .refresh = ui_browser__list_head_refresh,
1854 .seek = ui_browser__list_head_seek,
1855 .write = perf_evsel_menu__write,
1856 .filter = filter_group_entries,
1857 .nr_entries = nr_entries,
1863 ui_helpline__push("Press ESC to exit");
1865 list_for_each_entry(pos, &evlist->entries, node) {
1866 const char *ev_name = perf_evsel__name(pos);
1867 size_t line_len = strlen(ev_name) + 7;
1869 if (menu.b.width < line_len)
1870 menu.b.width = line_len;
1873 return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
1876 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
1877 struct hist_browser_timer *hbt,
1878 struct perf_session_env *env)
1880 int nr_entries = evlist->nr_entries;
1883 if (nr_entries == 1) {
1884 struct perf_evsel *first = list_entry(evlist->entries.next,
1885 struct perf_evsel, node);
1886 const char *ev_name = perf_evsel__name(first);
1888 return perf_evsel__hists_browse(first, nr_entries, help,
1889 ev_name, false, hbt, env);
1892 if (symbol_conf.event_group) {
1893 struct perf_evsel *pos;
1896 list_for_each_entry(pos, &evlist->entries, node)
1897 if (perf_evsel__is_group_leader(pos))
1900 if (nr_entries == 1)
1904 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,