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;
1144 char script_opt[64];
1146 if (browser == NULL)
1149 fstack = pstack__new(2);
1153 ui_helpline__push(helpline);
1155 memset(options, 0, sizeof(options));
1158 const struct thread *thread = NULL;
1159 const struct dso *dso = NULL;
1161 annotate = -2, zoom_dso = -2, zoom_thread = -2,
1162 annotate_f = -2, annotate_t = -2, browse_map = -2;
1163 int scripts_comm = -2, scripts_symbol = -2, scripts_all = -2;
1167 key = hist_browser__run(browser, ev_name, timer, arg, delay_secs);
1169 if (browser->he_selection != NULL) {
1170 thread = hist_browser__selected_thread(browser);
1171 dso = browser->selection->map ? browser->selection->map->dso : NULL;
1179 * Exit the browser, let hists__browser_tree
1180 * go to the next or previous
1182 goto out_free_stack;
1184 if (!browser->has_symbols) {
1185 ui_browser__warning(&browser->b, delay_secs * 2,
1186 "Annotation is only available for symbolic views, "
1187 "include \"sym*\" in --sort to use it.");
1191 if (browser->selection == NULL ||
1192 browser->selection->sym == NULL ||
1193 browser->selection->map->dso->annotate_warned)
1197 hist_browser__dump(browser);
1202 browser->show_dso = !browser->show_dso;
1207 if (ui_browser__input_window("Symbol to show",
1208 "Please enter the name of symbol you want to see",
1209 buf, "ENTER: OK, ESC: Cancel",
1210 delay_secs * 2) == K_ENTER) {
1211 hists->symbol_filter_str = *buf ? buf : NULL;
1212 hists__filter_by_symbol(hists);
1213 hist_browser__reset(browser);
1221 ui_browser__help_window(&browser->b,
1222 "h/?/F1 Show this window\n"
1224 "PGDN/SPACE Navigate\n"
1225 "q/ESC/CTRL+C Exit browser\n\n"
1226 "For multiple event sessions:\n\n"
1227 "TAB/UNTAB Switch events\n\n"
1228 "For symbolic views (--sort has sym):\n\n"
1229 "-> Zoom into DSO/Threads & Annotate current symbol\n"
1231 "a Annotate current symbol\n"
1232 "C Collapse all callchains\n"
1233 "E Expand all callchains\n"
1234 "d Zoom into current DSO\n"
1235 "t Zoom into current Thread\n"
1236 "r Run available scripts\n"
1237 "P Print histograms to perf.hist.N\n"
1238 "V Verbose (DSO names in callchains, etc)\n"
1239 "/ Filter symbol by name");
1248 if (pstack__empty(fstack)) {
1250 * Go back to the perf_evsel_menu__run or other user
1253 goto out_free_stack;
1256 top = pstack__pop(fstack);
1257 if (top == &browser->hists->dso_filter)
1259 if (top == &browser->hists->thread_filter)
1260 goto zoom_out_thread;
1265 !ui_browser__dialog_yesno(&browser->b,
1266 "Do you really want to exit?"))
1271 goto out_free_stack;
1276 if (!browser->has_symbols)
1277 goto add_exit_option;
1279 if (sort__branch_mode == 1) {
1280 bi = browser->he_selection->branch_info;
1281 if (browser->selection != NULL &&
1283 bi->from.sym != NULL &&
1284 !bi->from.map->dso->annotate_warned &&
1285 asprintf(&options[nr_options], "Annotate %s",
1286 bi->from.sym->name) > 0)
1287 annotate_f = nr_options++;
1289 if (browser->selection != NULL &&
1291 bi->to.sym != NULL &&
1292 !bi->to.map->dso->annotate_warned &&
1293 (bi->to.sym != bi->from.sym ||
1294 bi->to.map->dso != bi->from.map->dso) &&
1295 asprintf(&options[nr_options], "Annotate %s",
1296 bi->to.sym->name) > 0)
1297 annotate_t = nr_options++;
1300 if (browser->selection != NULL &&
1301 browser->selection->sym != NULL &&
1302 !browser->selection->map->dso->annotate_warned &&
1303 asprintf(&options[nr_options], "Annotate %s",
1304 browser->selection->sym->name) > 0)
1305 annotate = nr_options++;
1308 if (thread != NULL &&
1309 asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
1310 (browser->hists->thread_filter ? "out of" : "into"),
1311 (thread->comm_set ? thread->comm : ""),
1313 zoom_thread = nr_options++;
1316 asprintf(&options[nr_options], "Zoom %s %s DSO",
1317 (browser->hists->dso_filter ? "out of" : "into"),
1318 (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
1319 zoom_dso = nr_options++;
1321 if (browser->selection != NULL &&
1322 browser->selection->map != NULL &&
1323 asprintf(&options[nr_options], "Browse map details") > 0)
1324 browse_map = nr_options++;
1326 /* perf script support */
1327 if (browser->he_selection) {
1330 if (asprintf(&options[nr_options], "Run scripts for samples of thread [%s]",
1331 browser->he_selection->thread->comm) > 0)
1332 scripts_comm = nr_options++;
1334 sym = browser->he_selection->ms.sym;
1335 if (sym && sym->namelen &&
1336 asprintf(&options[nr_options], "Run scripts for samples of symbol [%s]",
1338 scripts_symbol = nr_options++;
1341 if (asprintf(&options[nr_options], "Run scripts for all samples") > 0)
1342 scripts_all = nr_options++;
1345 options[nr_options++] = (char *)"Exit";
1347 choice = ui__popup_menu(nr_options, options);
1349 if (choice == nr_options - 1)
1353 free_popup_options(options, nr_options - 1);
1357 if (choice == annotate || choice == annotate_t || choice == annotate_f) {
1358 struct hist_entry *he;
1361 he = hist_browser__selected_entry(browser);
1366 * we stash the branch_info symbol + map into the
1367 * the ms so we don't have to rewrite all the annotation
1368 * code to use branch_info.
1369 * in branch mode, the ms struct is not used
1371 if (choice == annotate_f) {
1372 he->ms.sym = he->branch_info->from.sym;
1373 he->ms.map = he->branch_info->from.map;
1374 } else if (choice == annotate_t) {
1375 he->ms.sym = he->branch_info->to.sym;
1376 he->ms.map = he->branch_info->to.map;
1380 * Don't let this be freed, say, by hists__decay_entry.
1383 err = hist_entry__tui_annotate(he, evsel->idx,
1384 timer, arg, delay_secs);
1387 * offer option to annotate the other branch source or target
1388 * (if they exists) when returning from annotate
1390 if ((err == 'q' || err == CTRL('c'))
1391 && annotate_t != -2 && annotate_f != -2)
1392 goto retry_popup_menu;
1394 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1396 ui_browser__handle_resize(&browser->b);
1398 } else if (choice == browse_map)
1399 map__browse(browser->selection->map);
1400 else if (choice == zoom_dso) {
1402 if (browser->hists->dso_filter) {
1403 pstack__remove(fstack, &browser->hists->dso_filter);
1406 browser->hists->dso_filter = NULL;
1407 sort_dso.elide = false;
1411 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1412 dso->kernel ? "the Kernel" : dso->short_name);
1413 browser->hists->dso_filter = dso;
1414 sort_dso.elide = true;
1415 pstack__push(fstack, &browser->hists->dso_filter);
1417 hists__filter_by_dso(hists);
1418 hist_browser__reset(browser);
1419 } else if (choice == zoom_thread) {
1421 if (browser->hists->thread_filter) {
1422 pstack__remove(fstack, &browser->hists->thread_filter);
1425 browser->hists->thread_filter = NULL;
1426 sort_thread.elide = false;
1428 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1429 thread->comm_set ? thread->comm : "",
1431 browser->hists->thread_filter = thread;
1432 sort_thread.elide = true;
1433 pstack__push(fstack, &browser->hists->thread_filter);
1435 hists__filter_by_thread(hists);
1436 hist_browser__reset(browser);
1438 /* perf scripts support */
1439 else if (choice == scripts_all || choice == scripts_comm ||
1440 choice == scripts_symbol) {
1442 memset(script_opt, 0, 64);
1444 if (choice == scripts_comm)
1445 sprintf(script_opt, " -c %s ", browser->he_selection->thread->comm);
1447 if (choice == scripts_symbol)
1448 sprintf(script_opt, " -S %s ", browser->he_selection->ms.sym->name);
1450 script_browse(script_opt);
1454 pstack__delete(fstack);
1456 hist_browser__delete(browser);
1457 free_popup_options(options, nr_options - 1);
1461 struct perf_evsel_menu {
1462 struct ui_browser b;
1463 struct perf_evsel *selection;
1464 bool lost_events, lost_events_warned;
1467 static void perf_evsel_menu__write(struct ui_browser *browser,
1468 void *entry, int row)
1470 struct perf_evsel_menu *menu = container_of(browser,
1471 struct perf_evsel_menu, b);
1472 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1473 bool current_entry = ui_browser__is_current_entry(browser, row);
1474 unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1475 const char *ev_name = perf_evsel__name(evsel);
1477 const char *warn = " ";
1480 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
1481 HE_COLORSET_NORMAL);
1483 nr_events = convert_unit(nr_events, &unit);
1484 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
1485 unit, unit == ' ' ? "" : " ", ev_name);
1486 slsmg_printf("%s", bf);
1488 nr_events = evsel->hists.stats.nr_events[PERF_RECORD_LOST];
1489 if (nr_events != 0) {
1490 menu->lost_events = true;
1492 ui_browser__set_color(browser, HE_COLORSET_TOP);
1493 nr_events = convert_unit(nr_events, &unit);
1494 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
1495 nr_events, unit, unit == ' ' ? "" : " ");
1499 slsmg_write_nstring(warn, browser->width - printed);
1502 menu->selection = evsel;
1505 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
1506 int nr_events, const char *help,
1507 void(*timer)(void *arg), void *arg, int delay_secs)
1509 struct perf_evlist *evlist = menu->b.priv;
1510 struct perf_evsel *pos;
1511 const char *ev_name, *title = "Available samples";
1514 if (ui_browser__show(&menu->b, title,
1515 "ESC: exit, ENTER|->: Browse histograms") < 0)
1519 key = ui_browser__run(&menu->b, delay_secs);
1525 if (!menu->lost_events_warned && menu->lost_events) {
1526 ui_browser__warn_lost_events(&menu->b);
1527 menu->lost_events_warned = true;
1532 if (!menu->selection)
1534 pos = menu->selection;
1536 perf_evlist__set_selected(evlist, pos);
1538 * Give the calling tool a chance to populate the non
1539 * default evsel resorted hists tree.
1543 ev_name = perf_evsel__name(pos);
1544 key = perf_evsel__hists_browse(pos, nr_events, help,
1545 ev_name, true, timer,
1547 ui_browser__show_title(&menu->b, title);
1550 if (pos->node.next == &evlist->entries)
1551 pos = list_entry(evlist->entries.next, struct perf_evsel, node);
1553 pos = list_entry(pos->node.next, struct perf_evsel, node);
1556 if (pos->node.prev == &evlist->entries)
1557 pos = list_entry(evlist->entries.prev, struct perf_evsel, node);
1559 pos = list_entry(pos->node.prev, struct perf_evsel, node);
1562 if (!ui_browser__dialog_yesno(&menu->b,
1563 "Do you really want to exit?"))
1575 if (!ui_browser__dialog_yesno(&menu->b,
1576 "Do you really want to exit?"))
1588 ui_browser__hide(&menu->b);
1592 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
1594 void(*timer)(void *arg), void *arg,
1597 struct perf_evsel *pos;
1598 struct perf_evsel_menu menu = {
1600 .entries = &evlist->entries,
1601 .refresh = ui_browser__list_head_refresh,
1602 .seek = ui_browser__list_head_seek,
1603 .write = perf_evsel_menu__write,
1604 .nr_entries = evlist->nr_entries,
1609 ui_helpline__push("Press ESC to exit");
1611 list_for_each_entry(pos, &evlist->entries, node) {
1612 const char *ev_name = perf_evsel__name(pos);
1613 size_t line_len = strlen(ev_name) + 7;
1615 if (menu.b.width < line_len)
1616 menu.b.width = line_len;
1619 return perf_evsel_menu__run(&menu, evlist->nr_entries, help, timer,
1623 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
1624 void(*timer)(void *arg), void *arg,
1627 if (evlist->nr_entries == 1) {
1628 struct perf_evsel *first = list_entry(evlist->entries.next,
1629 struct perf_evsel, node);
1630 const char *ev_name = perf_evsel__name(first);
1631 return perf_evsel__hists_browse(first, evlist->nr_entries, help,
1632 ev_name, false, timer, arg,
1636 return __perf_evlist__tui_browse_hists(evlist, help,
1637 timer, arg, delay_secs);