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 double percent = 100.0 * he->_field / hpp->total_period; \
573 *(double *)hpp->ptr = percent; \
574 return scnprintf(hpp->buf, hpp->size, "%6.2f%%", percent); \
577 HPP__COLOR_FN(overhead, period)
578 HPP__COLOR_FN(overhead_sys, period_sys)
579 HPP__COLOR_FN(overhead_us, period_us)
580 HPP__COLOR_FN(overhead_guest_sys, period_guest_sys)
581 HPP__COLOR_FN(overhead_guest_us, period_guest_us)
585 void hist_browser__init_hpp(void)
587 perf_hpp__init(false, false);
589 perf_hpp__format[PERF_HPP__OVERHEAD].color =
590 hist_browser__hpp_color_overhead;
591 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
592 hist_browser__hpp_color_overhead_sys;
593 perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
594 hist_browser__hpp_color_overhead_us;
595 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
596 hist_browser__hpp_color_overhead_guest_sys;
597 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
598 hist_browser__hpp_color_overhead_guest_us;
601 static int hist_browser__show_entry(struct hist_browser *browser,
602 struct hist_entry *entry,
608 int width = browser->b.width;
609 char folded_sign = ' ';
610 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
611 off_t row_offset = entry->row_offset;
614 browser->he_selection = entry;
615 browser->selection = &entry->ms;
618 if (symbol_conf.use_callchain) {
619 hist_entry__init_have_children(entry);
620 folded_sign = hist_entry__folded(entry);
623 if (row_offset == 0) {
624 struct perf_hpp hpp = {
627 .total_period = browser->hists->stats.total_period,
630 ui_browser__gotorc(&browser->b, row, 0);
632 for (i = 0; i < PERF_HPP__MAX_INDEX; i++) {
633 if (!perf_hpp__format[i].cond)
641 if (perf_hpp__format[i].color) {
643 /* It will set percent for us. See HPP__COLOR_FN above. */
644 width -= perf_hpp__format[i].color(&hpp, entry);
646 ui_browser__set_percent_color(&browser->b, percent, current_entry);
648 if (i == 0 && symbol_conf.use_callchain) {
649 slsmg_printf("%c ", folded_sign);
653 slsmg_printf("%s", s);
655 if (!current_entry || !browser->b.navkeypressed)
656 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
658 width -= perf_hpp__format[i].entry(&hpp, entry);
659 slsmg_printf("%s", s);
663 /* The scroll bar isn't being used */
664 if (!browser->b.navkeypressed)
667 hist_entry__sort_snprintf(entry, s, sizeof(s), browser->hists);
668 slsmg_write_nstring(s, width);
674 if (folded_sign == '-' && row != browser->b.height) {
675 printed += hist_browser__show_callchain(browser, &entry->sorted_chain,
679 browser->he_selection = entry;
685 static void ui_browser__hists_init_top(struct ui_browser *browser)
687 if (browser->top == NULL) {
688 struct hist_browser *hb;
690 hb = container_of(browser, struct hist_browser, b);
691 browser->top = rb_first(&hb->hists->entries);
695 static unsigned int hist_browser__refresh(struct ui_browser *browser)
699 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
701 ui_browser__hists_init_top(browser);
703 for (nd = browser->top; nd; nd = rb_next(nd)) {
704 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
709 row += hist_browser__show_entry(hb, h, row);
710 if (row == browser->height)
717 static struct rb_node *hists__filter_entries(struct rb_node *nd)
720 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
730 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd)
733 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
743 static void ui_browser__hists_seek(struct ui_browser *browser,
744 off_t offset, int whence)
746 struct hist_entry *h;
750 if (browser->nr_entries == 0)
753 ui_browser__hists_init_top(browser);
757 nd = hists__filter_entries(rb_first(browser->entries));
763 nd = hists__filter_prev_entries(rb_last(browser->entries));
771 * Moves not relative to the first visible entry invalidates its
774 h = rb_entry(browser->top, struct hist_entry, rb_node);
778 * Here we have to check if nd is expanded (+), if it is we can't go
779 * the next top level hist_entry, instead we must compute an offset of
780 * what _not_ to show and not change the first visible entry.
782 * This offset increments when we are going from top to bottom and
783 * decreases when we're going from bottom to top.
785 * As we don't have backpointers to the top level in the callchains
786 * structure, we need to always print the whole hist_entry callchain,
787 * skipping the first ones that are before the first visible entry
788 * and stop when we printed enough lines to fill the screen.
793 h = rb_entry(nd, struct hist_entry, rb_node);
794 if (h->ms.unfolded) {
795 u16 remaining = h->nr_rows - h->row_offset;
796 if (offset > remaining) {
800 h->row_offset += offset;
806 nd = hists__filter_entries(rb_next(nd));
811 } while (offset != 0);
812 } else if (offset < 0) {
814 h = rb_entry(nd, struct hist_entry, rb_node);
815 if (h->ms.unfolded) {
817 if (-offset > h->row_offset) {
818 offset += h->row_offset;
821 h->row_offset += offset;
827 if (-offset > h->nr_rows) {
828 offset += h->nr_rows;
831 h->row_offset = h->nr_rows + offset;
839 nd = hists__filter_prev_entries(rb_prev(nd));
846 * Last unfiltered hist_entry, check if it is
847 * unfolded, if it is then we should have
848 * row_offset at its last entry.
850 h = rb_entry(nd, struct hist_entry, rb_node);
852 h->row_offset = h->nr_rows;
859 h = rb_entry(nd, struct hist_entry, rb_node);
864 static int hist_browser__fprintf_callchain_node_rb_tree(struct hist_browser *browser,
865 struct callchain_node *chain_node,
866 u64 total, int level,
869 struct rb_node *node;
870 int offset = level * LEVEL_OFFSET_STEP;
871 u64 new_total, remaining;
874 if (callchain_param.mode == CHAIN_GRAPH_REL)
875 new_total = chain_node->children_hit;
879 remaining = new_total;
880 node = rb_first(&chain_node->rb_root);
882 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
883 struct rb_node *next = rb_next(node);
884 u64 cumul = callchain_cumul_hits(child);
885 struct callchain_list *chain;
886 char folded_sign = ' ';
888 int extra_offset = 0;
892 list_for_each_entry(chain, &child->val, list) {
893 char bf[1024], *alloc_str;
895 bool was_first = first;
900 extra_offset = LEVEL_OFFSET_STEP;
902 folded_sign = callchain_list__folded(chain);
905 str = callchain_list__sym_name(chain, bf, sizeof(bf),
908 double percent = cumul * 100.0 / new_total;
910 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
911 str = "Not enough memory!";
916 printed += fprintf(fp, "%*s%c %s\n", offset + extra_offset, " ", folded_sign, str);
918 if (folded_sign == '+')
922 if (folded_sign == '-') {
923 const int new_level = level + (extra_offset ? 2 : 1);
924 printed += hist_browser__fprintf_callchain_node_rb_tree(browser, child, new_total,
934 static int hist_browser__fprintf_callchain_node(struct hist_browser *browser,
935 struct callchain_node *node,
938 struct callchain_list *chain;
939 int offset = level * LEVEL_OFFSET_STEP;
940 char folded_sign = ' ';
943 list_for_each_entry(chain, &node->val, list) {
946 folded_sign = callchain_list__folded(chain);
947 s = callchain_list__sym_name(chain, bf, sizeof(bf), browser->show_dso);
948 printed += fprintf(fp, "%*s%c %s\n", offset, " ", folded_sign, s);
951 if (folded_sign == '-')
952 printed += hist_browser__fprintf_callchain_node_rb_tree(browser, node,
953 browser->hists->stats.total_period,
958 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
959 struct rb_root *chain, int level, FILE *fp)
964 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
965 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
967 printed += hist_browser__fprintf_callchain_node(browser, node, level, fp);
973 static int hist_browser__fprintf_entry(struct hist_browser *browser,
974 struct hist_entry *he, FILE *fp)
979 char folded_sign = ' ';
981 if (symbol_conf.use_callchain)
982 folded_sign = hist_entry__folded(he);
984 hist_entry__sort_snprintf(he, s, sizeof(s), browser->hists);
985 percent = (he->period * 100.0) / browser->hists->stats.total_period;
987 if (symbol_conf.use_callchain)
988 printed += fprintf(fp, "%c ", folded_sign);
990 printed += fprintf(fp, " %5.2f%%", percent);
992 if (symbol_conf.show_nr_samples)
993 printed += fprintf(fp, " %11u", he->nr_events);
995 if (symbol_conf.show_total_period)
996 printed += fprintf(fp, " %12" PRIu64, he->period);
998 printed += fprintf(fp, "%s\n", rtrim(s));
1000 if (folded_sign == '-')
1001 printed += hist_browser__fprintf_callchain(browser, &he->sorted_chain, 1, fp);
1006 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1008 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries));
1012 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1014 printed += hist_browser__fprintf_entry(browser, h, fp);
1015 nd = hists__filter_entries(rb_next(nd));
1021 static int hist_browser__dump(struct hist_browser *browser)
1027 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
1028 if (access(filename, F_OK))
1031 * XXX: Just an arbitrary lazy upper limit
1033 if (++browser->print_seq == 8192) {
1034 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1039 fp = fopen(filename, "w");
1042 const char *err = strerror_r(errno, bf, sizeof(bf));
1043 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
1047 ++browser->print_seq;
1048 hist_browser__fprintf(browser, fp);
1050 ui_helpline__fpush("%s written!", filename);
1055 static struct hist_browser *hist_browser__new(struct hists *hists)
1057 struct hist_browser *browser = zalloc(sizeof(*browser));
1060 browser->hists = hists;
1061 browser->b.refresh = hist_browser__refresh;
1062 browser->b.seek = ui_browser__hists_seek;
1063 browser->b.use_navkeypressed = true;
1064 if (sort__branch_mode == 1)
1065 browser->has_symbols = sort_sym_from.list.next != NULL;
1067 browser->has_symbols = sort_sym.list.next != NULL;
1073 static void hist_browser__delete(struct hist_browser *browser)
1078 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
1080 return browser->he_selection;
1083 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
1085 return browser->he_selection->thread;
1088 static int hists__browser_title(struct hists *hists, char *bf, size_t size,
1089 const char *ev_name)
1093 const struct dso *dso = hists->dso_filter;
1094 const struct thread *thread = hists->thread_filter;
1095 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1096 u64 nr_events = hists->stats.total_period;
1098 nr_samples = convert_unit(nr_samples, &unit);
1099 printed = scnprintf(bf, size,
1100 "Samples: %lu%c of event '%s', Event count (approx.): %lu",
1101 nr_samples, unit, ev_name, nr_events);
1104 if (hists->uid_filter_str)
1105 printed += snprintf(bf + printed, size - printed,
1106 ", UID: %s", hists->uid_filter_str);
1108 printed += scnprintf(bf + printed, size - printed,
1110 (thread->comm_set ? thread->comm : ""),
1113 printed += scnprintf(bf + printed, size - printed,
1114 ", DSO: %s", dso->short_name);
1118 static inline void free_popup_options(char **options, int n)
1122 for (i = 0; i < n; ++i) {
1128 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
1129 const char *helpline, const char *ev_name,
1131 void(*timer)(void *arg), void *arg,
1134 struct hists *hists = &evsel->hists;
1135 struct hist_browser *browser = hist_browser__new(hists);
1136 struct branch_info *bi;
1137 struct pstack *fstack;
1143 if (browser == NULL)
1146 fstack = pstack__new(2);
1150 ui_helpline__push(helpline);
1152 memset(options, 0, sizeof(options));
1155 const struct thread *thread = NULL;
1156 const struct dso *dso = NULL;
1158 annotate = -2, zoom_dso = -2, zoom_thread = -2,
1159 annotate_f = -2, annotate_t = -2, browse_map = -2;
1163 key = hist_browser__run(browser, ev_name, timer, arg, delay_secs);
1165 if (browser->he_selection != NULL) {
1166 thread = hist_browser__selected_thread(browser);
1167 dso = browser->selection->map ? browser->selection->map->dso : NULL;
1175 * Exit the browser, let hists__browser_tree
1176 * go to the next or previous
1178 goto out_free_stack;
1180 if (!browser->has_symbols) {
1181 ui_browser__warning(&browser->b, delay_secs * 2,
1182 "Annotation is only available for symbolic views, "
1183 "include \"sym*\" in --sort to use it.");
1187 if (browser->selection == NULL ||
1188 browser->selection->sym == NULL ||
1189 browser->selection->map->dso->annotate_warned)
1193 hist_browser__dump(browser);
1198 browser->show_dso = !browser->show_dso;
1203 if (ui_browser__input_window("Symbol to show",
1204 "Please enter the name of symbol you want to see",
1205 buf, "ENTER: OK, ESC: Cancel",
1206 delay_secs * 2) == K_ENTER) {
1207 hists->symbol_filter_str = *buf ? buf : NULL;
1208 hists__filter_by_symbol(hists);
1209 hist_browser__reset(browser);
1215 ui_browser__help_window(&browser->b,
1216 "h/?/F1 Show this window\n"
1218 "PGDN/SPACE Navigate\n"
1219 "q/ESC/CTRL+C Exit browser\n\n"
1220 "For multiple event sessions:\n\n"
1221 "TAB/UNTAB Switch events\n\n"
1222 "For symbolic views (--sort has sym):\n\n"
1223 "-> Zoom into DSO/Threads & Annotate current symbol\n"
1225 "a Annotate current symbol\n"
1226 "C Collapse all callchains\n"
1227 "E Expand all callchains\n"
1228 "d Zoom into current DSO\n"
1229 "t Zoom into current Thread\n"
1230 "P Print histograms to perf.hist.N\n"
1231 "V Verbose (DSO names in callchains, etc)\n"
1232 "/ Filter symbol by name");
1241 if (pstack__empty(fstack)) {
1243 * Go back to the perf_evsel_menu__run or other user
1246 goto out_free_stack;
1249 top = pstack__pop(fstack);
1250 if (top == &browser->hists->dso_filter)
1252 if (top == &browser->hists->thread_filter)
1253 goto zoom_out_thread;
1258 !ui_browser__dialog_yesno(&browser->b,
1259 "Do you really want to exit?"))
1264 goto out_free_stack;
1269 if (!browser->has_symbols)
1270 goto add_exit_option;
1272 if (sort__branch_mode == 1) {
1273 bi = browser->he_selection->branch_info;
1274 if (browser->selection != NULL &&
1276 bi->from.sym != NULL &&
1277 !bi->from.map->dso->annotate_warned &&
1278 asprintf(&options[nr_options], "Annotate %s",
1279 bi->from.sym->name) > 0)
1280 annotate_f = nr_options++;
1282 if (browser->selection != NULL &&
1284 bi->to.sym != NULL &&
1285 !bi->to.map->dso->annotate_warned &&
1286 (bi->to.sym != bi->from.sym ||
1287 bi->to.map->dso != bi->from.map->dso) &&
1288 asprintf(&options[nr_options], "Annotate %s",
1289 bi->to.sym->name) > 0)
1290 annotate_t = nr_options++;
1293 if (browser->selection != NULL &&
1294 browser->selection->sym != NULL &&
1295 !browser->selection->map->dso->annotate_warned &&
1296 asprintf(&options[nr_options], "Annotate %s",
1297 browser->selection->sym->name) > 0)
1298 annotate = nr_options++;
1301 if (thread != NULL &&
1302 asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
1303 (browser->hists->thread_filter ? "out of" : "into"),
1304 (thread->comm_set ? thread->comm : ""),
1306 zoom_thread = nr_options++;
1309 asprintf(&options[nr_options], "Zoom %s %s DSO",
1310 (browser->hists->dso_filter ? "out of" : "into"),
1311 (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
1312 zoom_dso = nr_options++;
1314 if (browser->selection != NULL &&
1315 browser->selection->map != NULL &&
1316 asprintf(&options[nr_options], "Browse map details") > 0)
1317 browse_map = nr_options++;
1319 options[nr_options++] = (char *)"Exit";
1321 choice = ui__popup_menu(nr_options, options);
1323 if (choice == nr_options - 1)
1327 free_popup_options(options, nr_options - 1);
1331 if (choice == annotate || choice == annotate_t || choice == annotate_f) {
1332 struct hist_entry *he;
1335 he = hist_browser__selected_entry(browser);
1340 * we stash the branch_info symbol + map into the
1341 * the ms so we don't have to rewrite all the annotation
1342 * code to use branch_info.
1343 * in branch mode, the ms struct is not used
1345 if (choice == annotate_f) {
1346 he->ms.sym = he->branch_info->from.sym;
1347 he->ms.map = he->branch_info->from.map;
1348 } else if (choice == annotate_t) {
1349 he->ms.sym = he->branch_info->to.sym;
1350 he->ms.map = he->branch_info->to.map;
1354 * Don't let this be freed, say, by hists__decay_entry.
1357 err = hist_entry__tui_annotate(he, evsel->idx,
1358 timer, arg, delay_secs);
1361 * offer option to annotate the other branch source or target
1362 * (if they exists) when returning from annotate
1364 if ((err == 'q' || err == CTRL('c'))
1365 && annotate_t != -2 && annotate_f != -2)
1366 goto retry_popup_menu;
1368 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1370 ui_browser__handle_resize(&browser->b);
1372 } else if (choice == browse_map)
1373 map__browse(browser->selection->map);
1374 else if (choice == zoom_dso) {
1376 if (browser->hists->dso_filter) {
1377 pstack__remove(fstack, &browser->hists->dso_filter);
1380 browser->hists->dso_filter = NULL;
1381 sort_dso.elide = false;
1385 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1386 dso->kernel ? "the Kernel" : dso->short_name);
1387 browser->hists->dso_filter = dso;
1388 sort_dso.elide = true;
1389 pstack__push(fstack, &browser->hists->dso_filter);
1391 hists__filter_by_dso(hists);
1392 hist_browser__reset(browser);
1393 } else if (choice == zoom_thread) {
1395 if (browser->hists->thread_filter) {
1396 pstack__remove(fstack, &browser->hists->thread_filter);
1399 browser->hists->thread_filter = NULL;
1400 sort_thread.elide = false;
1402 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1403 thread->comm_set ? thread->comm : "",
1405 browser->hists->thread_filter = thread;
1406 sort_thread.elide = true;
1407 pstack__push(fstack, &browser->hists->thread_filter);
1409 hists__filter_by_thread(hists);
1410 hist_browser__reset(browser);
1414 pstack__delete(fstack);
1416 hist_browser__delete(browser);
1417 free_popup_options(options, nr_options - 1);
1421 struct perf_evsel_menu {
1422 struct ui_browser b;
1423 struct perf_evsel *selection;
1424 bool lost_events, lost_events_warned;
1427 static void perf_evsel_menu__write(struct ui_browser *browser,
1428 void *entry, int row)
1430 struct perf_evsel_menu *menu = container_of(browser,
1431 struct perf_evsel_menu, b);
1432 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1433 bool current_entry = ui_browser__is_current_entry(browser, row);
1434 unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1435 const char *ev_name = perf_evsel__name(evsel);
1437 const char *warn = " ";
1440 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
1441 HE_COLORSET_NORMAL);
1443 nr_events = convert_unit(nr_events, &unit);
1444 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
1445 unit, unit == ' ' ? "" : " ", ev_name);
1446 slsmg_printf("%s", bf);
1448 nr_events = evsel->hists.stats.nr_events[PERF_RECORD_LOST];
1449 if (nr_events != 0) {
1450 menu->lost_events = true;
1452 ui_browser__set_color(browser, HE_COLORSET_TOP);
1453 nr_events = convert_unit(nr_events, &unit);
1454 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
1455 nr_events, unit, unit == ' ' ? "" : " ");
1459 slsmg_write_nstring(warn, browser->width - printed);
1462 menu->selection = evsel;
1465 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
1466 int nr_events, const char *help,
1467 void(*timer)(void *arg), void *arg, int delay_secs)
1469 struct perf_evlist *evlist = menu->b.priv;
1470 struct perf_evsel *pos;
1471 const char *ev_name, *title = "Available samples";
1474 if (ui_browser__show(&menu->b, title,
1475 "ESC: exit, ENTER|->: Browse histograms") < 0)
1479 key = ui_browser__run(&menu->b, delay_secs);
1485 if (!menu->lost_events_warned && menu->lost_events) {
1486 ui_browser__warn_lost_events(&menu->b);
1487 menu->lost_events_warned = true;
1492 if (!menu->selection)
1494 pos = menu->selection;
1496 perf_evlist__set_selected(evlist, pos);
1498 * Give the calling tool a chance to populate the non
1499 * default evsel resorted hists tree.
1503 ev_name = perf_evsel__name(pos);
1504 key = perf_evsel__hists_browse(pos, nr_events, help,
1505 ev_name, true, timer,
1507 ui_browser__show_title(&menu->b, title);
1510 if (pos->node.next == &evlist->entries)
1511 pos = list_entry(evlist->entries.next, struct perf_evsel, node);
1513 pos = list_entry(pos->node.next, struct perf_evsel, node);
1516 if (pos->node.prev == &evlist->entries)
1517 pos = list_entry(evlist->entries.prev, struct perf_evsel, node);
1519 pos = list_entry(pos->node.prev, struct perf_evsel, node);
1522 if (!ui_browser__dialog_yesno(&menu->b,
1523 "Do you really want to exit?"))
1535 if (!ui_browser__dialog_yesno(&menu->b,
1536 "Do you really want to exit?"))
1548 ui_browser__hide(&menu->b);
1552 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
1554 void(*timer)(void *arg), void *arg,
1557 struct perf_evsel *pos;
1558 struct perf_evsel_menu menu = {
1560 .entries = &evlist->entries,
1561 .refresh = ui_browser__list_head_refresh,
1562 .seek = ui_browser__list_head_seek,
1563 .write = perf_evsel_menu__write,
1564 .nr_entries = evlist->nr_entries,
1569 ui_helpline__push("Press ESC to exit");
1571 list_for_each_entry(pos, &evlist->entries, node) {
1572 const char *ev_name = perf_evsel__name(pos);
1573 size_t line_len = strlen(ev_name) + 7;
1575 if (menu.b.width < line_len)
1576 menu.b.width = line_len;
1579 return perf_evsel_menu__run(&menu, evlist->nr_entries, help, timer,
1583 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
1584 void(*timer)(void *arg), void *arg,
1587 if (evlist->nr_entries == 1) {
1588 struct perf_evsel *first = list_entry(evlist->entries.next,
1589 struct perf_evsel, node);
1590 const char *ev_name = perf_evsel__name(first);
1591 return perf_evsel__hists_browse(first, evlist->nr_entries, help,
1592 ev_name, false, timer, arg,
1596 return __perf_evlist__tui_browse_hists(evlist, help,
1597 timer, arg, delay_secs);