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;
1244 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
1245 const char *helpline, const char *ev_name,
1247 struct hist_browser_timer *hbt,
1248 struct perf_session_env *env)
1250 struct hists *hists = &evsel->hists;
1251 struct hist_browser *browser = hist_browser__new(hists);
1252 struct branch_info *bi;
1253 struct pstack *fstack;
1258 char script_opt[64];
1259 int delay_secs = hbt ? hbt->refresh : 0;
1261 if (browser == NULL)
1264 fstack = pstack__new(2);
1268 ui_helpline__push(helpline);
1270 memset(options, 0, sizeof(options));
1273 const struct thread *thread = NULL;
1274 const struct dso *dso = NULL;
1276 annotate = -2, zoom_dso = -2, zoom_thread = -2,
1277 annotate_f = -2, annotate_t = -2, browse_map = -2;
1278 int scripts_comm = -2, scripts_symbol = -2, scripts_all = -2;
1282 key = hist_browser__run(browser, ev_name, hbt);
1284 if (browser->he_selection != NULL) {
1285 thread = hist_browser__selected_thread(browser);
1286 dso = browser->selection->map ? browser->selection->map->dso : NULL;
1294 * Exit the browser, let hists__browser_tree
1295 * go to the next or previous
1297 goto out_free_stack;
1299 if (!browser->has_symbols) {
1300 ui_browser__warning(&browser->b, delay_secs * 2,
1301 "Annotation is only available for symbolic views, "
1302 "include \"sym*\" in --sort to use it.");
1306 if (browser->selection == NULL ||
1307 browser->selection->sym == NULL ||
1308 browser->selection->map->dso->annotate_warned)
1312 hist_browser__dump(browser);
1317 browser->show_dso = !browser->show_dso;
1322 if (ui_browser__input_window("Symbol to show",
1323 "Please enter the name of symbol you want to see",
1324 buf, "ENTER: OK, ESC: Cancel",
1325 delay_secs * 2) == K_ENTER) {
1326 hists->symbol_filter_str = *buf ? buf : NULL;
1327 hists__filter_by_symbol(hists);
1328 hist_browser__reset(browser);
1332 if (is_report_browser(hbt))
1338 ui_browser__help_window(&browser->b,
1339 "h/?/F1 Show this window\n"
1341 "PGDN/SPACE Navigate\n"
1342 "q/ESC/CTRL+C Exit browser\n\n"
1343 "For multiple event sessions:\n\n"
1344 "TAB/UNTAB Switch events\n\n"
1345 "For symbolic views (--sort has sym):\n\n"
1346 "-> Zoom into DSO/Threads & Annotate current symbol\n"
1348 "a Annotate current symbol\n"
1349 "C Collapse all callchains\n"
1350 "E Expand all callchains\n"
1351 "d Zoom into current DSO\n"
1352 "t Zoom into current Thread\n"
1353 "r Run available scripts('perf report' only)\n"
1354 "P Print histograms to perf.hist.N\n"
1355 "V Verbose (DSO names in callchains, etc)\n"
1356 "/ Filter symbol by name");
1365 if (pstack__empty(fstack)) {
1367 * Go back to the perf_evsel_menu__run or other user
1370 goto out_free_stack;
1373 top = pstack__pop(fstack);
1374 if (top == &browser->hists->dso_filter)
1376 if (top == &browser->hists->thread_filter)
1377 goto zoom_out_thread;
1382 !ui_browser__dialog_yesno(&browser->b,
1383 "Do you really want to exit?"))
1388 goto out_free_stack;
1393 if (!browser->has_symbols)
1394 goto add_exit_option;
1396 if (sort__branch_mode == 1) {
1397 bi = browser->he_selection->branch_info;
1398 if (browser->selection != NULL &&
1400 bi->from.sym != NULL &&
1401 !bi->from.map->dso->annotate_warned &&
1402 asprintf(&options[nr_options], "Annotate %s",
1403 bi->from.sym->name) > 0)
1404 annotate_f = nr_options++;
1406 if (browser->selection != NULL &&
1408 bi->to.sym != NULL &&
1409 !bi->to.map->dso->annotate_warned &&
1410 (bi->to.sym != bi->from.sym ||
1411 bi->to.map->dso != bi->from.map->dso) &&
1412 asprintf(&options[nr_options], "Annotate %s",
1413 bi->to.sym->name) > 0)
1414 annotate_t = nr_options++;
1417 if (browser->selection != NULL &&
1418 browser->selection->sym != NULL &&
1419 !browser->selection->map->dso->annotate_warned &&
1420 asprintf(&options[nr_options], "Annotate %s",
1421 browser->selection->sym->name) > 0)
1422 annotate = nr_options++;
1425 if (thread != NULL &&
1426 asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
1427 (browser->hists->thread_filter ? "out of" : "into"),
1428 (thread->comm_set ? thread->comm : ""),
1430 zoom_thread = nr_options++;
1433 asprintf(&options[nr_options], "Zoom %s %s DSO",
1434 (browser->hists->dso_filter ? "out of" : "into"),
1435 (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
1436 zoom_dso = nr_options++;
1438 if (browser->selection != NULL &&
1439 browser->selection->map != NULL &&
1440 asprintf(&options[nr_options], "Browse map details") > 0)
1441 browse_map = nr_options++;
1443 /* perf script support */
1444 if (browser->he_selection) {
1447 if (asprintf(&options[nr_options], "Run scripts for samples of thread [%s]",
1448 browser->he_selection->thread->comm) > 0)
1449 scripts_comm = nr_options++;
1451 sym = browser->he_selection->ms.sym;
1452 if (sym && sym->namelen &&
1453 asprintf(&options[nr_options], "Run scripts for samples of symbol [%s]",
1455 scripts_symbol = nr_options++;
1458 if (asprintf(&options[nr_options], "Run scripts for all samples") > 0)
1459 scripts_all = nr_options++;
1462 options[nr_options++] = (char *)"Exit";
1464 choice = ui__popup_menu(nr_options, options);
1466 if (choice == nr_options - 1)
1470 free_popup_options(options, nr_options - 1);
1474 if (choice == annotate || choice == annotate_t || choice == annotate_f) {
1475 struct hist_entry *he;
1478 if (!objdump_path && perf_session_env__lookup_objdump(env))
1481 he = hist_browser__selected_entry(browser);
1486 * we stash the branch_info symbol + map into the
1487 * the ms so we don't have to rewrite all the annotation
1488 * code to use branch_info.
1489 * in branch mode, the ms struct is not used
1491 if (choice == annotate_f) {
1492 he->ms.sym = he->branch_info->from.sym;
1493 he->ms.map = he->branch_info->from.map;
1494 } else if (choice == annotate_t) {
1495 he->ms.sym = he->branch_info->to.sym;
1496 he->ms.map = he->branch_info->to.map;
1500 * Don't let this be freed, say, by hists__decay_entry.
1503 err = hist_entry__tui_annotate(he, evsel->idx, hbt);
1506 * offer option to annotate the other branch source or target
1507 * (if they exists) when returning from annotate
1509 if ((err == 'q' || err == CTRL('c'))
1510 && annotate_t != -2 && annotate_f != -2)
1511 goto retry_popup_menu;
1513 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1515 ui_browser__handle_resize(&browser->b);
1517 } else if (choice == browse_map)
1518 map__browse(browser->selection->map);
1519 else if (choice == zoom_dso) {
1521 if (browser->hists->dso_filter) {
1522 pstack__remove(fstack, &browser->hists->dso_filter);
1525 browser->hists->dso_filter = NULL;
1526 sort_dso.elide = false;
1530 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1531 dso->kernel ? "the Kernel" : dso->short_name);
1532 browser->hists->dso_filter = dso;
1533 sort_dso.elide = true;
1534 pstack__push(fstack, &browser->hists->dso_filter);
1536 hists__filter_by_dso(hists);
1537 hist_browser__reset(browser);
1538 } else if (choice == zoom_thread) {
1540 if (browser->hists->thread_filter) {
1541 pstack__remove(fstack, &browser->hists->thread_filter);
1544 browser->hists->thread_filter = NULL;
1545 sort_thread.elide = false;
1547 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1548 thread->comm_set ? thread->comm : "",
1550 browser->hists->thread_filter = thread;
1551 sort_thread.elide = true;
1552 pstack__push(fstack, &browser->hists->thread_filter);
1554 hists__filter_by_thread(hists);
1555 hist_browser__reset(browser);
1557 /* perf scripts support */
1558 else if (choice == scripts_all || choice == scripts_comm ||
1559 choice == scripts_symbol) {
1561 memset(script_opt, 0, 64);
1563 if (choice == scripts_comm)
1564 sprintf(script_opt, " -c %s ", browser->he_selection->thread->comm);
1566 if (choice == scripts_symbol)
1567 sprintf(script_opt, " -S %s ", browser->he_selection->ms.sym->name);
1569 script_browse(script_opt);
1573 pstack__delete(fstack);
1575 hist_browser__delete(browser);
1576 free_popup_options(options, nr_options - 1);
1580 struct perf_evsel_menu {
1581 struct ui_browser b;
1582 struct perf_evsel *selection;
1583 bool lost_events, lost_events_warned;
1584 struct perf_session_env *env;
1587 static void perf_evsel_menu__write(struct ui_browser *browser,
1588 void *entry, int row)
1590 struct perf_evsel_menu *menu = container_of(browser,
1591 struct perf_evsel_menu, b);
1592 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1593 bool current_entry = ui_browser__is_current_entry(browser, row);
1594 unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1595 const char *ev_name = perf_evsel__name(evsel);
1597 const char *warn = " ";
1600 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
1601 HE_COLORSET_NORMAL);
1603 if (symbol_conf.event_group && evsel->nr_members > 1) {
1604 struct perf_evsel *pos;
1606 ev_name = perf_evsel__group_name(evsel);
1608 for_each_group_member(pos, evsel) {
1609 nr_events += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1613 nr_events = convert_unit(nr_events, &unit);
1614 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
1615 unit, unit == ' ' ? "" : " ", ev_name);
1616 slsmg_printf("%s", bf);
1618 nr_events = evsel->hists.stats.nr_events[PERF_RECORD_LOST];
1619 if (nr_events != 0) {
1620 menu->lost_events = true;
1622 ui_browser__set_color(browser, HE_COLORSET_TOP);
1623 nr_events = convert_unit(nr_events, &unit);
1624 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
1625 nr_events, unit, unit == ' ' ? "" : " ");
1629 slsmg_write_nstring(warn, browser->width - printed);
1632 menu->selection = evsel;
1635 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
1636 int nr_events, const char *help,
1637 struct hist_browser_timer *hbt)
1639 struct perf_evlist *evlist = menu->b.priv;
1640 struct perf_evsel *pos;
1641 const char *ev_name, *title = "Available samples";
1642 int delay_secs = hbt ? hbt->refresh : 0;
1645 if (ui_browser__show(&menu->b, title,
1646 "ESC: exit, ENTER|->: Browse histograms") < 0)
1650 key = ui_browser__run(&menu->b, delay_secs);
1654 hbt->timer(hbt->arg);
1656 if (!menu->lost_events_warned && menu->lost_events) {
1657 ui_browser__warn_lost_events(&menu->b);
1658 menu->lost_events_warned = true;
1663 if (!menu->selection)
1665 pos = menu->selection;
1667 perf_evlist__set_selected(evlist, pos);
1669 * Give the calling tool a chance to populate the non
1670 * default evsel resorted hists tree.
1673 hbt->timer(hbt->arg);
1674 ev_name = perf_evsel__name(pos);
1675 key = perf_evsel__hists_browse(pos, nr_events, help,
1678 ui_browser__show_title(&menu->b, title);
1681 if (pos->node.next == &evlist->entries)
1682 pos = list_entry(evlist->entries.next, struct perf_evsel, node);
1684 pos = list_entry(pos->node.next, struct perf_evsel, node);
1687 if (pos->node.prev == &evlist->entries)
1688 pos = list_entry(evlist->entries.prev, struct perf_evsel, node);
1690 pos = list_entry(pos->node.prev, struct perf_evsel, node);
1693 if (!ui_browser__dialog_yesno(&menu->b,
1694 "Do you really want to exit?"))
1706 if (!ui_browser__dialog_yesno(&menu->b,
1707 "Do you really want to exit?"))
1719 ui_browser__hide(&menu->b);
1723 static bool filter_group_entries(struct ui_browser *self __maybe_unused,
1726 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1728 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
1734 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
1735 int nr_entries, const char *help,
1736 struct hist_browser_timer *hbt,
1737 struct perf_session_env *env)
1739 struct perf_evsel *pos;
1740 struct perf_evsel_menu menu = {
1742 .entries = &evlist->entries,
1743 .refresh = ui_browser__list_head_refresh,
1744 .seek = ui_browser__list_head_seek,
1745 .write = perf_evsel_menu__write,
1746 .filter = filter_group_entries,
1747 .nr_entries = nr_entries,
1753 ui_helpline__push("Press ESC to exit");
1755 list_for_each_entry(pos, &evlist->entries, node) {
1756 const char *ev_name = perf_evsel__name(pos);
1757 size_t line_len = strlen(ev_name) + 7;
1759 if (menu.b.width < line_len)
1760 menu.b.width = line_len;
1763 return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
1766 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
1767 struct hist_browser_timer *hbt,
1768 struct perf_session_env *env)
1770 int nr_entries = evlist->nr_entries;
1773 if (nr_entries == 1) {
1774 struct perf_evsel *first = list_entry(evlist->entries.next,
1775 struct perf_evsel, node);
1776 const char *ev_name = perf_evsel__name(first);
1778 return perf_evsel__hists_browse(first, nr_entries, help,
1779 ev_name, false, hbt, env);
1782 if (symbol_conf.event_group) {
1783 struct perf_evsel *pos;
1786 list_for_each_entry(pos, &evlist->entries, node)
1787 if (perf_evsel__is_group_leader(pos))
1790 if (nr_entries == 1)
1794 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,