4 #include <linux/rbtree.h>
6 #include "../../util/evsel.h"
7 #include "../../util/evlist.h"
8 #include "../../util/hist.h"
9 #include "../../util/pstack.h"
10 #include "../../util/sort.h"
11 #include "../../util/util.h"
12 #include "../../util/top.h"
13 #include "../../arch/common.h"
15 #include "../browsers/hists.h"
16 #include "../helpline.h"
22 extern void hist_browser__init_hpp(void);
24 static int perf_evsel_browser_title(struct hist_browser *browser,
25 char *bf, size_t size);
26 static void hist_browser__update_nr_entries(struct hist_browser *hb);
28 static struct rb_node *hists__filter_entries(struct rb_node *nd,
31 static bool hist_browser__has_filter(struct hist_browser *hb)
33 return hists__has_filter(hb->hists) || hb->min_pcnt || symbol_conf.has_filter;
36 static int hist_browser__get_folding(struct hist_browser *browser)
39 struct hists *hists = browser->hists;
40 int unfolded_rows = 0;
42 for (nd = rb_first(&hists->entries);
43 (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
44 nd = rb_hierarchy_next(nd)) {
45 struct hist_entry *he =
46 rb_entry(nd, struct hist_entry, rb_node);
48 if (he->leaf && he->unfolded)
49 unfolded_rows += he->nr_rows;
54 static u32 hist_browser__nr_entries(struct hist_browser *hb)
58 if (symbol_conf.report_hierarchy)
59 nr_entries = hb->nr_hierarchy_entries;
60 else if (hist_browser__has_filter(hb))
61 nr_entries = hb->nr_non_filtered_entries;
63 nr_entries = hb->hists->nr_entries;
65 hb->nr_callchain_rows = hist_browser__get_folding(hb);
66 return nr_entries + hb->nr_callchain_rows;
69 static void hist_browser__update_rows(struct hist_browser *hb)
71 struct ui_browser *browser = &hb->b;
72 struct hists *hists = hb->hists;
73 struct perf_hpp_list *hpp_list = hists->hpp_list;
74 u16 header_offset, index_row;
76 header_offset = hb->show_headers ? hpp_list->nr_header_lines : 0;
77 browser->rows = browser->height - header_offset;
79 * Verify if we were at the last line and that line isn't
80 * visibe because we now show the header line(s).
82 index_row = browser->index - browser->top_idx;
83 if (index_row >= browser->rows)
84 browser->index -= index_row - browser->rows + 1;
87 static void hist_browser__refresh_dimensions(struct ui_browser *browser)
89 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
91 /* 3 == +/- toggle symbol before actual hist_entry rendering */
92 browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]"));
94 * FIXME: Just keeping existing behaviour, but this really should be
95 * before updating browser->width, as it will invalidate the
96 * calculation above. Fix this and the fallout in another
99 ui_browser__refresh_dimensions(browser);
100 hist_browser__update_rows(hb);
103 static void hist_browser__gotorc(struct hist_browser *browser, int row, int column)
105 struct hists *hists = browser->hists;
106 struct perf_hpp_list *hpp_list = hists->hpp_list;
109 header_offset = browser->show_headers ? hpp_list->nr_header_lines : 0;
110 ui_browser__gotorc(&browser->b, row + header_offset, column);
113 static void hist_browser__reset(struct hist_browser *browser)
116 * The hists__remove_entry_filter() already folds non-filtered
117 * entries so we can assume it has 0 callchain rows.
119 browser->nr_callchain_rows = 0;
121 hist_browser__update_nr_entries(browser);
122 browser->b.nr_entries = hist_browser__nr_entries(browser);
123 hist_browser__refresh_dimensions(&browser->b);
124 ui_browser__reset_index(&browser->b);
127 static char tree__folded_sign(bool unfolded)
129 return unfolded ? '-' : '+';
132 static char hist_entry__folded(const struct hist_entry *he)
134 return he->has_children ? tree__folded_sign(he->unfolded) : ' ';
137 static char callchain_list__folded(const struct callchain_list *cl)
139 return cl->has_children ? tree__folded_sign(cl->unfolded) : ' ';
142 static void callchain_list__set_folding(struct callchain_list *cl, bool unfold)
144 cl->unfolded = unfold ? cl->has_children : false;
147 static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
152 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
153 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
154 struct callchain_list *chain;
155 char folded_sign = ' '; /* No children */
157 list_for_each_entry(chain, &child->val, list) {
159 /* We need this because we may not have children */
160 folded_sign = callchain_list__folded(chain);
161 if (folded_sign == '+')
165 if (folded_sign == '-') /* Have children and they're unfolded */
166 n += callchain_node__count_rows_rb_tree(child);
172 static int callchain_node__count_flat_rows(struct callchain_node *node)
174 struct callchain_list *chain;
175 char folded_sign = 0;
178 list_for_each_entry(chain, &node->parent_val, list) {
180 /* only check first chain list entry */
181 folded_sign = callchain_list__folded(chain);
182 if (folded_sign == '+')
188 list_for_each_entry(chain, &node->val, list) {
190 /* node->parent_val list might be empty */
191 folded_sign = callchain_list__folded(chain);
192 if (folded_sign == '+')
201 static int callchain_node__count_folded_rows(struct callchain_node *node __maybe_unused)
206 static int callchain_node__count_rows(struct callchain_node *node)
208 struct callchain_list *chain;
209 bool unfolded = false;
212 if (callchain_param.mode == CHAIN_FLAT)
213 return callchain_node__count_flat_rows(node);
214 else if (callchain_param.mode == CHAIN_FOLDED)
215 return callchain_node__count_folded_rows(node);
217 list_for_each_entry(chain, &node->val, list) {
219 unfolded = chain->unfolded;
223 n += callchain_node__count_rows_rb_tree(node);
228 static int callchain__count_rows(struct rb_root *chain)
233 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
234 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
235 n += callchain_node__count_rows(node);
241 static int hierarchy_count_rows(struct hist_browser *hb, struct hist_entry *he,
242 bool include_children)
245 struct rb_node *node;
246 struct hist_entry *child;
249 return callchain__count_rows(&he->sorted_chain);
251 if (he->has_no_entry)
254 node = rb_first(&he->hroot_out);
258 child = rb_entry(node, struct hist_entry, rb_node);
259 percent = hist_entry__get_percent_limit(child);
261 if (!child->filtered && percent >= hb->min_pcnt) {
264 if (include_children && child->unfolded)
265 count += hierarchy_count_rows(hb, child, true);
268 node = rb_next(node);
273 static bool hist_entry__toggle_fold(struct hist_entry *he)
278 if (!he->has_children)
281 he->unfolded = !he->unfolded;
285 static bool callchain_list__toggle_fold(struct callchain_list *cl)
290 if (!cl->has_children)
293 cl->unfolded = !cl->unfolded;
297 static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
299 struct rb_node *nd = rb_first(&node->rb_root);
301 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
302 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
303 struct callchain_list *chain;
306 list_for_each_entry(chain, &child->val, list) {
309 chain->has_children = chain->list.next != &child->val ||
310 !RB_EMPTY_ROOT(&child->rb_root);
312 chain->has_children = chain->list.next == &child->val &&
313 !RB_EMPTY_ROOT(&child->rb_root);
316 callchain_node__init_have_children_rb_tree(child);
320 static void callchain_node__init_have_children(struct callchain_node *node,
323 struct callchain_list *chain;
325 chain = list_entry(node->val.next, struct callchain_list, list);
326 chain->has_children = has_sibling;
328 if (!list_empty(&node->val)) {
329 chain = list_entry(node->val.prev, struct callchain_list, list);
330 chain->has_children = !RB_EMPTY_ROOT(&node->rb_root);
333 callchain_node__init_have_children_rb_tree(node);
336 static void callchain__init_have_children(struct rb_root *root)
338 struct rb_node *nd = rb_first(root);
339 bool has_sibling = nd && rb_next(nd);
341 for (nd = rb_first(root); nd; nd = rb_next(nd)) {
342 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
343 callchain_node__init_have_children(node, has_sibling);
344 if (callchain_param.mode == CHAIN_FLAT ||
345 callchain_param.mode == CHAIN_FOLDED)
346 callchain_node__make_parent_list(node);
350 static void hist_entry__init_have_children(struct hist_entry *he)
352 if (he->init_have_children)
356 he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
357 callchain__init_have_children(&he->sorted_chain);
359 he->has_children = !RB_EMPTY_ROOT(&he->hroot_out);
362 he->init_have_children = true;
365 static bool hist_browser__toggle_fold(struct hist_browser *browser)
367 struct hist_entry *he = browser->he_selection;
368 struct map_symbol *ms = browser->selection;
369 struct callchain_list *cl = container_of(ms, struct callchain_list, ms);
376 has_children = hist_entry__toggle_fold(he);
378 has_children = callchain_list__toggle_fold(cl);
383 hist_entry__init_have_children(he);
384 browser->b.nr_entries -= he->nr_rows;
387 browser->nr_callchain_rows -= he->nr_rows;
389 browser->nr_hierarchy_entries -= he->nr_rows;
391 if (symbol_conf.report_hierarchy)
392 child_rows = hierarchy_count_rows(browser, he, true);
396 he->nr_rows = callchain__count_rows(&he->sorted_chain);
398 he->nr_rows = hierarchy_count_rows(browser, he, false);
400 /* account grand children */
401 if (symbol_conf.report_hierarchy)
402 browser->b.nr_entries += child_rows - he->nr_rows;
404 if (!he->leaf && he->nr_rows == 0) {
405 he->has_no_entry = true;
409 if (symbol_conf.report_hierarchy)
410 browser->b.nr_entries -= child_rows - he->nr_rows;
412 if (he->has_no_entry)
413 he->has_no_entry = false;
418 browser->b.nr_entries += he->nr_rows;
421 browser->nr_callchain_rows += he->nr_rows;
423 browser->nr_hierarchy_entries += he->nr_rows;
428 /* If it doesn't have children, no toggling performed */
432 static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
437 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
438 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
439 struct callchain_list *chain;
440 bool has_children = false;
442 list_for_each_entry(chain, &child->val, list) {
444 callchain_list__set_folding(chain, unfold);
445 has_children = chain->has_children;
449 n += callchain_node__set_folding_rb_tree(child, unfold);
455 static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
457 struct callchain_list *chain;
458 bool has_children = false;
461 list_for_each_entry(chain, &node->val, list) {
463 callchain_list__set_folding(chain, unfold);
464 has_children = chain->has_children;
468 n += callchain_node__set_folding_rb_tree(node, unfold);
473 static int callchain__set_folding(struct rb_root *chain, bool unfold)
478 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
479 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
480 n += callchain_node__set_folding(node, unfold);
486 static int hierarchy_set_folding(struct hist_browser *hb, struct hist_entry *he,
487 bool unfold __maybe_unused)
491 struct hist_entry *child;
494 for (nd = rb_first(&he->hroot_out); nd; nd = rb_next(nd)) {
495 child = rb_entry(nd, struct hist_entry, rb_node);
496 percent = hist_entry__get_percent_limit(child);
497 if (!child->filtered && percent >= hb->min_pcnt)
504 static void hist_entry__set_folding(struct hist_entry *he,
505 struct hist_browser *hb, bool unfold)
507 hist_entry__init_have_children(he);
508 he->unfolded = unfold ? he->has_children : false;
510 if (he->has_children) {
514 n = callchain__set_folding(&he->sorted_chain, unfold);
516 n = hierarchy_set_folding(hb, he, unfold);
518 he->nr_rows = unfold ? n : 0;
524 __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
527 struct hist_entry *he;
530 nd = rb_first(&browser->hists->entries);
532 he = rb_entry(nd, struct hist_entry, rb_node);
534 /* set folding state even if it's currently folded */
535 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
537 hist_entry__set_folding(he, browser, unfold);
539 percent = hist_entry__get_percent_limit(he);
540 if (he->filtered || percent < browser->min_pcnt)
543 if (!he->depth || unfold)
544 browser->nr_hierarchy_entries++;
546 browser->nr_callchain_rows += he->nr_rows;
547 else if (unfold && !hist_entry__has_hierarchy_children(he, browser->min_pcnt)) {
548 browser->nr_hierarchy_entries++;
549 he->has_no_entry = true;
552 he->has_no_entry = false;
556 static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
558 browser->nr_hierarchy_entries = 0;
559 browser->nr_callchain_rows = 0;
560 __hist_browser__set_folding(browser, unfold);
562 browser->b.nr_entries = hist_browser__nr_entries(browser);
563 /* Go to the start, we may be way after valid entries after a collapse */
564 ui_browser__reset_index(&browser->b);
567 static void ui_browser__warn_lost_events(struct ui_browser *browser)
569 ui_browser__warning(browser, 4,
570 "Events are being lost, check IO/CPU overload!\n\n"
571 "You may want to run 'perf' using a RT scheduler policy:\n\n"
572 " perf top -r 80\n\n"
573 "Or reduce the sampling frequency.");
576 static int hist_browser__title(struct hist_browser *browser, char *bf, size_t size)
578 return browser->title ? browser->title(browser, bf, size) : 0;
581 int hist_browser__run(struct hist_browser *browser, const char *help)
585 struct hist_browser_timer *hbt = browser->hbt;
586 int delay_secs = hbt ? hbt->refresh : 0;
588 browser->b.entries = &browser->hists->entries;
589 browser->b.nr_entries = hist_browser__nr_entries(browser);
591 hist_browser__title(browser, title, sizeof(title));
593 if (ui_browser__show(&browser->b, title, "%s", help) < 0)
597 key = ui_browser__run(&browser->b, delay_secs);
602 hbt->timer(hbt->arg);
604 if (hist_browser__has_filter(browser))
605 hist_browser__update_nr_entries(browser);
607 nr_entries = hist_browser__nr_entries(browser);
608 ui_browser__update_nr_entries(&browser->b, nr_entries);
610 if (browser->hists->stats.nr_lost_warned !=
611 browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
612 browser->hists->stats.nr_lost_warned =
613 browser->hists->stats.nr_events[PERF_RECORD_LOST];
614 ui_browser__warn_lost_events(&browser->b);
617 hist_browser__title(browser, title, sizeof(title));
618 ui_browser__show_title(&browser->b, title);
621 case 'D': { /* Debug */
623 struct hist_entry *h = rb_entry(browser->b.top,
624 struct hist_entry, rb_node);
626 ui_helpline__fpush("%d: nr_ent=(%d,%d), rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
627 seq++, browser->b.nr_entries,
628 browser->hists->nr_entries,
632 h->row_offset, h->nr_rows);
636 /* Collapse the whole world. */
637 hist_browser__set_folding(browser, false);
640 /* Expand the whole world. */
641 hist_browser__set_folding(browser, true);
644 browser->show_headers = !browser->show_headers;
645 hist_browser__update_rows(browser);
648 if (hist_browser__toggle_fold(browser))
656 ui_browser__hide(&browser->b);
660 struct callchain_print_arg {
661 /* for hists browser */
663 bool is_current_entry;
670 typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
671 struct callchain_list *chain,
672 const char *str, int offset,
674 struct callchain_print_arg *arg);
676 static void hist_browser__show_callchain_entry(struct hist_browser *browser,
677 struct callchain_list *chain,
678 const char *str, int offset,
680 struct callchain_print_arg *arg)
683 char folded_sign = callchain_list__folded(chain);
684 bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
686 color = HE_COLORSET_NORMAL;
687 width = browser->b.width - (offset + 2);
688 if (ui_browser__is_current_entry(&browser->b, row)) {
689 browser->selection = &chain->ms;
690 color = HE_COLORSET_SELECTED;
691 arg->is_current_entry = true;
694 ui_browser__set_color(&browser->b, color);
695 hist_browser__gotorc(browser, row, 0);
696 ui_browser__write_nstring(&browser->b, " ", offset);
697 ui_browser__printf(&browser->b, "%c", folded_sign);
698 ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
699 ui_browser__write_nstring(&browser->b, str, width);
702 static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
703 struct callchain_list *chain,
704 const char *str, int offset,
705 unsigned short row __maybe_unused,
706 struct callchain_print_arg *arg)
708 char folded_sign = callchain_list__folded(chain);
710 arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
714 typedef bool (*check_output_full_fn)(struct hist_browser *browser,
717 static bool hist_browser__check_output_full(struct hist_browser *browser,
720 return browser->b.rows == row;
723 static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
724 unsigned short row __maybe_unused)
729 #define LEVEL_OFFSET_STEP 3
731 static int hist_browser__show_callchain_list(struct hist_browser *browser,
732 struct callchain_node *node,
733 struct callchain_list *chain,
734 unsigned short row, u64 total,
735 bool need_percent, int offset,
736 print_callchain_entry_fn print,
737 struct callchain_print_arg *arg)
739 char bf[1024], *alloc_str;
742 if (arg->row_offset != 0) {
748 str = callchain_list__sym_name(chain, bf, sizeof(bf),
754 callchain_node__scnprintf_value(node, buf, sizeof(buf),
757 if (asprintf(&alloc_str, "%s %s", buf, str) < 0)
758 str = "Not enough memory!";
763 print(browser, chain, str, offset, row, arg);
769 static bool check_percent_display(struct rb_node *node, u64 parent_total)
771 struct callchain_node *child;
779 child = rb_entry(node, struct callchain_node, rb_node);
780 return callchain_cumul_hits(child) != parent_total;
783 static int hist_browser__show_callchain_flat(struct hist_browser *browser,
784 struct rb_root *root,
785 unsigned short row, u64 total,
787 print_callchain_entry_fn print,
788 struct callchain_print_arg *arg,
789 check_output_full_fn is_output_full)
791 struct rb_node *node;
792 int first_row = row, offset = LEVEL_OFFSET_STEP;
795 node = rb_first(root);
796 need_percent = check_percent_display(node, parent_total);
799 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
800 struct rb_node *next = rb_next(node);
801 struct callchain_list *chain;
802 char folded_sign = ' ';
804 int extra_offset = 0;
806 list_for_each_entry(chain, &child->parent_val, list) {
807 bool was_first = first;
811 else if (need_percent)
812 extra_offset = LEVEL_OFFSET_STEP;
814 folded_sign = callchain_list__folded(chain);
816 row += hist_browser__show_callchain_list(browser, child,
818 was_first && need_percent,
819 offset + extra_offset,
822 if (is_output_full(browser, row))
825 if (folded_sign == '+')
829 list_for_each_entry(chain, &child->val, list) {
830 bool was_first = first;
834 else if (need_percent)
835 extra_offset = LEVEL_OFFSET_STEP;
837 folded_sign = callchain_list__folded(chain);
839 row += hist_browser__show_callchain_list(browser, child,
841 was_first && need_percent,
842 offset + extra_offset,
845 if (is_output_full(browser, row))
848 if (folded_sign == '+')
853 if (is_output_full(browser, row))
858 return row - first_row;
861 static char *hist_browser__folded_callchain_str(struct hist_browser *browser,
862 struct callchain_list *chain,
863 char *value_str, char *old_str)
869 str = callchain_list__sym_name(chain, bf, sizeof(bf),
872 if (asprintf(&new, "%s%s%s", old_str,
873 symbol_conf.field_sep ?: ";", str) < 0)
877 if (asprintf(&new, "%s %s", value_str, str) < 0)
880 if (asprintf(&new, "%s", str) < 0)
887 static int hist_browser__show_callchain_folded(struct hist_browser *browser,
888 struct rb_root *root,
889 unsigned short row, u64 total,
891 print_callchain_entry_fn print,
892 struct callchain_print_arg *arg,
893 check_output_full_fn is_output_full)
895 struct rb_node *node;
896 int first_row = row, offset = LEVEL_OFFSET_STEP;
899 node = rb_first(root);
900 need_percent = check_percent_display(node, parent_total);
903 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
904 struct rb_node *next = rb_next(node);
905 struct callchain_list *chain, *first_chain = NULL;
907 char *value_str = NULL, *value_str_alloc = NULL;
908 char *chain_str = NULL, *chain_str_alloc = NULL;
910 if (arg->row_offset != 0) {
918 callchain_node__scnprintf_value(child, buf, sizeof(buf), total);
919 if (asprintf(&value_str, "%s", buf) < 0) {
920 value_str = (char *)"<...>";
923 value_str_alloc = value_str;
926 list_for_each_entry(chain, &child->parent_val, list) {
927 chain_str = hist_browser__folded_callchain_str(browser,
928 chain, value_str, chain_str);
934 if (chain_str == NULL) {
935 chain_str = (char *)"Not enough memory!";
939 chain_str_alloc = chain_str;
942 list_for_each_entry(chain, &child->val, list) {
943 chain_str = hist_browser__folded_callchain_str(browser,
944 chain, value_str, chain_str);
950 if (chain_str == NULL) {
951 chain_str = (char *)"Not enough memory!";
955 chain_str_alloc = chain_str;
959 print(browser, first_chain, chain_str, offset, row++, arg);
960 free(value_str_alloc);
961 free(chain_str_alloc);
964 if (is_output_full(browser, row))
969 return row - first_row;
972 static int hist_browser__show_callchain_graph(struct hist_browser *browser,
973 struct rb_root *root, int level,
974 unsigned short row, u64 total,
976 print_callchain_entry_fn print,
977 struct callchain_print_arg *arg,
978 check_output_full_fn is_output_full)
980 struct rb_node *node;
981 int first_row = row, offset = level * LEVEL_OFFSET_STEP;
983 u64 percent_total = total;
985 if (callchain_param.mode == CHAIN_GRAPH_REL)
986 percent_total = parent_total;
988 node = rb_first(root);
989 need_percent = check_percent_display(node, parent_total);
992 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
993 struct rb_node *next = rb_next(node);
994 struct callchain_list *chain;
995 char folded_sign = ' ';
997 int extra_offset = 0;
999 list_for_each_entry(chain, &child->val, list) {
1000 bool was_first = first;
1004 else if (need_percent)
1005 extra_offset = LEVEL_OFFSET_STEP;
1007 folded_sign = callchain_list__folded(chain);
1009 row += hist_browser__show_callchain_list(browser, child,
1010 chain, row, percent_total,
1011 was_first && need_percent,
1012 offset + extra_offset,
1015 if (is_output_full(browser, row))
1018 if (folded_sign == '+')
1022 if (folded_sign == '-') {
1023 const int new_level = level + (extra_offset ? 2 : 1);
1025 row += hist_browser__show_callchain_graph(browser, &child->rb_root,
1026 new_level, row, total,
1027 child->children_hit,
1028 print, arg, is_output_full);
1030 if (is_output_full(browser, row))
1035 return row - first_row;
1038 static int hist_browser__show_callchain(struct hist_browser *browser,
1039 struct hist_entry *entry, int level,
1041 print_callchain_entry_fn print,
1042 struct callchain_print_arg *arg,
1043 check_output_full_fn is_output_full)
1045 u64 total = hists__total_period(entry->hists);
1049 if (symbol_conf.cumulate_callchain)
1050 parent_total = entry->stat_acc->period;
1052 parent_total = entry->stat.period;
1054 if (callchain_param.mode == CHAIN_FLAT) {
1055 printed = hist_browser__show_callchain_flat(browser,
1056 &entry->sorted_chain, row,
1057 total, parent_total, print, arg,
1059 } else if (callchain_param.mode == CHAIN_FOLDED) {
1060 printed = hist_browser__show_callchain_folded(browser,
1061 &entry->sorted_chain, row,
1062 total, parent_total, print, arg,
1065 printed = hist_browser__show_callchain_graph(browser,
1066 &entry->sorted_chain, level, row,
1067 total, parent_total, print, arg,
1071 if (arg->is_current_entry)
1072 browser->he_selection = entry;
1078 struct ui_browser *b;
1083 static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
1085 struct hpp_arg *arg = hpp->ptr;
1090 va_start(args, fmt);
1091 len = va_arg(args, int);
1092 percent = va_arg(args, double);
1095 ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
1097 ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
1098 ui_browser__printf(arg->b, "%s", hpp->buf);
1100 advance_hpp(hpp, ret);
1104 #define __HPP_COLOR_PERCENT_FN(_type, _field) \
1105 static u64 __hpp_get_##_field(struct hist_entry *he) \
1107 return he->stat._field; \
1111 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
1112 struct perf_hpp *hpp, \
1113 struct hist_entry *he) \
1115 return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%", \
1116 __hpp__slsmg_color_printf, true); \
1119 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
1120 static u64 __hpp_get_acc_##_field(struct hist_entry *he) \
1122 return he->stat_acc->_field; \
1126 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
1127 struct perf_hpp *hpp, \
1128 struct hist_entry *he) \
1130 if (!symbol_conf.cumulate_callchain) { \
1131 struct hpp_arg *arg = hpp->ptr; \
1132 int len = fmt->user_len ?: fmt->len; \
1133 int ret = scnprintf(hpp->buf, hpp->size, \
1134 "%*s", len, "N/A"); \
1135 ui_browser__printf(arg->b, "%s", hpp->buf); \
1139 return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field, \
1140 " %*.2f%%", __hpp__slsmg_color_printf, true); \
1143 __HPP_COLOR_PERCENT_FN(overhead, period)
1144 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
1145 __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
1146 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
1147 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
1148 __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
1150 #undef __HPP_COLOR_PERCENT_FN
1151 #undef __HPP_COLOR_ACC_PERCENT_FN
1153 void hist_browser__init_hpp(void)
1155 perf_hpp__format[PERF_HPP__OVERHEAD].color =
1156 hist_browser__hpp_color_overhead;
1157 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
1158 hist_browser__hpp_color_overhead_sys;
1159 perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
1160 hist_browser__hpp_color_overhead_us;
1161 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
1162 hist_browser__hpp_color_overhead_guest_sys;
1163 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
1164 hist_browser__hpp_color_overhead_guest_us;
1165 perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
1166 hist_browser__hpp_color_overhead_acc;
1169 static int hist_browser__show_entry(struct hist_browser *browser,
1170 struct hist_entry *entry,
1174 int width = browser->b.width;
1175 char folded_sign = ' ';
1176 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1177 off_t row_offset = entry->row_offset;
1179 struct perf_hpp_fmt *fmt;
1181 if (current_entry) {
1182 browser->he_selection = entry;
1183 browser->selection = &entry->ms;
1186 if (symbol_conf.use_callchain) {
1187 hist_entry__init_have_children(entry);
1188 folded_sign = hist_entry__folded(entry);
1191 if (row_offset == 0) {
1192 struct hpp_arg arg = {
1194 .folded_sign = folded_sign,
1195 .current_entry = current_entry,
1199 hist_browser__gotorc(browser, row, 0);
1201 hists__for_each_format(browser->hists, fmt) {
1203 struct perf_hpp hpp = {
1209 if (perf_hpp__should_skip(fmt, entry->hists) ||
1210 column++ < browser->b.horiz_scroll)
1213 if (current_entry && browser->b.navkeypressed) {
1214 ui_browser__set_color(&browser->b,
1215 HE_COLORSET_SELECTED);
1217 ui_browser__set_color(&browser->b,
1218 HE_COLORSET_NORMAL);
1222 if (symbol_conf.use_callchain) {
1223 ui_browser__printf(&browser->b, "%c ", folded_sign);
1228 ui_browser__printf(&browser->b, " ");
1233 int ret = fmt->color(fmt, &hpp, entry);
1234 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1236 * fmt->color() already used ui_browser to
1237 * print the non alignment bits, skip it (+ret):
1239 ui_browser__printf(&browser->b, "%s", s + ret);
1241 hist_entry__snprintf_alignment(entry, &hpp, fmt, fmt->entry(fmt, &hpp, entry));
1242 ui_browser__printf(&browser->b, "%s", s);
1244 width -= hpp.buf - s;
1247 /* The scroll bar isn't being used */
1248 if (!browser->b.navkeypressed)
1251 ui_browser__write_nstring(&browser->b, "", width);
1258 if (folded_sign == '-' && row != browser->b.rows) {
1259 struct callchain_print_arg arg = {
1260 .row_offset = row_offset,
1261 .is_current_entry = current_entry,
1264 printed += hist_browser__show_callchain(browser, entry, 1, row,
1265 hist_browser__show_callchain_entry, &arg,
1266 hist_browser__check_output_full);
1272 static int hist_browser__show_hierarchy_entry(struct hist_browser *browser,
1273 struct hist_entry *entry,
1278 int width = browser->b.width;
1279 char folded_sign = ' ';
1280 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1281 off_t row_offset = entry->row_offset;
1283 struct perf_hpp_fmt *fmt;
1284 struct perf_hpp_list_node *fmt_node;
1285 struct hpp_arg arg = {
1287 .current_entry = current_entry,
1290 int hierarchy_indent = (entry->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
1292 if (current_entry) {
1293 browser->he_selection = entry;
1294 browser->selection = &entry->ms;
1297 hist_entry__init_have_children(entry);
1298 folded_sign = hist_entry__folded(entry);
1299 arg.folded_sign = folded_sign;
1301 if (entry->leaf && row_offset) {
1303 goto show_callchain;
1306 hist_browser__gotorc(browser, row, 0);
1308 if (current_entry && browser->b.navkeypressed)
1309 ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1311 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1313 ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1314 width -= level * HIERARCHY_INDENT;
1316 /* the first hpp_list_node is for overhead columns */
1317 fmt_node = list_first_entry(&entry->hists->hpp_formats,
1318 struct perf_hpp_list_node, list);
1319 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1321 struct perf_hpp hpp = {
1327 if (perf_hpp__should_skip(fmt, entry->hists) ||
1328 column++ < browser->b.horiz_scroll)
1331 if (current_entry && browser->b.navkeypressed) {
1332 ui_browser__set_color(&browser->b,
1333 HE_COLORSET_SELECTED);
1335 ui_browser__set_color(&browser->b,
1336 HE_COLORSET_NORMAL);
1340 ui_browser__printf(&browser->b, "%c", folded_sign);
1344 ui_browser__printf(&browser->b, " ");
1349 int ret = fmt->color(fmt, &hpp, entry);
1350 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1352 * fmt->color() already used ui_browser to
1353 * print the non alignment bits, skip it (+ret):
1355 ui_browser__printf(&browser->b, "%s", s + ret);
1357 int ret = fmt->entry(fmt, &hpp, entry);
1358 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1359 ui_browser__printf(&browser->b, "%s", s);
1361 width -= hpp.buf - s;
1364 ui_browser__write_nstring(&browser->b, "", hierarchy_indent);
1365 width -= hierarchy_indent;
1367 if (column >= browser->b.horiz_scroll) {
1369 struct perf_hpp hpp = {
1375 if (current_entry && browser->b.navkeypressed) {
1376 ui_browser__set_color(&browser->b,
1377 HE_COLORSET_SELECTED);
1379 ui_browser__set_color(&browser->b,
1380 HE_COLORSET_NORMAL);
1383 perf_hpp_list__for_each_format(entry->hpp_list, fmt) {
1384 ui_browser__write_nstring(&browser->b, "", 2);
1388 * No need to call hist_entry__snprintf_alignment()
1389 * since this fmt is always the last column in the
1393 width -= fmt->color(fmt, &hpp, entry);
1397 width -= fmt->entry(fmt, &hpp, entry);
1398 ui_browser__printf(&browser->b, "%s", ltrim(s));
1400 while (isspace(s[i++]))
1406 /* The scroll bar isn't being used */
1407 if (!browser->b.navkeypressed)
1410 ui_browser__write_nstring(&browser->b, "", width);
1416 if (entry->leaf && folded_sign == '-' && row != browser->b.rows) {
1417 struct callchain_print_arg carg = {
1418 .row_offset = row_offset,
1421 printed += hist_browser__show_callchain(browser, entry,
1423 hist_browser__show_callchain_entry, &carg,
1424 hist_browser__check_output_full);
1430 static int hist_browser__show_no_entry(struct hist_browser *browser,
1431 unsigned short row, int level)
1433 int width = browser->b.width;
1434 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1438 struct perf_hpp_fmt *fmt;
1439 struct perf_hpp_list_node *fmt_node;
1440 int indent = browser->hists->nr_hpp_node - 2;
1442 if (current_entry) {
1443 browser->he_selection = NULL;
1444 browser->selection = NULL;
1447 hist_browser__gotorc(browser, row, 0);
1449 if (current_entry && browser->b.navkeypressed)
1450 ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1452 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1454 ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1455 width -= level * HIERARCHY_INDENT;
1457 /* the first hpp_list_node is for overhead columns */
1458 fmt_node = list_first_entry(&browser->hists->hpp_formats,
1459 struct perf_hpp_list_node, list);
1460 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1461 if (perf_hpp__should_skip(fmt, browser->hists) ||
1462 column++ < browser->b.horiz_scroll)
1465 ret = fmt->width(fmt, NULL, browser->hists);
1468 /* for folded sign */
1472 /* space between columns */
1476 ui_browser__write_nstring(&browser->b, "", ret);
1480 ui_browser__write_nstring(&browser->b, "", indent * HIERARCHY_INDENT);
1481 width -= indent * HIERARCHY_INDENT;
1483 if (column >= browser->b.horiz_scroll) {
1486 ret = snprintf(buf, sizeof(buf), "no entry >= %.2f%%", browser->min_pcnt);
1487 ui_browser__printf(&browser->b, " %s", buf);
1491 /* The scroll bar isn't being used */
1492 if (!browser->b.navkeypressed)
1495 ui_browser__write_nstring(&browser->b, "", width);
1499 static int advance_hpp_check(struct perf_hpp *hpp, int inc)
1501 advance_hpp(hpp, inc);
1502 return hpp->size <= 0;
1505 static int hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf, size_t size)
1507 struct hists *hists = browser->hists;
1508 struct perf_hpp dummy_hpp = {
1512 struct perf_hpp_fmt *fmt;
1516 if (symbol_conf.use_callchain) {
1517 ret = scnprintf(buf, size, " ");
1518 if (advance_hpp_check(&dummy_hpp, ret))
1522 hists__for_each_format(browser->hists, fmt) {
1523 if (perf_hpp__should_skip(fmt, hists) || column++ < browser->b.horiz_scroll)
1526 ret = fmt->header(fmt, &dummy_hpp, hists, 0);
1527 if (advance_hpp_check(&dummy_hpp, ret))
1530 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
1531 if (advance_hpp_check(&dummy_hpp, ret))
1538 static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser, char *buf, size_t size)
1540 struct hists *hists = browser->hists;
1541 struct perf_hpp dummy_hpp = {
1545 struct perf_hpp_fmt *fmt;
1546 struct perf_hpp_list_node *fmt_node;
1549 int indent = hists->nr_hpp_node - 2;
1550 bool first_node, first_col;
1552 ret = scnprintf(buf, size, " ");
1553 if (advance_hpp_check(&dummy_hpp, ret))
1556 /* the first hpp_list_node is for overhead columns */
1557 fmt_node = list_first_entry(&hists->hpp_formats,
1558 struct perf_hpp_list_node, list);
1559 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1560 if (column++ < browser->b.horiz_scroll)
1563 ret = fmt->header(fmt, &dummy_hpp, hists, 0);
1564 if (advance_hpp_check(&dummy_hpp, ret))
1567 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
1568 if (advance_hpp_check(&dummy_hpp, ret))
1572 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "%*s",
1573 indent * HIERARCHY_INDENT, "");
1574 if (advance_hpp_check(&dummy_hpp, ret))
1578 list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
1580 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " / ");
1581 if (advance_hpp_check(&dummy_hpp, ret))
1587 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1590 if (perf_hpp__should_skip(fmt, hists))
1594 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "+");
1595 if (advance_hpp_check(&dummy_hpp, ret))
1600 ret = fmt->header(fmt, &dummy_hpp, hists, 0);
1601 dummy_hpp.buf[ret] = '\0';
1603 start = trim(dummy_hpp.buf);
1604 ret = strlen(start);
1606 if (start != dummy_hpp.buf)
1607 memmove(dummy_hpp.buf, start, ret + 1);
1609 if (advance_hpp_check(&dummy_hpp, ret))
1617 static void hists_browser__hierarchy_headers(struct hist_browser *browser)
1621 hists_browser__scnprintf_hierarchy_headers(browser, headers,
1624 ui_browser__gotorc(&browser->b, 0, 0);
1625 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1626 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1629 static void hists_browser__headers(struct hist_browser *browser)
1633 hists_browser__scnprintf_headers(browser, headers,
1636 ui_browser__gotorc(&browser->b, 0, 0);
1637 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1638 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1641 static void hist_browser__show_headers(struct hist_browser *browser)
1643 if (symbol_conf.report_hierarchy)
1644 hists_browser__hierarchy_headers(browser);
1646 hists_browser__headers(browser);
1649 static void ui_browser__hists_init_top(struct ui_browser *browser)
1651 if (browser->top == NULL) {
1652 struct hist_browser *hb;
1654 hb = container_of(browser, struct hist_browser, b);
1655 browser->top = rb_first(&hb->hists->entries);
1659 static unsigned int hist_browser__refresh(struct ui_browser *browser)
1662 u16 header_offset = 0;
1664 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
1665 struct hists *hists = hb->hists;
1667 if (hb->show_headers) {
1668 struct perf_hpp_list *hpp_list = hists->hpp_list;
1670 hist_browser__show_headers(hb);
1671 header_offset = hpp_list->nr_header_lines;
1674 ui_browser__hists_init_top(browser);
1675 hb->he_selection = NULL;
1676 hb->selection = NULL;
1678 for (nd = browser->top; nd; nd = rb_hierarchy_next(nd)) {
1679 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1683 /* let it move to sibling */
1684 h->unfolded = false;
1688 percent = hist_entry__get_percent_limit(h);
1689 if (percent < hb->min_pcnt)
1692 if (symbol_conf.report_hierarchy) {
1693 row += hist_browser__show_hierarchy_entry(hb, h, row,
1695 if (row == browser->rows)
1698 if (h->has_no_entry) {
1699 hist_browser__show_no_entry(hb, row, h->depth + 1);
1703 row += hist_browser__show_entry(hb, h, row);
1706 if (row == browser->rows)
1710 return row + header_offset;
1713 static struct rb_node *hists__filter_entries(struct rb_node *nd,
1716 while (nd != NULL) {
1717 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1718 float percent = hist_entry__get_percent_limit(h);
1720 if (!h->filtered && percent >= min_pcnt)
1724 * If it's filtered, its all children also were filtered.
1725 * So move to sibling node.
1730 nd = rb_hierarchy_next(nd);
1736 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
1739 while (nd != NULL) {
1740 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1741 float percent = hist_entry__get_percent_limit(h);
1743 if (!h->filtered && percent >= min_pcnt)
1746 nd = rb_hierarchy_prev(nd);
1752 static void ui_browser__hists_seek(struct ui_browser *browser,
1753 off_t offset, int whence)
1755 struct hist_entry *h;
1758 struct hist_browser *hb;
1760 hb = container_of(browser, struct hist_browser, b);
1762 if (browser->nr_entries == 0)
1765 ui_browser__hists_init_top(browser);
1769 nd = hists__filter_entries(rb_first(browser->entries),
1776 nd = rb_hierarchy_last(rb_last(browser->entries));
1777 nd = hists__filter_prev_entries(nd, hb->min_pcnt);
1785 * Moves not relative to the first visible entry invalidates its
1788 h = rb_entry(browser->top, struct hist_entry, rb_node);
1792 * Here we have to check if nd is expanded (+), if it is we can't go
1793 * the next top level hist_entry, instead we must compute an offset of
1794 * what _not_ to show and not change the first visible entry.
1796 * This offset increments when we are going from top to bottom and
1797 * decreases when we're going from bottom to top.
1799 * As we don't have backpointers to the top level in the callchains
1800 * structure, we need to always print the whole hist_entry callchain,
1801 * skipping the first ones that are before the first visible entry
1802 * and stop when we printed enough lines to fill the screen.
1810 h = rb_entry(nd, struct hist_entry, rb_node);
1811 if (h->unfolded && h->leaf) {
1812 u16 remaining = h->nr_rows - h->row_offset;
1813 if (offset > remaining) {
1814 offset -= remaining;
1817 h->row_offset += offset;
1823 nd = hists__filter_entries(rb_hierarchy_next(nd),
1829 } while (offset != 0);
1830 } else if (offset < 0) {
1832 h = rb_entry(nd, struct hist_entry, rb_node);
1833 if (h->unfolded && h->leaf) {
1835 if (-offset > h->row_offset) {
1836 offset += h->row_offset;
1839 h->row_offset += offset;
1845 if (-offset > h->nr_rows) {
1846 offset += h->nr_rows;
1849 h->row_offset = h->nr_rows + offset;
1857 nd = hists__filter_prev_entries(rb_hierarchy_prev(nd),
1865 * Last unfiltered hist_entry, check if it is
1866 * unfolded, if it is then we should have
1867 * row_offset at its last entry.
1869 h = rb_entry(nd, struct hist_entry, rb_node);
1870 if (h->unfolded && h->leaf)
1871 h->row_offset = h->nr_rows;
1878 h = rb_entry(nd, struct hist_entry, rb_node);
1883 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1884 struct hist_entry *he, FILE *fp,
1887 struct callchain_print_arg arg = {
1891 hist_browser__show_callchain(browser, he, level, 0,
1892 hist_browser__fprintf_callchain_entry, &arg,
1893 hist_browser__check_dump_full);
1897 static int hist_browser__fprintf_entry(struct hist_browser *browser,
1898 struct hist_entry *he, FILE *fp)
1902 char folded_sign = ' ';
1903 struct perf_hpp hpp = {
1907 struct perf_hpp_fmt *fmt;
1911 if (symbol_conf.use_callchain) {
1912 folded_sign = hist_entry__folded(he);
1913 printed += fprintf(fp, "%c ", folded_sign);
1916 hists__for_each_format(browser->hists, fmt) {
1917 if (perf_hpp__should_skip(fmt, he->hists))
1921 ret = scnprintf(hpp.buf, hpp.size, " ");
1922 advance_hpp(&hpp, ret);
1926 ret = fmt->entry(fmt, &hpp, he);
1927 ret = hist_entry__snprintf_alignment(he, &hpp, fmt, ret);
1928 advance_hpp(&hpp, ret);
1930 printed += fprintf(fp, "%s\n", s);
1932 if (folded_sign == '-')
1933 printed += hist_browser__fprintf_callchain(browser, he, fp, 1);
1939 static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,
1940 struct hist_entry *he,
1941 FILE *fp, int level)
1945 char folded_sign = ' ';
1946 struct perf_hpp hpp = {
1950 struct perf_hpp_fmt *fmt;
1951 struct perf_hpp_list_node *fmt_node;
1954 int hierarchy_indent = (he->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
1956 printed = fprintf(fp, "%*s", level * HIERARCHY_INDENT, "");
1958 folded_sign = hist_entry__folded(he);
1959 printed += fprintf(fp, "%c", folded_sign);
1961 /* the first hpp_list_node is for overhead columns */
1962 fmt_node = list_first_entry(&he->hists->hpp_formats,
1963 struct perf_hpp_list_node, list);
1964 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1966 ret = scnprintf(hpp.buf, hpp.size, " ");
1967 advance_hpp(&hpp, ret);
1971 ret = fmt->entry(fmt, &hpp, he);
1972 advance_hpp(&hpp, ret);
1975 ret = scnprintf(hpp.buf, hpp.size, "%*s", hierarchy_indent, "");
1976 advance_hpp(&hpp, ret);
1978 perf_hpp_list__for_each_format(he->hpp_list, fmt) {
1979 ret = scnprintf(hpp.buf, hpp.size, " ");
1980 advance_hpp(&hpp, ret);
1982 ret = fmt->entry(fmt, &hpp, he);
1983 advance_hpp(&hpp, ret);
1986 printed += fprintf(fp, "%s\n", rtrim(s));
1988 if (he->leaf && folded_sign == '-') {
1989 printed += hist_browser__fprintf_callchain(browser, he, fp,
1996 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1998 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
2003 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
2005 if (symbol_conf.report_hierarchy) {
2006 printed += hist_browser__fprintf_hierarchy_entry(browser,
2010 printed += hist_browser__fprintf_entry(browser, h, fp);
2013 nd = hists__filter_entries(rb_hierarchy_next(nd),
2020 static int hist_browser__dump(struct hist_browser *browser)
2026 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
2027 if (access(filename, F_OK))
2030 * XXX: Just an arbitrary lazy upper limit
2032 if (++browser->print_seq == 8192) {
2033 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
2038 fp = fopen(filename, "w");
2041 const char *err = str_error_r(errno, bf, sizeof(bf));
2042 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
2046 ++browser->print_seq;
2047 hist_browser__fprintf(browser, fp);
2049 ui_helpline__fpush("%s written!", filename);
2054 void hist_browser__init(struct hist_browser *browser,
2055 struct hists *hists)
2057 struct perf_hpp_fmt *fmt;
2059 browser->hists = hists;
2060 browser->b.refresh = hist_browser__refresh;
2061 browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
2062 browser->b.seek = ui_browser__hists_seek;
2063 browser->b.use_navkeypressed = true;
2064 browser->show_headers = symbol_conf.show_hist_headers;
2066 hists__for_each_format(hists, fmt) {
2067 perf_hpp__reset_width(fmt, hists);
2068 ++browser->b.columns;
2072 struct hist_browser *hist_browser__new(struct hists *hists)
2074 struct hist_browser *browser = zalloc(sizeof(*browser));
2077 hist_browser__init(browser, hists);
2082 static struct hist_browser *
2083 perf_evsel_browser__new(struct perf_evsel *evsel,
2084 struct hist_browser_timer *hbt,
2085 struct perf_env *env)
2087 struct hist_browser *browser = hist_browser__new(evsel__hists(evsel));
2092 browser->title = perf_evsel_browser_title;
2097 void hist_browser__delete(struct hist_browser *browser)
2102 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
2104 return browser->he_selection;
2107 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
2109 return browser->he_selection->thread;
2112 /* Check whether the browser is for 'top' or 'report' */
2113 static inline bool is_report_browser(void *timer)
2115 return timer == NULL;
2118 static int perf_evsel_browser_title(struct hist_browser *browser,
2119 char *bf, size_t size)
2121 struct hist_browser_timer *hbt = browser->hbt;
2122 struct hists *hists = browser->hists;
2125 const struct dso *dso = hists->dso_filter;
2126 const struct thread *thread = hists->thread_filter;
2127 int socket_id = hists->socket_filter;
2128 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
2129 u64 nr_events = hists->stats.total_period;
2130 struct perf_evsel *evsel = hists_to_evsel(hists);
2131 const char *ev_name = perf_evsel__name(evsel);
2133 size_t buflen = sizeof(buf);
2134 char ref[30] = " show reference callgraph, ";
2135 bool enable_ref = false;
2137 if (symbol_conf.filter_relative) {
2138 nr_samples = hists->stats.nr_non_filtered_samples;
2139 nr_events = hists->stats.total_non_filtered_period;
2142 if (perf_evsel__is_group_event(evsel)) {
2143 struct perf_evsel *pos;
2145 perf_evsel__group_desc(evsel, buf, buflen);
2148 for_each_group_member(pos, evsel) {
2149 struct hists *pos_hists = evsel__hists(pos);
2151 if (symbol_conf.filter_relative) {
2152 nr_samples += pos_hists->stats.nr_non_filtered_samples;
2153 nr_events += pos_hists->stats.total_non_filtered_period;
2155 nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
2156 nr_events += pos_hists->stats.total_period;
2161 if (symbol_conf.show_ref_callgraph &&
2162 strstr(ev_name, "call-graph=no"))
2164 nr_samples = convert_unit(nr_samples, &unit);
2165 printed = scnprintf(bf, size,
2166 "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64,
2167 nr_samples, unit, ev_name, enable_ref ? ref : " ", nr_events);
2170 if (hists->uid_filter_str)
2171 printed += snprintf(bf + printed, size - printed,
2172 ", UID: %s", hists->uid_filter_str);
2174 if (hists__has(hists, thread)) {
2175 printed += scnprintf(bf + printed, size - printed,
2177 (thread->comm_set ? thread__comm_str(thread) : ""),
2180 printed += scnprintf(bf + printed, size - printed,
2182 (thread->comm_set ? thread__comm_str(thread) : ""));
2186 printed += scnprintf(bf + printed, size - printed,
2187 ", DSO: %s", dso->short_name);
2189 printed += scnprintf(bf + printed, size - printed,
2190 ", Processor Socket: %d", socket_id);
2191 if (!is_report_browser(hbt)) {
2192 struct perf_top *top = hbt->arg;
2195 printed += scnprintf(bf + printed, size - printed, " [z]");
2201 static inline void free_popup_options(char **options, int n)
2205 for (i = 0; i < n; ++i)
2210 * Only runtime switching of perf data file will make "input_name" point
2211 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
2212 * whether we need to call free() for current "input_name" during the switch.
2214 static bool is_input_name_malloced = false;
2216 static int switch_data_file(void)
2218 char *pwd, *options[32], *abs_path[32], *tmp;
2220 int nr_options = 0, choice = -1, ret = -1;
2221 struct dirent *dent;
2223 pwd = getenv("PWD");
2227 pwd_dir = opendir(pwd);
2231 memset(options, 0, sizeof(options));
2232 memset(options, 0, sizeof(abs_path));
2234 while ((dent = readdir(pwd_dir))) {
2235 char path[PATH_MAX];
2237 char *name = dent->d_name;
2240 if (!(dent->d_type == DT_REG))
2243 snprintf(path, sizeof(path), "%s/%s", pwd, name);
2245 file = fopen(path, "r");
2249 if (fread(&magic, 1, 8, file) < 8)
2250 goto close_file_and_continue;
2252 if (is_perf_magic(magic)) {
2253 options[nr_options] = strdup(name);
2254 if (!options[nr_options])
2255 goto close_file_and_continue;
2257 abs_path[nr_options] = strdup(path);
2258 if (!abs_path[nr_options]) {
2259 zfree(&options[nr_options]);
2260 ui__warning("Can't search all data files due to memory shortage.\n");
2268 close_file_and_continue:
2270 if (nr_options >= 32) {
2271 ui__warning("Too many perf data files in PWD!\n"
2272 "Only the first 32 files will be listed.\n");
2279 choice = ui__popup_menu(nr_options, options);
2280 if (choice < nr_options && choice >= 0) {
2281 tmp = strdup(abs_path[choice]);
2283 if (is_input_name_malloced)
2284 free((void *)input_name);
2286 is_input_name_malloced = true;
2289 ui__warning("Data switch failed due to memory shortage!\n");
2293 free_popup_options(options, nr_options);
2294 free_popup_options(abs_path, nr_options);
2298 struct popup_action {
2299 struct thread *thread;
2300 struct map_symbol ms;
2303 int (*fn)(struct hist_browser *browser, struct popup_action *act);
2307 do_annotate(struct hist_browser *browser, struct popup_action *act)
2309 struct perf_evsel *evsel;
2310 struct annotation *notes;
2311 struct hist_entry *he;
2314 if (!objdump_path && perf_env__lookup_objdump(browser->env))
2317 notes = symbol__annotation(act->ms.sym);
2321 evsel = hists_to_evsel(browser->hists);
2322 err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
2323 he = hist_browser__selected_entry(browser);
2325 * offer option to annotate the other branch source or target
2326 * (if they exists) when returning from annotate
2328 if ((err == 'q' || err == CTRL('c')) && he->branch_info)
2331 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
2333 ui_browser__handle_resize(&browser->b);
2338 add_annotate_opt(struct hist_browser *browser __maybe_unused,
2339 struct popup_action *act, char **optstr,
2340 struct map *map, struct symbol *sym)
2342 if (sym == NULL || map->dso->annotate_warned)
2345 if (asprintf(optstr, "Annotate %s", sym->name) < 0)
2350 act->fn = do_annotate;
2355 do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
2357 struct thread *thread = act->thread;
2359 if ((!hists__has(browser->hists, thread) &&
2360 !hists__has(browser->hists, comm)) || thread == NULL)
2363 if (browser->hists->thread_filter) {
2364 pstack__remove(browser->pstack, &browser->hists->thread_filter);
2365 perf_hpp__set_elide(HISTC_THREAD, false);
2366 thread__zput(browser->hists->thread_filter);
2369 if (hists__has(browser->hists, thread)) {
2370 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
2371 thread->comm_set ? thread__comm_str(thread) : "",
2374 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
2375 thread->comm_set ? thread__comm_str(thread) : "");
2378 browser->hists->thread_filter = thread__get(thread);
2379 perf_hpp__set_elide(HISTC_THREAD, false);
2380 pstack__push(browser->pstack, &browser->hists->thread_filter);
2383 hists__filter_by_thread(browser->hists);
2384 hist_browser__reset(browser);
2389 add_thread_opt(struct hist_browser *browser, struct popup_action *act,
2390 char **optstr, struct thread *thread)
2394 if ((!hists__has(browser->hists, thread) &&
2395 !hists__has(browser->hists, comm)) || thread == NULL)
2398 if (hists__has(browser->hists, thread)) {
2399 ret = asprintf(optstr, "Zoom %s %s(%d) thread",
2400 browser->hists->thread_filter ? "out of" : "into",
2401 thread->comm_set ? thread__comm_str(thread) : "",
2404 ret = asprintf(optstr, "Zoom %s %s thread",
2405 browser->hists->thread_filter ? "out of" : "into",
2406 thread->comm_set ? thread__comm_str(thread) : "");
2411 act->thread = thread;
2412 act->fn = do_zoom_thread;
2417 do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
2419 struct map *map = act->ms.map;
2421 if (!hists__has(browser->hists, dso) || map == NULL)
2424 if (browser->hists->dso_filter) {
2425 pstack__remove(browser->pstack, &browser->hists->dso_filter);
2426 perf_hpp__set_elide(HISTC_DSO, false);
2427 browser->hists->dso_filter = NULL;
2432 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
2433 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
2434 browser->hists->dso_filter = map->dso;
2435 perf_hpp__set_elide(HISTC_DSO, true);
2436 pstack__push(browser->pstack, &browser->hists->dso_filter);
2439 hists__filter_by_dso(browser->hists);
2440 hist_browser__reset(browser);
2445 add_dso_opt(struct hist_browser *browser, struct popup_action *act,
2446 char **optstr, struct map *map)
2448 if (!hists__has(browser->hists, dso) || map == NULL)
2451 if (asprintf(optstr, "Zoom %s %s DSO",
2452 browser->hists->dso_filter ? "out of" : "into",
2453 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
2457 act->fn = do_zoom_dso;
2462 do_browse_map(struct hist_browser *browser __maybe_unused,
2463 struct popup_action *act)
2465 map__browse(act->ms.map);
2470 add_map_opt(struct hist_browser *browser,
2471 struct popup_action *act, char **optstr, struct map *map)
2473 if (!hists__has(browser->hists, dso) || map == NULL)
2476 if (asprintf(optstr, "Browse map details") < 0)
2480 act->fn = do_browse_map;
2485 do_run_script(struct hist_browser *browser __maybe_unused,
2486 struct popup_action *act)
2488 char script_opt[64];
2489 memset(script_opt, 0, sizeof(script_opt));
2492 scnprintf(script_opt, sizeof(script_opt), " -c %s ",
2493 thread__comm_str(act->thread));
2494 } else if (act->ms.sym) {
2495 scnprintf(script_opt, sizeof(script_opt), " -S %s ",
2499 script_browse(script_opt);
2504 add_script_opt(struct hist_browser *browser __maybe_unused,
2505 struct popup_action *act, char **optstr,
2506 struct thread *thread, struct symbol *sym)
2509 if (asprintf(optstr, "Run scripts for samples of thread [%s]",
2510 thread__comm_str(thread)) < 0)
2513 if (asprintf(optstr, "Run scripts for samples of symbol [%s]",
2517 if (asprintf(optstr, "Run scripts for all samples") < 0)
2521 act->thread = thread;
2523 act->fn = do_run_script;
2528 do_switch_data(struct hist_browser *browser __maybe_unused,
2529 struct popup_action *act __maybe_unused)
2531 if (switch_data_file()) {
2532 ui__warning("Won't switch the data files due to\n"
2533 "no valid data file get selected!\n");
2537 return K_SWITCH_INPUT_DATA;
2541 add_switch_opt(struct hist_browser *browser,
2542 struct popup_action *act, char **optstr)
2544 if (!is_report_browser(browser->hbt))
2547 if (asprintf(optstr, "Switch to another data file in PWD") < 0)
2550 act->fn = do_switch_data;
2555 do_exit_browser(struct hist_browser *browser __maybe_unused,
2556 struct popup_action *act __maybe_unused)
2562 add_exit_opt(struct hist_browser *browser __maybe_unused,
2563 struct popup_action *act, char **optstr)
2565 if (asprintf(optstr, "Exit") < 0)
2568 act->fn = do_exit_browser;
2573 do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
2575 if (!hists__has(browser->hists, socket) || act->socket < 0)
2578 if (browser->hists->socket_filter > -1) {
2579 pstack__remove(browser->pstack, &browser->hists->socket_filter);
2580 browser->hists->socket_filter = -1;
2581 perf_hpp__set_elide(HISTC_SOCKET, false);
2583 browser->hists->socket_filter = act->socket;
2584 perf_hpp__set_elide(HISTC_SOCKET, true);
2585 pstack__push(browser->pstack, &browser->hists->socket_filter);
2588 hists__filter_by_socket(browser->hists);
2589 hist_browser__reset(browser);
2594 add_socket_opt(struct hist_browser *browser, struct popup_action *act,
2595 char **optstr, int socket_id)
2597 if (!hists__has(browser->hists, socket) || socket_id < 0)
2600 if (asprintf(optstr, "Zoom %s Processor Socket %d",
2601 (browser->hists->socket_filter > -1) ? "out of" : "into",
2605 act->socket = socket_id;
2606 act->fn = do_zoom_socket;
2610 static void hist_browser__update_nr_entries(struct hist_browser *hb)
2613 struct rb_node *nd = rb_first(&hb->hists->entries);
2615 if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
2616 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
2620 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2622 nd = rb_hierarchy_next(nd);
2625 hb->nr_non_filtered_entries = nr_entries;
2626 hb->nr_hierarchy_entries = nr_entries;
2629 static void hist_browser__update_percent_limit(struct hist_browser *hb,
2632 struct hist_entry *he;
2633 struct rb_node *nd = rb_first(&hb->hists->entries);
2634 u64 total = hists__total_period(hb->hists);
2635 u64 min_callchain_hits = total * (percent / 100);
2637 hb->min_pcnt = callchain_param.min_percent = percent;
2639 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2640 he = rb_entry(nd, struct hist_entry, rb_node);
2642 if (he->has_no_entry) {
2643 he->has_no_entry = false;
2647 if (!he->leaf || !symbol_conf.use_callchain)
2650 if (callchain_param.mode == CHAIN_GRAPH_REL) {
2651 total = he->stat.period;
2653 if (symbol_conf.cumulate_callchain)
2654 total = he->stat_acc->period;
2656 min_callchain_hits = total * (percent / 100);
2659 callchain_param.sort(&he->sorted_chain, he->callchain,
2660 min_callchain_hits, &callchain_param);
2663 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
2665 /* force to re-evaluate folding state of callchains */
2666 he->init_have_children = false;
2667 hist_entry__set_folding(he, hb, false);
2671 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
2672 const char *helpline,
2674 struct hist_browser_timer *hbt,
2676 struct perf_env *env)
2678 struct hists *hists = evsel__hists(evsel);
2679 struct hist_browser *browser = perf_evsel_browser__new(evsel, hbt, env);
2680 struct branch_info *bi;
2681 #define MAX_OPTIONS 16
2682 char *options[MAX_OPTIONS];
2683 struct popup_action actions[MAX_OPTIONS];
2687 int delay_secs = hbt ? hbt->refresh : 0;
2689 #define HIST_BROWSER_HELP_COMMON \
2690 "h/?/F1 Show this window\n" \
2692 "PGDN/SPACE Navigate\n" \
2693 "q/ESC/CTRL+C Exit browser\n\n" \
2694 "For multiple event sessions:\n\n" \
2695 "TAB/UNTAB Switch events\n\n" \
2696 "For symbolic views (--sort has sym):\n\n" \
2697 "ENTER Zoom into DSO/Threads & Annotate current symbol\n" \
2699 "a Annotate current symbol\n" \
2700 "C Collapse all callchains\n" \
2701 "d Zoom into current DSO\n" \
2702 "E Expand all callchains\n" \
2703 "F Toggle percentage of filtered entries\n" \
2704 "H Display column headers\n" \
2705 "L Change percent limit\n" \
2706 "m Display context menu\n" \
2707 "S Zoom into current Processor Socket\n" \
2709 /* help messages are sorted by lexical order of the hotkey */
2710 const char report_help[] = HIST_BROWSER_HELP_COMMON
2711 "i Show header information\n"
2712 "P Print histograms to perf.hist.N\n"
2713 "r Run available scripts\n"
2714 "s Switch to another data file in PWD\n"
2715 "t Zoom into current Thread\n"
2716 "V Verbose (DSO names in callchains, etc)\n"
2717 "/ Filter symbol by name";
2718 const char top_help[] = HIST_BROWSER_HELP_COMMON
2719 "P Print histograms to perf.hist.N\n"
2720 "t Zoom into current Thread\n"
2721 "V Verbose (DSO names in callchains, etc)\n"
2722 "z Toggle zeroing of samples\n"
2723 "f Enable/Disable events\n"
2724 "/ Filter symbol by name";
2726 if (browser == NULL)
2729 /* reset abort key so that it can get Ctrl-C as a key */
2731 SLang_init_tty(0, 0, 0);
2734 browser->min_pcnt = min_pcnt;
2735 hist_browser__update_nr_entries(browser);
2737 browser->pstack = pstack__new(3);
2738 if (browser->pstack == NULL)
2741 ui_helpline__push(helpline);
2743 memset(options, 0, sizeof(options));
2744 memset(actions, 0, sizeof(actions));
2746 if (symbol_conf.col_width_list_str)
2747 perf_hpp__set_user_width(symbol_conf.col_width_list_str);
2750 struct thread *thread = NULL;
2751 struct map *map = NULL;
2757 key = hist_browser__run(browser, helpline);
2759 if (browser->he_selection != NULL) {
2760 thread = hist_browser__selected_thread(browser);
2761 map = browser->selection->map;
2762 socked_id = browser->he_selection->socket;
2770 * Exit the browser, let hists__browser_tree
2771 * go to the next or previous
2773 goto out_free_stack;
2775 if (!hists__has(hists, sym)) {
2776 ui_browser__warning(&browser->b, delay_secs * 2,
2777 "Annotation is only available for symbolic views, "
2778 "include \"sym*\" in --sort to use it.");
2782 if (browser->selection == NULL ||
2783 browser->selection->sym == NULL ||
2784 browser->selection->map->dso->annotate_warned)
2787 actions->ms.map = browser->selection->map;
2788 actions->ms.sym = browser->selection->sym;
2789 do_annotate(browser, actions);
2792 hist_browser__dump(browser);
2795 actions->ms.map = map;
2796 do_zoom_dso(browser, actions);
2799 browser->show_dso = !browser->show_dso;
2802 actions->thread = thread;
2803 do_zoom_thread(browser, actions);
2806 actions->socket = socked_id;
2807 do_zoom_socket(browser, actions);
2810 if (ui_browser__input_window("Symbol to show",
2811 "Please enter the name of symbol you want to see.\n"
2812 "To remove the filter later, press / + ENTER.",
2813 buf, "ENTER: OK, ESC: Cancel",
2814 delay_secs * 2) == K_ENTER) {
2815 hists->symbol_filter_str = *buf ? buf : NULL;
2816 hists__filter_by_symbol(hists);
2817 hist_browser__reset(browser);
2821 if (is_report_browser(hbt)) {
2822 actions->thread = NULL;
2823 actions->ms.sym = NULL;
2824 do_run_script(browser, actions);
2828 if (is_report_browser(hbt)) {
2829 key = do_switch_data(browser, actions);
2830 if (key == K_SWITCH_INPUT_DATA)
2831 goto out_free_stack;
2835 /* env->arch is NULL for live-mode (i.e. perf top) */
2837 tui__header_window(env);
2840 symbol_conf.filter_relative ^= 1;
2843 if (!is_report_browser(hbt)) {
2844 struct perf_top *top = hbt->arg;
2846 top->zero = !top->zero;
2850 if (ui_browser__input_window("Percent Limit",
2851 "Please enter the value you want to hide entries under that percent.",
2852 buf, "ENTER: OK, ESC: Cancel",
2853 delay_secs * 2) == K_ENTER) {
2855 double new_percent = strtod(buf, &end);
2857 if (new_percent < 0 || new_percent > 100) {
2858 ui_browser__warning(&browser->b, delay_secs * 2,
2859 "Invalid percent: %.2f", new_percent);
2863 hist_browser__update_percent_limit(browser, new_percent);
2864 hist_browser__reset(browser);
2870 ui_browser__help_window(&browser->b,
2871 is_report_browser(hbt) ? report_help : top_help);
2882 if (pstack__empty(browser->pstack)) {
2884 * Go back to the perf_evsel_menu__run or other user
2887 goto out_free_stack;
2890 ui_browser__dialog_yesno(&browser->b,
2891 "Do you really want to exit?"))
2892 goto out_free_stack;
2896 top = pstack__peek(browser->pstack);
2897 if (top == &browser->hists->dso_filter) {
2899 * No need to set actions->dso here since
2900 * it's just to remove the current filter.
2901 * Ditto for thread below.
2903 do_zoom_dso(browser, actions);
2904 } else if (top == &browser->hists->thread_filter) {
2905 do_zoom_thread(browser, actions);
2906 } else if (top == &browser->hists->socket_filter) {
2907 do_zoom_socket(browser, actions);
2913 goto out_free_stack;
2915 if (!is_report_browser(hbt)) {
2916 struct perf_top *top = hbt->arg;
2918 perf_evlist__toggle_enable(top->evlist);
2920 * No need to refresh, resort/decay histogram
2921 * entries if we are not collecting samples:
2923 if (top->evlist->enabled) {
2924 helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
2925 hbt->refresh = delay_secs;
2927 helpline = "Press 'f' again to re-enable the events";
2934 helpline = "Press '?' for help on key bindings";
2938 if (!hists__has(hists, sym) || browser->selection == NULL)
2939 goto skip_annotation;
2941 if (sort__mode == SORT_MODE__BRANCH) {
2942 bi = browser->he_selection->branch_info;
2945 goto skip_annotation;
2947 nr_options += add_annotate_opt(browser,
2948 &actions[nr_options],
2949 &options[nr_options],
2952 if (bi->to.sym != bi->from.sym)
2953 nr_options += add_annotate_opt(browser,
2954 &actions[nr_options],
2955 &options[nr_options],
2959 nr_options += add_annotate_opt(browser,
2960 &actions[nr_options],
2961 &options[nr_options],
2962 browser->selection->map,
2963 browser->selection->sym);
2966 nr_options += add_thread_opt(browser, &actions[nr_options],
2967 &options[nr_options], thread);
2968 nr_options += add_dso_opt(browser, &actions[nr_options],
2969 &options[nr_options], map);
2970 nr_options += add_map_opt(browser, &actions[nr_options],
2971 &options[nr_options],
2972 browser->selection ?
2973 browser->selection->map : NULL);
2974 nr_options += add_socket_opt(browser, &actions[nr_options],
2975 &options[nr_options],
2977 /* perf script support */
2978 if (!is_report_browser(hbt))
2979 goto skip_scripting;
2981 if (browser->he_selection) {
2982 if (hists__has(hists, thread) && thread) {
2983 nr_options += add_script_opt(browser,
2984 &actions[nr_options],
2985 &options[nr_options],
2989 * Note that browser->selection != NULL
2990 * when browser->he_selection is not NULL,
2991 * so we don't need to check browser->selection
2992 * before fetching browser->selection->sym like what
2993 * we do before fetching browser->selection->map.
2995 * See hist_browser__show_entry.
2997 if (hists__has(hists, sym) && browser->selection->sym) {
2998 nr_options += add_script_opt(browser,
2999 &actions[nr_options],
3000 &options[nr_options],
3001 NULL, browser->selection->sym);
3004 nr_options += add_script_opt(browser, &actions[nr_options],
3005 &options[nr_options], NULL, NULL);
3006 nr_options += add_switch_opt(browser, &actions[nr_options],
3007 &options[nr_options]);
3009 nr_options += add_exit_opt(browser, &actions[nr_options],
3010 &options[nr_options]);
3013 struct popup_action *act;
3015 choice = ui__popup_menu(nr_options, options);
3016 if (choice == -1 || choice >= nr_options)
3019 act = &actions[choice];
3020 key = act->fn(browser, act);
3023 if (key == K_SWITCH_INPUT_DATA)
3027 pstack__delete(browser->pstack);
3029 hist_browser__delete(browser);
3030 free_popup_options(options, MAX_OPTIONS);
3034 struct perf_evsel_menu {
3035 struct ui_browser b;
3036 struct perf_evsel *selection;
3037 bool lost_events, lost_events_warned;
3039 struct perf_env *env;
3042 static void perf_evsel_menu__write(struct ui_browser *browser,
3043 void *entry, int row)
3045 struct perf_evsel_menu *menu = container_of(browser,
3046 struct perf_evsel_menu, b);
3047 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
3048 struct hists *hists = evsel__hists(evsel);
3049 bool current_entry = ui_browser__is_current_entry(browser, row);
3050 unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
3051 const char *ev_name = perf_evsel__name(evsel);
3053 const char *warn = " ";
3056 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
3057 HE_COLORSET_NORMAL);
3059 if (perf_evsel__is_group_event(evsel)) {
3060 struct perf_evsel *pos;
3062 ev_name = perf_evsel__group_name(evsel);
3064 for_each_group_member(pos, evsel) {
3065 struct hists *pos_hists = evsel__hists(pos);
3066 nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
3070 nr_events = convert_unit(nr_events, &unit);
3071 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
3072 unit, unit == ' ' ? "" : " ", ev_name);
3073 ui_browser__printf(browser, "%s", bf);
3075 nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
3076 if (nr_events != 0) {
3077 menu->lost_events = true;
3079 ui_browser__set_color(browser, HE_COLORSET_TOP);
3080 nr_events = convert_unit(nr_events, &unit);
3081 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
3082 nr_events, unit, unit == ' ' ? "" : " ");
3086 ui_browser__write_nstring(browser, warn, browser->width - printed);
3089 menu->selection = evsel;
3092 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
3093 int nr_events, const char *help,
3094 struct hist_browser_timer *hbt)
3096 struct perf_evlist *evlist = menu->b.priv;
3097 struct perf_evsel *pos;
3098 const char *title = "Available samples";
3099 int delay_secs = hbt ? hbt->refresh : 0;
3102 if (ui_browser__show(&menu->b, title,
3103 "ESC: exit, ENTER|->: Browse histograms") < 0)
3107 key = ui_browser__run(&menu->b, delay_secs);
3111 hbt->timer(hbt->arg);
3113 if (!menu->lost_events_warned && menu->lost_events) {
3114 ui_browser__warn_lost_events(&menu->b);
3115 menu->lost_events_warned = true;
3120 if (!menu->selection)
3122 pos = menu->selection;
3124 perf_evlist__set_selected(evlist, pos);
3126 * Give the calling tool a chance to populate the non
3127 * default evsel resorted hists tree.
3130 hbt->timer(hbt->arg);
3131 key = perf_evsel__hists_browse(pos, nr_events, help,
3135 ui_browser__show_title(&menu->b, title);
3138 if (pos->node.next == &evlist->entries)
3139 pos = perf_evlist__first(evlist);
3141 pos = perf_evsel__next(pos);
3144 if (pos->node.prev == &evlist->entries)
3145 pos = perf_evlist__last(evlist);
3147 pos = perf_evsel__prev(pos);
3149 case K_SWITCH_INPUT_DATA:
3160 if (!ui_browser__dialog_yesno(&menu->b,
3161 "Do you really want to exit?"))
3173 ui_browser__hide(&menu->b);
3177 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
3180 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
3182 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
3188 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
3189 int nr_entries, const char *help,
3190 struct hist_browser_timer *hbt,
3192 struct perf_env *env)
3194 struct perf_evsel *pos;
3195 struct perf_evsel_menu menu = {
3197 .entries = &evlist->entries,
3198 .refresh = ui_browser__list_head_refresh,
3199 .seek = ui_browser__list_head_seek,
3200 .write = perf_evsel_menu__write,
3201 .filter = filter_group_entries,
3202 .nr_entries = nr_entries,
3205 .min_pcnt = min_pcnt,
3209 ui_helpline__push("Press ESC to exit");
3211 evlist__for_each_entry(evlist, pos) {
3212 const char *ev_name = perf_evsel__name(pos);
3213 size_t line_len = strlen(ev_name) + 7;
3215 if (menu.b.width < line_len)
3216 menu.b.width = line_len;
3219 return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
3222 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
3223 struct hist_browser_timer *hbt,
3225 struct perf_env *env)
3227 int nr_entries = evlist->nr_entries;
3230 if (nr_entries == 1) {
3231 struct perf_evsel *first = perf_evlist__first(evlist);
3233 return perf_evsel__hists_browse(first, nr_entries, help,
3234 false, hbt, min_pcnt,
3238 if (symbol_conf.event_group) {
3239 struct perf_evsel *pos;
3242 evlist__for_each_entry(evlist, pos) {
3243 if (perf_evsel__is_group_leader(pos))
3247 if (nr_entries == 1)
3251 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
3252 hbt, min_pcnt, env);