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 "../browser.h"
16 #include "../helpline.h"
25 struct hist_entry
*he_selection
;
26 struct map_symbol
*selection
;
27 struct hist_browser_timer
*hbt
;
28 struct pstack
*pstack
;
34 u64 nr_non_filtered_entries
;
35 u64 nr_callchain_rows
;
38 extern void hist_browser__init_hpp(void);
40 static int hists__browser_title(struct hists
*hists
,
41 struct hist_browser_timer
*hbt
,
42 char *bf
, size_t size
);
43 static void hist_browser__update_nr_entries(struct hist_browser
*hb
);
45 static struct rb_node
*hists__filter_entries(struct rb_node
*nd
,
48 static bool hist_browser__has_filter(struct hist_browser
*hb
)
50 return hists__has_filter(hb
->hists
) || hb
->min_pcnt
|| symbol_conf
.has_filter
;
53 static int hist_browser__get_folding(struct hist_browser
*browser
)
56 struct hists
*hists
= browser
->hists
;
57 int unfolded_rows
= 0;
59 for (nd
= rb_first(&hists
->entries
);
60 (nd
= hists__filter_entries(nd
, browser
->min_pcnt
)) != NULL
;
62 struct hist_entry
*he
=
63 rb_entry(nd
, struct hist_entry
, rb_node
);
66 unfolded_rows
+= he
->nr_rows
;
71 static u32
hist_browser__nr_entries(struct hist_browser
*hb
)
75 if (hist_browser__has_filter(hb
))
76 nr_entries
= hb
->nr_non_filtered_entries
;
78 nr_entries
= hb
->hists
->nr_entries
;
80 hb
->nr_callchain_rows
= hist_browser__get_folding(hb
);
81 return nr_entries
+ hb
->nr_callchain_rows
;
84 static void hist_browser__update_rows(struct hist_browser
*hb
)
86 struct ui_browser
*browser
= &hb
->b
;
87 u16 header_offset
= hb
->show_headers
? 1 : 0, index_row
;
89 browser
->rows
= browser
->height
- header_offset
;
91 * Verify if we were at the last line and that line isn't
92 * visibe because we now show the header line(s).
94 index_row
= browser
->index
- browser
->top_idx
;
95 if (index_row
>= browser
->rows
)
96 browser
->index
-= index_row
- browser
->rows
+ 1;
99 static void hist_browser__refresh_dimensions(struct ui_browser
*browser
)
101 struct hist_browser
*hb
= container_of(browser
, struct hist_browser
, b
);
103 /* 3 == +/- toggle symbol before actual hist_entry rendering */
104 browser
->width
= 3 + (hists__sort_list_width(hb
->hists
) + sizeof("[k]"));
106 * FIXME: Just keeping existing behaviour, but this really should be
107 * before updating browser->width, as it will invalidate the
108 * calculation above. Fix this and the fallout in another
111 ui_browser__refresh_dimensions(browser
);
112 hist_browser__update_rows(hb
);
115 static void hist_browser__gotorc(struct hist_browser
*browser
, int row
, int column
)
117 u16 header_offset
= browser
->show_headers
? 1 : 0;
119 ui_browser__gotorc(&browser
->b
, row
+ header_offset
, column
);
122 static void hist_browser__reset(struct hist_browser
*browser
)
125 * The hists__remove_entry_filter() already folds non-filtered
126 * entries so we can assume it has 0 callchain rows.
128 browser
->nr_callchain_rows
= 0;
130 hist_browser__update_nr_entries(browser
);
131 browser
->b
.nr_entries
= hist_browser__nr_entries(browser
);
132 hist_browser__refresh_dimensions(&browser
->b
);
133 ui_browser__reset_index(&browser
->b
);
136 static char tree__folded_sign(bool unfolded
)
138 return unfolded
? '-' : '+';
141 static char hist_entry__folded(const struct hist_entry
*he
)
143 return he
->has_children
? tree__folded_sign(he
->unfolded
) : ' ';
146 static char callchain_list__folded(const struct callchain_list
*cl
)
148 return cl
->has_children
? tree__folded_sign(cl
->unfolded
) : ' ';
151 static void callchain_list__set_folding(struct callchain_list
*cl
, bool unfold
)
153 cl
->unfolded
= unfold
? cl
->has_children
: false;
156 static int callchain_node__count_rows_rb_tree(struct callchain_node
*node
)
161 for (nd
= rb_first(&node
->rb_root
); nd
; nd
= rb_next(nd
)) {
162 struct callchain_node
*child
= rb_entry(nd
, struct callchain_node
, rb_node
);
163 struct callchain_list
*chain
;
164 char folded_sign
= ' '; /* No children */
166 list_for_each_entry(chain
, &child
->val
, list
) {
168 /* We need this because we may not have children */
169 folded_sign
= callchain_list__folded(chain
);
170 if (folded_sign
== '+')
174 if (folded_sign
== '-') /* Have children and they're unfolded */
175 n
+= callchain_node__count_rows_rb_tree(child
);
181 static int callchain_node__count_flat_rows(struct callchain_node
*node
)
183 struct callchain_list
*chain
;
184 char folded_sign
= 0;
187 list_for_each_entry(chain
, &node
->parent_val
, list
) {
189 /* only check first chain list entry */
190 folded_sign
= callchain_list__folded(chain
);
191 if (folded_sign
== '+')
197 list_for_each_entry(chain
, &node
->val
, list
) {
199 /* node->parent_val list might be empty */
200 folded_sign
= callchain_list__folded(chain
);
201 if (folded_sign
== '+')
210 static int callchain_node__count_folded_rows(struct callchain_node
*node __maybe_unused
)
215 static int callchain_node__count_rows(struct callchain_node
*node
)
217 struct callchain_list
*chain
;
218 bool unfolded
= false;
221 if (callchain_param
.mode
== CHAIN_FLAT
)
222 return callchain_node__count_flat_rows(node
);
223 else if (callchain_param
.mode
== CHAIN_FOLDED
)
224 return callchain_node__count_folded_rows(node
);
226 list_for_each_entry(chain
, &node
->val
, list
) {
228 unfolded
= chain
->unfolded
;
232 n
+= callchain_node__count_rows_rb_tree(node
);
237 static int callchain__count_rows(struct rb_root
*chain
)
242 for (nd
= rb_first(chain
); nd
; nd
= rb_next(nd
)) {
243 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
244 n
+= callchain_node__count_rows(node
);
250 static bool hist_entry__toggle_fold(struct hist_entry
*he
)
255 if (!he
->has_children
)
258 he
->unfolded
= !he
->unfolded
;
262 static bool callchain_list__toggle_fold(struct callchain_list
*cl
)
267 if (!cl
->has_children
)
270 cl
->unfolded
= !cl
->unfolded
;
274 static void callchain_node__init_have_children_rb_tree(struct callchain_node
*node
)
276 struct rb_node
*nd
= rb_first(&node
->rb_root
);
278 for (nd
= rb_first(&node
->rb_root
); nd
; nd
= rb_next(nd
)) {
279 struct callchain_node
*child
= rb_entry(nd
, struct callchain_node
, rb_node
);
280 struct callchain_list
*chain
;
283 list_for_each_entry(chain
, &child
->val
, list
) {
286 chain
->has_children
= chain
->list
.next
!= &child
->val
||
287 !RB_EMPTY_ROOT(&child
->rb_root
);
289 chain
->has_children
= chain
->list
.next
== &child
->val
&&
290 !RB_EMPTY_ROOT(&child
->rb_root
);
293 callchain_node__init_have_children_rb_tree(child
);
297 static void callchain_node__init_have_children(struct callchain_node
*node
,
300 struct callchain_list
*chain
;
302 chain
= list_entry(node
->val
.next
, struct callchain_list
, list
);
303 chain
->has_children
= has_sibling
;
305 if (node
->val
.next
!= node
->val
.prev
) {
306 chain
= list_entry(node
->val
.prev
, struct callchain_list
, list
);
307 chain
->has_children
= !RB_EMPTY_ROOT(&node
->rb_root
);
310 callchain_node__init_have_children_rb_tree(node
);
313 static void callchain__init_have_children(struct rb_root
*root
)
315 struct rb_node
*nd
= rb_first(root
);
316 bool has_sibling
= nd
&& rb_next(nd
);
318 for (nd
= rb_first(root
); nd
; nd
= rb_next(nd
)) {
319 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
320 callchain_node__init_have_children(node
, has_sibling
);
321 if (callchain_param
.mode
== CHAIN_FLAT
||
322 callchain_param
.mode
== CHAIN_FOLDED
)
323 callchain_node__make_parent_list(node
);
327 static void hist_entry__init_have_children(struct hist_entry
*he
)
329 if (!he
->init_have_children
) {
330 he
->has_children
= !RB_EMPTY_ROOT(&he
->sorted_chain
);
331 callchain__init_have_children(&he
->sorted_chain
);
332 he
->init_have_children
= true;
336 static bool hist_browser__toggle_fold(struct hist_browser
*browser
)
338 struct hist_entry
*he
= browser
->he_selection
;
339 struct map_symbol
*ms
= browser
->selection
;
340 struct callchain_list
*cl
= container_of(ms
, struct callchain_list
, ms
);
347 has_children
= hist_entry__toggle_fold(he
);
349 has_children
= callchain_list__toggle_fold(cl
);
352 hist_entry__init_have_children(he
);
353 browser
->b
.nr_entries
-= he
->nr_rows
;
354 browser
->nr_callchain_rows
-= he
->nr_rows
;
357 he
->nr_rows
= callchain__count_rows(&he
->sorted_chain
);
361 browser
->b
.nr_entries
+= he
->nr_rows
;
362 browser
->nr_callchain_rows
+= he
->nr_rows
;
367 /* If it doesn't have children, no toggling performed */
371 static int callchain_node__set_folding_rb_tree(struct callchain_node
*node
, bool unfold
)
376 for (nd
= rb_first(&node
->rb_root
); nd
; nd
= rb_next(nd
)) {
377 struct callchain_node
*child
= rb_entry(nd
, struct callchain_node
, rb_node
);
378 struct callchain_list
*chain
;
379 bool has_children
= false;
381 list_for_each_entry(chain
, &child
->val
, list
) {
383 callchain_list__set_folding(chain
, unfold
);
384 has_children
= chain
->has_children
;
388 n
+= callchain_node__set_folding_rb_tree(child
, unfold
);
394 static int callchain_node__set_folding(struct callchain_node
*node
, bool unfold
)
396 struct callchain_list
*chain
;
397 bool has_children
= false;
400 list_for_each_entry(chain
, &node
->val
, list
) {
402 callchain_list__set_folding(chain
, unfold
);
403 has_children
= chain
->has_children
;
407 n
+= callchain_node__set_folding_rb_tree(node
, unfold
);
412 static int callchain__set_folding(struct rb_root
*chain
, bool unfold
)
417 for (nd
= rb_first(chain
); nd
; nd
= rb_next(nd
)) {
418 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
419 n
+= callchain_node__set_folding(node
, unfold
);
425 static void hist_entry__set_folding(struct hist_entry
*he
, bool unfold
)
427 hist_entry__init_have_children(he
);
428 he
->unfolded
= unfold
? he
->has_children
: false;
430 if (he
->has_children
) {
431 int n
= callchain__set_folding(&he
->sorted_chain
, unfold
);
432 he
->nr_rows
= unfold
? n
: 0;
438 __hist_browser__set_folding(struct hist_browser
*browser
, bool unfold
)
441 struct hists
*hists
= browser
->hists
;
443 for (nd
= rb_first(&hists
->entries
);
444 (nd
= hists__filter_entries(nd
, browser
->min_pcnt
)) != NULL
;
446 struct hist_entry
*he
= rb_entry(nd
, struct hist_entry
, rb_node
);
447 hist_entry__set_folding(he
, unfold
);
448 browser
->nr_callchain_rows
+= he
->nr_rows
;
452 static void hist_browser__set_folding(struct hist_browser
*browser
, bool unfold
)
454 browser
->nr_callchain_rows
= 0;
455 __hist_browser__set_folding(browser
, unfold
);
457 browser
->b
.nr_entries
= hist_browser__nr_entries(browser
);
458 /* Go to the start, we may be way after valid entries after a collapse */
459 ui_browser__reset_index(&browser
->b
);
462 static void ui_browser__warn_lost_events(struct ui_browser
*browser
)
464 ui_browser__warning(browser
, 4,
465 "Events are being lost, check IO/CPU overload!\n\n"
466 "You may want to run 'perf' using a RT scheduler policy:\n\n"
467 " perf top -r 80\n\n"
468 "Or reduce the sampling frequency.");
471 static int hist_browser__run(struct hist_browser
*browser
, const char *help
)
475 struct hist_browser_timer
*hbt
= browser
->hbt
;
476 int delay_secs
= hbt
? hbt
->refresh
: 0;
478 browser
->b
.entries
= &browser
->hists
->entries
;
479 browser
->b
.nr_entries
= hist_browser__nr_entries(browser
);
481 hists__browser_title(browser
->hists
, hbt
, title
, sizeof(title
));
483 if (ui_browser__show(&browser
->b
, title
, "%s", help
) < 0)
487 key
= ui_browser__run(&browser
->b
, delay_secs
);
492 hbt
->timer(hbt
->arg
);
494 if (hist_browser__has_filter(browser
))
495 hist_browser__update_nr_entries(browser
);
497 nr_entries
= hist_browser__nr_entries(browser
);
498 ui_browser__update_nr_entries(&browser
->b
, nr_entries
);
500 if (browser
->hists
->stats
.nr_lost_warned
!=
501 browser
->hists
->stats
.nr_events
[PERF_RECORD_LOST
]) {
502 browser
->hists
->stats
.nr_lost_warned
=
503 browser
->hists
->stats
.nr_events
[PERF_RECORD_LOST
];
504 ui_browser__warn_lost_events(&browser
->b
);
507 hists__browser_title(browser
->hists
,
508 hbt
, title
, sizeof(title
));
509 ui_browser__show_title(&browser
->b
, title
);
512 case 'D': { /* Debug */
514 struct hist_entry
*h
= rb_entry(browser
->b
.top
,
515 struct hist_entry
, rb_node
);
517 ui_helpline__fpush("%d: nr_ent=(%d,%d), rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
518 seq
++, browser
->b
.nr_entries
,
519 browser
->hists
->nr_entries
,
523 h
->row_offset
, h
->nr_rows
);
527 /* Collapse the whole world. */
528 hist_browser__set_folding(browser
, false);
531 /* Expand the whole world. */
532 hist_browser__set_folding(browser
, true);
535 browser
->show_headers
= !browser
->show_headers
;
536 hist_browser__update_rows(browser
);
539 if (hist_browser__toggle_fold(browser
))
547 ui_browser__hide(&browser
->b
);
551 struct callchain_print_arg
{
552 /* for hists browser */
554 bool is_current_entry
;
561 typedef void (*print_callchain_entry_fn
)(struct hist_browser
*browser
,
562 struct callchain_list
*chain
,
563 const char *str
, int offset
,
565 struct callchain_print_arg
*arg
);
567 static void hist_browser__show_callchain_entry(struct hist_browser
*browser
,
568 struct callchain_list
*chain
,
569 const char *str
, int offset
,
571 struct callchain_print_arg
*arg
)
574 char folded_sign
= callchain_list__folded(chain
);
575 bool show_annotated
= browser
->show_dso
&& chain
->ms
.sym
&& symbol__annotation(chain
->ms
.sym
)->src
;
577 color
= HE_COLORSET_NORMAL
;
578 width
= browser
->b
.width
- (offset
+ 2);
579 if (ui_browser__is_current_entry(&browser
->b
, row
)) {
580 browser
->selection
= &chain
->ms
;
581 color
= HE_COLORSET_SELECTED
;
582 arg
->is_current_entry
= true;
585 ui_browser__set_color(&browser
->b
, color
);
586 hist_browser__gotorc(browser
, row
, 0);
587 ui_browser__write_nstring(&browser
->b
, " ", offset
);
588 ui_browser__printf(&browser
->b
, "%c", folded_sign
);
589 ui_browser__write_graph(&browser
->b
, show_annotated
? SLSMG_RARROW_CHAR
: ' ');
590 ui_browser__write_nstring(&browser
->b
, str
, width
);
593 static void hist_browser__fprintf_callchain_entry(struct hist_browser
*b __maybe_unused
,
594 struct callchain_list
*chain
,
595 const char *str
, int offset
,
596 unsigned short row __maybe_unused
,
597 struct callchain_print_arg
*arg
)
599 char folded_sign
= callchain_list__folded(chain
);
601 arg
->printed
+= fprintf(arg
->fp
, "%*s%c %s\n", offset
, " ",
605 typedef bool (*check_output_full_fn
)(struct hist_browser
*browser
,
608 static bool hist_browser__check_output_full(struct hist_browser
*browser
,
611 return browser
->b
.rows
== row
;
614 static bool hist_browser__check_dump_full(struct hist_browser
*browser __maybe_unused
,
615 unsigned short row __maybe_unused
)
620 #define LEVEL_OFFSET_STEP 3
622 static int hist_browser__show_callchain_list(struct hist_browser
*browser
,
623 struct callchain_node
*node
,
624 struct callchain_list
*chain
,
625 unsigned short row
, u64 total
,
626 bool need_percent
, int offset
,
627 print_callchain_entry_fn print
,
628 struct callchain_print_arg
*arg
)
630 char bf
[1024], *alloc_str
;
633 if (arg
->row_offset
!= 0) {
639 str
= callchain_list__sym_name(chain
, bf
, sizeof(bf
),
645 callchain_node__scnprintf_value(node
, buf
, sizeof(buf
),
648 if (asprintf(&alloc_str
, "%s %s", buf
, str
) < 0)
649 str
= "Not enough memory!";
654 print(browser
, chain
, str
, offset
, row
, arg
);
660 static int hist_browser__show_callchain_flat(struct hist_browser
*browser
,
661 struct rb_root
*root
,
662 unsigned short row
, u64 total
,
663 u64 parent_total __maybe_unused
,
664 print_callchain_entry_fn print
,
665 struct callchain_print_arg
*arg
,
666 check_output_full_fn is_output_full
)
668 struct rb_node
*node
;
669 int first_row
= row
, offset
= LEVEL_OFFSET_STEP
;
672 node
= rb_first(root
);
673 need_percent
= node
&& rb_next(node
);
676 struct callchain_node
*child
= rb_entry(node
, struct callchain_node
, rb_node
);
677 struct rb_node
*next
= rb_next(node
);
678 struct callchain_list
*chain
;
679 char folded_sign
= ' ';
681 int extra_offset
= 0;
683 list_for_each_entry(chain
, &child
->parent_val
, list
) {
684 bool was_first
= first
;
688 else if (need_percent
)
689 extra_offset
= LEVEL_OFFSET_STEP
;
691 folded_sign
= callchain_list__folded(chain
);
693 row
+= hist_browser__show_callchain_list(browser
, child
,
695 was_first
&& need_percent
,
696 offset
+ extra_offset
,
699 if (is_output_full(browser
, row
))
702 if (folded_sign
== '+')
706 list_for_each_entry(chain
, &child
->val
, list
) {
707 bool was_first
= first
;
711 else if (need_percent
)
712 extra_offset
= LEVEL_OFFSET_STEP
;
714 folded_sign
= callchain_list__folded(chain
);
716 row
+= hist_browser__show_callchain_list(browser
, child
,
718 was_first
&& need_percent
,
719 offset
+ extra_offset
,
722 if (is_output_full(browser
, row
))
725 if (folded_sign
== '+')
730 if (is_output_full(browser
, row
))
735 return row
- first_row
;
738 static char *hist_browser__folded_callchain_str(struct hist_browser
*browser
,
739 struct callchain_list
*chain
,
740 char *value_str
, char *old_str
)
746 str
= callchain_list__sym_name(chain
, bf
, sizeof(bf
),
749 if (asprintf(&new, "%s%s%s", old_str
,
750 symbol_conf
.field_sep
?: ";", str
) < 0)
754 if (asprintf(&new, "%s %s", value_str
, str
) < 0)
757 if (asprintf(&new, "%s", str
) < 0)
764 static int hist_browser__show_callchain_folded(struct hist_browser
*browser
,
765 struct rb_root
*root
,
766 unsigned short row
, u64 total
,
767 u64 parent_total __maybe_unused
,
768 print_callchain_entry_fn print
,
769 struct callchain_print_arg
*arg
,
770 check_output_full_fn is_output_full
)
772 struct rb_node
*node
;
773 int first_row
= row
, offset
= LEVEL_OFFSET_STEP
;
776 node
= rb_first(root
);
777 need_percent
= node
&& rb_next(node
);
780 struct callchain_node
*child
= rb_entry(node
, struct callchain_node
, rb_node
);
781 struct rb_node
*next
= rb_next(node
);
782 struct callchain_list
*chain
, *first_chain
= NULL
;
784 char *value_str
= NULL
, *value_str_alloc
= NULL
;
785 char *chain_str
= NULL
, *chain_str_alloc
= NULL
;
787 if (arg
->row_offset
!= 0) {
795 callchain_node__scnprintf_value(child
, buf
, sizeof(buf
), total
);
796 if (asprintf(&value_str
, "%s", buf
) < 0) {
797 value_str
= (char *)"<...>";
800 value_str_alloc
= value_str
;
803 list_for_each_entry(chain
, &child
->parent_val
, list
) {
804 chain_str
= hist_browser__folded_callchain_str(browser
,
805 chain
, value_str
, chain_str
);
811 if (chain_str
== NULL
) {
812 chain_str
= (char *)"Not enough memory!";
816 chain_str_alloc
= chain_str
;
819 list_for_each_entry(chain
, &child
->val
, list
) {
820 chain_str
= hist_browser__folded_callchain_str(browser
,
821 chain
, value_str
, chain_str
);
827 if (chain_str
== NULL
) {
828 chain_str
= (char *)"Not enough memory!";
832 chain_str_alloc
= chain_str
;
836 print(browser
, first_chain
, chain_str
, offset
, row
++, arg
);
837 free(value_str_alloc
);
838 free(chain_str_alloc
);
841 if (is_output_full(browser
, row
))
846 return row
- first_row
;
849 static int hist_browser__show_callchain_graph(struct hist_browser
*browser
,
850 struct rb_root
*root
, int level
,
851 unsigned short row
, u64 total
,
853 print_callchain_entry_fn print
,
854 struct callchain_print_arg
*arg
,
855 check_output_full_fn is_output_full
)
857 struct rb_node
*node
;
858 int first_row
= row
, offset
= level
* LEVEL_OFFSET_STEP
;
860 u64 percent_total
= total
;
862 if (callchain_param
.mode
== CHAIN_GRAPH_REL
)
863 percent_total
= parent_total
;
865 node
= rb_first(root
);
866 need_percent
= node
&& rb_next(node
);
869 struct callchain_node
*child
= rb_entry(node
, struct callchain_node
, rb_node
);
870 struct rb_node
*next
= rb_next(node
);
871 struct callchain_list
*chain
;
872 char folded_sign
= ' ';
874 int extra_offset
= 0;
876 list_for_each_entry(chain
, &child
->val
, list
) {
877 bool was_first
= first
;
881 else if (need_percent
)
882 extra_offset
= LEVEL_OFFSET_STEP
;
884 folded_sign
= callchain_list__folded(chain
);
886 row
+= hist_browser__show_callchain_list(browser
, child
,
887 chain
, row
, percent_total
,
888 was_first
&& need_percent
,
889 offset
+ extra_offset
,
892 if (is_output_full(browser
, row
))
895 if (folded_sign
== '+')
899 if (folded_sign
== '-') {
900 const int new_level
= level
+ (extra_offset
? 2 : 1);
902 row
+= hist_browser__show_callchain_graph(browser
, &child
->rb_root
,
903 new_level
, row
, total
,
905 print
, arg
, is_output_full
);
907 if (is_output_full(browser
, row
))
912 return row
- first_row
;
915 static int hist_browser__show_callchain(struct hist_browser
*browser
,
916 struct hist_entry
*entry
, int level
,
918 print_callchain_entry_fn print
,
919 struct callchain_print_arg
*arg
,
920 check_output_full_fn is_output_full
)
922 u64 total
= hists__total_period(entry
->hists
);
926 if (symbol_conf
.cumulate_callchain
)
927 parent_total
= entry
->stat_acc
->period
;
929 parent_total
= entry
->stat
.period
;
931 if (callchain_param
.mode
== CHAIN_FLAT
) {
932 printed
= hist_browser__show_callchain_flat(browser
,
933 &entry
->sorted_chain
, row
,
934 total
, parent_total
, print
, arg
,
936 } else if (callchain_param
.mode
== CHAIN_FOLDED
) {
937 printed
= hist_browser__show_callchain_folded(browser
,
938 &entry
->sorted_chain
, row
,
939 total
, parent_total
, print
, arg
,
942 printed
= hist_browser__show_callchain_graph(browser
,
943 &entry
->sorted_chain
, level
, row
,
944 total
, parent_total
, print
, arg
,
948 if (arg
->is_current_entry
)
949 browser
->he_selection
= entry
;
955 struct ui_browser
*b
;
960 static int __hpp__slsmg_color_printf(struct perf_hpp
*hpp
, const char *fmt
, ...)
962 struct hpp_arg
*arg
= hpp
->ptr
;
968 len
= va_arg(args
, int);
969 percent
= va_arg(args
, double);
972 ui_browser__set_percent_color(arg
->b
, percent
, arg
->current_entry
);
974 ret
= scnprintf(hpp
->buf
, hpp
->size
, fmt
, len
, percent
);
975 ui_browser__printf(arg
->b
, "%s", hpp
->buf
);
977 advance_hpp(hpp
, ret
);
981 #define __HPP_COLOR_PERCENT_FN(_type, _field) \
982 static u64 __hpp_get_##_field(struct hist_entry *he) \
984 return he->stat._field; \
988 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
989 struct perf_hpp *hpp, \
990 struct hist_entry *he) \
992 return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%", \
993 __hpp__slsmg_color_printf, true); \
996 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
997 static u64 __hpp_get_acc_##_field(struct hist_entry *he) \
999 return he->stat_acc->_field; \
1003 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
1004 struct perf_hpp *hpp, \
1005 struct hist_entry *he) \
1007 if (!symbol_conf.cumulate_callchain) { \
1008 struct hpp_arg *arg = hpp->ptr; \
1009 int len = fmt->user_len ?: fmt->len; \
1010 int ret = scnprintf(hpp->buf, hpp->size, \
1011 "%*s", len, "N/A"); \
1012 ui_browser__printf(arg->b, "%s", hpp->buf); \
1016 return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field, \
1017 " %*.2f%%", __hpp__slsmg_color_printf, true); \
1020 __HPP_COLOR_PERCENT_FN(overhead
, period
)
1021 __HPP_COLOR_PERCENT_FN(overhead_sys
, period_sys
)
1022 __HPP_COLOR_PERCENT_FN(overhead_us
, period_us
)
1023 __HPP_COLOR_PERCENT_FN(overhead_guest_sys
, period_guest_sys
)
1024 __HPP_COLOR_PERCENT_FN(overhead_guest_us
, period_guest_us
)
1025 __HPP_COLOR_ACC_PERCENT_FN(overhead_acc
, period
)
1027 #undef __HPP_COLOR_PERCENT_FN
1028 #undef __HPP_COLOR_ACC_PERCENT_FN
1030 void hist_browser__init_hpp(void)
1032 perf_hpp__format
[PERF_HPP__OVERHEAD
].color
=
1033 hist_browser__hpp_color_overhead
;
1034 perf_hpp__format
[PERF_HPP__OVERHEAD_SYS
].color
=
1035 hist_browser__hpp_color_overhead_sys
;
1036 perf_hpp__format
[PERF_HPP__OVERHEAD_US
].color
=
1037 hist_browser__hpp_color_overhead_us
;
1038 perf_hpp__format
[PERF_HPP__OVERHEAD_GUEST_SYS
].color
=
1039 hist_browser__hpp_color_overhead_guest_sys
;
1040 perf_hpp__format
[PERF_HPP__OVERHEAD_GUEST_US
].color
=
1041 hist_browser__hpp_color_overhead_guest_us
;
1042 perf_hpp__format
[PERF_HPP__OVERHEAD_ACC
].color
=
1043 hist_browser__hpp_color_overhead_acc
;
1046 static int hist_browser__show_entry(struct hist_browser
*browser
,
1047 struct hist_entry
*entry
,
1052 int width
= browser
->b
.width
;
1053 char folded_sign
= ' ';
1054 bool current_entry
= ui_browser__is_current_entry(&browser
->b
, row
);
1055 off_t row_offset
= entry
->row_offset
;
1057 struct perf_hpp_fmt
*fmt
;
1059 if (current_entry
) {
1060 browser
->he_selection
= entry
;
1061 browser
->selection
= &entry
->ms
;
1064 if (symbol_conf
.use_callchain
) {
1065 hist_entry__init_have_children(entry
);
1066 folded_sign
= hist_entry__folded(entry
);
1069 if (row_offset
== 0) {
1070 struct hpp_arg arg
= {
1072 .folded_sign
= folded_sign
,
1073 .current_entry
= current_entry
,
1075 struct perf_hpp hpp
= {
1082 hist_browser__gotorc(browser
, row
, 0);
1084 perf_hpp__for_each_format(fmt
) {
1085 if (perf_hpp__should_skip(fmt
, entry
->hists
) ||
1086 column
++ < browser
->b
.horiz_scroll
)
1089 if (current_entry
&& browser
->b
.navkeypressed
) {
1090 ui_browser__set_color(&browser
->b
,
1091 HE_COLORSET_SELECTED
);
1093 ui_browser__set_color(&browser
->b
,
1094 HE_COLORSET_NORMAL
);
1098 if (symbol_conf
.use_callchain
) {
1099 ui_browser__printf(&browser
->b
, "%c ", folded_sign
);
1104 ui_browser__printf(&browser
->b
, " ");
1109 width
-= fmt
->color(fmt
, &hpp
, entry
);
1111 width
-= fmt
->entry(fmt
, &hpp
, entry
);
1112 ui_browser__printf(&browser
->b
, "%s", s
);
1116 /* The scroll bar isn't being used */
1117 if (!browser
->b
.navkeypressed
)
1120 ui_browser__write_nstring(&browser
->b
, "", width
);
1127 if (folded_sign
== '-' && row
!= browser
->b
.rows
) {
1128 struct callchain_print_arg arg
= {
1129 .row_offset
= row_offset
,
1130 .is_current_entry
= current_entry
,
1133 printed
+= hist_browser__show_callchain(browser
, entry
, 1, row
,
1134 hist_browser__show_callchain_entry
, &arg
,
1135 hist_browser__check_output_full
);
1141 static int advance_hpp_check(struct perf_hpp
*hpp
, int inc
)
1143 advance_hpp(hpp
, inc
);
1144 return hpp
->size
<= 0;
1147 static int hists_browser__scnprintf_headers(struct hist_browser
*browser
, char *buf
, size_t size
)
1149 struct hists
*hists
= browser
->hists
;
1150 struct perf_hpp dummy_hpp
= {
1154 struct perf_hpp_fmt
*fmt
;
1158 if (symbol_conf
.use_callchain
) {
1159 ret
= scnprintf(buf
, size
, " ");
1160 if (advance_hpp_check(&dummy_hpp
, ret
))
1164 perf_hpp__for_each_format(fmt
) {
1165 if (perf_hpp__should_skip(fmt
, hists
) || column
++ < browser
->b
.horiz_scroll
)
1168 ret
= fmt
->header(fmt
, &dummy_hpp
, hists_to_evsel(hists
));
1169 if (advance_hpp_check(&dummy_hpp
, ret
))
1172 ret
= scnprintf(dummy_hpp
.buf
, dummy_hpp
.size
, " ");
1173 if (advance_hpp_check(&dummy_hpp
, ret
))
1180 static void hist_browser__show_headers(struct hist_browser
*browser
)
1184 hists_browser__scnprintf_headers(browser
, headers
, sizeof(headers
));
1185 ui_browser__gotorc(&browser
->b
, 0, 0);
1186 ui_browser__set_color(&browser
->b
, HE_COLORSET_ROOT
);
1187 ui_browser__write_nstring(&browser
->b
, headers
, browser
->b
.width
+ 1);
1190 static void ui_browser__hists_init_top(struct ui_browser
*browser
)
1192 if (browser
->top
== NULL
) {
1193 struct hist_browser
*hb
;
1195 hb
= container_of(browser
, struct hist_browser
, b
);
1196 browser
->top
= rb_first(&hb
->hists
->entries
);
1200 static unsigned int hist_browser__refresh(struct ui_browser
*browser
)
1203 u16 header_offset
= 0;
1205 struct hist_browser
*hb
= container_of(browser
, struct hist_browser
, b
);
1207 if (hb
->show_headers
) {
1208 hist_browser__show_headers(hb
);
1212 ui_browser__hists_init_top(browser
);
1213 hb
->he_selection
= NULL
;
1214 hb
->selection
= NULL
;
1216 for (nd
= browser
->top
; nd
; nd
= rb_next(nd
)) {
1217 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1223 percent
= hist_entry__get_percent_limit(h
);
1224 if (percent
< hb
->min_pcnt
)
1227 row
+= hist_browser__show_entry(hb
, h
, row
);
1228 if (row
== browser
->rows
)
1232 return row
+ header_offset
;
1235 static struct rb_node
*hists__filter_entries(struct rb_node
*nd
,
1238 while (nd
!= NULL
) {
1239 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1240 float percent
= hist_entry__get_percent_limit(h
);
1242 if (!h
->filtered
&& percent
>= min_pcnt
)
1251 static struct rb_node
*hists__filter_prev_entries(struct rb_node
*nd
,
1254 while (nd
!= NULL
) {
1255 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1256 float percent
= hist_entry__get_percent_limit(h
);
1258 if (!h
->filtered
&& percent
>= min_pcnt
)
1267 static void ui_browser__hists_seek(struct ui_browser
*browser
,
1268 off_t offset
, int whence
)
1270 struct hist_entry
*h
;
1273 struct hist_browser
*hb
;
1275 hb
= container_of(browser
, struct hist_browser
, b
);
1277 if (browser
->nr_entries
== 0)
1280 ui_browser__hists_init_top(browser
);
1284 nd
= hists__filter_entries(rb_first(browser
->entries
),
1291 nd
= hists__filter_prev_entries(rb_last(browser
->entries
),
1300 * Moves not relative to the first visible entry invalidates its
1303 h
= rb_entry(browser
->top
, struct hist_entry
, rb_node
);
1307 * Here we have to check if nd is expanded (+), if it is we can't go
1308 * the next top level hist_entry, instead we must compute an offset of
1309 * what _not_ to show and not change the first visible entry.
1311 * This offset increments when we are going from top to bottom and
1312 * decreases when we're going from bottom to top.
1314 * As we don't have backpointers to the top level in the callchains
1315 * structure, we need to always print the whole hist_entry callchain,
1316 * skipping the first ones that are before the first visible entry
1317 * and stop when we printed enough lines to fill the screen.
1325 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1327 u16 remaining
= h
->nr_rows
- h
->row_offset
;
1328 if (offset
> remaining
) {
1329 offset
-= remaining
;
1332 h
->row_offset
+= offset
;
1338 nd
= hists__filter_entries(rb_next(nd
), hb
->min_pcnt
);
1343 } while (offset
!= 0);
1344 } else if (offset
< 0) {
1346 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1349 if (-offset
> h
->row_offset
) {
1350 offset
+= h
->row_offset
;
1353 h
->row_offset
+= offset
;
1359 if (-offset
> h
->nr_rows
) {
1360 offset
+= h
->nr_rows
;
1363 h
->row_offset
= h
->nr_rows
+ offset
;
1371 nd
= hists__filter_prev_entries(rb_prev(nd
),
1379 * Last unfiltered hist_entry, check if it is
1380 * unfolded, if it is then we should have
1381 * row_offset at its last entry.
1383 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1385 h
->row_offset
= h
->nr_rows
;
1392 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1397 static int hist_browser__fprintf_callchain(struct hist_browser
*browser
,
1398 struct hist_entry
*he
, FILE *fp
)
1400 struct callchain_print_arg arg
= {
1404 hist_browser__show_callchain(browser
, he
, 1, 0,
1405 hist_browser__fprintf_callchain_entry
, &arg
,
1406 hist_browser__check_dump_full
);
1410 static int hist_browser__fprintf_entry(struct hist_browser
*browser
,
1411 struct hist_entry
*he
, FILE *fp
)
1415 char folded_sign
= ' ';
1416 struct perf_hpp hpp
= {
1420 struct perf_hpp_fmt
*fmt
;
1424 if (symbol_conf
.use_callchain
)
1425 folded_sign
= hist_entry__folded(he
);
1427 if (symbol_conf
.use_callchain
)
1428 printed
+= fprintf(fp
, "%c ", folded_sign
);
1430 perf_hpp__for_each_format(fmt
) {
1431 if (perf_hpp__should_skip(fmt
, he
->hists
))
1435 ret
= scnprintf(hpp
.buf
, hpp
.size
, " ");
1436 advance_hpp(&hpp
, ret
);
1440 ret
= fmt
->entry(fmt
, &hpp
, he
);
1441 advance_hpp(&hpp
, ret
);
1443 printed
+= fprintf(fp
, "%s\n", rtrim(s
));
1445 if (folded_sign
== '-')
1446 printed
+= hist_browser__fprintf_callchain(browser
, he
, fp
);
1451 static int hist_browser__fprintf(struct hist_browser
*browser
, FILE *fp
)
1453 struct rb_node
*nd
= hists__filter_entries(rb_first(browser
->b
.entries
),
1458 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1460 printed
+= hist_browser__fprintf_entry(browser
, h
, fp
);
1461 nd
= hists__filter_entries(rb_next(nd
), browser
->min_pcnt
);
1467 static int hist_browser__dump(struct hist_browser
*browser
)
1473 scnprintf(filename
, sizeof(filename
), "perf.hist.%d", browser
->print_seq
);
1474 if (access(filename
, F_OK
))
1477 * XXX: Just an arbitrary lazy upper limit
1479 if (++browser
->print_seq
== 8192) {
1480 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1485 fp
= fopen(filename
, "w");
1488 const char *err
= strerror_r(errno
, bf
, sizeof(bf
));
1489 ui_helpline__fpush("Couldn't write to %s: %s", filename
, err
);
1493 ++browser
->print_seq
;
1494 hist_browser__fprintf(browser
, fp
);
1496 ui_helpline__fpush("%s written!", filename
);
1501 static struct hist_browser
*hist_browser__new(struct hists
*hists
,
1502 struct hist_browser_timer
*hbt
,
1503 struct perf_env
*env
)
1505 struct hist_browser
*browser
= zalloc(sizeof(*browser
));
1508 browser
->hists
= hists
;
1509 browser
->b
.refresh
= hist_browser__refresh
;
1510 browser
->b
.refresh_dimensions
= hist_browser__refresh_dimensions
;
1511 browser
->b
.seek
= ui_browser__hists_seek
;
1512 browser
->b
.use_navkeypressed
= true;
1513 browser
->show_headers
= symbol_conf
.show_hist_headers
;
1521 static void hist_browser__delete(struct hist_browser
*browser
)
1526 static struct hist_entry
*hist_browser__selected_entry(struct hist_browser
*browser
)
1528 return browser
->he_selection
;
1531 static struct thread
*hist_browser__selected_thread(struct hist_browser
*browser
)
1533 return browser
->he_selection
->thread
;
1536 /* Check whether the browser is for 'top' or 'report' */
1537 static inline bool is_report_browser(void *timer
)
1539 return timer
== NULL
;
1542 static int hists__browser_title(struct hists
*hists
,
1543 struct hist_browser_timer
*hbt
,
1544 char *bf
, size_t size
)
1548 const struct dso
*dso
= hists
->dso_filter
;
1549 const struct thread
*thread
= hists
->thread_filter
;
1550 int socket_id
= hists
->socket_filter
;
1551 unsigned long nr_samples
= hists
->stats
.nr_events
[PERF_RECORD_SAMPLE
];
1552 u64 nr_events
= hists
->stats
.total_period
;
1553 struct perf_evsel
*evsel
= hists_to_evsel(hists
);
1554 const char *ev_name
= perf_evsel__name(evsel
);
1556 size_t buflen
= sizeof(buf
);
1557 char ref
[30] = " show reference callgraph, ";
1558 bool enable_ref
= false;
1560 if (symbol_conf
.filter_relative
) {
1561 nr_samples
= hists
->stats
.nr_non_filtered_samples
;
1562 nr_events
= hists
->stats
.total_non_filtered_period
;
1565 if (perf_evsel__is_group_event(evsel
)) {
1566 struct perf_evsel
*pos
;
1568 perf_evsel__group_desc(evsel
, buf
, buflen
);
1571 for_each_group_member(pos
, evsel
) {
1572 struct hists
*pos_hists
= evsel__hists(pos
);
1574 if (symbol_conf
.filter_relative
) {
1575 nr_samples
+= pos_hists
->stats
.nr_non_filtered_samples
;
1576 nr_events
+= pos_hists
->stats
.total_non_filtered_period
;
1578 nr_samples
+= pos_hists
->stats
.nr_events
[PERF_RECORD_SAMPLE
];
1579 nr_events
+= pos_hists
->stats
.total_period
;
1584 if (symbol_conf
.show_ref_callgraph
&&
1585 strstr(ev_name
, "call-graph=no"))
1587 nr_samples
= convert_unit(nr_samples
, &unit
);
1588 printed
= scnprintf(bf
, size
,
1589 "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64
,
1590 nr_samples
, unit
, ev_name
, enable_ref
? ref
: " ", nr_events
);
1593 if (hists
->uid_filter_str
)
1594 printed
+= snprintf(bf
+ printed
, size
- printed
,
1595 ", UID: %s", hists
->uid_filter_str
);
1597 printed
+= scnprintf(bf
+ printed
, size
- printed
,
1599 (thread
->comm_set
? thread__comm_str(thread
) : ""),
1602 printed
+= scnprintf(bf
+ printed
, size
- printed
,
1603 ", DSO: %s", dso
->short_name
);
1605 printed
+= scnprintf(bf
+ printed
, size
- printed
,
1606 ", Processor Socket: %d", socket_id
);
1607 if (!is_report_browser(hbt
)) {
1608 struct perf_top
*top
= hbt
->arg
;
1611 printed
+= scnprintf(bf
+ printed
, size
- printed
, " [z]");
1617 static inline void free_popup_options(char **options
, int n
)
1621 for (i
= 0; i
< n
; ++i
)
1626 * Only runtime switching of perf data file will make "input_name" point
1627 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1628 * whether we need to call free() for current "input_name" during the switch.
1630 static bool is_input_name_malloced
= false;
1632 static int switch_data_file(void)
1634 char *pwd
, *options
[32], *abs_path
[32], *tmp
;
1636 int nr_options
= 0, choice
= -1, ret
= -1;
1637 struct dirent
*dent
;
1639 pwd
= getenv("PWD");
1643 pwd_dir
= opendir(pwd
);
1647 memset(options
, 0, sizeof(options
));
1648 memset(options
, 0, sizeof(abs_path
));
1650 while ((dent
= readdir(pwd_dir
))) {
1651 char path
[PATH_MAX
];
1653 char *name
= dent
->d_name
;
1656 if (!(dent
->d_type
== DT_REG
))
1659 snprintf(path
, sizeof(path
), "%s/%s", pwd
, name
);
1661 file
= fopen(path
, "r");
1665 if (fread(&magic
, 1, 8, file
) < 8)
1666 goto close_file_and_continue
;
1668 if (is_perf_magic(magic
)) {
1669 options
[nr_options
] = strdup(name
);
1670 if (!options
[nr_options
])
1671 goto close_file_and_continue
;
1673 abs_path
[nr_options
] = strdup(path
);
1674 if (!abs_path
[nr_options
]) {
1675 zfree(&options
[nr_options
]);
1676 ui__warning("Can't search all data files due to memory shortage.\n");
1684 close_file_and_continue
:
1686 if (nr_options
>= 32) {
1687 ui__warning("Too many perf data files in PWD!\n"
1688 "Only the first 32 files will be listed.\n");
1695 choice
= ui__popup_menu(nr_options
, options
);
1696 if (choice
< nr_options
&& choice
>= 0) {
1697 tmp
= strdup(abs_path
[choice
]);
1699 if (is_input_name_malloced
)
1700 free((void *)input_name
);
1702 is_input_name_malloced
= true;
1705 ui__warning("Data switch failed due to memory shortage!\n");
1709 free_popup_options(options
, nr_options
);
1710 free_popup_options(abs_path
, nr_options
);
1714 struct popup_action
{
1715 struct thread
*thread
;
1716 struct map_symbol ms
;
1719 int (*fn
)(struct hist_browser
*browser
, struct popup_action
*act
);
1723 do_annotate(struct hist_browser
*browser
, struct popup_action
*act
)
1725 struct perf_evsel
*evsel
;
1726 struct annotation
*notes
;
1727 struct hist_entry
*he
;
1730 if (!objdump_path
&& perf_env__lookup_objdump(browser
->env
))
1733 notes
= symbol__annotation(act
->ms
.sym
);
1737 evsel
= hists_to_evsel(browser
->hists
);
1738 err
= map_symbol__tui_annotate(&act
->ms
, evsel
, browser
->hbt
);
1739 he
= hist_browser__selected_entry(browser
);
1741 * offer option to annotate the other branch source or target
1742 * (if they exists) when returning from annotate
1744 if ((err
== 'q' || err
== CTRL('c')) && he
->branch_info
)
1747 ui_browser__update_nr_entries(&browser
->b
, browser
->hists
->nr_entries
);
1749 ui_browser__handle_resize(&browser
->b
);
1754 add_annotate_opt(struct hist_browser
*browser __maybe_unused
,
1755 struct popup_action
*act
, char **optstr
,
1756 struct map
*map
, struct symbol
*sym
)
1758 if (sym
== NULL
|| map
->dso
->annotate_warned
)
1761 if (asprintf(optstr
, "Annotate %s", sym
->name
) < 0)
1766 act
->fn
= do_annotate
;
1771 do_zoom_thread(struct hist_browser
*browser
, struct popup_action
*act
)
1773 struct thread
*thread
= act
->thread
;
1775 if (browser
->hists
->thread_filter
) {
1776 pstack__remove(browser
->pstack
, &browser
->hists
->thread_filter
);
1777 perf_hpp__set_elide(HISTC_THREAD
, false);
1778 thread__zput(browser
->hists
->thread_filter
);
1781 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
1782 thread
->comm_set
? thread__comm_str(thread
) : "",
1784 browser
->hists
->thread_filter
= thread__get(thread
);
1785 perf_hpp__set_elide(HISTC_THREAD
, false);
1786 pstack__push(browser
->pstack
, &browser
->hists
->thread_filter
);
1789 hists__filter_by_thread(browser
->hists
);
1790 hist_browser__reset(browser
);
1795 add_thread_opt(struct hist_browser
*browser
, struct popup_action
*act
,
1796 char **optstr
, struct thread
*thread
)
1798 if (!sort__has_thread
|| thread
== NULL
)
1801 if (asprintf(optstr
, "Zoom %s %s(%d) thread",
1802 browser
->hists
->thread_filter
? "out of" : "into",
1803 thread
->comm_set
? thread__comm_str(thread
) : "",
1807 act
->thread
= thread
;
1808 act
->fn
= do_zoom_thread
;
1813 do_zoom_dso(struct hist_browser
*browser
, struct popup_action
*act
)
1815 struct map
*map
= act
->ms
.map
;
1817 if (browser
->hists
->dso_filter
) {
1818 pstack__remove(browser
->pstack
, &browser
->hists
->dso_filter
);
1819 perf_hpp__set_elide(HISTC_DSO
, false);
1820 browser
->hists
->dso_filter
= NULL
;
1825 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
1826 __map__is_kernel(map
) ? "the Kernel" : map
->dso
->short_name
);
1827 browser
->hists
->dso_filter
= map
->dso
;
1828 perf_hpp__set_elide(HISTC_DSO
, true);
1829 pstack__push(browser
->pstack
, &browser
->hists
->dso_filter
);
1832 hists__filter_by_dso(browser
->hists
);
1833 hist_browser__reset(browser
);
1838 add_dso_opt(struct hist_browser
*browser
, struct popup_action
*act
,
1839 char **optstr
, struct map
*map
)
1841 if (!sort__has_dso
|| map
== NULL
)
1844 if (asprintf(optstr
, "Zoom %s %s DSO",
1845 browser
->hists
->dso_filter
? "out of" : "into",
1846 __map__is_kernel(map
) ? "the Kernel" : map
->dso
->short_name
) < 0)
1850 act
->fn
= do_zoom_dso
;
1855 do_browse_map(struct hist_browser
*browser __maybe_unused
,
1856 struct popup_action
*act
)
1858 map__browse(act
->ms
.map
);
1863 add_map_opt(struct hist_browser
*browser __maybe_unused
,
1864 struct popup_action
*act
, char **optstr
, struct map
*map
)
1866 if (!sort__has_dso
|| map
== NULL
)
1869 if (asprintf(optstr
, "Browse map details") < 0)
1873 act
->fn
= do_browse_map
;
1878 do_run_script(struct hist_browser
*browser __maybe_unused
,
1879 struct popup_action
*act
)
1881 char script_opt
[64];
1882 memset(script_opt
, 0, sizeof(script_opt
));
1885 scnprintf(script_opt
, sizeof(script_opt
), " -c %s ",
1886 thread__comm_str(act
->thread
));
1887 } else if (act
->ms
.sym
) {
1888 scnprintf(script_opt
, sizeof(script_opt
), " -S %s ",
1892 script_browse(script_opt
);
1897 add_script_opt(struct hist_browser
*browser __maybe_unused
,
1898 struct popup_action
*act
, char **optstr
,
1899 struct thread
*thread
, struct symbol
*sym
)
1902 if (asprintf(optstr
, "Run scripts for samples of thread [%s]",
1903 thread__comm_str(thread
)) < 0)
1906 if (asprintf(optstr
, "Run scripts for samples of symbol [%s]",
1910 if (asprintf(optstr
, "Run scripts for all samples") < 0)
1914 act
->thread
= thread
;
1916 act
->fn
= do_run_script
;
1921 do_switch_data(struct hist_browser
*browser __maybe_unused
,
1922 struct popup_action
*act __maybe_unused
)
1924 if (switch_data_file()) {
1925 ui__warning("Won't switch the data files due to\n"
1926 "no valid data file get selected!\n");
1930 return K_SWITCH_INPUT_DATA
;
1934 add_switch_opt(struct hist_browser
*browser
,
1935 struct popup_action
*act
, char **optstr
)
1937 if (!is_report_browser(browser
->hbt
))
1940 if (asprintf(optstr
, "Switch to another data file in PWD") < 0)
1943 act
->fn
= do_switch_data
;
1948 do_exit_browser(struct hist_browser
*browser __maybe_unused
,
1949 struct popup_action
*act __maybe_unused
)
1955 add_exit_opt(struct hist_browser
*browser __maybe_unused
,
1956 struct popup_action
*act
, char **optstr
)
1958 if (asprintf(optstr
, "Exit") < 0)
1961 act
->fn
= do_exit_browser
;
1966 do_zoom_socket(struct hist_browser
*browser
, struct popup_action
*act
)
1968 if (browser
->hists
->socket_filter
> -1) {
1969 pstack__remove(browser
->pstack
, &browser
->hists
->socket_filter
);
1970 browser
->hists
->socket_filter
= -1;
1971 perf_hpp__set_elide(HISTC_SOCKET
, false);
1973 browser
->hists
->socket_filter
= act
->socket
;
1974 perf_hpp__set_elide(HISTC_SOCKET
, true);
1975 pstack__push(browser
->pstack
, &browser
->hists
->socket_filter
);
1978 hists__filter_by_socket(browser
->hists
);
1979 hist_browser__reset(browser
);
1984 add_socket_opt(struct hist_browser
*browser
, struct popup_action
*act
,
1985 char **optstr
, int socket_id
)
1987 if (!sort__has_socket
|| socket_id
< 0)
1990 if (asprintf(optstr
, "Zoom %s Processor Socket %d",
1991 (browser
->hists
->socket_filter
> -1) ? "out of" : "into",
1995 act
->socket
= socket_id
;
1996 act
->fn
= do_zoom_socket
;
2000 static void hist_browser__update_nr_entries(struct hist_browser
*hb
)
2003 struct rb_node
*nd
= rb_first(&hb
->hists
->entries
);
2005 if (hb
->min_pcnt
== 0) {
2006 hb
->nr_non_filtered_entries
= hb
->hists
->nr_non_filtered_entries
;
2010 while ((nd
= hists__filter_entries(nd
, hb
->min_pcnt
)) != NULL
) {
2015 hb
->nr_non_filtered_entries
= nr_entries
;
2018 static int perf_evsel__hists_browse(struct perf_evsel
*evsel
, int nr_events
,
2019 const char *helpline
,
2021 struct hist_browser_timer
*hbt
,
2023 struct perf_env
*env
)
2025 struct hists
*hists
= evsel__hists(evsel
);
2026 struct hist_browser
*browser
= hist_browser__new(hists
, hbt
, env
);
2027 struct branch_info
*bi
;
2028 #define MAX_OPTIONS 16
2029 char *options
[MAX_OPTIONS
];
2030 struct popup_action actions
[MAX_OPTIONS
];
2034 int delay_secs
= hbt
? hbt
->refresh
: 0;
2035 struct perf_hpp_fmt
*fmt
;
2037 #define HIST_BROWSER_HELP_COMMON \
2038 "h/?/F1 Show this window\n" \
2040 "PGDN/SPACE Navigate\n" \
2041 "q/ESC/CTRL+C Exit browser\n\n" \
2042 "For multiple event sessions:\n\n" \
2043 "TAB/UNTAB Switch events\n\n" \
2044 "For symbolic views (--sort has sym):\n\n" \
2045 "ENTER Zoom into DSO/Threads & Annotate current symbol\n" \
2047 "a Annotate current symbol\n" \
2048 "C Collapse all callchains\n" \
2049 "d Zoom into current DSO\n" \
2050 "E Expand all callchains\n" \
2051 "F Toggle percentage of filtered entries\n" \
2052 "H Display column headers\n" \
2053 "m Display context menu\n" \
2054 "S Zoom into current Processor Socket\n" \
2056 /* help messages are sorted by lexical order of the hotkey */
2057 const char report_help
[] = HIST_BROWSER_HELP_COMMON
2058 "i Show header information\n"
2059 "P Print histograms to perf.hist.N\n"
2060 "r Run available scripts\n"
2061 "s Switch to another data file in PWD\n"
2062 "t Zoom into current Thread\n"
2063 "V Verbose (DSO names in callchains, etc)\n"
2064 "/ Filter symbol by name";
2065 const char top_help
[] = HIST_BROWSER_HELP_COMMON
2066 "P Print histograms to perf.hist.N\n"
2067 "t Zoom into current Thread\n"
2068 "V Verbose (DSO names in callchains, etc)\n"
2069 "z Toggle zeroing of samples\n"
2070 "f Enable/Disable events\n"
2071 "/ Filter symbol by name";
2073 if (browser
== NULL
)
2076 /* reset abort key so that it can get Ctrl-C as a key */
2078 SLang_init_tty(0, 0, 0);
2081 browser
->min_pcnt
= min_pcnt
;
2082 hist_browser__update_nr_entries(browser
);
2084 browser
->pstack
= pstack__new(3);
2085 if (browser
->pstack
== NULL
)
2088 ui_helpline__push(helpline
);
2090 memset(options
, 0, sizeof(options
));
2091 memset(actions
, 0, sizeof(actions
));
2093 perf_hpp__for_each_format(fmt
) {
2094 perf_hpp__reset_width(fmt
, hists
);
2096 * This is done just once, and activates the horizontal scrolling
2097 * code in the ui_browser code, it would be better to have a the
2098 * counter in the perf_hpp code, but I couldn't find doing it here
2099 * works, FIXME by setting this in hist_browser__new, for now, be
2102 ++browser
->b
.columns
;
2105 if (symbol_conf
.col_width_list_str
)
2106 perf_hpp__set_user_width(symbol_conf
.col_width_list_str
);
2109 struct thread
*thread
= NULL
;
2110 struct map
*map
= NULL
;
2116 key
= hist_browser__run(browser
, helpline
);
2118 if (browser
->he_selection
!= NULL
) {
2119 thread
= hist_browser__selected_thread(browser
);
2120 map
= browser
->selection
->map
;
2121 socked_id
= browser
->he_selection
->socket
;
2129 * Exit the browser, let hists__browser_tree
2130 * go to the next or previous
2132 goto out_free_stack
;
2134 if (!sort__has_sym
) {
2135 ui_browser__warning(&browser
->b
, delay_secs
* 2,
2136 "Annotation is only available for symbolic views, "
2137 "include \"sym*\" in --sort to use it.");
2141 if (browser
->selection
== NULL
||
2142 browser
->selection
->sym
== NULL
||
2143 browser
->selection
->map
->dso
->annotate_warned
)
2146 actions
->ms
.map
= browser
->selection
->map
;
2147 actions
->ms
.sym
= browser
->selection
->sym
;
2148 do_annotate(browser
, actions
);
2151 hist_browser__dump(browser
);
2154 actions
->ms
.map
= map
;
2155 do_zoom_dso(browser
, actions
);
2158 browser
->show_dso
= !browser
->show_dso
;
2161 actions
->thread
= thread
;
2162 do_zoom_thread(browser
, actions
);
2165 actions
->socket
= socked_id
;
2166 do_zoom_socket(browser
, actions
);
2169 if (ui_browser__input_window("Symbol to show",
2170 "Please enter the name of symbol you want to see.\n"
2171 "To remove the filter later, press / + ENTER.",
2172 buf
, "ENTER: OK, ESC: Cancel",
2173 delay_secs
* 2) == K_ENTER
) {
2174 hists
->symbol_filter_str
= *buf
? buf
: NULL
;
2175 hists__filter_by_symbol(hists
);
2176 hist_browser__reset(browser
);
2180 if (is_report_browser(hbt
)) {
2181 actions
->thread
= NULL
;
2182 actions
->ms
.sym
= NULL
;
2183 do_run_script(browser
, actions
);
2187 if (is_report_browser(hbt
)) {
2188 key
= do_switch_data(browser
, actions
);
2189 if (key
== K_SWITCH_INPUT_DATA
)
2190 goto out_free_stack
;
2194 /* env->arch is NULL for live-mode (i.e. perf top) */
2196 tui__header_window(env
);
2199 symbol_conf
.filter_relative
^= 1;
2202 if (!is_report_browser(hbt
)) {
2203 struct perf_top
*top
= hbt
->arg
;
2205 top
->zero
= !top
->zero
;
2211 ui_browser__help_window(&browser
->b
,
2212 is_report_browser(hbt
) ? report_help
: top_help
);
2223 if (pstack__empty(browser
->pstack
)) {
2225 * Go back to the perf_evsel_menu__run or other user
2228 goto out_free_stack
;
2231 ui_browser__dialog_yesno(&browser
->b
,
2232 "Do you really want to exit?"))
2233 goto out_free_stack
;
2237 top
= pstack__peek(browser
->pstack
);
2238 if (top
== &browser
->hists
->dso_filter
) {
2240 * No need to set actions->dso here since
2241 * it's just to remove the current filter.
2242 * Ditto for thread below.
2244 do_zoom_dso(browser
, actions
);
2245 } else if (top
== &browser
->hists
->thread_filter
) {
2246 do_zoom_thread(browser
, actions
);
2247 } else if (top
== &browser
->hists
->socket_filter
) {
2248 do_zoom_socket(browser
, actions
);
2254 goto out_free_stack
;
2256 if (!is_report_browser(hbt
)) {
2257 struct perf_top
*top
= hbt
->arg
;
2259 perf_evlist__toggle_enable(top
->evlist
);
2261 * No need to refresh, resort/decay histogram
2262 * entries if we are not collecting samples:
2264 if (top
->evlist
->enabled
) {
2265 helpline
= "Press 'f' to disable the events or 'h' to see other hotkeys";
2266 hbt
->refresh
= delay_secs
;
2268 helpline
= "Press 'f' again to re-enable the events";
2275 helpline
= "Press '?' for help on key bindings";
2279 if (!sort__has_sym
|| browser
->selection
== NULL
)
2280 goto skip_annotation
;
2282 if (sort__mode
== SORT_MODE__BRANCH
) {
2283 bi
= browser
->he_selection
->branch_info
;
2286 goto skip_annotation
;
2288 nr_options
+= add_annotate_opt(browser
,
2289 &actions
[nr_options
],
2290 &options
[nr_options
],
2293 if (bi
->to
.sym
!= bi
->from
.sym
)
2294 nr_options
+= add_annotate_opt(browser
,
2295 &actions
[nr_options
],
2296 &options
[nr_options
],
2300 nr_options
+= add_annotate_opt(browser
,
2301 &actions
[nr_options
],
2302 &options
[nr_options
],
2303 browser
->selection
->map
,
2304 browser
->selection
->sym
);
2307 nr_options
+= add_thread_opt(browser
, &actions
[nr_options
],
2308 &options
[nr_options
], thread
);
2309 nr_options
+= add_dso_opt(browser
, &actions
[nr_options
],
2310 &options
[nr_options
], map
);
2311 nr_options
+= add_map_opt(browser
, &actions
[nr_options
],
2312 &options
[nr_options
],
2313 browser
->selection
?
2314 browser
->selection
->map
: NULL
);
2315 nr_options
+= add_socket_opt(browser
, &actions
[nr_options
],
2316 &options
[nr_options
],
2318 /* perf script support */
2319 if (!is_report_browser(hbt
))
2320 goto skip_scripting
;
2322 if (browser
->he_selection
) {
2323 if (sort__has_thread
&& thread
) {
2324 nr_options
+= add_script_opt(browser
,
2325 &actions
[nr_options
],
2326 &options
[nr_options
],
2330 * Note that browser->selection != NULL
2331 * when browser->he_selection is not NULL,
2332 * so we don't need to check browser->selection
2333 * before fetching browser->selection->sym like what
2334 * we do before fetching browser->selection->map.
2336 * See hist_browser__show_entry.
2338 if (sort__has_sym
&& browser
->selection
->sym
) {
2339 nr_options
+= add_script_opt(browser
,
2340 &actions
[nr_options
],
2341 &options
[nr_options
],
2342 NULL
, browser
->selection
->sym
);
2345 nr_options
+= add_script_opt(browser
, &actions
[nr_options
],
2346 &options
[nr_options
], NULL
, NULL
);
2347 nr_options
+= add_switch_opt(browser
, &actions
[nr_options
],
2348 &options
[nr_options
]);
2350 nr_options
+= add_exit_opt(browser
, &actions
[nr_options
],
2351 &options
[nr_options
]);
2354 struct popup_action
*act
;
2356 choice
= ui__popup_menu(nr_options
, options
);
2357 if (choice
== -1 || choice
>= nr_options
)
2360 act
= &actions
[choice
];
2361 key
= act
->fn(browser
, act
);
2364 if (key
== K_SWITCH_INPUT_DATA
)
2368 pstack__delete(browser
->pstack
);
2370 hist_browser__delete(browser
);
2371 free_popup_options(options
, MAX_OPTIONS
);
2375 struct perf_evsel_menu
{
2376 struct ui_browser b
;
2377 struct perf_evsel
*selection
;
2378 bool lost_events
, lost_events_warned
;
2380 struct perf_env
*env
;
2383 static void perf_evsel_menu__write(struct ui_browser
*browser
,
2384 void *entry
, int row
)
2386 struct perf_evsel_menu
*menu
= container_of(browser
,
2387 struct perf_evsel_menu
, b
);
2388 struct perf_evsel
*evsel
= list_entry(entry
, struct perf_evsel
, node
);
2389 struct hists
*hists
= evsel__hists(evsel
);
2390 bool current_entry
= ui_browser__is_current_entry(browser
, row
);
2391 unsigned long nr_events
= hists
->stats
.nr_events
[PERF_RECORD_SAMPLE
];
2392 const char *ev_name
= perf_evsel__name(evsel
);
2394 const char *warn
= " ";
2397 ui_browser__set_color(browser
, current_entry
? HE_COLORSET_SELECTED
:
2398 HE_COLORSET_NORMAL
);
2400 if (perf_evsel__is_group_event(evsel
)) {
2401 struct perf_evsel
*pos
;
2403 ev_name
= perf_evsel__group_name(evsel
);
2405 for_each_group_member(pos
, evsel
) {
2406 struct hists
*pos_hists
= evsel__hists(pos
);
2407 nr_events
+= pos_hists
->stats
.nr_events
[PERF_RECORD_SAMPLE
];
2411 nr_events
= convert_unit(nr_events
, &unit
);
2412 printed
= scnprintf(bf
, sizeof(bf
), "%lu%c%s%s", nr_events
,
2413 unit
, unit
== ' ' ? "" : " ", ev_name
);
2414 ui_browser__printf(browser
, "%s", bf
);
2416 nr_events
= hists
->stats
.nr_events
[PERF_RECORD_LOST
];
2417 if (nr_events
!= 0) {
2418 menu
->lost_events
= true;
2420 ui_browser__set_color(browser
, HE_COLORSET_TOP
);
2421 nr_events
= convert_unit(nr_events
, &unit
);
2422 printed
+= scnprintf(bf
, sizeof(bf
), ": %ld%c%schunks LOST!",
2423 nr_events
, unit
, unit
== ' ' ? "" : " ");
2427 ui_browser__write_nstring(browser
, warn
, browser
->width
- printed
);
2430 menu
->selection
= evsel
;
2433 static int perf_evsel_menu__run(struct perf_evsel_menu
*menu
,
2434 int nr_events
, const char *help
,
2435 struct hist_browser_timer
*hbt
)
2437 struct perf_evlist
*evlist
= menu
->b
.priv
;
2438 struct perf_evsel
*pos
;
2439 const char *title
= "Available samples";
2440 int delay_secs
= hbt
? hbt
->refresh
: 0;
2443 if (ui_browser__show(&menu
->b
, title
,
2444 "ESC: exit, ENTER|->: Browse histograms") < 0)
2448 key
= ui_browser__run(&menu
->b
, delay_secs
);
2452 hbt
->timer(hbt
->arg
);
2454 if (!menu
->lost_events_warned
&& menu
->lost_events
) {
2455 ui_browser__warn_lost_events(&menu
->b
);
2456 menu
->lost_events_warned
= true;
2461 if (!menu
->selection
)
2463 pos
= menu
->selection
;
2465 perf_evlist__set_selected(evlist
, pos
);
2467 * Give the calling tool a chance to populate the non
2468 * default evsel resorted hists tree.
2471 hbt
->timer(hbt
->arg
);
2472 key
= perf_evsel__hists_browse(pos
, nr_events
, help
,
2476 ui_browser__show_title(&menu
->b
, title
);
2479 if (pos
->node
.next
== &evlist
->entries
)
2480 pos
= perf_evlist__first(evlist
);
2482 pos
= perf_evsel__next(pos
);
2485 if (pos
->node
.prev
== &evlist
->entries
)
2486 pos
= perf_evlist__last(evlist
);
2488 pos
= perf_evsel__prev(pos
);
2490 case K_SWITCH_INPUT_DATA
:
2501 if (!ui_browser__dialog_yesno(&menu
->b
,
2502 "Do you really want to exit?"))
2514 ui_browser__hide(&menu
->b
);
2518 static bool filter_group_entries(struct ui_browser
*browser __maybe_unused
,
2521 struct perf_evsel
*evsel
= list_entry(entry
, struct perf_evsel
, node
);
2523 if (symbol_conf
.event_group
&& !perf_evsel__is_group_leader(evsel
))
2529 static int __perf_evlist__tui_browse_hists(struct perf_evlist
*evlist
,
2530 int nr_entries
, const char *help
,
2531 struct hist_browser_timer
*hbt
,
2533 struct perf_env
*env
)
2535 struct perf_evsel
*pos
;
2536 struct perf_evsel_menu menu
= {
2538 .entries
= &evlist
->entries
,
2539 .refresh
= ui_browser__list_head_refresh
,
2540 .seek
= ui_browser__list_head_seek
,
2541 .write
= perf_evsel_menu__write
,
2542 .filter
= filter_group_entries
,
2543 .nr_entries
= nr_entries
,
2546 .min_pcnt
= min_pcnt
,
2550 ui_helpline__push("Press ESC to exit");
2552 evlist__for_each(evlist
, pos
) {
2553 const char *ev_name
= perf_evsel__name(pos
);
2554 size_t line_len
= strlen(ev_name
) + 7;
2556 if (menu
.b
.width
< line_len
)
2557 menu
.b
.width
= line_len
;
2560 return perf_evsel_menu__run(&menu
, nr_entries
, help
, hbt
);
2563 int perf_evlist__tui_browse_hists(struct perf_evlist
*evlist
, const char *help
,
2564 struct hist_browser_timer
*hbt
,
2566 struct perf_env
*env
)
2568 int nr_entries
= evlist
->nr_entries
;
2571 if (nr_entries
== 1) {
2572 struct perf_evsel
*first
= perf_evlist__first(evlist
);
2574 return perf_evsel__hists_browse(first
, nr_entries
, help
,
2575 false, hbt
, min_pcnt
,
2579 if (symbol_conf
.event_group
) {
2580 struct perf_evsel
*pos
;
2583 evlist__for_each(evlist
, pos
) {
2584 if (perf_evsel__is_group_leader(pos
))
2588 if (nr_entries
== 1)
2592 return __perf_evlist__tui_browse_hists(evlist
, nr_entries
, help
,
2593 hbt
, min_pcnt
, env
);