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"
15 #include "../browser.h"
16 #include "../helpline.h"
24 struct hist_entry *he_selection;
25 struct map_symbol *selection;
31 extern void hist_browser__init_hpp(void);
33 static int hists__browser_title(struct hists *hists, char *bf, size_t size,
36 static void hist_browser__refresh_dimensions(struct hist_browser *browser)
38 /* 3 == +/- toggle symbol before actual hist_entry rendering */
39 browser->b.width = 3 + (hists__sort_list_width(browser->hists) +
43 static void hist_browser__reset(struct hist_browser *browser)
45 browser->b.nr_entries = browser->hists->nr_entries;
46 hist_browser__refresh_dimensions(browser);
47 ui_browser__reset_index(&browser->b);
50 static char tree__folded_sign(bool unfolded)
52 return unfolded ? '-' : '+';
55 static char map_symbol__folded(const struct map_symbol *ms)
57 return ms->has_children ? tree__folded_sign(ms->unfolded) : ' ';
60 static char hist_entry__folded(const struct hist_entry *he)
62 return map_symbol__folded(&he->ms);
65 static char callchain_list__folded(const struct callchain_list *cl)
67 return map_symbol__folded(&cl->ms);
70 static void map_symbol__set_folding(struct map_symbol *ms, bool unfold)
72 ms->unfolded = unfold ? ms->has_children : false;
75 static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
80 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
81 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
82 struct callchain_list *chain;
83 char folded_sign = ' '; /* No children */
85 list_for_each_entry(chain, &child->val, list) {
87 /* We need this because we may not have children */
88 folded_sign = callchain_list__folded(chain);
89 if (folded_sign == '+')
93 if (folded_sign == '-') /* Have children and they're unfolded */
94 n += callchain_node__count_rows_rb_tree(child);
100 static int callchain_node__count_rows(struct callchain_node *node)
102 struct callchain_list *chain;
103 bool unfolded = false;
106 list_for_each_entry(chain, &node->val, list) {
108 unfolded = chain->ms.unfolded;
112 n += callchain_node__count_rows_rb_tree(node);
117 static int callchain__count_rows(struct rb_root *chain)
122 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
123 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
124 n += callchain_node__count_rows(node);
130 static bool map_symbol__toggle_fold(struct map_symbol *ms)
135 if (!ms->has_children)
138 ms->unfolded = !ms->unfolded;
142 static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
144 struct rb_node *nd = rb_first(&node->rb_root);
146 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
147 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
148 struct callchain_list *chain;
151 list_for_each_entry(chain, &child->val, list) {
154 chain->ms.has_children = chain->list.next != &child->val ||
155 !RB_EMPTY_ROOT(&child->rb_root);
157 chain->ms.has_children = chain->list.next == &child->val &&
158 !RB_EMPTY_ROOT(&child->rb_root);
161 callchain_node__init_have_children_rb_tree(child);
165 static void callchain_node__init_have_children(struct callchain_node *node)
167 struct callchain_list *chain;
169 list_for_each_entry(chain, &node->val, list)
170 chain->ms.has_children = !RB_EMPTY_ROOT(&node->rb_root);
172 callchain_node__init_have_children_rb_tree(node);
175 static void callchain__init_have_children(struct rb_root *root)
179 for (nd = rb_first(root); nd; nd = rb_next(nd)) {
180 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
181 callchain_node__init_have_children(node);
185 static void hist_entry__init_have_children(struct hist_entry *he)
187 if (!he->init_have_children) {
188 he->ms.has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
189 callchain__init_have_children(&he->sorted_chain);
190 he->init_have_children = true;
194 static bool hist_browser__toggle_fold(struct hist_browser *browser)
196 if (map_symbol__toggle_fold(browser->selection)) {
197 struct hist_entry *he = browser->he_selection;
199 hist_entry__init_have_children(he);
200 browser->hists->nr_entries -= he->nr_rows;
203 he->nr_rows = callchain__count_rows(&he->sorted_chain);
206 browser->hists->nr_entries += he->nr_rows;
207 browser->b.nr_entries = browser->hists->nr_entries;
212 /* If it doesn't have children, no toggling performed */
216 static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
221 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
222 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
223 struct callchain_list *chain;
224 bool has_children = false;
226 list_for_each_entry(chain, &child->val, list) {
228 map_symbol__set_folding(&chain->ms, unfold);
229 has_children = chain->ms.has_children;
233 n += callchain_node__set_folding_rb_tree(child, unfold);
239 static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
241 struct callchain_list *chain;
242 bool has_children = false;
245 list_for_each_entry(chain, &node->val, list) {
247 map_symbol__set_folding(&chain->ms, unfold);
248 has_children = chain->ms.has_children;
252 n += callchain_node__set_folding_rb_tree(node, unfold);
257 static int callchain__set_folding(struct rb_root *chain, bool unfold)
262 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
263 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
264 n += callchain_node__set_folding(node, unfold);
270 static void hist_entry__set_folding(struct hist_entry *he, bool unfold)
272 hist_entry__init_have_children(he);
273 map_symbol__set_folding(&he->ms, unfold);
275 if (he->ms.has_children) {
276 int n = callchain__set_folding(&he->sorted_chain, unfold);
277 he->nr_rows = unfold ? n : 0;
282 static void hists__set_folding(struct hists *hists, bool unfold)
286 hists->nr_entries = 0;
288 for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
289 struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
290 hist_entry__set_folding(he, unfold);
291 hists->nr_entries += 1 + he->nr_rows;
295 static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
297 hists__set_folding(browser->hists, unfold);
298 browser->b.nr_entries = browser->hists->nr_entries;
299 /* Go to the start, we may be way after valid entries after a collapse */
300 ui_browser__reset_index(&browser->b);
303 static void ui_browser__warn_lost_events(struct ui_browser *browser)
305 ui_browser__warning(browser, 4,
306 "Events are being lost, check IO/CPU overload!\n\n"
307 "You may want to run 'perf' using a RT scheduler policy:\n\n"
308 " perf top -r 80\n\n"
309 "Or reduce the sampling frequency.");
312 static int hist_browser__run(struct hist_browser *browser, const char *ev_name,
313 void(*timer)(void *arg), void *arg, int delay_secs)
318 browser->b.entries = &browser->hists->entries;
319 browser->b.nr_entries = browser->hists->nr_entries;
321 hist_browser__refresh_dimensions(browser);
322 hists__browser_title(browser->hists, title, sizeof(title), ev_name);
324 if (ui_browser__show(&browser->b, title,
325 "Press '?' for help on key bindings") < 0)
329 key = ui_browser__run(&browser->b, delay_secs);
334 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
336 if (browser->hists->stats.nr_lost_warned !=
337 browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
338 browser->hists->stats.nr_lost_warned =
339 browser->hists->stats.nr_events[PERF_RECORD_LOST];
340 ui_browser__warn_lost_events(&browser->b);
343 hists__browser_title(browser->hists, title, sizeof(title), ev_name);
344 ui_browser__show_title(&browser->b, title);
346 case 'D': { /* Debug */
348 struct hist_entry *h = rb_entry(browser->b.top,
349 struct hist_entry, rb_node);
351 ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
352 seq++, browser->b.nr_entries,
353 browser->hists->nr_entries,
357 h->row_offset, h->nr_rows);
361 /* Collapse the whole world. */
362 hist_browser__set_folding(browser, false);
365 /* Expand the whole world. */
366 hist_browser__set_folding(browser, true);
369 if (hist_browser__toggle_fold(browser))
377 ui_browser__hide(&browser->b);
381 static char *callchain_list__sym_name(struct callchain_list *cl,
382 char *bf, size_t bfsize, bool show_dso)
387 printed = scnprintf(bf, bfsize, "%s", cl->ms.sym->name);
389 printed = scnprintf(bf, bfsize, "%#" PRIx64, cl->ip);
392 scnprintf(bf + printed, bfsize - printed, " %s",
393 cl->ms.map ? cl->ms.map->dso->short_name : "unknown");
398 #define LEVEL_OFFSET_STEP 3
400 static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browser,
401 struct callchain_node *chain_node,
402 u64 total, int level,
405 bool *is_current_entry)
407 struct rb_node *node;
408 int first_row = row, width, offset = level * LEVEL_OFFSET_STEP;
409 u64 new_total, remaining;
411 if (callchain_param.mode == CHAIN_GRAPH_REL)
412 new_total = chain_node->children_hit;
416 remaining = new_total;
417 node = rb_first(&chain_node->rb_root);
419 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
420 struct rb_node *next = rb_next(node);
421 u64 cumul = callchain_cumul_hits(child);
422 struct callchain_list *chain;
423 char folded_sign = ' ';
425 int extra_offset = 0;
429 list_for_each_entry(chain, &child->val, list) {
430 char bf[1024], *alloc_str;
433 bool was_first = first;
438 extra_offset = LEVEL_OFFSET_STEP;
440 folded_sign = callchain_list__folded(chain);
441 if (*row_offset != 0) {
447 str = callchain_list__sym_name(chain, bf, sizeof(bf),
450 double percent = cumul * 100.0 / new_total;
452 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
453 str = "Not enough memory!";
458 color = HE_COLORSET_NORMAL;
459 width = browser->b.width - (offset + extra_offset + 2);
460 if (ui_browser__is_current_entry(&browser->b, row)) {
461 browser->selection = &chain->ms;
462 color = HE_COLORSET_SELECTED;
463 *is_current_entry = true;
466 ui_browser__set_color(&browser->b, color);
467 ui_browser__gotorc(&browser->b, row, 0);
468 slsmg_write_nstring(" ", offset + extra_offset);
469 slsmg_printf("%c ", folded_sign);
470 slsmg_write_nstring(str, width);
473 if (++row == browser->b.height)
476 if (folded_sign == '+')
480 if (folded_sign == '-') {
481 const int new_level = level + (extra_offset ? 2 : 1);
482 row += hist_browser__show_callchain_node_rb_tree(browser, child, new_total,
483 new_level, row, row_offset,
486 if (row == browser->b.height)
491 return row - first_row;
494 static int hist_browser__show_callchain_node(struct hist_browser *browser,
495 struct callchain_node *node,
496 int level, unsigned short row,
498 bool *is_current_entry)
500 struct callchain_list *chain;
502 offset = level * LEVEL_OFFSET_STEP,
503 width = browser->b.width - offset;
504 char folded_sign = ' ';
506 list_for_each_entry(chain, &node->val, list) {
510 folded_sign = callchain_list__folded(chain);
512 if (*row_offset != 0) {
517 color = HE_COLORSET_NORMAL;
518 if (ui_browser__is_current_entry(&browser->b, row)) {
519 browser->selection = &chain->ms;
520 color = HE_COLORSET_SELECTED;
521 *is_current_entry = true;
524 s = callchain_list__sym_name(chain, bf, sizeof(bf),
526 ui_browser__gotorc(&browser->b, row, 0);
527 ui_browser__set_color(&browser->b, color);
528 slsmg_write_nstring(" ", offset);
529 slsmg_printf("%c ", folded_sign);
530 slsmg_write_nstring(s, width - 2);
532 if (++row == browser->b.height)
536 if (folded_sign == '-')
537 row += hist_browser__show_callchain_node_rb_tree(browser, node,
538 browser->hists->stats.total_period,
543 return row - first_row;
546 static int hist_browser__show_callchain(struct hist_browser *browser,
547 struct rb_root *chain,
548 int level, unsigned short row,
550 bool *is_current_entry)
555 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
556 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
558 row += hist_browser__show_callchain_node(browser, node, level,
561 if (row == browser->b.height)
565 return row - first_row;
568 #define HPP__COLOR_FN(_name, _field) \
569 static int hist_browser__hpp_color_ ## _name(struct perf_hpp *hpp, \
570 struct hist_entry *he) \
572 struct hists *hists = he->hists; \
573 double percent = 100.0 * he->stat._field / hists->stats.total_period; \
574 *(double *)hpp->ptr = percent; \
575 return scnprintf(hpp->buf, hpp->size, "%6.2f%%", percent); \
578 HPP__COLOR_FN(overhead, period)
579 HPP__COLOR_FN(overhead_sys, period_sys)
580 HPP__COLOR_FN(overhead_us, period_us)
581 HPP__COLOR_FN(overhead_guest_sys, period_guest_sys)
582 HPP__COLOR_FN(overhead_guest_us, period_guest_us)
586 void hist_browser__init_hpp(void)
590 perf_hpp__format[PERF_HPP__OVERHEAD].color =
591 hist_browser__hpp_color_overhead;
592 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
593 hist_browser__hpp_color_overhead_sys;
594 perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
595 hist_browser__hpp_color_overhead_us;
596 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
597 hist_browser__hpp_color_overhead_guest_sys;
598 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
599 hist_browser__hpp_color_overhead_guest_us;
602 static int hist_browser__show_entry(struct hist_browser *browser,
603 struct hist_entry *entry,
609 int width = browser->b.width;
610 char folded_sign = ' ';
611 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
612 off_t row_offset = entry->row_offset;
616 browser->he_selection = entry;
617 browser->selection = &entry->ms;
620 if (symbol_conf.use_callchain) {
621 hist_entry__init_have_children(entry);
622 folded_sign = hist_entry__folded(entry);
625 if (row_offset == 0) {
626 struct perf_hpp hpp = {
631 ui_browser__gotorc(&browser->b, row, 0);
633 for (i = 0; i < PERF_HPP__MAX_INDEX; i++) {
634 if (!perf_hpp__format[i].cond)
643 if (perf_hpp__format[i].color) {
645 /* It will set percent for us. See HPP__COLOR_FN above. */
646 width -= perf_hpp__format[i].color(&hpp, entry);
648 ui_browser__set_percent_color(&browser->b, percent, current_entry);
650 if (i == PERF_HPP__OVERHEAD && symbol_conf.use_callchain) {
651 slsmg_printf("%c ", folded_sign);
655 slsmg_printf("%s", s);
657 if (!current_entry || !browser->b.navkeypressed)
658 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
660 width -= perf_hpp__format[i].entry(&hpp, entry);
661 slsmg_printf("%s", s);
665 /* The scroll bar isn't being used */
666 if (!browser->b.navkeypressed)
669 hist_entry__sort_snprintf(entry, s, sizeof(s), browser->hists);
670 slsmg_write_nstring(s, width);
676 if (folded_sign == '-' && row != browser->b.height) {
677 printed += hist_browser__show_callchain(browser, &entry->sorted_chain,
681 browser->he_selection = entry;
687 static void ui_browser__hists_init_top(struct ui_browser *browser)
689 if (browser->top == NULL) {
690 struct hist_browser *hb;
692 hb = container_of(browser, struct hist_browser, b);
693 browser->top = rb_first(&hb->hists->entries);
697 static unsigned int hist_browser__refresh(struct ui_browser *browser)
701 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
703 ui_browser__hists_init_top(browser);
705 for (nd = browser->top; nd; nd = rb_next(nd)) {
706 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
711 row += hist_browser__show_entry(hb, h, row);
712 if (row == browser->height)
719 static struct rb_node *hists__filter_entries(struct rb_node *nd)
722 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
732 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd)
735 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
745 static void ui_browser__hists_seek(struct ui_browser *browser,
746 off_t offset, int whence)
748 struct hist_entry *h;
752 if (browser->nr_entries == 0)
755 ui_browser__hists_init_top(browser);
759 nd = hists__filter_entries(rb_first(browser->entries));
765 nd = hists__filter_prev_entries(rb_last(browser->entries));
773 * Moves not relative to the first visible entry invalidates its
776 h = rb_entry(browser->top, struct hist_entry, rb_node);
780 * Here we have to check if nd is expanded (+), if it is we can't go
781 * the next top level hist_entry, instead we must compute an offset of
782 * what _not_ to show and not change the first visible entry.
784 * This offset increments when we are going from top to bottom and
785 * decreases when we're going from bottom to top.
787 * As we don't have backpointers to the top level in the callchains
788 * structure, we need to always print the whole hist_entry callchain,
789 * skipping the first ones that are before the first visible entry
790 * and stop when we printed enough lines to fill the screen.
795 h = rb_entry(nd, struct hist_entry, rb_node);
796 if (h->ms.unfolded) {
797 u16 remaining = h->nr_rows - h->row_offset;
798 if (offset > remaining) {
802 h->row_offset += offset;
808 nd = hists__filter_entries(rb_next(nd));
813 } while (offset != 0);
814 } else if (offset < 0) {
816 h = rb_entry(nd, struct hist_entry, rb_node);
817 if (h->ms.unfolded) {
819 if (-offset > h->row_offset) {
820 offset += h->row_offset;
823 h->row_offset += offset;
829 if (-offset > h->nr_rows) {
830 offset += h->nr_rows;
833 h->row_offset = h->nr_rows + offset;
841 nd = hists__filter_prev_entries(rb_prev(nd));
848 * Last unfiltered hist_entry, check if it is
849 * unfolded, if it is then we should have
850 * row_offset at its last entry.
852 h = rb_entry(nd, struct hist_entry, rb_node);
854 h->row_offset = h->nr_rows;
861 h = rb_entry(nd, struct hist_entry, rb_node);
866 static int hist_browser__fprintf_callchain_node_rb_tree(struct hist_browser *browser,
867 struct callchain_node *chain_node,
868 u64 total, int level,
871 struct rb_node *node;
872 int offset = level * LEVEL_OFFSET_STEP;
873 u64 new_total, remaining;
876 if (callchain_param.mode == CHAIN_GRAPH_REL)
877 new_total = chain_node->children_hit;
881 remaining = new_total;
882 node = rb_first(&chain_node->rb_root);
884 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
885 struct rb_node *next = rb_next(node);
886 u64 cumul = callchain_cumul_hits(child);
887 struct callchain_list *chain;
888 char folded_sign = ' ';
890 int extra_offset = 0;
894 list_for_each_entry(chain, &child->val, list) {
895 char bf[1024], *alloc_str;
897 bool was_first = first;
902 extra_offset = LEVEL_OFFSET_STEP;
904 folded_sign = callchain_list__folded(chain);
907 str = callchain_list__sym_name(chain, bf, sizeof(bf),
910 double percent = cumul * 100.0 / new_total;
912 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
913 str = "Not enough memory!";
918 printed += fprintf(fp, "%*s%c %s\n", offset + extra_offset, " ", folded_sign, str);
920 if (folded_sign == '+')
924 if (folded_sign == '-') {
925 const int new_level = level + (extra_offset ? 2 : 1);
926 printed += hist_browser__fprintf_callchain_node_rb_tree(browser, child, new_total,
936 static int hist_browser__fprintf_callchain_node(struct hist_browser *browser,
937 struct callchain_node *node,
940 struct callchain_list *chain;
941 int offset = level * LEVEL_OFFSET_STEP;
942 char folded_sign = ' ';
945 list_for_each_entry(chain, &node->val, list) {
948 folded_sign = callchain_list__folded(chain);
949 s = callchain_list__sym_name(chain, bf, sizeof(bf), browser->show_dso);
950 printed += fprintf(fp, "%*s%c %s\n", offset, " ", folded_sign, s);
953 if (folded_sign == '-')
954 printed += hist_browser__fprintf_callchain_node_rb_tree(browser, node,
955 browser->hists->stats.total_period,
960 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
961 struct rb_root *chain, int level, FILE *fp)
966 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
967 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
969 printed += hist_browser__fprintf_callchain_node(browser, node, level, fp);
975 static int hist_browser__fprintf_entry(struct hist_browser *browser,
976 struct hist_entry *he, FILE *fp)
981 char folded_sign = ' ';
983 if (symbol_conf.use_callchain)
984 folded_sign = hist_entry__folded(he);
986 hist_entry__sort_snprintf(he, s, sizeof(s), browser->hists);
987 percent = (he->stat.period * 100.0) / browser->hists->stats.total_period;
989 if (symbol_conf.use_callchain)
990 printed += fprintf(fp, "%c ", folded_sign);
992 printed += fprintf(fp, " %5.2f%%", percent);
994 if (symbol_conf.show_nr_samples)
995 printed += fprintf(fp, " %11u", he->stat.nr_events);
997 if (symbol_conf.show_total_period)
998 printed += fprintf(fp, " %12" PRIu64, he->stat.period);
1000 printed += fprintf(fp, "%s\n", rtrim(s));
1002 if (folded_sign == '-')
1003 printed += hist_browser__fprintf_callchain(browser, &he->sorted_chain, 1, fp);
1008 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1010 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries));
1014 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1016 printed += hist_browser__fprintf_entry(browser, h, fp);
1017 nd = hists__filter_entries(rb_next(nd));
1023 static int hist_browser__dump(struct hist_browser *browser)
1029 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
1030 if (access(filename, F_OK))
1033 * XXX: Just an arbitrary lazy upper limit
1035 if (++browser->print_seq == 8192) {
1036 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1041 fp = fopen(filename, "w");
1044 const char *err = strerror_r(errno, bf, sizeof(bf));
1045 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
1049 ++browser->print_seq;
1050 hist_browser__fprintf(browser, fp);
1052 ui_helpline__fpush("%s written!", filename);
1057 static struct hist_browser *hist_browser__new(struct hists *hists)
1059 struct hist_browser *browser = zalloc(sizeof(*browser));
1062 browser->hists = hists;
1063 browser->b.refresh = hist_browser__refresh;
1064 browser->b.seek = ui_browser__hists_seek;
1065 browser->b.use_navkeypressed = true;
1066 if (sort__branch_mode == 1)
1067 browser->has_symbols = sort_sym_from.list.next != NULL;
1069 browser->has_symbols = sort_sym.list.next != NULL;
1075 static void hist_browser__delete(struct hist_browser *browser)
1080 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
1082 return browser->he_selection;
1085 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
1087 return browser->he_selection->thread;
1090 static int hists__browser_title(struct hists *hists, char *bf, size_t size,
1091 const char *ev_name)
1095 const struct dso *dso = hists->dso_filter;
1096 const struct thread *thread = hists->thread_filter;
1097 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1098 u64 nr_events = hists->stats.total_period;
1100 nr_samples = convert_unit(nr_samples, &unit);
1101 printed = scnprintf(bf, size,
1102 "Samples: %lu%c of event '%s', Event count (approx.): %lu",
1103 nr_samples, unit, ev_name, nr_events);
1106 if (hists->uid_filter_str)
1107 printed += snprintf(bf + printed, size - printed,
1108 ", UID: %s", hists->uid_filter_str);
1110 printed += scnprintf(bf + printed, size - printed,
1112 (thread->comm_set ? thread->comm : ""),
1115 printed += scnprintf(bf + printed, size - printed,
1116 ", DSO: %s", dso->short_name);
1120 static inline void free_popup_options(char **options, int n)
1124 for (i = 0; i < n; ++i) {
1130 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
1131 const char *helpline, const char *ev_name,
1133 void(*timer)(void *arg), void *arg,
1136 struct hists *hists = &evsel->hists;
1137 struct hist_browser *browser = hist_browser__new(hists);
1138 struct branch_info *bi;
1139 struct pstack *fstack;
1145 if (browser == NULL)
1148 fstack = pstack__new(2);
1152 ui_helpline__push(helpline);
1154 memset(options, 0, sizeof(options));
1157 const struct thread *thread = NULL;
1158 const struct dso *dso = NULL;
1160 annotate = -2, zoom_dso = -2, zoom_thread = -2,
1161 annotate_f = -2, annotate_t = -2, browse_map = -2;
1165 key = hist_browser__run(browser, ev_name, timer, arg, delay_secs);
1167 if (browser->he_selection != NULL) {
1168 thread = hist_browser__selected_thread(browser);
1169 dso = browser->selection->map ? browser->selection->map->dso : NULL;
1177 * Exit the browser, let hists__browser_tree
1178 * go to the next or previous
1180 goto out_free_stack;
1182 if (!browser->has_symbols) {
1183 ui_browser__warning(&browser->b, delay_secs * 2,
1184 "Annotation is only available for symbolic views, "
1185 "include \"sym*\" in --sort to use it.");
1189 if (browser->selection == NULL ||
1190 browser->selection->sym == NULL ||
1191 browser->selection->map->dso->annotate_warned)
1195 hist_browser__dump(browser);
1200 browser->show_dso = !browser->show_dso;
1205 if (ui_browser__input_window("Symbol to show",
1206 "Please enter the name of symbol you want to see",
1207 buf, "ENTER: OK, ESC: Cancel",
1208 delay_secs * 2) == K_ENTER) {
1209 hists->symbol_filter_str = *buf ? buf : NULL;
1210 hists__filter_by_symbol(hists);
1211 hist_browser__reset(browser);
1217 ui_browser__help_window(&browser->b,
1218 "h/?/F1 Show this window\n"
1220 "PGDN/SPACE Navigate\n"
1221 "q/ESC/CTRL+C Exit browser\n\n"
1222 "For multiple event sessions:\n\n"
1223 "TAB/UNTAB Switch events\n\n"
1224 "For symbolic views (--sort has sym):\n\n"
1225 "-> Zoom into DSO/Threads & Annotate current symbol\n"
1227 "a Annotate current symbol\n"
1228 "C Collapse all callchains\n"
1229 "E Expand all callchains\n"
1230 "d Zoom into current DSO\n"
1231 "t Zoom into current Thread\n"
1232 "P Print histograms to perf.hist.N\n"
1233 "V Verbose (DSO names in callchains, etc)\n"
1234 "/ Filter symbol by name");
1243 if (pstack__empty(fstack)) {
1245 * Go back to the perf_evsel_menu__run or other user
1248 goto out_free_stack;
1251 top = pstack__pop(fstack);
1252 if (top == &browser->hists->dso_filter)
1254 if (top == &browser->hists->thread_filter)
1255 goto zoom_out_thread;
1260 !ui_browser__dialog_yesno(&browser->b,
1261 "Do you really want to exit?"))
1266 goto out_free_stack;
1271 if (!browser->has_symbols)
1272 goto add_exit_option;
1274 if (sort__branch_mode == 1) {
1275 bi = browser->he_selection->branch_info;
1276 if (browser->selection != NULL &&
1278 bi->from.sym != NULL &&
1279 !bi->from.map->dso->annotate_warned &&
1280 asprintf(&options[nr_options], "Annotate %s",
1281 bi->from.sym->name) > 0)
1282 annotate_f = nr_options++;
1284 if (browser->selection != NULL &&
1286 bi->to.sym != NULL &&
1287 !bi->to.map->dso->annotate_warned &&
1288 (bi->to.sym != bi->from.sym ||
1289 bi->to.map->dso != bi->from.map->dso) &&
1290 asprintf(&options[nr_options], "Annotate %s",
1291 bi->to.sym->name) > 0)
1292 annotate_t = nr_options++;
1295 if (browser->selection != NULL &&
1296 browser->selection->sym != NULL &&
1297 !browser->selection->map->dso->annotate_warned &&
1298 asprintf(&options[nr_options], "Annotate %s",
1299 browser->selection->sym->name) > 0)
1300 annotate = nr_options++;
1303 if (thread != NULL &&
1304 asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
1305 (browser->hists->thread_filter ? "out of" : "into"),
1306 (thread->comm_set ? thread->comm : ""),
1308 zoom_thread = nr_options++;
1311 asprintf(&options[nr_options], "Zoom %s %s DSO",
1312 (browser->hists->dso_filter ? "out of" : "into"),
1313 (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
1314 zoom_dso = nr_options++;
1316 if (browser->selection != NULL &&
1317 browser->selection->map != NULL &&
1318 asprintf(&options[nr_options], "Browse map details") > 0)
1319 browse_map = nr_options++;
1321 options[nr_options++] = (char *)"Exit";
1323 choice = ui__popup_menu(nr_options, options);
1325 if (choice == nr_options - 1)
1329 free_popup_options(options, nr_options - 1);
1333 if (choice == annotate || choice == annotate_t || choice == annotate_f) {
1334 struct hist_entry *he;
1337 he = hist_browser__selected_entry(browser);
1342 * we stash the branch_info symbol + map into the
1343 * the ms so we don't have to rewrite all the annotation
1344 * code to use branch_info.
1345 * in branch mode, the ms struct is not used
1347 if (choice == annotate_f) {
1348 he->ms.sym = he->branch_info->from.sym;
1349 he->ms.map = he->branch_info->from.map;
1350 } else if (choice == annotate_t) {
1351 he->ms.sym = he->branch_info->to.sym;
1352 he->ms.map = he->branch_info->to.map;
1356 * Don't let this be freed, say, by hists__decay_entry.
1359 err = hist_entry__tui_annotate(he, evsel->idx,
1360 timer, arg, delay_secs);
1363 * offer option to annotate the other branch source or target
1364 * (if they exists) when returning from annotate
1366 if ((err == 'q' || err == CTRL('c'))
1367 && annotate_t != -2 && annotate_f != -2)
1368 goto retry_popup_menu;
1370 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1372 ui_browser__handle_resize(&browser->b);
1374 } else if (choice == browse_map)
1375 map__browse(browser->selection->map);
1376 else if (choice == zoom_dso) {
1378 if (browser->hists->dso_filter) {
1379 pstack__remove(fstack, &browser->hists->dso_filter);
1382 browser->hists->dso_filter = NULL;
1383 sort_dso.elide = false;
1387 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1388 dso->kernel ? "the Kernel" : dso->short_name);
1389 browser->hists->dso_filter = dso;
1390 sort_dso.elide = true;
1391 pstack__push(fstack, &browser->hists->dso_filter);
1393 hists__filter_by_dso(hists);
1394 hist_browser__reset(browser);
1395 } else if (choice == zoom_thread) {
1397 if (browser->hists->thread_filter) {
1398 pstack__remove(fstack, &browser->hists->thread_filter);
1401 browser->hists->thread_filter = NULL;
1402 sort_thread.elide = false;
1404 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1405 thread->comm_set ? thread->comm : "",
1407 browser->hists->thread_filter = thread;
1408 sort_thread.elide = true;
1409 pstack__push(fstack, &browser->hists->thread_filter);
1411 hists__filter_by_thread(hists);
1412 hist_browser__reset(browser);
1416 pstack__delete(fstack);
1418 hist_browser__delete(browser);
1419 free_popup_options(options, nr_options - 1);
1423 struct perf_evsel_menu {
1424 struct ui_browser b;
1425 struct perf_evsel *selection;
1426 bool lost_events, lost_events_warned;
1429 static void perf_evsel_menu__write(struct ui_browser *browser,
1430 void *entry, int row)
1432 struct perf_evsel_menu *menu = container_of(browser,
1433 struct perf_evsel_menu, b);
1434 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1435 bool current_entry = ui_browser__is_current_entry(browser, row);
1436 unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1437 const char *ev_name = perf_evsel__name(evsel);
1439 const char *warn = " ";
1442 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
1443 HE_COLORSET_NORMAL);
1445 nr_events = convert_unit(nr_events, &unit);
1446 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
1447 unit, unit == ' ' ? "" : " ", ev_name);
1448 slsmg_printf("%s", bf);
1450 nr_events = evsel->hists.stats.nr_events[PERF_RECORD_LOST];
1451 if (nr_events != 0) {
1452 menu->lost_events = true;
1454 ui_browser__set_color(browser, HE_COLORSET_TOP);
1455 nr_events = convert_unit(nr_events, &unit);
1456 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
1457 nr_events, unit, unit == ' ' ? "" : " ");
1461 slsmg_write_nstring(warn, browser->width - printed);
1464 menu->selection = evsel;
1467 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
1468 int nr_events, const char *help,
1469 void(*timer)(void *arg), void *arg, int delay_secs)
1471 struct perf_evlist *evlist = menu->b.priv;
1472 struct perf_evsel *pos;
1473 const char *ev_name, *title = "Available samples";
1476 if (ui_browser__show(&menu->b, title,
1477 "ESC: exit, ENTER|->: Browse histograms") < 0)
1481 key = ui_browser__run(&menu->b, delay_secs);
1487 if (!menu->lost_events_warned && menu->lost_events) {
1488 ui_browser__warn_lost_events(&menu->b);
1489 menu->lost_events_warned = true;
1494 if (!menu->selection)
1496 pos = menu->selection;
1498 perf_evlist__set_selected(evlist, pos);
1500 * Give the calling tool a chance to populate the non
1501 * default evsel resorted hists tree.
1505 ev_name = perf_evsel__name(pos);
1506 key = perf_evsel__hists_browse(pos, nr_events, help,
1507 ev_name, true, timer,
1509 ui_browser__show_title(&menu->b, title);
1512 if (pos->node.next == &evlist->entries)
1513 pos = list_entry(evlist->entries.next, struct perf_evsel, node);
1515 pos = list_entry(pos->node.next, struct perf_evsel, node);
1518 if (pos->node.prev == &evlist->entries)
1519 pos = list_entry(evlist->entries.prev, struct perf_evsel, node);
1521 pos = list_entry(pos->node.prev, struct perf_evsel, node);
1524 if (!ui_browser__dialog_yesno(&menu->b,
1525 "Do you really want to exit?"))
1537 if (!ui_browser__dialog_yesno(&menu->b,
1538 "Do you really want to exit?"))
1550 ui_browser__hide(&menu->b);
1554 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
1556 void(*timer)(void *arg), void *arg,
1559 struct perf_evsel *pos;
1560 struct perf_evsel_menu menu = {
1562 .entries = &evlist->entries,
1563 .refresh = ui_browser__list_head_refresh,
1564 .seek = ui_browser__list_head_seek,
1565 .write = perf_evsel_menu__write,
1566 .nr_entries = evlist->nr_entries,
1571 ui_helpline__push("Press ESC to exit");
1573 list_for_each_entry(pos, &evlist->entries, node) {
1574 const char *ev_name = perf_evsel__name(pos);
1575 size_t line_len = strlen(ev_name) + 7;
1577 if (menu.b.width < line_len)
1578 menu.b.width = line_len;
1581 return perf_evsel_menu__run(&menu, evlist->nr_entries, help, timer,
1585 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
1586 void(*timer)(void *arg), void *arg,
1589 if (evlist->nr_entries == 1) {
1590 struct perf_evsel *first = list_entry(evlist->entries.next,
1591 struct perf_evsel, node);
1592 const char *ev_name = perf_evsel__name(first);
1593 return perf_evsel__hists_browse(first, evlist->nr_entries, help,
1594 ev_name, false, timer, arg,
1598 return __perf_evlist__tui_browse_hists(evlist, help,
1599 timer, arg, delay_secs);