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 || hb->c2c_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 symbol_conf.report_hierarchy)
606 hist_browser__update_nr_entries(browser);
608 nr_entries = hist_browser__nr_entries(browser);
609 ui_browser__update_nr_entries(&browser->b, nr_entries);
611 if (browser->hists->stats.nr_lost_warned !=
612 browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
613 browser->hists->stats.nr_lost_warned =
614 browser->hists->stats.nr_events[PERF_RECORD_LOST];
615 ui_browser__warn_lost_events(&browser->b);
618 hist_browser__title(browser, title, sizeof(title));
619 ui_browser__show_title(&browser->b, title);
622 case 'D': { /* Debug */
624 struct hist_entry *h = rb_entry(browser->b.top,
625 struct hist_entry, rb_node);
627 ui_helpline__fpush("%d: nr_ent=(%d,%d), rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
628 seq++, browser->b.nr_entries,
629 browser->hists->nr_entries,
633 h->row_offset, h->nr_rows);
637 /* Collapse the whole world. */
638 hist_browser__set_folding(browser, false);
641 /* Expand the whole world. */
642 hist_browser__set_folding(browser, true);
645 browser->show_headers = !browser->show_headers;
646 hist_browser__update_rows(browser);
649 if (hist_browser__toggle_fold(browser))
657 ui_browser__hide(&browser->b);
661 struct callchain_print_arg {
662 /* for hists browser */
664 bool is_current_entry;
671 typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
672 struct callchain_list *chain,
673 const char *str, int offset,
675 struct callchain_print_arg *arg);
677 static void hist_browser__show_callchain_entry(struct hist_browser *browser,
678 struct callchain_list *chain,
679 const char *str, int offset,
681 struct callchain_print_arg *arg)
684 char folded_sign = callchain_list__folded(chain);
685 bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
687 color = HE_COLORSET_NORMAL;
688 width = browser->b.width - (offset + 2);
689 if (ui_browser__is_current_entry(&browser->b, row)) {
690 browser->selection = &chain->ms;
691 color = HE_COLORSET_SELECTED;
692 arg->is_current_entry = true;
695 ui_browser__set_color(&browser->b, color);
696 hist_browser__gotorc(browser, row, 0);
697 ui_browser__write_nstring(&browser->b, " ", offset);
698 ui_browser__printf(&browser->b, "%c", folded_sign);
699 ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
700 ui_browser__write_nstring(&browser->b, str, width);
703 static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
704 struct callchain_list *chain,
705 const char *str, int offset,
706 unsigned short row __maybe_unused,
707 struct callchain_print_arg *arg)
709 char folded_sign = callchain_list__folded(chain);
711 arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
715 typedef bool (*check_output_full_fn)(struct hist_browser *browser,
718 static bool hist_browser__check_output_full(struct hist_browser *browser,
721 return browser->b.rows == row;
724 static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
725 unsigned short row __maybe_unused)
730 #define LEVEL_OFFSET_STEP 3
732 static int hist_browser__show_callchain_list(struct hist_browser *browser,
733 struct callchain_node *node,
734 struct callchain_list *chain,
735 unsigned short row, u64 total,
736 bool need_percent, int offset,
737 print_callchain_entry_fn print,
738 struct callchain_print_arg *arg)
740 char bf[1024], *alloc_str;
741 char buf[64], *alloc_str2;
744 if (arg->row_offset != 0) {
752 str = callchain_list__sym_name(chain, bf, sizeof(bf),
755 if (symbol_conf.show_branchflag_count) {
757 callchain_list_counts__printf_value(node, chain, NULL,
760 callchain_list_counts__printf_value(NULL, chain, NULL,
763 if (asprintf(&alloc_str2, "%s%s", str, buf) < 0)
764 str = "Not enough memory!";
770 callchain_node__scnprintf_value(node, buf, sizeof(buf),
773 if (asprintf(&alloc_str, "%s %s", buf, str) < 0)
774 str = "Not enough memory!";
779 print(browser, chain, str, offset, row, arg);
786 static bool check_percent_display(struct rb_node *node, u64 parent_total)
788 struct callchain_node *child;
796 child = rb_entry(node, struct callchain_node, rb_node);
797 return callchain_cumul_hits(child) != parent_total;
800 static int hist_browser__show_callchain_flat(struct hist_browser *browser,
801 struct rb_root *root,
802 unsigned short row, u64 total,
804 print_callchain_entry_fn print,
805 struct callchain_print_arg *arg,
806 check_output_full_fn is_output_full)
808 struct rb_node *node;
809 int first_row = row, offset = LEVEL_OFFSET_STEP;
812 node = rb_first(root);
813 need_percent = check_percent_display(node, parent_total);
816 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
817 struct rb_node *next = rb_next(node);
818 struct callchain_list *chain;
819 char folded_sign = ' ';
821 int extra_offset = 0;
823 list_for_each_entry(chain, &child->parent_val, list) {
824 bool was_first = first;
828 else if (need_percent)
829 extra_offset = LEVEL_OFFSET_STEP;
831 folded_sign = callchain_list__folded(chain);
833 row += hist_browser__show_callchain_list(browser, child,
835 was_first && need_percent,
836 offset + extra_offset,
839 if (is_output_full(browser, row))
842 if (folded_sign == '+')
846 list_for_each_entry(chain, &child->val, list) {
847 bool was_first = first;
851 else if (need_percent)
852 extra_offset = LEVEL_OFFSET_STEP;
854 folded_sign = callchain_list__folded(chain);
856 row += hist_browser__show_callchain_list(browser, child,
858 was_first && need_percent,
859 offset + extra_offset,
862 if (is_output_full(browser, row))
865 if (folded_sign == '+')
870 if (is_output_full(browser, row))
875 return row - first_row;
878 static char *hist_browser__folded_callchain_str(struct hist_browser *browser,
879 struct callchain_list *chain,
880 char *value_str, char *old_str)
886 str = callchain_list__sym_name(chain, bf, sizeof(bf),
889 if (asprintf(&new, "%s%s%s", old_str,
890 symbol_conf.field_sep ?: ";", str) < 0)
894 if (asprintf(&new, "%s %s", value_str, str) < 0)
897 if (asprintf(&new, "%s", str) < 0)
904 static int hist_browser__show_callchain_folded(struct hist_browser *browser,
905 struct rb_root *root,
906 unsigned short row, u64 total,
908 print_callchain_entry_fn print,
909 struct callchain_print_arg *arg,
910 check_output_full_fn is_output_full)
912 struct rb_node *node;
913 int first_row = row, offset = LEVEL_OFFSET_STEP;
916 node = rb_first(root);
917 need_percent = check_percent_display(node, parent_total);
920 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
921 struct rb_node *next = rb_next(node);
922 struct callchain_list *chain, *first_chain = NULL;
924 char *value_str = NULL, *value_str_alloc = NULL;
925 char *chain_str = NULL, *chain_str_alloc = NULL;
927 if (arg->row_offset != 0) {
935 callchain_node__scnprintf_value(child, buf, sizeof(buf), total);
936 if (asprintf(&value_str, "%s", buf) < 0) {
937 value_str = (char *)"<...>";
940 value_str_alloc = value_str;
943 list_for_each_entry(chain, &child->parent_val, list) {
944 chain_str = hist_browser__folded_callchain_str(browser,
945 chain, value_str, chain_str);
951 if (chain_str == NULL) {
952 chain_str = (char *)"Not enough memory!";
956 chain_str_alloc = chain_str;
959 list_for_each_entry(chain, &child->val, list) {
960 chain_str = hist_browser__folded_callchain_str(browser,
961 chain, value_str, chain_str);
967 if (chain_str == NULL) {
968 chain_str = (char *)"Not enough memory!";
972 chain_str_alloc = chain_str;
976 print(browser, first_chain, chain_str, offset, row++, arg);
977 free(value_str_alloc);
978 free(chain_str_alloc);
981 if (is_output_full(browser, row))
986 return row - first_row;
989 static int hist_browser__show_callchain_graph(struct hist_browser *browser,
990 struct rb_root *root, int level,
991 unsigned short row, u64 total,
993 print_callchain_entry_fn print,
994 struct callchain_print_arg *arg,
995 check_output_full_fn is_output_full)
997 struct rb_node *node;
998 int first_row = row, offset = level * LEVEL_OFFSET_STEP;
1000 u64 percent_total = total;
1002 if (callchain_param.mode == CHAIN_GRAPH_REL)
1003 percent_total = parent_total;
1005 node = rb_first(root);
1006 need_percent = check_percent_display(node, parent_total);
1009 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1010 struct rb_node *next = rb_next(node);
1011 struct callchain_list *chain;
1012 char folded_sign = ' ';
1014 int extra_offset = 0;
1016 list_for_each_entry(chain, &child->val, list) {
1017 bool was_first = first;
1021 else if (need_percent)
1022 extra_offset = LEVEL_OFFSET_STEP;
1024 folded_sign = callchain_list__folded(chain);
1026 row += hist_browser__show_callchain_list(browser, child,
1027 chain, row, percent_total,
1028 was_first && need_percent,
1029 offset + extra_offset,
1032 if (is_output_full(browser, row))
1035 if (folded_sign == '+')
1039 if (folded_sign == '-') {
1040 const int new_level = level + (extra_offset ? 2 : 1);
1042 row += hist_browser__show_callchain_graph(browser, &child->rb_root,
1043 new_level, row, total,
1044 child->children_hit,
1045 print, arg, is_output_full);
1047 if (is_output_full(browser, row))
1052 return row - first_row;
1055 static int hist_browser__show_callchain(struct hist_browser *browser,
1056 struct hist_entry *entry, int level,
1058 print_callchain_entry_fn print,
1059 struct callchain_print_arg *arg,
1060 check_output_full_fn is_output_full)
1062 u64 total = hists__total_period(entry->hists);
1066 if (symbol_conf.cumulate_callchain)
1067 parent_total = entry->stat_acc->period;
1069 parent_total = entry->stat.period;
1071 if (callchain_param.mode == CHAIN_FLAT) {
1072 printed = hist_browser__show_callchain_flat(browser,
1073 &entry->sorted_chain, row,
1074 total, parent_total, print, arg,
1076 } else if (callchain_param.mode == CHAIN_FOLDED) {
1077 printed = hist_browser__show_callchain_folded(browser,
1078 &entry->sorted_chain, row,
1079 total, parent_total, print, arg,
1082 printed = hist_browser__show_callchain_graph(browser,
1083 &entry->sorted_chain, level, row,
1084 total, parent_total, print, arg,
1088 if (arg->is_current_entry)
1089 browser->he_selection = entry;
1095 struct ui_browser *b;
1100 int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
1102 struct hpp_arg *arg = hpp->ptr;
1107 va_start(args, fmt);
1108 len = va_arg(args, int);
1109 percent = va_arg(args, double);
1112 ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
1114 ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
1115 ui_browser__printf(arg->b, "%s", hpp->buf);
1120 #define __HPP_COLOR_PERCENT_FN(_type, _field) \
1121 static u64 __hpp_get_##_field(struct hist_entry *he) \
1123 return he->stat._field; \
1127 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
1128 struct perf_hpp *hpp, \
1129 struct hist_entry *he) \
1131 return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%", \
1132 __hpp__slsmg_color_printf, true); \
1135 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
1136 static u64 __hpp_get_acc_##_field(struct hist_entry *he) \
1138 return he->stat_acc->_field; \
1142 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
1143 struct perf_hpp *hpp, \
1144 struct hist_entry *he) \
1146 if (!symbol_conf.cumulate_callchain) { \
1147 struct hpp_arg *arg = hpp->ptr; \
1148 int len = fmt->user_len ?: fmt->len; \
1149 int ret = scnprintf(hpp->buf, hpp->size, \
1150 "%*s", len, "N/A"); \
1151 ui_browser__printf(arg->b, "%s", hpp->buf); \
1155 return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field, \
1156 " %*.2f%%", __hpp__slsmg_color_printf, true); \
1159 __HPP_COLOR_PERCENT_FN(overhead, period)
1160 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
1161 __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
1162 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
1163 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
1164 __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
1166 #undef __HPP_COLOR_PERCENT_FN
1167 #undef __HPP_COLOR_ACC_PERCENT_FN
1169 void hist_browser__init_hpp(void)
1171 perf_hpp__format[PERF_HPP__OVERHEAD].color =
1172 hist_browser__hpp_color_overhead;
1173 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
1174 hist_browser__hpp_color_overhead_sys;
1175 perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
1176 hist_browser__hpp_color_overhead_us;
1177 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
1178 hist_browser__hpp_color_overhead_guest_sys;
1179 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
1180 hist_browser__hpp_color_overhead_guest_us;
1181 perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
1182 hist_browser__hpp_color_overhead_acc;
1185 static int hist_browser__show_entry(struct hist_browser *browser,
1186 struct hist_entry *entry,
1190 int width = browser->b.width;
1191 char folded_sign = ' ';
1192 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1193 off_t row_offset = entry->row_offset;
1195 struct perf_hpp_fmt *fmt;
1197 if (current_entry) {
1198 browser->he_selection = entry;
1199 browser->selection = &entry->ms;
1202 if (symbol_conf.use_callchain) {
1203 hist_entry__init_have_children(entry);
1204 folded_sign = hist_entry__folded(entry);
1207 if (row_offset == 0) {
1208 struct hpp_arg arg = {
1210 .folded_sign = folded_sign,
1211 .current_entry = current_entry,
1215 hist_browser__gotorc(browser, row, 0);
1217 hists__for_each_format(browser->hists, fmt) {
1219 struct perf_hpp hpp = {
1225 if (perf_hpp__should_skip(fmt, entry->hists) ||
1226 column++ < browser->b.horiz_scroll)
1229 if (current_entry && browser->b.navkeypressed) {
1230 ui_browser__set_color(&browser->b,
1231 HE_COLORSET_SELECTED);
1233 ui_browser__set_color(&browser->b,
1234 HE_COLORSET_NORMAL);
1238 if (symbol_conf.use_callchain) {
1239 ui_browser__printf(&browser->b, "%c ", folded_sign);
1244 ui_browser__printf(&browser->b, " ");
1249 int ret = fmt->color(fmt, &hpp, entry);
1250 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1252 * fmt->color() already used ui_browser to
1253 * print the non alignment bits, skip it (+ret):
1255 ui_browser__printf(&browser->b, "%s", s + ret);
1257 hist_entry__snprintf_alignment(entry, &hpp, fmt, fmt->entry(fmt, &hpp, entry));
1258 ui_browser__printf(&browser->b, "%s", s);
1260 width -= hpp.buf - s;
1263 /* The scroll bar isn't being used */
1264 if (!browser->b.navkeypressed)
1267 ui_browser__write_nstring(&browser->b, "", width);
1274 if (folded_sign == '-' && row != browser->b.rows) {
1275 struct callchain_print_arg arg = {
1276 .row_offset = row_offset,
1277 .is_current_entry = current_entry,
1280 printed += hist_browser__show_callchain(browser, entry, 1, row,
1281 hist_browser__show_callchain_entry, &arg,
1282 hist_browser__check_output_full);
1288 static int hist_browser__show_hierarchy_entry(struct hist_browser *browser,
1289 struct hist_entry *entry,
1294 int width = browser->b.width;
1295 char folded_sign = ' ';
1296 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1297 off_t row_offset = entry->row_offset;
1299 struct perf_hpp_fmt *fmt;
1300 struct perf_hpp_list_node *fmt_node;
1301 struct hpp_arg arg = {
1303 .current_entry = current_entry,
1306 int hierarchy_indent = (entry->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
1308 if (current_entry) {
1309 browser->he_selection = entry;
1310 browser->selection = &entry->ms;
1313 hist_entry__init_have_children(entry);
1314 folded_sign = hist_entry__folded(entry);
1315 arg.folded_sign = folded_sign;
1317 if (entry->leaf && row_offset) {
1319 goto show_callchain;
1322 hist_browser__gotorc(browser, row, 0);
1324 if (current_entry && browser->b.navkeypressed)
1325 ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1327 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1329 ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1330 width -= level * HIERARCHY_INDENT;
1332 /* the first hpp_list_node is for overhead columns */
1333 fmt_node = list_first_entry(&entry->hists->hpp_formats,
1334 struct perf_hpp_list_node, list);
1335 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1337 struct perf_hpp hpp = {
1343 if (perf_hpp__should_skip(fmt, entry->hists) ||
1344 column++ < browser->b.horiz_scroll)
1347 if (current_entry && browser->b.navkeypressed) {
1348 ui_browser__set_color(&browser->b,
1349 HE_COLORSET_SELECTED);
1351 ui_browser__set_color(&browser->b,
1352 HE_COLORSET_NORMAL);
1356 ui_browser__printf(&browser->b, "%c ", folded_sign);
1360 ui_browser__printf(&browser->b, " ");
1365 int ret = fmt->color(fmt, &hpp, entry);
1366 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1368 * fmt->color() already used ui_browser to
1369 * print the non alignment bits, skip it (+ret):
1371 ui_browser__printf(&browser->b, "%s", s + ret);
1373 int ret = fmt->entry(fmt, &hpp, entry);
1374 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1375 ui_browser__printf(&browser->b, "%s", s);
1377 width -= hpp.buf - s;
1381 ui_browser__write_nstring(&browser->b, "", hierarchy_indent);
1382 width -= hierarchy_indent;
1385 if (column >= browser->b.horiz_scroll) {
1387 struct perf_hpp hpp = {
1393 if (current_entry && browser->b.navkeypressed) {
1394 ui_browser__set_color(&browser->b,
1395 HE_COLORSET_SELECTED);
1397 ui_browser__set_color(&browser->b,
1398 HE_COLORSET_NORMAL);
1401 perf_hpp_list__for_each_format(entry->hpp_list, fmt) {
1403 ui_browser__printf(&browser->b, "%c ", folded_sign);
1406 ui_browser__write_nstring(&browser->b, "", 2);
1412 * No need to call hist_entry__snprintf_alignment()
1413 * since this fmt is always the last column in the
1417 width -= fmt->color(fmt, &hpp, entry);
1421 width -= fmt->entry(fmt, &hpp, entry);
1422 ui_browser__printf(&browser->b, "%s", ltrim(s));
1424 while (isspace(s[i++]))
1430 /* The scroll bar isn't being used */
1431 if (!browser->b.navkeypressed)
1434 ui_browser__write_nstring(&browser->b, "", width);
1440 if (entry->leaf && folded_sign == '-' && row != browser->b.rows) {
1441 struct callchain_print_arg carg = {
1442 .row_offset = row_offset,
1445 printed += hist_browser__show_callchain(browser, entry,
1447 hist_browser__show_callchain_entry, &carg,
1448 hist_browser__check_output_full);
1454 static int hist_browser__show_no_entry(struct hist_browser *browser,
1455 unsigned short row, int level)
1457 int width = browser->b.width;
1458 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1462 struct perf_hpp_fmt *fmt;
1463 struct perf_hpp_list_node *fmt_node;
1464 int indent = browser->hists->nr_hpp_node - 2;
1466 if (current_entry) {
1467 browser->he_selection = NULL;
1468 browser->selection = NULL;
1471 hist_browser__gotorc(browser, row, 0);
1473 if (current_entry && browser->b.navkeypressed)
1474 ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1476 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1478 ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1479 width -= level * HIERARCHY_INDENT;
1481 /* the first hpp_list_node is for overhead columns */
1482 fmt_node = list_first_entry(&browser->hists->hpp_formats,
1483 struct perf_hpp_list_node, list);
1484 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1485 if (perf_hpp__should_skip(fmt, browser->hists) ||
1486 column++ < browser->b.horiz_scroll)
1489 ret = fmt->width(fmt, NULL, browser->hists);
1492 /* for folded sign */
1496 /* space between columns */
1500 ui_browser__write_nstring(&browser->b, "", ret);
1504 ui_browser__write_nstring(&browser->b, "", indent * HIERARCHY_INDENT);
1505 width -= indent * HIERARCHY_INDENT;
1507 if (column >= browser->b.horiz_scroll) {
1510 ret = snprintf(buf, sizeof(buf), "no entry >= %.2f%%", browser->min_pcnt);
1511 ui_browser__printf(&browser->b, " %s", buf);
1515 /* The scroll bar isn't being used */
1516 if (!browser->b.navkeypressed)
1519 ui_browser__write_nstring(&browser->b, "", width);
1523 static int advance_hpp_check(struct perf_hpp *hpp, int inc)
1525 advance_hpp(hpp, inc);
1526 return hpp->size <= 0;
1530 hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf,
1531 size_t size, int line)
1533 struct hists *hists = browser->hists;
1534 struct perf_hpp dummy_hpp = {
1538 struct perf_hpp_fmt *fmt;
1543 if (symbol_conf.use_callchain) {
1544 ret = scnprintf(buf, size, " ");
1545 if (advance_hpp_check(&dummy_hpp, ret))
1549 hists__for_each_format(browser->hists, fmt) {
1550 if (perf_hpp__should_skip(fmt, hists) || column++ < browser->b.horiz_scroll)
1553 ret = fmt->header(fmt, &dummy_hpp, hists, line, &span);
1554 if (advance_hpp_check(&dummy_hpp, ret))
1560 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
1561 if (advance_hpp_check(&dummy_hpp, ret))
1568 static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser, char *buf, size_t size)
1570 struct hists *hists = browser->hists;
1571 struct perf_hpp dummy_hpp = {
1575 struct perf_hpp_fmt *fmt;
1576 struct perf_hpp_list_node *fmt_node;
1579 int indent = hists->nr_hpp_node - 2;
1580 bool first_node, first_col;
1582 ret = scnprintf(buf, size, " ");
1583 if (advance_hpp_check(&dummy_hpp, ret))
1587 /* the first hpp_list_node is for overhead columns */
1588 fmt_node = list_first_entry(&hists->hpp_formats,
1589 struct perf_hpp_list_node, list);
1590 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1591 if (column++ < browser->b.horiz_scroll)
1594 ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1595 if (advance_hpp_check(&dummy_hpp, ret))
1598 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
1599 if (advance_hpp_check(&dummy_hpp, ret))
1606 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "%*s",
1607 indent * HIERARCHY_INDENT, "");
1608 if (advance_hpp_check(&dummy_hpp, ret))
1613 list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
1615 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " / ");
1616 if (advance_hpp_check(&dummy_hpp, ret))
1622 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1625 if (perf_hpp__should_skip(fmt, hists))
1629 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "+");
1630 if (advance_hpp_check(&dummy_hpp, ret))
1635 ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1636 dummy_hpp.buf[ret] = '\0';
1638 start = trim(dummy_hpp.buf);
1639 ret = strlen(start);
1641 if (start != dummy_hpp.buf)
1642 memmove(dummy_hpp.buf, start, ret + 1);
1644 if (advance_hpp_check(&dummy_hpp, ret))
1652 static void hists_browser__hierarchy_headers(struct hist_browser *browser)
1656 hists_browser__scnprintf_hierarchy_headers(browser, headers,
1659 ui_browser__gotorc(&browser->b, 0, 0);
1660 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1661 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1664 static void hists_browser__headers(struct hist_browser *browser)
1666 struct hists *hists = browser->hists;
1667 struct perf_hpp_list *hpp_list = hists->hpp_list;
1671 for (line = 0; line < hpp_list->nr_header_lines; line++) {
1674 hists_browser__scnprintf_headers(browser, headers,
1675 sizeof(headers), line);
1677 ui_browser__gotorc(&browser->b, line, 0);
1678 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1679 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1683 static void hist_browser__show_headers(struct hist_browser *browser)
1685 if (symbol_conf.report_hierarchy)
1686 hists_browser__hierarchy_headers(browser);
1688 hists_browser__headers(browser);
1691 static void ui_browser__hists_init_top(struct ui_browser *browser)
1693 if (browser->top == NULL) {
1694 struct hist_browser *hb;
1696 hb = container_of(browser, struct hist_browser, b);
1697 browser->top = rb_first(&hb->hists->entries);
1701 static unsigned int hist_browser__refresh(struct ui_browser *browser)
1704 u16 header_offset = 0;
1706 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
1707 struct hists *hists = hb->hists;
1709 if (hb->show_headers) {
1710 struct perf_hpp_list *hpp_list = hists->hpp_list;
1712 hist_browser__show_headers(hb);
1713 header_offset = hpp_list->nr_header_lines;
1716 ui_browser__hists_init_top(browser);
1717 hb->he_selection = NULL;
1718 hb->selection = NULL;
1720 for (nd = browser->top; nd; nd = rb_hierarchy_next(nd)) {
1721 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1725 /* let it move to sibling */
1726 h->unfolded = false;
1730 percent = hist_entry__get_percent_limit(h);
1731 if (percent < hb->min_pcnt)
1734 if (symbol_conf.report_hierarchy) {
1735 row += hist_browser__show_hierarchy_entry(hb, h, row,
1737 if (row == browser->rows)
1740 if (h->has_no_entry) {
1741 hist_browser__show_no_entry(hb, row, h->depth + 1);
1745 row += hist_browser__show_entry(hb, h, row);
1748 if (row == browser->rows)
1752 return row + header_offset;
1755 static struct rb_node *hists__filter_entries(struct rb_node *nd,
1758 while (nd != NULL) {
1759 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1760 float percent = hist_entry__get_percent_limit(h);
1762 if (!h->filtered && percent >= min_pcnt)
1766 * If it's filtered, its all children also were filtered.
1767 * So move to sibling node.
1772 nd = rb_hierarchy_next(nd);
1778 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
1781 while (nd != NULL) {
1782 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1783 float percent = hist_entry__get_percent_limit(h);
1785 if (!h->filtered && percent >= min_pcnt)
1788 nd = rb_hierarchy_prev(nd);
1794 static void ui_browser__hists_seek(struct ui_browser *browser,
1795 off_t offset, int whence)
1797 struct hist_entry *h;
1800 struct hist_browser *hb;
1802 hb = container_of(browser, struct hist_browser, b);
1804 if (browser->nr_entries == 0)
1807 ui_browser__hists_init_top(browser);
1811 nd = hists__filter_entries(rb_first(browser->entries),
1818 nd = rb_hierarchy_last(rb_last(browser->entries));
1819 nd = hists__filter_prev_entries(nd, hb->min_pcnt);
1827 * Moves not relative to the first visible entry invalidates its
1830 h = rb_entry(browser->top, struct hist_entry, rb_node);
1834 * Here we have to check if nd is expanded (+), if it is we can't go
1835 * the next top level hist_entry, instead we must compute an offset of
1836 * what _not_ to show and not change the first visible entry.
1838 * This offset increments when we are going from top to bottom and
1839 * decreases when we're going from bottom to top.
1841 * As we don't have backpointers to the top level in the callchains
1842 * structure, we need to always print the whole hist_entry callchain,
1843 * skipping the first ones that are before the first visible entry
1844 * and stop when we printed enough lines to fill the screen.
1852 h = rb_entry(nd, struct hist_entry, rb_node);
1853 if (h->unfolded && h->leaf) {
1854 u16 remaining = h->nr_rows - h->row_offset;
1855 if (offset > remaining) {
1856 offset -= remaining;
1859 h->row_offset += offset;
1865 nd = hists__filter_entries(rb_hierarchy_next(nd),
1871 } while (offset != 0);
1872 } else if (offset < 0) {
1874 h = rb_entry(nd, struct hist_entry, rb_node);
1875 if (h->unfolded && h->leaf) {
1877 if (-offset > h->row_offset) {
1878 offset += h->row_offset;
1881 h->row_offset += offset;
1887 if (-offset > h->nr_rows) {
1888 offset += h->nr_rows;
1891 h->row_offset = h->nr_rows + offset;
1899 nd = hists__filter_prev_entries(rb_hierarchy_prev(nd),
1907 * Last unfiltered hist_entry, check if it is
1908 * unfolded, if it is then we should have
1909 * row_offset at its last entry.
1911 h = rb_entry(nd, struct hist_entry, rb_node);
1912 if (h->unfolded && h->leaf)
1913 h->row_offset = h->nr_rows;
1920 h = rb_entry(nd, struct hist_entry, rb_node);
1925 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1926 struct hist_entry *he, FILE *fp,
1929 struct callchain_print_arg arg = {
1933 hist_browser__show_callchain(browser, he, level, 0,
1934 hist_browser__fprintf_callchain_entry, &arg,
1935 hist_browser__check_dump_full);
1939 static int hist_browser__fprintf_entry(struct hist_browser *browser,
1940 struct hist_entry *he, FILE *fp)
1944 char folded_sign = ' ';
1945 struct perf_hpp hpp = {
1949 struct perf_hpp_fmt *fmt;
1953 if (symbol_conf.use_callchain) {
1954 folded_sign = hist_entry__folded(he);
1955 printed += fprintf(fp, "%c ", folded_sign);
1958 hists__for_each_format(browser->hists, fmt) {
1959 if (perf_hpp__should_skip(fmt, he->hists))
1963 ret = scnprintf(hpp.buf, hpp.size, " ");
1964 advance_hpp(&hpp, ret);
1968 ret = fmt->entry(fmt, &hpp, he);
1969 ret = hist_entry__snprintf_alignment(he, &hpp, fmt, ret);
1970 advance_hpp(&hpp, ret);
1972 printed += fprintf(fp, "%s\n", s);
1974 if (folded_sign == '-')
1975 printed += hist_browser__fprintf_callchain(browser, he, fp, 1);
1981 static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,
1982 struct hist_entry *he,
1983 FILE *fp, int level)
1987 char folded_sign = ' ';
1988 struct perf_hpp hpp = {
1992 struct perf_hpp_fmt *fmt;
1993 struct perf_hpp_list_node *fmt_node;
1996 int hierarchy_indent = (he->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
1998 printed = fprintf(fp, "%*s", level * HIERARCHY_INDENT, "");
2000 folded_sign = hist_entry__folded(he);
2001 printed += fprintf(fp, "%c", folded_sign);
2003 /* the first hpp_list_node is for overhead columns */
2004 fmt_node = list_first_entry(&he->hists->hpp_formats,
2005 struct perf_hpp_list_node, list);
2006 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
2008 ret = scnprintf(hpp.buf, hpp.size, " ");
2009 advance_hpp(&hpp, ret);
2013 ret = fmt->entry(fmt, &hpp, he);
2014 advance_hpp(&hpp, ret);
2017 ret = scnprintf(hpp.buf, hpp.size, "%*s", hierarchy_indent, "");
2018 advance_hpp(&hpp, ret);
2020 perf_hpp_list__for_each_format(he->hpp_list, fmt) {
2021 ret = scnprintf(hpp.buf, hpp.size, " ");
2022 advance_hpp(&hpp, ret);
2024 ret = fmt->entry(fmt, &hpp, he);
2025 advance_hpp(&hpp, ret);
2028 printed += fprintf(fp, "%s\n", rtrim(s));
2030 if (he->leaf && folded_sign == '-') {
2031 printed += hist_browser__fprintf_callchain(browser, he, fp,
2038 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
2040 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
2045 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
2047 if (symbol_conf.report_hierarchy) {
2048 printed += hist_browser__fprintf_hierarchy_entry(browser,
2052 printed += hist_browser__fprintf_entry(browser, h, fp);
2055 nd = hists__filter_entries(rb_hierarchy_next(nd),
2062 static int hist_browser__dump(struct hist_browser *browser)
2068 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
2069 if (access(filename, F_OK))
2072 * XXX: Just an arbitrary lazy upper limit
2074 if (++browser->print_seq == 8192) {
2075 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
2080 fp = fopen(filename, "w");
2083 const char *err = str_error_r(errno, bf, sizeof(bf));
2084 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
2088 ++browser->print_seq;
2089 hist_browser__fprintf(browser, fp);
2091 ui_helpline__fpush("%s written!", filename);
2096 void hist_browser__init(struct hist_browser *browser,
2097 struct hists *hists)
2099 struct perf_hpp_fmt *fmt;
2101 browser->hists = hists;
2102 browser->b.refresh = hist_browser__refresh;
2103 browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
2104 browser->b.seek = ui_browser__hists_seek;
2105 browser->b.use_navkeypressed = true;
2106 browser->show_headers = symbol_conf.show_hist_headers;
2108 if (symbol_conf.report_hierarchy) {
2109 struct perf_hpp_list_node *fmt_node;
2111 /* count overhead columns (in the first node) */
2112 fmt_node = list_first_entry(&hists->hpp_formats,
2113 struct perf_hpp_list_node, list);
2114 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt)
2115 ++browser->b.columns;
2117 /* add a single column for whole hierarchy sort keys*/
2118 ++browser->b.columns;
2120 hists__for_each_format(hists, fmt)
2121 ++browser->b.columns;
2124 hists__reset_column_width(hists);
2127 struct hist_browser *hist_browser__new(struct hists *hists)
2129 struct hist_browser *browser = zalloc(sizeof(*browser));
2132 hist_browser__init(browser, hists);
2137 static struct hist_browser *
2138 perf_evsel_browser__new(struct perf_evsel *evsel,
2139 struct hist_browser_timer *hbt,
2140 struct perf_env *env)
2142 struct hist_browser *browser = hist_browser__new(evsel__hists(evsel));
2147 browser->title = perf_evsel_browser_title;
2152 void hist_browser__delete(struct hist_browser *browser)
2157 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
2159 return browser->he_selection;
2162 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
2164 return browser->he_selection->thread;
2167 /* Check whether the browser is for 'top' or 'report' */
2168 static inline bool is_report_browser(void *timer)
2170 return timer == NULL;
2173 static int perf_evsel_browser_title(struct hist_browser *browser,
2174 char *bf, size_t size)
2176 struct hist_browser_timer *hbt = browser->hbt;
2177 struct hists *hists = browser->hists;
2180 const struct dso *dso = hists->dso_filter;
2181 const struct thread *thread = hists->thread_filter;
2182 int socket_id = hists->socket_filter;
2183 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
2184 u64 nr_events = hists->stats.total_period;
2185 struct perf_evsel *evsel = hists_to_evsel(hists);
2186 const char *ev_name = perf_evsel__name(evsel);
2188 size_t buflen = sizeof(buf);
2189 char ref[30] = " show reference callgraph, ";
2190 bool enable_ref = false;
2192 if (symbol_conf.filter_relative) {
2193 nr_samples = hists->stats.nr_non_filtered_samples;
2194 nr_events = hists->stats.total_non_filtered_period;
2197 if (perf_evsel__is_group_event(evsel)) {
2198 struct perf_evsel *pos;
2200 perf_evsel__group_desc(evsel, buf, buflen);
2203 for_each_group_member(pos, evsel) {
2204 struct hists *pos_hists = evsel__hists(pos);
2206 if (symbol_conf.filter_relative) {
2207 nr_samples += pos_hists->stats.nr_non_filtered_samples;
2208 nr_events += pos_hists->stats.total_non_filtered_period;
2210 nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
2211 nr_events += pos_hists->stats.total_period;
2216 if (symbol_conf.show_ref_callgraph &&
2217 strstr(ev_name, "call-graph=no"))
2219 nr_samples = convert_unit(nr_samples, &unit);
2220 printed = scnprintf(bf, size,
2221 "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64,
2222 nr_samples, unit, ev_name, enable_ref ? ref : " ", nr_events);
2225 if (hists->uid_filter_str)
2226 printed += snprintf(bf + printed, size - printed,
2227 ", UID: %s", hists->uid_filter_str);
2229 if (hists__has(hists, thread)) {
2230 printed += scnprintf(bf + printed, size - printed,
2232 (thread->comm_set ? thread__comm_str(thread) : ""),
2235 printed += scnprintf(bf + printed, size - printed,
2237 (thread->comm_set ? thread__comm_str(thread) : ""));
2241 printed += scnprintf(bf + printed, size - printed,
2242 ", DSO: %s", dso->short_name);
2244 printed += scnprintf(bf + printed, size - printed,
2245 ", Processor Socket: %d", socket_id);
2246 if (!is_report_browser(hbt)) {
2247 struct perf_top *top = hbt->arg;
2250 printed += scnprintf(bf + printed, size - printed, " [z]");
2256 static inline void free_popup_options(char **options, int n)
2260 for (i = 0; i < n; ++i)
2265 * Only runtime switching of perf data file will make "input_name" point
2266 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
2267 * whether we need to call free() for current "input_name" during the switch.
2269 static bool is_input_name_malloced = false;
2271 static int switch_data_file(void)
2273 char *pwd, *options[32], *abs_path[32], *tmp;
2275 int nr_options = 0, choice = -1, ret = -1;
2276 struct dirent *dent;
2278 pwd = getenv("PWD");
2282 pwd_dir = opendir(pwd);
2286 memset(options, 0, sizeof(options));
2287 memset(options, 0, sizeof(abs_path));
2289 while ((dent = readdir(pwd_dir))) {
2290 char path[PATH_MAX];
2292 char *name = dent->d_name;
2295 if (!(dent->d_type == DT_REG))
2298 snprintf(path, sizeof(path), "%s/%s", pwd, name);
2300 file = fopen(path, "r");
2304 if (fread(&magic, 1, 8, file) < 8)
2305 goto close_file_and_continue;
2307 if (is_perf_magic(magic)) {
2308 options[nr_options] = strdup(name);
2309 if (!options[nr_options])
2310 goto close_file_and_continue;
2312 abs_path[nr_options] = strdup(path);
2313 if (!abs_path[nr_options]) {
2314 zfree(&options[nr_options]);
2315 ui__warning("Can't search all data files due to memory shortage.\n");
2323 close_file_and_continue:
2325 if (nr_options >= 32) {
2326 ui__warning("Too many perf data files in PWD!\n"
2327 "Only the first 32 files will be listed.\n");
2334 choice = ui__popup_menu(nr_options, options);
2335 if (choice < nr_options && choice >= 0) {
2336 tmp = strdup(abs_path[choice]);
2338 if (is_input_name_malloced)
2339 free((void *)input_name);
2341 is_input_name_malloced = true;
2344 ui__warning("Data switch failed due to memory shortage!\n");
2348 free_popup_options(options, nr_options);
2349 free_popup_options(abs_path, nr_options);
2353 struct popup_action {
2354 struct thread *thread;
2355 struct map_symbol ms;
2358 int (*fn)(struct hist_browser *browser, struct popup_action *act);
2362 do_annotate(struct hist_browser *browser, struct popup_action *act)
2364 struct perf_evsel *evsel;
2365 struct annotation *notes;
2366 struct hist_entry *he;
2369 if (!objdump_path && perf_env__lookup_objdump(browser->env))
2372 notes = symbol__annotation(act->ms.sym);
2376 evsel = hists_to_evsel(browser->hists);
2377 err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
2378 he = hist_browser__selected_entry(browser);
2380 * offer option to annotate the other branch source or target
2381 * (if they exists) when returning from annotate
2383 if ((err == 'q' || err == CTRL('c')) && he->branch_info)
2386 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
2388 ui_browser__handle_resize(&browser->b);
2393 add_annotate_opt(struct hist_browser *browser __maybe_unused,
2394 struct popup_action *act, char **optstr,
2395 struct map *map, struct symbol *sym)
2397 if (sym == NULL || map->dso->annotate_warned)
2400 if (asprintf(optstr, "Annotate %s", sym->name) < 0)
2405 act->fn = do_annotate;
2410 do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
2412 struct thread *thread = act->thread;
2414 if ((!hists__has(browser->hists, thread) &&
2415 !hists__has(browser->hists, comm)) || thread == NULL)
2418 if (browser->hists->thread_filter) {
2419 pstack__remove(browser->pstack, &browser->hists->thread_filter);
2420 perf_hpp__set_elide(HISTC_THREAD, false);
2421 thread__zput(browser->hists->thread_filter);
2424 if (hists__has(browser->hists, thread)) {
2425 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
2426 thread->comm_set ? thread__comm_str(thread) : "",
2429 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
2430 thread->comm_set ? thread__comm_str(thread) : "");
2433 browser->hists->thread_filter = thread__get(thread);
2434 perf_hpp__set_elide(HISTC_THREAD, false);
2435 pstack__push(browser->pstack, &browser->hists->thread_filter);
2438 hists__filter_by_thread(browser->hists);
2439 hist_browser__reset(browser);
2444 add_thread_opt(struct hist_browser *browser, struct popup_action *act,
2445 char **optstr, struct thread *thread)
2449 if ((!hists__has(browser->hists, thread) &&
2450 !hists__has(browser->hists, comm)) || thread == NULL)
2453 if (hists__has(browser->hists, thread)) {
2454 ret = asprintf(optstr, "Zoom %s %s(%d) thread",
2455 browser->hists->thread_filter ? "out of" : "into",
2456 thread->comm_set ? thread__comm_str(thread) : "",
2459 ret = asprintf(optstr, "Zoom %s %s thread",
2460 browser->hists->thread_filter ? "out of" : "into",
2461 thread->comm_set ? thread__comm_str(thread) : "");
2466 act->thread = thread;
2467 act->fn = do_zoom_thread;
2472 do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
2474 struct map *map = act->ms.map;
2476 if (!hists__has(browser->hists, dso) || map == NULL)
2479 if (browser->hists->dso_filter) {
2480 pstack__remove(browser->pstack, &browser->hists->dso_filter);
2481 perf_hpp__set_elide(HISTC_DSO, false);
2482 browser->hists->dso_filter = NULL;
2485 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
2486 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
2487 browser->hists->dso_filter = map->dso;
2488 perf_hpp__set_elide(HISTC_DSO, true);
2489 pstack__push(browser->pstack, &browser->hists->dso_filter);
2492 hists__filter_by_dso(browser->hists);
2493 hist_browser__reset(browser);
2498 add_dso_opt(struct hist_browser *browser, struct popup_action *act,
2499 char **optstr, struct map *map)
2501 if (!hists__has(browser->hists, dso) || map == NULL)
2504 if (asprintf(optstr, "Zoom %s %s DSO",
2505 browser->hists->dso_filter ? "out of" : "into",
2506 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
2510 act->fn = do_zoom_dso;
2515 do_browse_map(struct hist_browser *browser __maybe_unused,
2516 struct popup_action *act)
2518 map__browse(act->ms.map);
2523 add_map_opt(struct hist_browser *browser,
2524 struct popup_action *act, char **optstr, struct map *map)
2526 if (!hists__has(browser->hists, dso) || map == NULL)
2529 if (asprintf(optstr, "Browse map details") < 0)
2533 act->fn = do_browse_map;
2538 do_run_script(struct hist_browser *browser __maybe_unused,
2539 struct popup_action *act)
2541 char script_opt[64];
2542 memset(script_opt, 0, sizeof(script_opt));
2545 scnprintf(script_opt, sizeof(script_opt), " -c %s ",
2546 thread__comm_str(act->thread));
2547 } else if (act->ms.sym) {
2548 scnprintf(script_opt, sizeof(script_opt), " -S %s ",
2552 script_browse(script_opt);
2557 add_script_opt(struct hist_browser *browser __maybe_unused,
2558 struct popup_action *act, char **optstr,
2559 struct thread *thread, struct symbol *sym)
2562 if (asprintf(optstr, "Run scripts for samples of thread [%s]",
2563 thread__comm_str(thread)) < 0)
2566 if (asprintf(optstr, "Run scripts for samples of symbol [%s]",
2570 if (asprintf(optstr, "Run scripts for all samples") < 0)
2574 act->thread = thread;
2576 act->fn = do_run_script;
2581 do_switch_data(struct hist_browser *browser __maybe_unused,
2582 struct popup_action *act __maybe_unused)
2584 if (switch_data_file()) {
2585 ui__warning("Won't switch the data files due to\n"
2586 "no valid data file get selected!\n");
2590 return K_SWITCH_INPUT_DATA;
2594 add_switch_opt(struct hist_browser *browser,
2595 struct popup_action *act, char **optstr)
2597 if (!is_report_browser(browser->hbt))
2600 if (asprintf(optstr, "Switch to another data file in PWD") < 0)
2603 act->fn = do_switch_data;
2608 do_exit_browser(struct hist_browser *browser __maybe_unused,
2609 struct popup_action *act __maybe_unused)
2615 add_exit_opt(struct hist_browser *browser __maybe_unused,
2616 struct popup_action *act, char **optstr)
2618 if (asprintf(optstr, "Exit") < 0)
2621 act->fn = do_exit_browser;
2626 do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
2628 if (!hists__has(browser->hists, socket) || act->socket < 0)
2631 if (browser->hists->socket_filter > -1) {
2632 pstack__remove(browser->pstack, &browser->hists->socket_filter);
2633 browser->hists->socket_filter = -1;
2634 perf_hpp__set_elide(HISTC_SOCKET, false);
2636 browser->hists->socket_filter = act->socket;
2637 perf_hpp__set_elide(HISTC_SOCKET, true);
2638 pstack__push(browser->pstack, &browser->hists->socket_filter);
2641 hists__filter_by_socket(browser->hists);
2642 hist_browser__reset(browser);
2647 add_socket_opt(struct hist_browser *browser, struct popup_action *act,
2648 char **optstr, int socket_id)
2650 if (!hists__has(browser->hists, socket) || socket_id < 0)
2653 if (asprintf(optstr, "Zoom %s Processor Socket %d",
2654 (browser->hists->socket_filter > -1) ? "out of" : "into",
2658 act->socket = socket_id;
2659 act->fn = do_zoom_socket;
2663 static void hist_browser__update_nr_entries(struct hist_browser *hb)
2666 struct rb_node *nd = rb_first(&hb->hists->entries);
2668 if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
2669 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
2673 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2675 nd = rb_hierarchy_next(nd);
2678 hb->nr_non_filtered_entries = nr_entries;
2679 hb->nr_hierarchy_entries = nr_entries;
2682 static void hist_browser__update_percent_limit(struct hist_browser *hb,
2685 struct hist_entry *he;
2686 struct rb_node *nd = rb_first(&hb->hists->entries);
2687 u64 total = hists__total_period(hb->hists);
2688 u64 min_callchain_hits = total * (percent / 100);
2690 hb->min_pcnt = callchain_param.min_percent = percent;
2692 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2693 he = rb_entry(nd, struct hist_entry, rb_node);
2695 if (he->has_no_entry) {
2696 he->has_no_entry = false;
2700 if (!he->leaf || !symbol_conf.use_callchain)
2703 if (callchain_param.mode == CHAIN_GRAPH_REL) {
2704 total = he->stat.period;
2706 if (symbol_conf.cumulate_callchain)
2707 total = he->stat_acc->period;
2709 min_callchain_hits = total * (percent / 100);
2712 callchain_param.sort(&he->sorted_chain, he->callchain,
2713 min_callchain_hits, &callchain_param);
2716 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
2718 /* force to re-evaluate folding state of callchains */
2719 he->init_have_children = false;
2720 hist_entry__set_folding(he, hb, false);
2724 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
2725 const char *helpline,
2727 struct hist_browser_timer *hbt,
2729 struct perf_env *env)
2731 struct hists *hists = evsel__hists(evsel);
2732 struct hist_browser *browser = perf_evsel_browser__new(evsel, hbt, env);
2733 struct branch_info *bi;
2734 #define MAX_OPTIONS 16
2735 char *options[MAX_OPTIONS];
2736 struct popup_action actions[MAX_OPTIONS];
2740 int delay_secs = hbt ? hbt->refresh : 0;
2742 #define HIST_BROWSER_HELP_COMMON \
2743 "h/?/F1 Show this window\n" \
2745 "PGDN/SPACE Navigate\n" \
2746 "q/ESC/CTRL+C Exit browser\n\n" \
2747 "For multiple event sessions:\n\n" \
2748 "TAB/UNTAB Switch events\n\n" \
2749 "For symbolic views (--sort has sym):\n\n" \
2750 "ENTER Zoom into DSO/Threads & Annotate current symbol\n" \
2752 "a Annotate current symbol\n" \
2753 "C Collapse all callchains\n" \
2754 "d Zoom into current DSO\n" \
2755 "E Expand all callchains\n" \
2756 "F Toggle percentage of filtered entries\n" \
2757 "H Display column headers\n" \
2758 "L Change percent limit\n" \
2759 "m Display context menu\n" \
2760 "S Zoom into current Processor Socket\n" \
2762 /* help messages are sorted by lexical order of the hotkey */
2763 const char report_help[] = HIST_BROWSER_HELP_COMMON
2764 "i Show header information\n"
2765 "P Print histograms to perf.hist.N\n"
2766 "r Run available scripts\n"
2767 "s Switch to another data file in PWD\n"
2768 "t Zoom into current Thread\n"
2769 "V Verbose (DSO names in callchains, etc)\n"
2770 "/ Filter symbol by name";
2771 const char top_help[] = HIST_BROWSER_HELP_COMMON
2772 "P Print histograms to perf.hist.N\n"
2773 "t Zoom into current Thread\n"
2774 "V Verbose (DSO names in callchains, etc)\n"
2775 "z Toggle zeroing of samples\n"
2776 "f Enable/Disable events\n"
2777 "/ Filter symbol by name";
2779 if (browser == NULL)
2782 /* reset abort key so that it can get Ctrl-C as a key */
2784 SLang_init_tty(0, 0, 0);
2787 browser->min_pcnt = min_pcnt;
2788 hist_browser__update_nr_entries(browser);
2790 browser->pstack = pstack__new(3);
2791 if (browser->pstack == NULL)
2794 ui_helpline__push(helpline);
2796 memset(options, 0, sizeof(options));
2797 memset(actions, 0, sizeof(actions));
2799 if (symbol_conf.col_width_list_str)
2800 perf_hpp__set_user_width(symbol_conf.col_width_list_str);
2803 struct thread *thread = NULL;
2804 struct map *map = NULL;
2810 key = hist_browser__run(browser, helpline);
2812 if (browser->he_selection != NULL) {
2813 thread = hist_browser__selected_thread(browser);
2814 map = browser->selection->map;
2815 socked_id = browser->he_selection->socket;
2823 * Exit the browser, let hists__browser_tree
2824 * go to the next or previous
2826 goto out_free_stack;
2828 if (!hists__has(hists, sym)) {
2829 ui_browser__warning(&browser->b, delay_secs * 2,
2830 "Annotation is only available for symbolic views, "
2831 "include \"sym*\" in --sort to use it.");
2835 if (browser->selection == NULL ||
2836 browser->selection->sym == NULL ||
2837 browser->selection->map->dso->annotate_warned)
2840 actions->ms.map = browser->selection->map;
2841 actions->ms.sym = browser->selection->sym;
2842 do_annotate(browser, actions);
2845 hist_browser__dump(browser);
2848 actions->ms.map = map;
2849 do_zoom_dso(browser, actions);
2852 verbose = (verbose + 1) % 4;
2853 browser->show_dso = verbose > 0;
2854 ui_helpline__fpush("Verbosity level set to %d\n",
2858 actions->thread = thread;
2859 do_zoom_thread(browser, actions);
2862 actions->socket = socked_id;
2863 do_zoom_socket(browser, actions);
2866 if (ui_browser__input_window("Symbol to show",
2867 "Please enter the name of symbol you want to see.\n"
2868 "To remove the filter later, press / + ENTER.",
2869 buf, "ENTER: OK, ESC: Cancel",
2870 delay_secs * 2) == K_ENTER) {
2871 hists->symbol_filter_str = *buf ? buf : NULL;
2872 hists__filter_by_symbol(hists);
2873 hist_browser__reset(browser);
2877 if (is_report_browser(hbt)) {
2878 actions->thread = NULL;
2879 actions->ms.sym = NULL;
2880 do_run_script(browser, actions);
2884 if (is_report_browser(hbt)) {
2885 key = do_switch_data(browser, actions);
2886 if (key == K_SWITCH_INPUT_DATA)
2887 goto out_free_stack;
2891 /* env->arch is NULL for live-mode (i.e. perf top) */
2893 tui__header_window(env);
2896 symbol_conf.filter_relative ^= 1;
2899 if (!is_report_browser(hbt)) {
2900 struct perf_top *top = hbt->arg;
2902 top->zero = !top->zero;
2906 if (ui_browser__input_window("Percent Limit",
2907 "Please enter the value you want to hide entries under that percent.",
2908 buf, "ENTER: OK, ESC: Cancel",
2909 delay_secs * 2) == K_ENTER) {
2911 double new_percent = strtod(buf, &end);
2913 if (new_percent < 0 || new_percent > 100) {
2914 ui_browser__warning(&browser->b, delay_secs * 2,
2915 "Invalid percent: %.2f", new_percent);
2919 hist_browser__update_percent_limit(browser, new_percent);
2920 hist_browser__reset(browser);
2926 ui_browser__help_window(&browser->b,
2927 is_report_browser(hbt) ? report_help : top_help);
2938 if (pstack__empty(browser->pstack)) {
2940 * Go back to the perf_evsel_menu__run or other user
2943 goto out_free_stack;
2946 ui_browser__dialog_yesno(&browser->b,
2947 "Do you really want to exit?"))
2948 goto out_free_stack;
2952 top = pstack__peek(browser->pstack);
2953 if (top == &browser->hists->dso_filter) {
2955 * No need to set actions->dso here since
2956 * it's just to remove the current filter.
2957 * Ditto for thread below.
2959 do_zoom_dso(browser, actions);
2960 } else if (top == &browser->hists->thread_filter) {
2961 do_zoom_thread(browser, actions);
2962 } else if (top == &browser->hists->socket_filter) {
2963 do_zoom_socket(browser, actions);
2969 goto out_free_stack;
2971 if (!is_report_browser(hbt)) {
2972 struct perf_top *top = hbt->arg;
2974 perf_evlist__toggle_enable(top->evlist);
2976 * No need to refresh, resort/decay histogram
2977 * entries if we are not collecting samples:
2979 if (top->evlist->enabled) {
2980 helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
2981 hbt->refresh = delay_secs;
2983 helpline = "Press 'f' again to re-enable the events";
2990 helpline = "Press '?' for help on key bindings";
2994 if (!hists__has(hists, sym) || browser->selection == NULL)
2995 goto skip_annotation;
2997 if (sort__mode == SORT_MODE__BRANCH) {
2998 bi = browser->he_selection->branch_info;
3001 goto skip_annotation;
3003 nr_options += add_annotate_opt(browser,
3004 &actions[nr_options],
3005 &options[nr_options],
3008 if (bi->to.sym != bi->from.sym)
3009 nr_options += add_annotate_opt(browser,
3010 &actions[nr_options],
3011 &options[nr_options],
3015 nr_options += add_annotate_opt(browser,
3016 &actions[nr_options],
3017 &options[nr_options],
3018 browser->selection->map,
3019 browser->selection->sym);
3022 nr_options += add_thread_opt(browser, &actions[nr_options],
3023 &options[nr_options], thread);
3024 nr_options += add_dso_opt(browser, &actions[nr_options],
3025 &options[nr_options], map);
3026 nr_options += add_map_opt(browser, &actions[nr_options],
3027 &options[nr_options],
3028 browser->selection ?
3029 browser->selection->map : NULL);
3030 nr_options += add_socket_opt(browser, &actions[nr_options],
3031 &options[nr_options],
3033 /* perf script support */
3034 if (!is_report_browser(hbt))
3035 goto skip_scripting;
3037 if (browser->he_selection) {
3038 if (hists__has(hists, thread) && thread) {
3039 nr_options += add_script_opt(browser,
3040 &actions[nr_options],
3041 &options[nr_options],
3045 * Note that browser->selection != NULL
3046 * when browser->he_selection is not NULL,
3047 * so we don't need to check browser->selection
3048 * before fetching browser->selection->sym like what
3049 * we do before fetching browser->selection->map.
3051 * See hist_browser__show_entry.
3053 if (hists__has(hists, sym) && browser->selection->sym) {
3054 nr_options += add_script_opt(browser,
3055 &actions[nr_options],
3056 &options[nr_options],
3057 NULL, browser->selection->sym);
3060 nr_options += add_script_opt(browser, &actions[nr_options],
3061 &options[nr_options], NULL, NULL);
3062 nr_options += add_switch_opt(browser, &actions[nr_options],
3063 &options[nr_options]);
3065 nr_options += add_exit_opt(browser, &actions[nr_options],
3066 &options[nr_options]);
3069 struct popup_action *act;
3071 choice = ui__popup_menu(nr_options, options);
3072 if (choice == -1 || choice >= nr_options)
3075 act = &actions[choice];
3076 key = act->fn(browser, act);
3079 if (key == K_SWITCH_INPUT_DATA)
3083 pstack__delete(browser->pstack);
3085 hist_browser__delete(browser);
3086 free_popup_options(options, MAX_OPTIONS);
3090 struct perf_evsel_menu {
3091 struct ui_browser b;
3092 struct perf_evsel *selection;
3093 bool lost_events, lost_events_warned;
3095 struct perf_env *env;
3098 static void perf_evsel_menu__write(struct ui_browser *browser,
3099 void *entry, int row)
3101 struct perf_evsel_menu *menu = container_of(browser,
3102 struct perf_evsel_menu, b);
3103 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
3104 struct hists *hists = evsel__hists(evsel);
3105 bool current_entry = ui_browser__is_current_entry(browser, row);
3106 unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
3107 const char *ev_name = perf_evsel__name(evsel);
3109 const char *warn = " ";
3112 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
3113 HE_COLORSET_NORMAL);
3115 if (perf_evsel__is_group_event(evsel)) {
3116 struct perf_evsel *pos;
3118 ev_name = perf_evsel__group_name(evsel);
3120 for_each_group_member(pos, evsel) {
3121 struct hists *pos_hists = evsel__hists(pos);
3122 nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
3126 nr_events = convert_unit(nr_events, &unit);
3127 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
3128 unit, unit == ' ' ? "" : " ", ev_name);
3129 ui_browser__printf(browser, "%s", bf);
3131 nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
3132 if (nr_events != 0) {
3133 menu->lost_events = true;
3135 ui_browser__set_color(browser, HE_COLORSET_TOP);
3136 nr_events = convert_unit(nr_events, &unit);
3137 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
3138 nr_events, unit, unit == ' ' ? "" : " ");
3142 ui_browser__write_nstring(browser, warn, browser->width - printed);
3145 menu->selection = evsel;
3148 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
3149 int nr_events, const char *help,
3150 struct hist_browser_timer *hbt)
3152 struct perf_evlist *evlist = menu->b.priv;
3153 struct perf_evsel *pos;
3154 const char *title = "Available samples";
3155 int delay_secs = hbt ? hbt->refresh : 0;
3158 if (ui_browser__show(&menu->b, title,
3159 "ESC: exit, ENTER|->: Browse histograms") < 0)
3163 key = ui_browser__run(&menu->b, delay_secs);
3167 hbt->timer(hbt->arg);
3169 if (!menu->lost_events_warned && menu->lost_events) {
3170 ui_browser__warn_lost_events(&menu->b);
3171 menu->lost_events_warned = true;
3176 if (!menu->selection)
3178 pos = menu->selection;
3180 perf_evlist__set_selected(evlist, pos);
3182 * Give the calling tool a chance to populate the non
3183 * default evsel resorted hists tree.
3186 hbt->timer(hbt->arg);
3187 key = perf_evsel__hists_browse(pos, nr_events, help,
3191 ui_browser__show_title(&menu->b, title);
3194 if (pos->node.next == &evlist->entries)
3195 pos = perf_evlist__first(evlist);
3197 pos = perf_evsel__next(pos);
3200 if (pos->node.prev == &evlist->entries)
3201 pos = perf_evlist__last(evlist);
3203 pos = perf_evsel__prev(pos);
3205 case K_SWITCH_INPUT_DATA:
3216 if (!ui_browser__dialog_yesno(&menu->b,
3217 "Do you really want to exit?"))
3229 ui_browser__hide(&menu->b);
3233 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
3236 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
3238 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
3244 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
3245 int nr_entries, const char *help,
3246 struct hist_browser_timer *hbt,
3248 struct perf_env *env)
3250 struct perf_evsel *pos;
3251 struct perf_evsel_menu menu = {
3253 .entries = &evlist->entries,
3254 .refresh = ui_browser__list_head_refresh,
3255 .seek = ui_browser__list_head_seek,
3256 .write = perf_evsel_menu__write,
3257 .filter = filter_group_entries,
3258 .nr_entries = nr_entries,
3261 .min_pcnt = min_pcnt,
3265 ui_helpline__push("Press ESC to exit");
3267 evlist__for_each_entry(evlist, pos) {
3268 const char *ev_name = perf_evsel__name(pos);
3269 size_t line_len = strlen(ev_name) + 7;
3271 if (menu.b.width < line_len)
3272 menu.b.width = line_len;
3275 return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
3278 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
3279 struct hist_browser_timer *hbt,
3281 struct perf_env *env)
3283 int nr_entries = evlist->nr_entries;
3286 if (nr_entries == 1) {
3287 struct perf_evsel *first = perf_evlist__first(evlist);
3289 return perf_evsel__hists_browse(first, nr_entries, help,
3290 false, hbt, min_pcnt,
3294 if (symbol_conf.event_group) {
3295 struct perf_evsel *pos;
3298 evlist__for_each_entry(evlist, pos) {
3299 if (perf_evsel__is_group_leader(pos))
3303 if (nr_entries == 1)
3307 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
3308 hbt, min_pcnt, env);