1 // SPDX-License-Identifier: GPL-2.0
8 #include <linux/rbtree.h>
9 #include <linux/string.h>
10 #include <sys/ttydefaults.h>
11 #include <linux/time64.h>
12 #include <linux/zalloc.h>
14 #include "../../util/debug.h"
15 #include "../../util/dso.h"
16 #include "../../util/callchain.h"
17 #include "../../util/evsel.h"
18 #include "../../util/evlist.h"
19 #include "../../util/header.h"
20 #include "../../util/hist.h"
21 #include "../../util/machine.h"
22 #include "../../util/map.h"
23 #include "../../util/maps.h"
24 #include "../../util/symbol.h"
25 #include "../../util/map_symbol.h"
26 #include "../../util/branch.h"
27 #include "../../util/pstack.h"
28 #include "../../util/sort.h"
29 #include "../../util/top.h"
30 #include "../../util/thread.h"
31 #include "../../util/block-info.h"
32 #include "../../arch/common.h"
33 #include "../../perf.h"
35 #include "../browsers/hists.h"
36 #include "../helpline.h"
44 #include "time-utils.h"
46 #include <linux/ctype.h>
48 extern void hist_browser__init_hpp(void);
50 static int hists_browser__scnprintf_title(struct hist_browser *browser, char *bf, size_t size);
51 static void hist_browser__update_nr_entries(struct hist_browser *hb);
53 static struct rb_node *hists__filter_entries(struct rb_node *nd,
56 static bool hist_browser__has_filter(struct hist_browser *hb)
58 return hists__has_filter(hb->hists) || hb->min_pcnt || symbol_conf.has_filter || hb->c2c_filter;
61 static int hist_browser__get_folding(struct hist_browser *browser)
64 struct hists *hists = browser->hists;
65 int unfolded_rows = 0;
67 for (nd = rb_first_cached(&hists->entries);
68 (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
69 nd = rb_hierarchy_next(nd)) {
70 struct hist_entry *he =
71 rb_entry(nd, struct hist_entry, rb_node);
73 if (he->leaf && he->unfolded)
74 unfolded_rows += he->nr_rows;
79 static void hist_browser__set_title_space(struct hist_browser *hb)
81 struct ui_browser *browser = &hb->b;
82 struct hists *hists = hb->hists;
83 struct perf_hpp_list *hpp_list = hists->hpp_list;
85 browser->extra_title_lines = hb->show_headers ? hpp_list->nr_header_lines : 0;
88 static u32 hist_browser__nr_entries(struct hist_browser *hb)
92 if (symbol_conf.report_hierarchy)
93 nr_entries = hb->nr_hierarchy_entries;
94 else if (hist_browser__has_filter(hb))
95 nr_entries = hb->nr_non_filtered_entries;
97 nr_entries = hb->hists->nr_entries;
99 hb->nr_callchain_rows = hist_browser__get_folding(hb);
100 return nr_entries + hb->nr_callchain_rows;
103 static void hist_browser__update_rows(struct hist_browser *hb)
105 struct ui_browser *browser = &hb->b;
106 struct hists *hists = hb->hists;
107 struct perf_hpp_list *hpp_list = hists->hpp_list;
110 if (!hb->show_headers) {
111 browser->rows += browser->extra_title_lines;
112 browser->extra_title_lines = 0;
116 browser->extra_title_lines = hpp_list->nr_header_lines;
117 browser->rows -= browser->extra_title_lines;
119 * Verify if we were at the last line and that line isn't
120 * visible because we now show the header line(s).
122 index_row = browser->index - browser->top_idx;
123 if (index_row >= browser->rows)
124 browser->index -= index_row - browser->rows + 1;
127 static void hist_browser__refresh_dimensions(struct ui_browser *browser)
129 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
131 /* 3 == +/- toggle symbol before actual hist_entry rendering */
132 browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]"));
134 * FIXME: Just keeping existing behaviour, but this really should be
135 * before updating browser->width, as it will invalidate the
136 * calculation above. Fix this and the fallout in another
139 ui_browser__refresh_dimensions(browser);
142 static void hist_browser__reset(struct hist_browser *browser)
145 * The hists__remove_entry_filter() already folds non-filtered
146 * entries so we can assume it has 0 callchain rows.
148 browser->nr_callchain_rows = 0;
150 hist_browser__update_nr_entries(browser);
151 browser->b.nr_entries = hist_browser__nr_entries(browser);
152 hist_browser__refresh_dimensions(&browser->b);
153 ui_browser__reset_index(&browser->b);
156 static char tree__folded_sign(bool unfolded)
158 return unfolded ? '-' : '+';
161 static char hist_entry__folded(const struct hist_entry *he)
163 return he->has_children ? tree__folded_sign(he->unfolded) : ' ';
166 static char callchain_list__folded(const struct callchain_list *cl)
168 return cl->has_children ? tree__folded_sign(cl->unfolded) : ' ';
171 static void callchain_list__set_folding(struct callchain_list *cl, bool unfold)
173 cl->unfolded = unfold ? cl->has_children : false;
176 static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
181 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
182 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
183 struct callchain_list *chain;
184 char folded_sign = ' '; /* No children */
186 list_for_each_entry(chain, &child->val, list) {
189 /* We need this because we may not have children */
190 folded_sign = callchain_list__folded(chain);
191 if (folded_sign == '+')
195 if (folded_sign == '-') /* Have children and they're unfolded */
196 n += callchain_node__count_rows_rb_tree(child);
202 static int callchain_node__count_flat_rows(struct callchain_node *node)
204 struct callchain_list *chain;
205 char folded_sign = 0;
208 list_for_each_entry(chain, &node->parent_val, list) {
210 /* only check first chain list entry */
211 folded_sign = callchain_list__folded(chain);
212 if (folded_sign == '+')
218 list_for_each_entry(chain, &node->val, list) {
220 /* node->parent_val list might be empty */
221 folded_sign = callchain_list__folded(chain);
222 if (folded_sign == '+')
231 static int callchain_node__count_folded_rows(struct callchain_node *node __maybe_unused)
236 static int callchain_node__count_rows(struct callchain_node *node)
238 struct callchain_list *chain;
239 bool unfolded = false;
242 if (callchain_param.mode == CHAIN_FLAT)
243 return callchain_node__count_flat_rows(node);
244 else if (callchain_param.mode == CHAIN_FOLDED)
245 return callchain_node__count_folded_rows(node);
247 list_for_each_entry(chain, &node->val, list) {
250 unfolded = chain->unfolded;
254 n += callchain_node__count_rows_rb_tree(node);
259 static int callchain__count_rows(struct rb_root *chain)
264 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
265 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
266 n += callchain_node__count_rows(node);
272 static int hierarchy_count_rows(struct hist_browser *hb, struct hist_entry *he,
273 bool include_children)
276 struct rb_node *node;
277 struct hist_entry *child;
280 return callchain__count_rows(&he->sorted_chain);
282 if (he->has_no_entry)
285 node = rb_first_cached(&he->hroot_out);
289 child = rb_entry(node, struct hist_entry, rb_node);
290 percent = hist_entry__get_percent_limit(child);
292 if (!child->filtered && percent >= hb->min_pcnt) {
295 if (include_children && child->unfolded)
296 count += hierarchy_count_rows(hb, child, true);
299 node = rb_next(node);
304 static bool hist_entry__toggle_fold(struct hist_entry *he)
309 if (!he->has_children)
312 he->unfolded = !he->unfolded;
316 static bool callchain_list__toggle_fold(struct callchain_list *cl)
321 if (!cl->has_children)
324 cl->unfolded = !cl->unfolded;
328 static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
330 struct rb_node *nd = rb_first(&node->rb_root);
332 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
333 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
334 struct callchain_list *chain;
337 list_for_each_entry(chain, &child->val, list) {
340 chain->has_children = chain->list.next != &child->val ||
341 !RB_EMPTY_ROOT(&child->rb_root);
343 chain->has_children = chain->list.next == &child->val &&
344 !RB_EMPTY_ROOT(&child->rb_root);
347 callchain_node__init_have_children_rb_tree(child);
351 static void callchain_node__init_have_children(struct callchain_node *node,
354 struct callchain_list *chain;
356 chain = list_entry(node->val.next, struct callchain_list, list);
357 chain->has_children = has_sibling;
359 if (!list_empty(&node->val)) {
360 chain = list_entry(node->val.prev, struct callchain_list, list);
361 chain->has_children = !RB_EMPTY_ROOT(&node->rb_root);
364 callchain_node__init_have_children_rb_tree(node);
367 static void callchain__init_have_children(struct rb_root *root)
369 struct rb_node *nd = rb_first(root);
370 bool has_sibling = nd && rb_next(nd);
372 for (nd = rb_first(root); nd; nd = rb_next(nd)) {
373 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
374 callchain_node__init_have_children(node, has_sibling);
375 if (callchain_param.mode == CHAIN_FLAT ||
376 callchain_param.mode == CHAIN_FOLDED)
377 callchain_node__make_parent_list(node);
381 static void hist_entry__init_have_children(struct hist_entry *he)
383 if (he->init_have_children)
387 he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
388 callchain__init_have_children(&he->sorted_chain);
390 he->has_children = !RB_EMPTY_ROOT(&he->hroot_out.rb_root);
393 he->init_have_children = true;
396 static bool hist_browser__selection_has_children(struct hist_browser *browser)
398 struct hist_entry *he = browser->he_selection;
399 struct map_symbol *ms = browser->selection;
405 return he->has_children;
407 return container_of(ms, struct callchain_list, ms)->has_children;
410 static bool hist_browser__he_selection_unfolded(struct hist_browser *browser)
412 return browser->he_selection ? browser->he_selection->unfolded : false;
415 static bool hist_browser__selection_unfolded(struct hist_browser *browser)
417 struct hist_entry *he = browser->he_selection;
418 struct map_symbol *ms = browser->selection;
426 return container_of(ms, struct callchain_list, ms)->unfolded;
429 static char *hist_browser__selection_sym_name(struct hist_browser *browser, char *bf, size_t size)
431 struct hist_entry *he = browser->he_selection;
432 struct map_symbol *ms = browser->selection;
433 struct callchain_list *callchain_entry;
439 hist_entry__sym_snprintf(he, bf, size, 0);
440 return bf + 4; // skip the level, e.g. '[k] '
443 callchain_entry = container_of(ms, struct callchain_list, ms);
444 return callchain_list__sym_name(callchain_entry, bf, size, browser->show_dso);
447 static bool hist_browser__toggle_fold(struct hist_browser *browser)
449 struct hist_entry *he = browser->he_selection;
450 struct map_symbol *ms = browser->selection;
451 struct callchain_list *cl = container_of(ms, struct callchain_list, ms);
458 has_children = hist_entry__toggle_fold(he);
460 has_children = callchain_list__toggle_fold(cl);
465 hist_entry__init_have_children(he);
466 browser->b.nr_entries -= he->nr_rows;
469 browser->nr_callchain_rows -= he->nr_rows;
471 browser->nr_hierarchy_entries -= he->nr_rows;
473 if (symbol_conf.report_hierarchy)
474 child_rows = hierarchy_count_rows(browser, he, true);
478 he->nr_rows = callchain__count_rows(
481 he->nr_rows = hierarchy_count_rows(browser, he, false);
483 /* account grand children */
484 if (symbol_conf.report_hierarchy)
485 browser->b.nr_entries += child_rows - he->nr_rows;
487 if (!he->leaf && he->nr_rows == 0) {
488 he->has_no_entry = true;
492 if (symbol_conf.report_hierarchy)
493 browser->b.nr_entries -= child_rows - he->nr_rows;
495 if (he->has_no_entry)
496 he->has_no_entry = false;
501 browser->b.nr_entries += he->nr_rows;
504 browser->nr_callchain_rows += he->nr_rows;
506 browser->nr_hierarchy_entries += he->nr_rows;
511 /* If it doesn't have children, no toggling performed */
515 static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
520 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
521 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
522 struct callchain_list *chain;
523 bool has_children = false;
525 list_for_each_entry(chain, &child->val, list) {
527 callchain_list__set_folding(chain, unfold);
528 has_children = chain->has_children;
532 n += callchain_node__set_folding_rb_tree(child, unfold);
538 static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
540 struct callchain_list *chain;
541 bool has_children = false;
544 list_for_each_entry(chain, &node->val, list) {
546 callchain_list__set_folding(chain, unfold);
547 has_children = chain->has_children;
551 n += callchain_node__set_folding_rb_tree(node, unfold);
556 static int callchain__set_folding(struct rb_root *chain, bool unfold)
561 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
562 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
563 n += callchain_node__set_folding(node, unfold);
569 static int hierarchy_set_folding(struct hist_browser *hb, struct hist_entry *he,
570 bool unfold __maybe_unused)
574 struct hist_entry *child;
577 for (nd = rb_first_cached(&he->hroot_out); nd; nd = rb_next(nd)) {
578 child = rb_entry(nd, struct hist_entry, rb_node);
579 percent = hist_entry__get_percent_limit(child);
580 if (!child->filtered && percent >= hb->min_pcnt)
587 static void __hist_entry__set_folding(struct hist_entry *he,
588 struct hist_browser *hb, bool unfold)
590 hist_entry__init_have_children(he);
591 he->unfolded = unfold ? he->has_children : false;
593 if (he->has_children) {
597 n = callchain__set_folding(&he->sorted_chain, unfold);
599 n = hierarchy_set_folding(hb, he, unfold);
601 he->nr_rows = unfold ? n : 0;
606 static void hist_entry__set_folding(struct hist_entry *he,
607 struct hist_browser *browser, bool unfold)
611 percent = hist_entry__get_percent_limit(he);
612 if (he->filtered || percent < browser->min_pcnt)
615 __hist_entry__set_folding(he, browser, unfold);
617 if (!he->depth || unfold)
618 browser->nr_hierarchy_entries++;
620 browser->nr_callchain_rows += he->nr_rows;
621 else if (unfold && !hist_entry__has_hierarchy_children(he, browser->min_pcnt)) {
622 browser->nr_hierarchy_entries++;
623 he->has_no_entry = true;
626 he->has_no_entry = false;
630 __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
633 struct hist_entry *he;
635 nd = rb_first_cached(&browser->hists->entries);
637 he = rb_entry(nd, struct hist_entry, rb_node);
639 /* set folding state even if it's currently folded */
640 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
642 hist_entry__set_folding(he, browser, unfold);
646 static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
648 browser->nr_hierarchy_entries = 0;
649 browser->nr_callchain_rows = 0;
650 __hist_browser__set_folding(browser, unfold);
652 browser->b.nr_entries = hist_browser__nr_entries(browser);
653 /* Go to the start, we may be way after valid entries after a collapse */
654 ui_browser__reset_index(&browser->b);
657 static void hist_browser__set_folding_selected(struct hist_browser *browser, bool unfold)
659 if (!browser->he_selection)
662 hist_entry__set_folding(browser->he_selection, browser, unfold);
663 browser->b.nr_entries = hist_browser__nr_entries(browser);
666 static void ui_browser__warn_lost_events(struct ui_browser *browser)
668 ui_browser__warning(browser, 4,
669 "Events are being lost, check IO/CPU overload!\n\n"
670 "You may want to run 'perf' using a RT scheduler policy:\n\n"
671 " perf top -r 80\n\n"
672 "Or reduce the sampling frequency.");
675 static int hist_browser__title(struct hist_browser *browser, char *bf, size_t size)
677 return browser->title ? browser->title(browser, bf, size) : 0;
680 static int hist_browser__handle_hotkey(struct hist_browser *browser, bool warn_lost_event, char *title, size_t size, int key)
684 struct hist_browser_timer *hbt = browser->hbt;
685 struct evsel *evsel = hists_to_evsel(browser->hists);
691 hbt->timer(hbt->arg);
693 if (hist_browser__has_filter(browser) || symbol_conf.report_hierarchy)
694 hist_browser__update_nr_entries(browser);
696 nr_entries = hist_browser__nr_entries(browser);
697 ui_browser__update_nr_entries(&browser->b, nr_entries);
699 if (warn_lost_event &&
700 (evsel->evlist->stats.nr_lost_warned !=
701 evsel->evlist->stats.nr_events[PERF_RECORD_LOST])) {
702 evsel->evlist->stats.nr_lost_warned =
703 evsel->evlist->stats.nr_events[PERF_RECORD_LOST];
704 ui_browser__warn_lost_events(&browser->b);
707 hist_browser__title(browser, title, size);
708 ui_browser__show_title(&browser->b, title);
711 case 'D': { /* Debug */
712 struct hist_entry *h = rb_entry(browser->b.top, struct hist_entry, rb_node);
716 ui_helpline__fpush("%d: nr_ent=(%d,%d), etl: %d, rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
717 seq++, browser->b.nr_entries, browser->hists->nr_entries,
718 browser->b.extra_title_lines, browser->b.rows,
719 browser->b.index, browser->b.top_idx, h->row_offset, h->nr_rows);
723 /* Collapse the whole world. */
724 hist_browser__set_folding(browser, false);
727 /* Collapse the selected entry. */
728 hist_browser__set_folding_selected(browser, false);
731 /* Expand the whole world. */
732 hist_browser__set_folding(browser, true);
735 /* Expand the selected entry. */
736 hist_browser__set_folding_selected(browser, !hist_browser__he_selection_unfolded(browser));
739 browser->show_headers = !browser->show_headers;
740 hist_browser__update_rows(browser);
743 if (hist_browser__toggle_fold(browser))
753 int hist_browser__run(struct hist_browser *browser, const char *help,
754 bool warn_lost_event, int key)
757 struct hist_browser_timer *hbt = browser->hbt;
758 int delay_secs = hbt ? hbt->refresh : 0;
760 browser->b.entries = &browser->hists->entries;
761 browser->b.nr_entries = hist_browser__nr_entries(browser);
763 hist_browser__title(browser, title, sizeof(title));
765 if (ui_browser__show(&browser->b, title, "%s", help) < 0)
768 if (key && hist_browser__handle_hotkey(browser, warn_lost_event, title, sizeof(title), key))
772 key = ui_browser__run(&browser->b, delay_secs);
774 if (hist_browser__handle_hotkey(browser, warn_lost_event, title, sizeof(title), key))
778 ui_browser__hide(&browser->b);
782 struct callchain_print_arg {
783 /* for hists browser */
785 bool is_current_entry;
792 typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
793 struct callchain_list *chain,
794 const char *str, int offset,
796 struct callchain_print_arg *arg);
798 static void hist_browser__show_callchain_entry(struct hist_browser *browser,
799 struct callchain_list *chain,
800 const char *str, int offset,
802 struct callchain_print_arg *arg)
805 char folded_sign = callchain_list__folded(chain);
806 bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
808 color = HE_COLORSET_NORMAL;
809 width = browser->b.width - (offset + 2);
810 if (ui_browser__is_current_entry(&browser->b, row)) {
811 browser->selection = &chain->ms;
812 color = HE_COLORSET_SELECTED;
813 arg->is_current_entry = true;
816 ui_browser__set_color(&browser->b, color);
817 ui_browser__gotorc(&browser->b, row, 0);
818 ui_browser__write_nstring(&browser->b, " ", offset);
819 ui_browser__printf(&browser->b, "%c", folded_sign);
820 ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
821 ui_browser__write_nstring(&browser->b, str, width);
824 static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
825 struct callchain_list *chain,
826 const char *str, int offset,
827 unsigned short row __maybe_unused,
828 struct callchain_print_arg *arg)
830 char folded_sign = callchain_list__folded(chain);
832 arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
836 typedef bool (*check_output_full_fn)(struct hist_browser *browser,
839 static bool hist_browser__check_output_full(struct hist_browser *browser,
842 return browser->b.rows == row;
845 static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
846 unsigned short row __maybe_unused)
851 #define LEVEL_OFFSET_STEP 3
853 static int hist_browser__show_callchain_list(struct hist_browser *browser,
854 struct callchain_node *node,
855 struct callchain_list *chain,
856 unsigned short row, u64 total,
857 bool need_percent, int offset,
858 print_callchain_entry_fn print,
859 struct callchain_print_arg *arg)
861 char bf[1024], *alloc_str;
862 char buf[64], *alloc_str2;
866 if (arg->row_offset != 0) {
874 str = callchain_list__sym_name(chain, bf, sizeof(bf),
877 if (symbol_conf.show_branchflag_count) {
878 callchain_list_counts__printf_value(chain, NULL,
881 if (asprintf(&alloc_str2, "%s%s", str, buf) < 0)
882 str = "Not enough memory!";
888 callchain_node__scnprintf_value(node, buf, sizeof(buf),
891 if (asprintf(&alloc_str, "%s %s", buf, str) < 0)
892 str = "Not enough memory!";
897 print(browser, chain, str, offset, row, arg);
904 static bool check_percent_display(struct rb_node *node, u64 parent_total)
906 struct callchain_node *child;
914 child = rb_entry(node, struct callchain_node, rb_node);
915 return callchain_cumul_hits(child) != parent_total;
918 static int hist_browser__show_callchain_flat(struct hist_browser *browser,
919 struct rb_root *root,
920 unsigned short row, u64 total,
922 print_callchain_entry_fn print,
923 struct callchain_print_arg *arg,
924 check_output_full_fn is_output_full)
926 struct rb_node *node;
927 int first_row = row, offset = LEVEL_OFFSET_STEP;
930 node = rb_first(root);
931 need_percent = check_percent_display(node, parent_total);
934 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
935 struct rb_node *next = rb_next(node);
936 struct callchain_list *chain;
937 char folded_sign = ' ';
939 int extra_offset = 0;
941 list_for_each_entry(chain, &child->parent_val, list) {
942 bool was_first = first;
946 else if (need_percent)
947 extra_offset = LEVEL_OFFSET_STEP;
949 folded_sign = callchain_list__folded(chain);
951 row += hist_browser__show_callchain_list(browser, child,
953 was_first && need_percent,
954 offset + extra_offset,
957 if (is_output_full(browser, row))
960 if (folded_sign == '+')
964 list_for_each_entry(chain, &child->val, list) {
965 bool was_first = first;
969 else if (need_percent)
970 extra_offset = LEVEL_OFFSET_STEP;
972 folded_sign = callchain_list__folded(chain);
974 row += hist_browser__show_callchain_list(browser, child,
976 was_first && need_percent,
977 offset + extra_offset,
980 if (is_output_full(browser, row))
983 if (folded_sign == '+')
988 if (is_output_full(browser, row))
993 return row - first_row;
996 static char *hist_browser__folded_callchain_str(struct hist_browser *browser,
997 struct callchain_list *chain,
998 char *value_str, char *old_str)
1004 str = callchain_list__sym_name(chain, bf, sizeof(bf),
1007 if (asprintf(&new, "%s%s%s", old_str,
1008 symbol_conf.field_sep ?: ";", str) < 0)
1012 if (asprintf(&new, "%s %s", value_str, str) < 0)
1015 if (asprintf(&new, "%s", str) < 0)
1022 static int hist_browser__show_callchain_folded(struct hist_browser *browser,
1023 struct rb_root *root,
1024 unsigned short row, u64 total,
1026 print_callchain_entry_fn print,
1027 struct callchain_print_arg *arg,
1028 check_output_full_fn is_output_full)
1030 struct rb_node *node;
1031 int first_row = row, offset = LEVEL_OFFSET_STEP;
1034 node = rb_first(root);
1035 need_percent = check_percent_display(node, parent_total);
1038 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1039 struct rb_node *next = rb_next(node);
1040 struct callchain_list *chain, *first_chain = NULL;
1042 char *value_str = NULL, *value_str_alloc = NULL;
1043 char *chain_str = NULL, *chain_str_alloc = NULL;
1045 if (arg->row_offset != 0) {
1053 callchain_node__scnprintf_value(child, buf, sizeof(buf), total);
1054 if (asprintf(&value_str, "%s", buf) < 0) {
1055 value_str = (char *)"<...>";
1058 value_str_alloc = value_str;
1061 list_for_each_entry(chain, &child->parent_val, list) {
1062 chain_str = hist_browser__folded_callchain_str(browser,
1063 chain, value_str, chain_str);
1066 first_chain = chain;
1069 if (chain_str == NULL) {
1070 chain_str = (char *)"Not enough memory!";
1074 chain_str_alloc = chain_str;
1077 list_for_each_entry(chain, &child->val, list) {
1078 chain_str = hist_browser__folded_callchain_str(browser,
1079 chain, value_str, chain_str);
1082 first_chain = chain;
1085 if (chain_str == NULL) {
1086 chain_str = (char *)"Not enough memory!";
1090 chain_str_alloc = chain_str;
1094 print(browser, first_chain, chain_str, offset, row++, arg);
1095 free(value_str_alloc);
1096 free(chain_str_alloc);
1099 if (is_output_full(browser, row))
1104 return row - first_row;
1107 static int hist_browser__show_callchain_graph(struct hist_browser *browser,
1108 struct rb_root *root, int level,
1109 unsigned short row, u64 total,
1111 print_callchain_entry_fn print,
1112 struct callchain_print_arg *arg,
1113 check_output_full_fn is_output_full)
1115 struct rb_node *node;
1116 int first_row = row, offset = level * LEVEL_OFFSET_STEP;
1118 u64 percent_total = total;
1120 if (callchain_param.mode == CHAIN_GRAPH_REL)
1121 percent_total = parent_total;
1123 node = rb_first(root);
1124 need_percent = check_percent_display(node, parent_total);
1127 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1128 struct rb_node *next = rb_next(node);
1129 struct callchain_list *chain;
1130 char folded_sign = ' ';
1132 int extra_offset = 0;
1134 list_for_each_entry(chain, &child->val, list) {
1135 bool was_first = first;
1139 else if (need_percent)
1140 extra_offset = LEVEL_OFFSET_STEP;
1142 folded_sign = callchain_list__folded(chain);
1144 row += hist_browser__show_callchain_list(browser, child,
1145 chain, row, percent_total,
1146 was_first && need_percent,
1147 offset + extra_offset,
1150 if (is_output_full(browser, row))
1153 if (folded_sign == '+')
1157 if (folded_sign == '-') {
1158 const int new_level = level + (extra_offset ? 2 : 1);
1160 row += hist_browser__show_callchain_graph(browser, &child->rb_root,
1161 new_level, row, total,
1162 child->children_hit,
1163 print, arg, is_output_full);
1165 if (is_output_full(browser, row))
1170 return row - first_row;
1173 static int hist_browser__show_callchain(struct hist_browser *browser,
1174 struct hist_entry *entry, int level,
1176 print_callchain_entry_fn print,
1177 struct callchain_print_arg *arg,
1178 check_output_full_fn is_output_full)
1180 u64 total = hists__total_period(entry->hists);
1184 if (symbol_conf.cumulate_callchain)
1185 parent_total = entry->stat_acc->period;
1187 parent_total = entry->stat.period;
1189 if (callchain_param.mode == CHAIN_FLAT) {
1190 printed = hist_browser__show_callchain_flat(browser,
1191 &entry->sorted_chain, row,
1192 total, parent_total, print, arg,
1194 } else if (callchain_param.mode == CHAIN_FOLDED) {
1195 printed = hist_browser__show_callchain_folded(browser,
1196 &entry->sorted_chain, row,
1197 total, parent_total, print, arg,
1200 printed = hist_browser__show_callchain_graph(browser,
1201 &entry->sorted_chain, level, row,
1202 total, parent_total, print, arg,
1206 if (arg->is_current_entry)
1207 browser->he_selection = entry;
1213 struct ui_browser *b;
1218 int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
1220 struct hpp_arg *arg = hpp->ptr;
1225 va_start(args, fmt);
1226 len = va_arg(args, int);
1227 percent = va_arg(args, double);
1230 ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
1232 ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
1233 ui_browser__printf(arg->b, "%s", hpp->buf);
1238 #define __HPP_COLOR_PERCENT_FN(_type, _field) \
1239 static u64 __hpp_get_##_field(struct hist_entry *he) \
1241 return he->stat._field; \
1245 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
1246 struct perf_hpp *hpp, \
1247 struct hist_entry *he) \
1249 return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%", \
1250 __hpp__slsmg_color_printf, true); \
1253 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
1254 static u64 __hpp_get_acc_##_field(struct hist_entry *he) \
1256 return he->stat_acc->_field; \
1260 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
1261 struct perf_hpp *hpp, \
1262 struct hist_entry *he) \
1264 if (!symbol_conf.cumulate_callchain) { \
1265 struct hpp_arg *arg = hpp->ptr; \
1266 int len = fmt->user_len ?: fmt->len; \
1267 int ret = scnprintf(hpp->buf, hpp->size, \
1268 "%*s", len, "N/A"); \
1269 ui_browser__printf(arg->b, "%s", hpp->buf); \
1273 return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field, \
1274 " %*.2f%%", __hpp__slsmg_color_printf, true); \
1277 __HPP_COLOR_PERCENT_FN(overhead, period)
1278 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
1279 __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
1280 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
1281 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
1282 __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
1284 #undef __HPP_COLOR_PERCENT_FN
1285 #undef __HPP_COLOR_ACC_PERCENT_FN
1287 void hist_browser__init_hpp(void)
1289 perf_hpp__format[PERF_HPP__OVERHEAD].color =
1290 hist_browser__hpp_color_overhead;
1291 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
1292 hist_browser__hpp_color_overhead_sys;
1293 perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
1294 hist_browser__hpp_color_overhead_us;
1295 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
1296 hist_browser__hpp_color_overhead_guest_sys;
1297 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
1298 hist_browser__hpp_color_overhead_guest_us;
1299 perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
1300 hist_browser__hpp_color_overhead_acc;
1305 static int hist_browser__show_entry(struct hist_browser *browser,
1306 struct hist_entry *entry,
1310 int width = browser->b.width;
1311 char folded_sign = ' ';
1312 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1313 bool use_callchain = hist_entry__has_callchains(entry) && symbol_conf.use_callchain;
1314 off_t row_offset = entry->row_offset;
1316 struct perf_hpp_fmt *fmt;
1318 if (current_entry) {
1319 browser->he_selection = entry;
1320 browser->selection = &entry->ms;
1323 if (use_callchain) {
1324 hist_entry__init_have_children(entry);
1325 folded_sign = hist_entry__folded(entry);
1328 if (row_offset == 0) {
1329 struct hpp_arg arg = {
1331 .folded_sign = folded_sign,
1332 .current_entry = current_entry,
1336 ui_browser__gotorc(&browser->b, row, 0);
1338 hists__for_each_format(browser->hists, fmt) {
1340 struct perf_hpp hpp = {
1346 if (perf_hpp__should_skip(fmt, entry->hists) ||
1347 column++ < browser->b.horiz_scroll)
1350 if (current_entry && browser->b.navkeypressed) {
1351 ui_browser__set_color(&browser->b,
1352 HE_COLORSET_SELECTED);
1354 ui_browser__set_color(&browser->b,
1355 HE_COLORSET_NORMAL);
1359 if (use_callchain) {
1360 ui_browser__printf(&browser->b, "%c ", folded_sign);
1365 ui_browser__printf(&browser->b, " ");
1370 int ret = fmt->color(fmt, &hpp, entry);
1371 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1373 * fmt->color() already used ui_browser to
1374 * print the non alignment bits, skip it (+ret):
1376 ui_browser__printf(&browser->b, "%s", s + ret);
1378 hist_entry__snprintf_alignment(entry, &hpp, fmt, fmt->entry(fmt, &hpp, entry));
1379 ui_browser__printf(&browser->b, "%s", s);
1381 width -= hpp.buf - s;
1384 /* The scroll bar isn't being used */
1385 if (!browser->b.navkeypressed)
1388 ui_browser__write_nstring(&browser->b, "", width);
1395 if (folded_sign == '-' && row != browser->b.rows) {
1396 struct callchain_print_arg arg = {
1397 .row_offset = row_offset,
1398 .is_current_entry = current_entry,
1401 printed += hist_browser__show_callchain(browser,
1403 hist_browser__show_callchain_entry,
1405 hist_browser__check_output_full);
1411 static int hist_browser__show_hierarchy_entry(struct hist_browser *browser,
1412 struct hist_entry *entry,
1417 int width = browser->b.width;
1418 char folded_sign = ' ';
1419 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1420 off_t row_offset = entry->row_offset;
1422 struct perf_hpp_fmt *fmt;
1423 struct perf_hpp_list_node *fmt_node;
1424 struct hpp_arg arg = {
1426 .current_entry = current_entry,
1429 int hierarchy_indent = (entry->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
1431 if (current_entry) {
1432 browser->he_selection = entry;
1433 browser->selection = &entry->ms;
1436 hist_entry__init_have_children(entry);
1437 folded_sign = hist_entry__folded(entry);
1438 arg.folded_sign = folded_sign;
1440 if (entry->leaf && row_offset) {
1442 goto show_callchain;
1445 ui_browser__gotorc(&browser->b, row, 0);
1447 if (current_entry && browser->b.navkeypressed)
1448 ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1450 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1452 ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1453 width -= level * HIERARCHY_INDENT;
1455 /* the first hpp_list_node is for overhead columns */
1456 fmt_node = list_first_entry(&entry->hists->hpp_formats,
1457 struct perf_hpp_list_node, list);
1458 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1460 struct perf_hpp hpp = {
1466 if (perf_hpp__should_skip(fmt, entry->hists) ||
1467 column++ < browser->b.horiz_scroll)
1470 if (current_entry && browser->b.navkeypressed) {
1471 ui_browser__set_color(&browser->b,
1472 HE_COLORSET_SELECTED);
1474 ui_browser__set_color(&browser->b,
1475 HE_COLORSET_NORMAL);
1479 ui_browser__printf(&browser->b, "%c ", folded_sign);
1483 ui_browser__printf(&browser->b, " ");
1488 int ret = fmt->color(fmt, &hpp, entry);
1489 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1491 * fmt->color() already used ui_browser to
1492 * print the non alignment bits, skip it (+ret):
1494 ui_browser__printf(&browser->b, "%s", s + ret);
1496 int ret = fmt->entry(fmt, &hpp, entry);
1497 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1498 ui_browser__printf(&browser->b, "%s", s);
1500 width -= hpp.buf - s;
1504 ui_browser__write_nstring(&browser->b, "", hierarchy_indent);
1505 width -= hierarchy_indent;
1508 if (column >= browser->b.horiz_scroll) {
1510 struct perf_hpp hpp = {
1516 if (current_entry && browser->b.navkeypressed) {
1517 ui_browser__set_color(&browser->b,
1518 HE_COLORSET_SELECTED);
1520 ui_browser__set_color(&browser->b,
1521 HE_COLORSET_NORMAL);
1524 perf_hpp_list__for_each_format(entry->hpp_list, fmt) {
1526 ui_browser__printf(&browser->b, "%c ", folded_sign);
1529 ui_browser__write_nstring(&browser->b, "", 2);
1535 * No need to call hist_entry__snprintf_alignment()
1536 * since this fmt is always the last column in the
1540 width -= fmt->color(fmt, &hpp, entry);
1544 width -= fmt->entry(fmt, &hpp, entry);
1545 ui_browser__printf(&browser->b, "%s", skip_spaces(s));
1547 while (isspace(s[i++]))
1553 /* The scroll bar isn't being used */
1554 if (!browser->b.navkeypressed)
1557 ui_browser__write_nstring(&browser->b, "", width);
1563 if (entry->leaf && folded_sign == '-' && row != browser->b.rows) {
1564 struct callchain_print_arg carg = {
1565 .row_offset = row_offset,
1568 printed += hist_browser__show_callchain(browser, entry,
1570 hist_browser__show_callchain_entry, &carg,
1571 hist_browser__check_output_full);
1577 static int hist_browser__show_no_entry(struct hist_browser *browser,
1578 unsigned short row, int level)
1580 int width = browser->b.width;
1581 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1585 struct perf_hpp_fmt *fmt;
1586 struct perf_hpp_list_node *fmt_node;
1587 int indent = browser->hists->nr_hpp_node - 2;
1589 if (current_entry) {
1590 browser->he_selection = NULL;
1591 browser->selection = NULL;
1594 ui_browser__gotorc(&browser->b, row, 0);
1596 if (current_entry && browser->b.navkeypressed)
1597 ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1599 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1601 ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1602 width -= level * HIERARCHY_INDENT;
1604 /* the first hpp_list_node is for overhead columns */
1605 fmt_node = list_first_entry(&browser->hists->hpp_formats,
1606 struct perf_hpp_list_node, list);
1607 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1608 if (perf_hpp__should_skip(fmt, browser->hists) ||
1609 column++ < browser->b.horiz_scroll)
1612 ret = fmt->width(fmt, NULL, browser->hists);
1615 /* for folded sign */
1619 /* space between columns */
1623 ui_browser__write_nstring(&browser->b, "", ret);
1627 ui_browser__write_nstring(&browser->b, "", indent * HIERARCHY_INDENT);
1628 width -= indent * HIERARCHY_INDENT;
1630 if (column >= browser->b.horiz_scroll) {
1633 ret = snprintf(buf, sizeof(buf), "no entry >= %.2f%%", browser->min_pcnt);
1634 ui_browser__printf(&browser->b, " %s", buf);
1638 /* The scroll bar isn't being used */
1639 if (!browser->b.navkeypressed)
1642 ui_browser__write_nstring(&browser->b, "", width);
1646 static int advance_hpp_check(struct perf_hpp *hpp, int inc)
1648 advance_hpp(hpp, inc);
1649 return hpp->size <= 0;
1653 hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf,
1654 size_t size, int line)
1656 struct hists *hists = browser->hists;
1657 struct perf_hpp dummy_hpp = {
1661 struct perf_hpp_fmt *fmt;
1666 if (hists__has_callchains(hists) && symbol_conf.use_callchain) {
1667 ret = scnprintf(buf, size, " ");
1668 if (advance_hpp_check(&dummy_hpp, ret))
1672 hists__for_each_format(browser->hists, fmt) {
1673 if (perf_hpp__should_skip(fmt, hists) || column++ < browser->b.horiz_scroll)
1676 ret = fmt->header(fmt, &dummy_hpp, hists, line, &span);
1677 if (advance_hpp_check(&dummy_hpp, ret))
1683 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
1684 if (advance_hpp_check(&dummy_hpp, ret))
1691 static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser, char *buf, size_t size)
1693 struct hists *hists = browser->hists;
1694 struct perf_hpp dummy_hpp = {
1698 struct perf_hpp_fmt *fmt;
1699 struct perf_hpp_list_node *fmt_node;
1702 int indent = hists->nr_hpp_node - 2;
1703 bool first_node, first_col;
1705 ret = scnprintf(buf, size, " ");
1706 if (advance_hpp_check(&dummy_hpp, ret))
1710 /* the first hpp_list_node is for overhead columns */
1711 fmt_node = list_first_entry(&hists->hpp_formats,
1712 struct perf_hpp_list_node, list);
1713 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1714 if (column++ < browser->b.horiz_scroll)
1717 ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1718 if (advance_hpp_check(&dummy_hpp, ret))
1721 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
1722 if (advance_hpp_check(&dummy_hpp, ret))
1729 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "%*s",
1730 indent * HIERARCHY_INDENT, "");
1731 if (advance_hpp_check(&dummy_hpp, ret))
1736 list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
1738 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " / ");
1739 if (advance_hpp_check(&dummy_hpp, ret))
1745 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1748 if (perf_hpp__should_skip(fmt, hists))
1752 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "+");
1753 if (advance_hpp_check(&dummy_hpp, ret))
1758 ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1759 dummy_hpp.buf[ret] = '\0';
1761 start = strim(dummy_hpp.buf);
1762 ret = strlen(start);
1764 if (start != dummy_hpp.buf)
1765 memmove(dummy_hpp.buf, start, ret + 1);
1767 if (advance_hpp_check(&dummy_hpp, ret))
1775 static void hists_browser__hierarchy_headers(struct hist_browser *browser)
1779 hists_browser__scnprintf_hierarchy_headers(browser, headers,
1782 ui_browser__gotorc(&browser->b, 0, 0);
1783 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1784 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1787 static void hists_browser__headers(struct hist_browser *browser)
1789 struct hists *hists = browser->hists;
1790 struct perf_hpp_list *hpp_list = hists->hpp_list;
1794 for (line = 0; line < hpp_list->nr_header_lines; line++) {
1797 hists_browser__scnprintf_headers(browser, headers,
1798 sizeof(headers), line);
1800 ui_browser__gotorc_title(&browser->b, line, 0);
1801 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1802 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1806 static void hist_browser__show_headers(struct hist_browser *browser)
1808 if (symbol_conf.report_hierarchy)
1809 hists_browser__hierarchy_headers(browser);
1811 hists_browser__headers(browser);
1814 static void ui_browser__hists_init_top(struct ui_browser *browser)
1816 if (browser->top == NULL) {
1817 struct hist_browser *hb;
1819 hb = container_of(browser, struct hist_browser, b);
1820 browser->top = rb_first_cached(&hb->hists->entries);
1824 static unsigned int hist_browser__refresh(struct ui_browser *browser)
1828 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
1830 if (hb->show_headers)
1831 hist_browser__show_headers(hb);
1833 ui_browser__hists_init_top(browser);
1834 hb->he_selection = NULL;
1835 hb->selection = NULL;
1837 for (nd = browser->top; nd; nd = rb_hierarchy_next(nd)) {
1838 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1842 /* let it move to sibling */
1843 h->unfolded = false;
1847 if (symbol_conf.report_individual_block)
1848 percent = block_info__total_cycles_percent(h);
1850 percent = hist_entry__get_percent_limit(h);
1852 if (percent < hb->min_pcnt)
1855 if (symbol_conf.report_hierarchy) {
1856 row += hist_browser__show_hierarchy_entry(hb, h, row,
1858 if (row == browser->rows)
1861 if (h->has_no_entry) {
1862 hist_browser__show_no_entry(hb, row, h->depth + 1);
1866 row += hist_browser__show_entry(hb, h, row);
1869 if (row == browser->rows)
1876 static struct rb_node *hists__filter_entries(struct rb_node *nd,
1879 while (nd != NULL) {
1880 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1881 float percent = hist_entry__get_percent_limit(h);
1883 if (!h->filtered && percent >= min_pcnt)
1887 * If it's filtered, its all children also were filtered.
1888 * So move to sibling node.
1893 nd = rb_hierarchy_next(nd);
1899 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
1902 while (nd != NULL) {
1903 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1904 float percent = hist_entry__get_percent_limit(h);
1906 if (!h->filtered && percent >= min_pcnt)
1909 nd = rb_hierarchy_prev(nd);
1915 static void ui_browser__hists_seek(struct ui_browser *browser,
1916 off_t offset, int whence)
1918 struct hist_entry *h;
1921 struct hist_browser *hb;
1923 hb = container_of(browser, struct hist_browser, b);
1925 if (browser->nr_entries == 0)
1928 ui_browser__hists_init_top(browser);
1932 nd = hists__filter_entries(rb_first(browser->entries),
1939 nd = rb_hierarchy_last(rb_last(browser->entries));
1940 nd = hists__filter_prev_entries(nd, hb->min_pcnt);
1948 * Moves not relative to the first visible entry invalidates its
1951 h = rb_entry(browser->top, struct hist_entry, rb_node);
1955 * Here we have to check if nd is expanded (+), if it is we can't go
1956 * the next top level hist_entry, instead we must compute an offset of
1957 * what _not_ to show and not change the first visible entry.
1959 * This offset increments when we are going from top to bottom and
1960 * decreases when we're going from bottom to top.
1962 * As we don't have backpointers to the top level in the callchains
1963 * structure, we need to always print the whole hist_entry callchain,
1964 * skipping the first ones that are before the first visible entry
1965 * and stop when we printed enough lines to fill the screen.
1973 h = rb_entry(nd, struct hist_entry, rb_node);
1974 if (h->unfolded && h->leaf) {
1975 u16 remaining = h->nr_rows - h->row_offset;
1976 if (offset > remaining) {
1977 offset -= remaining;
1980 h->row_offset += offset;
1986 nd = hists__filter_entries(rb_hierarchy_next(nd),
1992 } while (offset != 0);
1993 } else if (offset < 0) {
1995 h = rb_entry(nd, struct hist_entry, rb_node);
1996 if (h->unfolded && h->leaf) {
1998 if (-offset > h->row_offset) {
1999 offset += h->row_offset;
2002 h->row_offset += offset;
2008 if (-offset > h->nr_rows) {
2009 offset += h->nr_rows;
2012 h->row_offset = h->nr_rows + offset;
2020 nd = hists__filter_prev_entries(rb_hierarchy_prev(nd),
2028 * Last unfiltered hist_entry, check if it is
2029 * unfolded, if it is then we should have
2030 * row_offset at its last entry.
2032 h = rb_entry(nd, struct hist_entry, rb_node);
2033 if (h->unfolded && h->leaf)
2034 h->row_offset = h->nr_rows;
2041 h = rb_entry(nd, struct hist_entry, rb_node);
2046 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
2047 struct hist_entry *he, FILE *fp,
2050 struct callchain_print_arg arg = {
2054 hist_browser__show_callchain(browser, he, level, 0,
2055 hist_browser__fprintf_callchain_entry, &arg,
2056 hist_browser__check_dump_full);
2060 static int hist_browser__fprintf_entry(struct hist_browser *browser,
2061 struct hist_entry *he, FILE *fp)
2065 char folded_sign = ' ';
2066 struct perf_hpp hpp = {
2070 struct perf_hpp_fmt *fmt;
2074 if (hist_entry__has_callchains(he) && symbol_conf.use_callchain) {
2075 folded_sign = hist_entry__folded(he);
2076 printed += fprintf(fp, "%c ", folded_sign);
2079 hists__for_each_format(browser->hists, fmt) {
2080 if (perf_hpp__should_skip(fmt, he->hists))
2084 ret = scnprintf(hpp.buf, hpp.size, " ");
2085 advance_hpp(&hpp, ret);
2089 ret = fmt->entry(fmt, &hpp, he);
2090 ret = hist_entry__snprintf_alignment(he, &hpp, fmt, ret);
2091 advance_hpp(&hpp, ret);
2093 printed += fprintf(fp, "%s\n", s);
2095 if (folded_sign == '-')
2096 printed += hist_browser__fprintf_callchain(browser, he, fp, 1);
2102 static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,
2103 struct hist_entry *he,
2104 FILE *fp, int level)
2108 char folded_sign = ' ';
2109 struct perf_hpp hpp = {
2113 struct perf_hpp_fmt *fmt;
2114 struct perf_hpp_list_node *fmt_node;
2117 int hierarchy_indent = (he->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
2119 printed = fprintf(fp, "%*s", level * HIERARCHY_INDENT, "");
2121 folded_sign = hist_entry__folded(he);
2122 printed += fprintf(fp, "%c", folded_sign);
2124 /* the first hpp_list_node is for overhead columns */
2125 fmt_node = list_first_entry(&he->hists->hpp_formats,
2126 struct perf_hpp_list_node, list);
2127 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
2129 ret = scnprintf(hpp.buf, hpp.size, " ");
2130 advance_hpp(&hpp, ret);
2134 ret = fmt->entry(fmt, &hpp, he);
2135 advance_hpp(&hpp, ret);
2138 ret = scnprintf(hpp.buf, hpp.size, "%*s", hierarchy_indent, "");
2139 advance_hpp(&hpp, ret);
2141 perf_hpp_list__for_each_format(he->hpp_list, fmt) {
2142 ret = scnprintf(hpp.buf, hpp.size, " ");
2143 advance_hpp(&hpp, ret);
2145 ret = fmt->entry(fmt, &hpp, he);
2146 advance_hpp(&hpp, ret);
2150 printed += fprintf(fp, "%s\n", s);
2152 if (he->leaf && folded_sign == '-') {
2153 printed += hist_browser__fprintf_callchain(browser, he, fp,
2160 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
2162 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
2167 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
2169 if (symbol_conf.report_hierarchy) {
2170 printed += hist_browser__fprintf_hierarchy_entry(browser,
2174 printed += hist_browser__fprintf_entry(browser, h, fp);
2177 nd = hists__filter_entries(rb_hierarchy_next(nd),
2184 static int hist_browser__dump(struct hist_browser *browser)
2190 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
2191 if (access(filename, F_OK))
2194 * XXX: Just an arbitrary lazy upper limit
2196 if (++browser->print_seq == 8192) {
2197 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
2202 fp = fopen(filename, "w");
2205 const char *err = str_error_r(errno, bf, sizeof(bf));
2206 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
2210 ++browser->print_seq;
2211 hist_browser__fprintf(browser, fp);
2213 ui_helpline__fpush("%s written!", filename);
2218 void hist_browser__init(struct hist_browser *browser,
2219 struct hists *hists)
2221 struct perf_hpp_fmt *fmt;
2223 browser->hists = hists;
2224 browser->b.refresh = hist_browser__refresh;
2225 browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
2226 browser->b.seek = ui_browser__hists_seek;
2227 browser->b.use_navkeypressed = true;
2228 browser->show_headers = symbol_conf.show_hist_headers;
2229 hist_browser__set_title_space(browser);
2231 if (symbol_conf.report_hierarchy) {
2232 struct perf_hpp_list_node *fmt_node;
2234 /* count overhead columns (in the first node) */
2235 fmt_node = list_first_entry(&hists->hpp_formats,
2236 struct perf_hpp_list_node, list);
2237 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt)
2238 ++browser->b.columns;
2240 /* add a single column for whole hierarchy sort keys*/
2241 ++browser->b.columns;
2243 hists__for_each_format(hists, fmt)
2244 ++browser->b.columns;
2247 hists__reset_column_width(hists);
2250 struct hist_browser *hist_browser__new(struct hists *hists)
2252 struct hist_browser *browser = zalloc(sizeof(*browser));
2255 hist_browser__init(browser, hists);
2260 static struct hist_browser *
2261 perf_evsel_browser__new(struct evsel *evsel,
2262 struct hist_browser_timer *hbt,
2263 struct perf_env *env,
2264 struct annotation_options *annotation_opts)
2266 struct hist_browser *browser = hist_browser__new(evsel__hists(evsel));
2271 browser->title = hists_browser__scnprintf_title;
2272 browser->annotation_opts = annotation_opts;
2277 void hist_browser__delete(struct hist_browser *browser)
2282 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
2284 return browser->he_selection;
2287 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
2289 return browser->he_selection->thread;
2292 static struct res_sample *hist_browser__selected_res_sample(struct hist_browser *browser)
2294 return browser->he_selection ? browser->he_selection->res_samples : NULL;
2297 /* Check whether the browser is for 'top' or 'report' */
2298 static inline bool is_report_browser(void *timer)
2300 return timer == NULL;
2303 static int hists_browser__scnprintf_title(struct hist_browser *browser, char *bf, size_t size)
2305 struct hist_browser_timer *hbt = browser->hbt;
2306 int printed = __hists__scnprintf_title(browser->hists, bf, size, !is_report_browser(hbt));
2308 if (!is_report_browser(hbt)) {
2309 struct perf_top *top = hbt->arg;
2311 printed += scnprintf(bf + printed, size - printed,
2312 " lost: %" PRIu64 "/%" PRIu64,
2313 top->lost, top->lost_total);
2315 printed += scnprintf(bf + printed, size - printed,
2316 " drop: %" PRIu64 "/%" PRIu64,
2317 top->drop, top->drop_total);
2320 printed += scnprintf(bf + printed, size - printed, " [z]");
2322 perf_top__reset_sample_counters(top);
2329 static inline void free_popup_options(char **options, int n)
2333 for (i = 0; i < n; ++i)
2338 * Only runtime switching of perf data file will make "input_name" point
2339 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
2340 * whether we need to call free() for current "input_name" during the switch.
2342 static bool is_input_name_malloced = false;
2344 static int switch_data_file(void)
2346 char *pwd, *options[32], *abs_path[32], *tmp;
2348 int nr_options = 0, choice = -1, ret = -1;
2349 struct dirent *dent;
2351 pwd = getenv("PWD");
2355 pwd_dir = opendir(pwd);
2359 memset(options, 0, sizeof(options));
2360 memset(abs_path, 0, sizeof(abs_path));
2362 while ((dent = readdir(pwd_dir))) {
2363 char path[PATH_MAX];
2365 char *name = dent->d_name;
2368 if (!(dent->d_type == DT_REG))
2371 snprintf(path, sizeof(path), "%s/%s", pwd, name);
2373 file = fopen(path, "r");
2377 if (fread(&magic, 1, 8, file) < 8)
2378 goto close_file_and_continue;
2380 if (is_perf_magic(magic)) {
2381 options[nr_options] = strdup(name);
2382 if (!options[nr_options])
2383 goto close_file_and_continue;
2385 abs_path[nr_options] = strdup(path);
2386 if (!abs_path[nr_options]) {
2387 zfree(&options[nr_options]);
2388 ui__warning("Can't search all data files due to memory shortage.\n");
2396 close_file_and_continue:
2398 if (nr_options >= 32) {
2399 ui__warning("Too many perf data files in PWD!\n"
2400 "Only the first 32 files will be listed.\n");
2407 choice = ui__popup_menu(nr_options, options, NULL);
2408 if (choice < nr_options && choice >= 0) {
2409 tmp = strdup(abs_path[choice]);
2411 if (is_input_name_malloced)
2412 free((void *)input_name);
2414 is_input_name_malloced = true;
2417 ui__warning("Data switch failed due to memory shortage!\n");
2421 free_popup_options(options, nr_options);
2422 free_popup_options(abs_path, nr_options);
2426 struct popup_action {
2428 struct thread *thread;
2429 struct map_symbol ms;
2431 struct evsel *evsel;
2434 int (*fn)(struct hist_browser *browser, struct popup_action *act);
2438 do_annotate(struct hist_browser *browser, struct popup_action *act)
2440 struct evsel *evsel;
2441 struct annotation *notes;
2442 struct hist_entry *he;
2445 if (!browser->annotation_opts->objdump_path &&
2446 perf_env__lookup_objdump(browser->env, &browser->annotation_opts->objdump_path))
2449 notes = symbol__annotation(act->ms.sym);
2453 if (browser->block_evsel)
2454 evsel = browser->block_evsel;
2456 evsel = hists_to_evsel(browser->hists);
2458 err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt,
2459 browser->annotation_opts);
2460 he = hist_browser__selected_entry(browser);
2462 * offer option to annotate the other branch source or target
2463 * (if they exists) when returning from annotate
2465 if ((err == 'q' || err == CTRL('c')) && he->branch_info)
2468 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
2470 ui_browser__handle_resize(&browser->b);
2474 static struct symbol *symbol__new_unresolved(u64 addr, struct map *map)
2476 struct annotated_source *src;
2480 snprintf(name, sizeof(name), "%.*" PRIx64, BITS_PER_LONG / 4, addr);
2482 sym = symbol__new(addr, ANNOTATION_DUMMY_LEN, 0, 0, name);
2484 src = symbol__hists(sym, 1);
2486 symbol__delete(sym);
2490 dso__insert_symbol(map->dso, sym);
2497 add_annotate_opt(struct hist_browser *browser __maybe_unused,
2498 struct popup_action *act, char **optstr,
2499 struct map_symbol *ms,
2502 if (!ms->map || !ms->map->dso || ms->map->dso->annotate_warned)
2506 ms->sym = symbol__new_unresolved(addr, ms->map);
2508 if (ms->sym == NULL || symbol__annotation(ms->sym)->src == NULL)
2511 if (asprintf(optstr, "Annotate %s", ms->sym->name) < 0)
2515 act->fn = do_annotate;
2520 do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
2522 struct thread *thread = act->thread;
2524 if ((!hists__has(browser->hists, thread) &&
2525 !hists__has(browser->hists, comm)) || thread == NULL)
2528 if (browser->hists->thread_filter) {
2529 pstack__remove(browser->pstack, &browser->hists->thread_filter);
2530 perf_hpp__set_elide(HISTC_THREAD, false);
2531 thread__zput(browser->hists->thread_filter);
2534 if (hists__has(browser->hists, thread)) {
2535 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
2536 thread->comm_set ? thread__comm_str(thread) : "",
2539 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
2540 thread->comm_set ? thread__comm_str(thread) : "");
2543 browser->hists->thread_filter = thread__get(thread);
2544 perf_hpp__set_elide(HISTC_THREAD, false);
2545 pstack__push(browser->pstack, &browser->hists->thread_filter);
2548 hists__filter_by_thread(browser->hists);
2549 hist_browser__reset(browser);
2554 add_thread_opt(struct hist_browser *browser, struct popup_action *act,
2555 char **optstr, struct thread *thread)
2559 if ((!hists__has(browser->hists, thread) &&
2560 !hists__has(browser->hists, comm)) || thread == NULL)
2563 if (hists__has(browser->hists, thread)) {
2564 ret = asprintf(optstr, "Zoom %s %s(%d) thread",
2565 browser->hists->thread_filter ? "out of" : "into",
2566 thread->comm_set ? thread__comm_str(thread) : "",
2569 ret = asprintf(optstr, "Zoom %s %s thread",
2570 browser->hists->thread_filter ? "out of" : "into",
2571 thread->comm_set ? thread__comm_str(thread) : "");
2576 act->thread = thread;
2577 act->fn = do_zoom_thread;
2581 static int hists_browser__zoom_map(struct hist_browser *browser, struct map *map)
2583 if (!hists__has(browser->hists, dso) || map == NULL)
2586 if (browser->hists->dso_filter) {
2587 pstack__remove(browser->pstack, &browser->hists->dso_filter);
2588 perf_hpp__set_elide(HISTC_DSO, false);
2589 browser->hists->dso_filter = NULL;
2592 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
2593 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
2594 browser->hists->dso_filter = map->dso;
2595 perf_hpp__set_elide(HISTC_DSO, true);
2596 pstack__push(browser->pstack, &browser->hists->dso_filter);
2599 hists__filter_by_dso(browser->hists);
2600 hist_browser__reset(browser);
2605 do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
2607 return hists_browser__zoom_map(browser, act->ms.map);
2611 add_dso_opt(struct hist_browser *browser, struct popup_action *act,
2612 char **optstr, struct map *map)
2614 if (!hists__has(browser->hists, dso) || map == NULL)
2617 if (asprintf(optstr, "Zoom %s %s DSO (use the 'k' hotkey to zoom directly into the kernel)",
2618 browser->hists->dso_filter ? "out of" : "into",
2619 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
2623 act->fn = do_zoom_dso;
2627 static int do_toggle_callchain(struct hist_browser *browser, struct popup_action *act __maybe_unused)
2629 hist_browser__toggle_fold(browser);
2633 static int add_callchain_toggle_opt(struct hist_browser *browser, struct popup_action *act, char **optstr)
2637 if (!hist_browser__selection_has_children(browser))
2640 if (asprintf(optstr, "%s [%s] callchain (one level, same as '+' hotkey, use 'e'/'c' for the whole main level entry)",
2641 hist_browser__selection_unfolded(browser) ? "Collapse" : "Expand",
2642 hist_browser__selection_sym_name(browser, sym_name, sizeof(sym_name))) < 0)
2645 act->fn = do_toggle_callchain;
2650 do_browse_map(struct hist_browser *browser __maybe_unused,
2651 struct popup_action *act)
2653 map__browse(act->ms.map);
2658 add_map_opt(struct hist_browser *browser,
2659 struct popup_action *act, char **optstr, struct map *map)
2661 if (!hists__has(browser->hists, dso) || map == NULL)
2664 if (asprintf(optstr, "Browse map details") < 0)
2668 act->fn = do_browse_map;
2673 do_run_script(struct hist_browser *browser __maybe_unused,
2674 struct popup_action *act)
2682 len += strlen(thread__comm_str(act->thread));
2683 else if (act->ms.sym)
2684 len += strlen(act->ms.sym->name);
2685 script_opt = malloc(len);
2691 n = scnprintf(script_opt, len, " -c %s ",
2692 thread__comm_str(act->thread));
2693 } else if (act->ms.sym) {
2694 n = scnprintf(script_opt, len, " -S %s ",
2699 char start[32], end[32];
2700 unsigned long starttime = act->time;
2701 unsigned long endtime = act->time + symbol_conf.time_quantum;
2703 if (starttime == endtime) { /* Display 1ms as fallback */
2704 starttime -= 1*NSEC_PER_MSEC;
2705 endtime += 1*NSEC_PER_MSEC;
2707 timestamp__scnprintf_usec(starttime, start, sizeof start);
2708 timestamp__scnprintf_usec(endtime, end, sizeof end);
2709 n += snprintf(script_opt + n, len - n, " --time %s,%s", start, end);
2712 script_browse(script_opt, act->evsel);
2718 do_res_sample_script(struct hist_browser *browser __maybe_unused,
2719 struct popup_action *act)
2721 struct hist_entry *he;
2723 he = hist_browser__selected_entry(browser);
2724 res_sample_browse(he->res_samples, he->num_res, act->evsel, act->rstype);
2729 add_script_opt_2(struct hist_browser *browser __maybe_unused,
2730 struct popup_action *act, char **optstr,
2731 struct thread *thread, struct symbol *sym,
2732 struct evsel *evsel, const char *tstr)
2736 if (asprintf(optstr, "Run scripts for samples of thread [%s]%s",
2737 thread__comm_str(thread), tstr) < 0)
2740 if (asprintf(optstr, "Run scripts for samples of symbol [%s]%s",
2741 sym->name, tstr) < 0)
2744 if (asprintf(optstr, "Run scripts for all samples%s", tstr) < 0)
2748 act->thread = thread;
2751 act->fn = do_run_script;
2756 add_script_opt(struct hist_browser *browser,
2757 struct popup_action *act, char **optstr,
2758 struct thread *thread, struct symbol *sym,
2759 struct evsel *evsel)
2762 struct hist_entry *he;
2764 n = add_script_opt_2(browser, act, optstr, thread, sym, evsel, "");
2766 he = hist_browser__selected_entry(browser);
2767 if (sort_order && strstr(sort_order, "time")) {
2772 j = sprintf(tstr, " in ");
2773 j += timestamp__scnprintf_usec(he->time, tstr + j,
2775 j += sprintf(tstr + j, "-");
2776 timestamp__scnprintf_usec(he->time + symbol_conf.time_quantum,
2777 tstr + j, sizeof tstr - j);
2778 n += add_script_opt_2(browser, act, optstr, thread, sym,
2780 act->time = he->time;
2786 add_res_sample_opt(struct hist_browser *browser __maybe_unused,
2787 struct popup_action *act, char **optstr,
2788 struct res_sample *res_sample,
2789 struct evsel *evsel,
2795 if (asprintf(optstr, "Show context for individual samples %s",
2796 type == A_ASM ? "with assembler" :
2797 type == A_SOURCE ? "with source" : "") < 0)
2800 act->fn = do_res_sample_script;
2807 do_switch_data(struct hist_browser *browser __maybe_unused,
2808 struct popup_action *act __maybe_unused)
2810 if (switch_data_file()) {
2811 ui__warning("Won't switch the data files due to\n"
2812 "no valid data file get selected!\n");
2816 return K_SWITCH_INPUT_DATA;
2820 add_switch_opt(struct hist_browser *browser,
2821 struct popup_action *act, char **optstr)
2823 if (!is_report_browser(browser->hbt))
2826 if (asprintf(optstr, "Switch to another data file in PWD") < 0)
2829 act->fn = do_switch_data;
2834 do_exit_browser(struct hist_browser *browser __maybe_unused,
2835 struct popup_action *act __maybe_unused)
2841 add_exit_opt(struct hist_browser *browser __maybe_unused,
2842 struct popup_action *act, char **optstr)
2844 if (asprintf(optstr, "Exit") < 0)
2847 act->fn = do_exit_browser;
2852 do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
2854 if (!hists__has(browser->hists, socket) || act->socket < 0)
2857 if (browser->hists->socket_filter > -1) {
2858 pstack__remove(browser->pstack, &browser->hists->socket_filter);
2859 browser->hists->socket_filter = -1;
2860 perf_hpp__set_elide(HISTC_SOCKET, false);
2862 browser->hists->socket_filter = act->socket;
2863 perf_hpp__set_elide(HISTC_SOCKET, true);
2864 pstack__push(browser->pstack, &browser->hists->socket_filter);
2867 hists__filter_by_socket(browser->hists);
2868 hist_browser__reset(browser);
2873 add_socket_opt(struct hist_browser *browser, struct popup_action *act,
2874 char **optstr, int socket_id)
2876 if (!hists__has(browser->hists, socket) || socket_id < 0)
2879 if (asprintf(optstr, "Zoom %s Processor Socket %d",
2880 (browser->hists->socket_filter > -1) ? "out of" : "into",
2884 act->socket = socket_id;
2885 act->fn = do_zoom_socket;
2889 static void hist_browser__update_nr_entries(struct hist_browser *hb)
2892 struct rb_node *nd = rb_first_cached(&hb->hists->entries);
2894 if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
2895 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
2899 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2901 nd = rb_hierarchy_next(nd);
2904 hb->nr_non_filtered_entries = nr_entries;
2905 hb->nr_hierarchy_entries = nr_entries;
2908 static void hist_browser__update_percent_limit(struct hist_browser *hb,
2911 struct hist_entry *he;
2912 struct rb_node *nd = rb_first_cached(&hb->hists->entries);
2913 u64 total = hists__total_period(hb->hists);
2914 u64 min_callchain_hits = total * (percent / 100);
2916 hb->min_pcnt = callchain_param.min_percent = percent;
2918 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2919 he = rb_entry(nd, struct hist_entry, rb_node);
2921 if (he->has_no_entry) {
2922 he->has_no_entry = false;
2926 if (!he->leaf || !hist_entry__has_callchains(he) || !symbol_conf.use_callchain)
2929 if (callchain_param.mode == CHAIN_GRAPH_REL) {
2930 total = he->stat.period;
2932 if (symbol_conf.cumulate_callchain)
2933 total = he->stat_acc->period;
2935 min_callchain_hits = total * (percent / 100);
2938 callchain_param.sort(&he->sorted_chain, he->callchain,
2939 min_callchain_hits, &callchain_param);
2942 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
2944 /* force to re-evaluate folding state of callchains */
2945 he->init_have_children = false;
2946 hist_entry__set_folding(he, hb, false);
2950 static int evsel__hists_browse(struct evsel *evsel, int nr_events, const char *helpline,
2951 bool left_exits, struct hist_browser_timer *hbt, float min_pcnt,
2952 struct perf_env *env, bool warn_lost_event,
2953 struct annotation_options *annotation_opts)
2955 struct hists *hists = evsel__hists(evsel);
2956 struct hist_browser *browser = perf_evsel_browser__new(evsel, hbt, env, annotation_opts);
2957 struct branch_info *bi = NULL;
2958 #define MAX_OPTIONS 16
2959 char *options[MAX_OPTIONS];
2960 struct popup_action actions[MAX_OPTIONS];
2964 int delay_secs = hbt ? hbt->refresh : 0;
2966 #define HIST_BROWSER_HELP_COMMON \
2967 "h/?/F1 Show this window\n" \
2969 "PGDN/SPACE Navigate\n" \
2970 "q/ESC/CTRL+C Exit browser or go back to previous screen\n\n" \
2971 "For multiple event sessions:\n\n" \
2972 "TAB/UNTAB Switch events\n\n" \
2973 "For symbolic views (--sort has sym):\n\n" \
2974 "ENTER Zoom into DSO/Threads & Annotate current symbol\n" \
2976 "+ Expand/Collapse one callchain level\n" \
2977 "a Annotate current symbol\n" \
2978 "C Collapse all callchains\n" \
2979 "d Zoom into current DSO\n" \
2980 "e Expand/Collapse main entry callchains\n" \
2981 "E Expand all callchains\n" \
2982 "F Toggle percentage of filtered entries\n" \
2983 "H Display column headers\n" \
2984 "k Zoom into the kernel map\n" \
2985 "L Change percent limit\n" \
2986 "m Display context menu\n" \
2987 "S Zoom into current Processor Socket\n" \
2989 /* help messages are sorted by lexical order of the hotkey */
2990 static const char report_help[] = HIST_BROWSER_HELP_COMMON
2991 "i Show header information\n"
2992 "P Print histograms to perf.hist.N\n"
2993 "r Run available scripts\n"
2994 "s Switch to another data file in PWD\n"
2995 "t Zoom into current Thread\n"
2996 "V Verbose (DSO names in callchains, etc)\n"
2997 "/ Filter symbol by name\n"
2998 "0-9 Sort by event n in group";
2999 static const char top_help[] = HIST_BROWSER_HELP_COMMON
3000 "P Print histograms to perf.hist.N\n"
3001 "t Zoom into current Thread\n"
3002 "V Verbose (DSO names in callchains, etc)\n"
3003 "z Toggle zeroing of samples\n"
3004 "f Enable/Disable events\n"
3005 "/ Filter symbol by name";
3007 if (browser == NULL)
3010 /* reset abort key so that it can get Ctrl-C as a key */
3012 SLang_init_tty(0, 0, 0);
3015 browser->min_pcnt = min_pcnt;
3016 hist_browser__update_nr_entries(browser);
3018 browser->pstack = pstack__new(3);
3019 if (browser->pstack == NULL)
3022 ui_helpline__push(helpline);
3024 memset(options, 0, sizeof(options));
3025 memset(actions, 0, sizeof(actions));
3027 if (symbol_conf.col_width_list_str)
3028 perf_hpp__set_user_width(symbol_conf.col_width_list_str);
3030 if (!is_report_browser(hbt))
3031 browser->b.no_samples_msg = "Collecting samples...";
3034 struct thread *thread = NULL;
3035 struct map *map = NULL;
3039 key = 0; // reset key
3040 do_hotkey: // key came straight from options ui__popup_menu()
3041 choice = nr_options = 0;
3042 key = hist_browser__run(browser, helpline, warn_lost_event, key);
3044 if (browser->he_selection != NULL) {
3045 thread = hist_browser__selected_thread(browser);
3046 map = browser->selection->map;
3047 socked_id = browser->he_selection->socket;
3055 * Exit the browser, let hists__browser_tree
3056 * go to the next or previous
3058 goto out_free_stack;
3060 if (!symbol_conf.event_group ||
3061 evsel->core.nr_members < 2) {
3062 snprintf(buf, sizeof(buf),
3063 "Sort by index only available with group events!");
3068 if (key - '0' == symbol_conf.group_sort_idx)
3071 symbol_conf.group_sort_idx = key - '0';
3073 if (symbol_conf.group_sort_idx >= evsel->core.nr_members) {
3074 snprintf(buf, sizeof(buf),
3075 "Max event group index to sort is %d (index from 0 to %d)",
3076 evsel->core.nr_members - 1,
3077 evsel->core.nr_members - 1);
3083 goto out_free_stack;
3085 if (!hists__has(hists, sym)) {
3086 ui_browser__warning(&browser->b, delay_secs * 2,
3087 "Annotation is only available for symbolic views, "
3088 "include \"sym*\" in --sort to use it.");
3092 if (!browser->selection ||
3093 !browser->selection->map ||
3094 !browser->selection->map->dso ||
3095 browser->selection->map->dso->annotate_warned) {
3099 if (!browser->selection->sym) {
3100 if (!browser->he_selection)
3103 if (sort__mode == SORT_MODE__BRANCH) {
3104 bi = browser->he_selection->branch_info;
3105 if (!bi || !bi->to.ms.map)
3108 actions->ms.sym = symbol__new_unresolved(bi->to.al_addr, bi->to.ms.map);
3109 actions->ms.map = bi->to.ms.map;
3111 actions->ms.sym = symbol__new_unresolved(browser->he_selection->ip,
3112 browser->selection->map);
3113 actions->ms.map = browser->selection->map;
3116 if (!actions->ms.sym)
3119 if (symbol__annotation(browser->selection->sym)->src == NULL) {
3120 ui_browser__warning(&browser->b, delay_secs * 2,
3121 "No samples for the \"%s\" symbol.\n\n"
3122 "Probably appeared just in a callchain",
3123 browser->selection->sym->name);
3127 actions->ms.map = browser->selection->map;
3128 actions->ms.sym = browser->selection->sym;
3131 do_annotate(browser, actions);
3134 hist_browser__dump(browser);
3137 actions->ms.map = map;
3138 do_zoom_dso(browser, actions);
3141 if (browser->selection != NULL)
3142 hists_browser__zoom_map(browser, browser->selection->maps->machine->vmlinux_map);
3145 verbose = (verbose + 1) % 4;
3146 browser->show_dso = verbose > 0;
3147 ui_helpline__fpush("Verbosity level set to %d\n",
3151 actions->thread = thread;
3152 do_zoom_thread(browser, actions);
3155 actions->socket = socked_id;
3156 do_zoom_socket(browser, actions);
3159 if (ui_browser__input_window("Symbol to show",
3160 "Please enter the name of symbol you want to see.\n"
3161 "To remove the filter later, press / + ENTER.",
3162 buf, "ENTER: OK, ESC: Cancel",
3163 delay_secs * 2) == K_ENTER) {
3164 hists->symbol_filter_str = *buf ? buf : NULL;
3165 hists__filter_by_symbol(hists);
3166 hist_browser__reset(browser);
3170 if (is_report_browser(hbt)) {
3171 actions->thread = NULL;
3172 actions->ms.sym = NULL;
3173 do_run_script(browser, actions);
3177 if (is_report_browser(hbt)) {
3178 key = do_switch_data(browser, actions);
3179 if (key == K_SWITCH_INPUT_DATA)
3180 goto out_free_stack;
3184 /* env->arch is NULL for live-mode (i.e. perf top) */
3186 tui__header_window(env);
3189 symbol_conf.filter_relative ^= 1;
3192 if (!is_report_browser(hbt)) {
3193 struct perf_top *top = hbt->arg;
3195 top->zero = !top->zero;
3199 if (ui_browser__input_window("Percent Limit",
3200 "Please enter the value you want to hide entries under that percent.",
3201 buf, "ENTER: OK, ESC: Cancel",
3202 delay_secs * 2) == K_ENTER) {
3204 double new_percent = strtod(buf, &end);
3206 if (new_percent < 0 || new_percent > 100) {
3207 ui_browser__warning(&browser->b, delay_secs * 2,
3208 "Invalid percent: %.2f", new_percent);
3212 hist_browser__update_percent_limit(browser, new_percent);
3213 hist_browser__reset(browser);
3219 ui_browser__help_window(&browser->b,
3220 is_report_browser(hbt) ? report_help : top_help);
3231 if (pstack__empty(browser->pstack)) {
3233 * Go back to the perf_evsel_menu__run or other user
3236 goto out_free_stack;
3239 ui_browser__dialog_yesno(&browser->b,
3240 "Do you really want to exit?"))
3241 goto out_free_stack;
3245 actions->ms.map = map;
3246 top = pstack__peek(browser->pstack);
3247 if (top == &browser->hists->dso_filter) {
3249 * No need to set actions->dso here since
3250 * it's just to remove the current filter.
3251 * Ditto for thread below.
3253 do_zoom_dso(browser, actions);
3254 } else if (top == &browser->hists->thread_filter) {
3255 do_zoom_thread(browser, actions);
3256 } else if (top == &browser->hists->socket_filter) {
3257 do_zoom_socket(browser, actions);
3263 goto out_free_stack;
3265 if (!is_report_browser(hbt)) {
3266 struct perf_top *top = hbt->arg;
3268 evlist__toggle_enable(top->evlist);
3270 * No need to refresh, resort/decay histogram
3271 * entries if we are not collecting samples:
3273 if (top->evlist->enabled) {
3274 helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
3275 hbt->refresh = delay_secs;
3277 helpline = "Press 'f' again to re-enable the events";
3284 helpline = "Press '?' for help on key bindings";
3288 if (!hists__has(hists, sym) || browser->selection == NULL)
3289 goto skip_annotation;
3291 if (sort__mode == SORT_MODE__BRANCH) {
3293 if (browser->he_selection)
3294 bi = browser->he_selection->branch_info;
3297 goto skip_annotation;
3299 nr_options += add_annotate_opt(browser,
3300 &actions[nr_options],
3301 &options[nr_options],
3304 if (bi->to.ms.sym != bi->from.ms.sym)
3305 nr_options += add_annotate_opt(browser,
3306 &actions[nr_options],
3307 &options[nr_options],
3311 nr_options += add_annotate_opt(browser,
3312 &actions[nr_options],
3313 &options[nr_options],
3315 browser->he_selection->ip);
3318 nr_options += add_thread_opt(browser, &actions[nr_options],
3319 &options[nr_options], thread);
3320 nr_options += add_dso_opt(browser, &actions[nr_options],
3321 &options[nr_options], map);
3322 nr_options += add_callchain_toggle_opt(browser, &actions[nr_options], &options[nr_options]);
3323 nr_options += add_map_opt(browser, &actions[nr_options],
3324 &options[nr_options],
3325 browser->selection ?
3326 browser->selection->map : NULL);
3327 nr_options += add_socket_opt(browser, &actions[nr_options],
3328 &options[nr_options],
3330 /* perf script support */
3331 if (!is_report_browser(hbt))
3332 goto skip_scripting;
3334 if (browser->he_selection) {
3335 if (hists__has(hists, thread) && thread) {
3336 nr_options += add_script_opt(browser,
3337 &actions[nr_options],
3338 &options[nr_options],
3339 thread, NULL, evsel);
3342 * Note that browser->selection != NULL
3343 * when browser->he_selection is not NULL,
3344 * so we don't need to check browser->selection
3345 * before fetching browser->selection->sym like what
3346 * we do before fetching browser->selection->map.
3348 * See hist_browser__show_entry.
3350 if (hists__has(hists, sym) && browser->selection->sym) {
3351 nr_options += add_script_opt(browser,
3352 &actions[nr_options],
3353 &options[nr_options],
3354 NULL, browser->selection->sym,
3358 nr_options += add_script_opt(browser, &actions[nr_options],
3359 &options[nr_options], NULL, NULL, evsel);
3360 nr_options += add_res_sample_opt(browser, &actions[nr_options],
3361 &options[nr_options],
3362 hist_browser__selected_res_sample(browser),
3364 nr_options += add_res_sample_opt(browser, &actions[nr_options],
3365 &options[nr_options],
3366 hist_browser__selected_res_sample(browser),
3368 nr_options += add_res_sample_opt(browser, &actions[nr_options],
3369 &options[nr_options],
3370 hist_browser__selected_res_sample(browser),
3372 nr_options += add_switch_opt(browser, &actions[nr_options],
3373 &options[nr_options]);
3375 nr_options += add_exit_opt(browser, &actions[nr_options],
3376 &options[nr_options]);
3379 struct popup_action *act;
3381 choice = ui__popup_menu(nr_options, options, &key);
3385 if (choice == nr_options)
3388 act = &actions[choice];
3389 key = act->fn(browser, act);
3392 if (key == K_SWITCH_INPUT_DATA)
3396 pstack__delete(browser->pstack);
3398 hist_browser__delete(browser);
3399 free_popup_options(options, MAX_OPTIONS);
3404 struct ui_browser b;
3405 struct evsel *selection;
3406 struct annotation_options *annotation_opts;
3407 bool lost_events, lost_events_warned;
3409 struct perf_env *env;
3412 static void perf_evsel_menu__write(struct ui_browser *browser,
3413 void *entry, int row)
3415 struct evsel_menu *menu = container_of(browser,
3416 struct evsel_menu, b);
3417 struct evsel *evsel = list_entry(entry, struct evsel, core.node);
3418 struct hists *hists = evsel__hists(evsel);
3419 bool current_entry = ui_browser__is_current_entry(browser, row);
3420 unsigned long nr_events = hists->stats.nr_samples;
3421 const char *ev_name = evsel__name(evsel);
3423 const char *warn = " ";
3426 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
3427 HE_COLORSET_NORMAL);
3429 if (evsel__is_group_event(evsel)) {
3432 ev_name = evsel__group_name(evsel);
3434 for_each_group_member(pos, evsel) {
3435 struct hists *pos_hists = evsel__hists(pos);
3436 nr_events += pos_hists->stats.nr_samples;
3440 nr_events = convert_unit(nr_events, &unit);
3441 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
3442 unit, unit == ' ' ? "" : " ", ev_name);
3443 ui_browser__printf(browser, "%s", bf);
3445 nr_events = evsel->evlist->stats.nr_events[PERF_RECORD_LOST];
3446 if (nr_events != 0) {
3447 menu->lost_events = true;
3449 ui_browser__set_color(browser, HE_COLORSET_TOP);
3450 nr_events = convert_unit(nr_events, &unit);
3451 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
3452 nr_events, unit, unit == ' ' ? "" : " ");
3456 ui_browser__write_nstring(browser, warn, browser->width - printed);
3459 menu->selection = evsel;
3462 static int perf_evsel_menu__run(struct evsel_menu *menu,
3463 int nr_events, const char *help,
3464 struct hist_browser_timer *hbt,
3465 bool warn_lost_event)
3467 struct evlist *evlist = menu->b.priv;
3469 const char *title = "Available samples";
3470 int delay_secs = hbt ? hbt->refresh : 0;
3473 if (ui_browser__show(&menu->b, title,
3474 "ESC: exit, ENTER|->: Browse histograms") < 0)
3478 key = ui_browser__run(&menu->b, delay_secs);
3483 hbt->timer(hbt->arg);
3485 if (!menu->lost_events_warned &&
3486 menu->lost_events &&
3488 ui_browser__warn_lost_events(&menu->b);
3489 menu->lost_events_warned = true;
3494 if (!menu->selection)
3496 pos = menu->selection;
3498 evlist__set_selected(evlist, pos);
3500 * Give the calling tool a chance to populate the non
3501 * default evsel resorted hists tree.
3504 hbt->timer(hbt->arg);
3505 key = evsel__hists_browse(pos, nr_events, help, true, hbt,
3506 menu->min_pcnt, menu->env,
3508 menu->annotation_opts);
3509 ui_browser__show_title(&menu->b, title);
3512 if (pos->core.node.next == &evlist->core.entries)
3513 pos = evlist__first(evlist);
3515 pos = evsel__next(pos);
3518 if (pos->core.node.prev == &evlist->core.entries)
3519 pos = evlist__last(evlist);
3521 pos = evsel__prev(pos);
3523 case K_SWITCH_INPUT_DATA:
3535 if (!ui_browser__dialog_yesno(&menu->b,
3536 "Do you really want to exit?"))
3548 ui_browser__hide(&menu->b);
3552 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
3555 struct evsel *evsel = list_entry(entry, struct evsel, core.node);
3557 if (symbol_conf.event_group && !evsel__is_group_leader(evsel))
3563 static int __evlist__tui_browse_hists(struct evlist *evlist, int nr_entries, const char *help,
3564 struct hist_browser_timer *hbt, float min_pcnt, struct perf_env *env,
3565 bool warn_lost_event, struct annotation_options *annotation_opts)
3568 struct evsel_menu menu = {
3570 .entries = &evlist->core.entries,
3571 .refresh = ui_browser__list_head_refresh,
3572 .seek = ui_browser__list_head_seek,
3573 .write = perf_evsel_menu__write,
3574 .filter = filter_group_entries,
3575 .nr_entries = nr_entries,
3578 .min_pcnt = min_pcnt,
3580 .annotation_opts = annotation_opts,
3583 ui_helpline__push("Press ESC to exit");
3585 evlist__for_each_entry(evlist, pos) {
3586 const char *ev_name = evsel__name(pos);
3587 size_t line_len = strlen(ev_name) + 7;
3589 if (menu.b.width < line_len)
3590 menu.b.width = line_len;
3593 return perf_evsel_menu__run(&menu, nr_entries, help,
3594 hbt, warn_lost_event);
3597 static bool evlist__single_entry(struct evlist *evlist)
3599 int nr_entries = evlist->core.nr_entries;
3601 if (nr_entries == 1)
3604 if (nr_entries == 2) {
3605 struct evsel *last = evlist__last(evlist);
3607 if (evsel__is_dummy_event(last))
3614 int evlist__tui_browse_hists(struct evlist *evlist, const char *help, struct hist_browser_timer *hbt,
3615 float min_pcnt, struct perf_env *env, bool warn_lost_event,
3616 struct annotation_options *annotation_opts)
3618 int nr_entries = evlist->core.nr_entries;
3620 if (evlist__single_entry(evlist)) {
3622 struct evsel *first = evlist__first(evlist);
3624 return evsel__hists_browse(first, nr_entries, help, false, hbt, min_pcnt,
3625 env, warn_lost_event, annotation_opts);
3629 if (symbol_conf.event_group) {
3633 evlist__for_each_entry(evlist, pos) {
3634 if (evsel__is_group_leader(pos))
3638 if (nr_entries == 1)
3642 return __evlist__tui_browse_hists(evlist, nr_entries, help, hbt, min_pcnt, env,
3643 warn_lost_event, annotation_opts);
3646 static int block_hists_browser__title(struct hist_browser *browser, char *bf,
3649 struct hists *hists = evsel__hists(browser->block_evsel);
3650 const char *evname = evsel__name(browser->block_evsel);
3651 unsigned long nr_samples = hists->stats.nr_samples;
3654 ret = scnprintf(bf, size, "# Samples: %lu", nr_samples);
3656 scnprintf(bf + ret, size - ret, " of event '%s'", evname);
3661 int block_hists_tui_browse(struct block_hist *bh, struct evsel *evsel,
3662 float min_percent, struct perf_env *env,
3663 struct annotation_options *annotation_opts)
3665 struct hists *hists = &bh->block_hists;
3666 struct hist_browser *browser;
3668 struct popup_action action;
3669 static const char help[] =
3672 browser = hist_browser__new(hists);
3676 browser->block_evsel = evsel;
3677 browser->title = block_hists_browser__title;
3678 browser->min_pcnt = min_percent;
3680 browser->annotation_opts = annotation_opts;
3682 /* reset abort key so that it can get Ctrl-C as a key */
3684 SLang_init_tty(0, 0, 0);
3686 memset(&action, 0, sizeof(action));
3689 key = hist_browser__run(browser, "? - help", true, 0);
3695 ui_browser__help_window(&browser->b, help);
3699 if (!browser->selection ||
3700 !browser->selection->sym) {
3704 action.ms.map = browser->selection->map;
3705 action.ms.sym = browser->selection->sym;
3706 do_annotate(browser, &action);
3714 hist_browser__delete(browser);