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 print_callchain_entry_fn print
,
664 struct callchain_print_arg
*arg
,
665 check_output_full_fn is_output_full
)
667 struct rb_node
*node
;
668 int first_row
= row
, offset
= LEVEL_OFFSET_STEP
;
671 node
= rb_first(root
);
672 need_percent
= node
&& rb_next(node
);
675 struct callchain_node
*child
= rb_entry(node
, struct callchain_node
, rb_node
);
676 struct rb_node
*next
= rb_next(node
);
677 struct callchain_list
*chain
;
678 char folded_sign
= ' ';
680 int extra_offset
= 0;
682 list_for_each_entry(chain
, &child
->parent_val
, list
) {
683 bool was_first
= first
;
687 else if (need_percent
)
688 extra_offset
= LEVEL_OFFSET_STEP
;
690 folded_sign
= callchain_list__folded(chain
);
692 row
+= hist_browser__show_callchain_list(browser
, child
,
694 was_first
&& need_percent
,
695 offset
+ extra_offset
,
698 if (is_output_full(browser
, row
))
701 if (folded_sign
== '+')
705 list_for_each_entry(chain
, &child
->val
, list
) {
706 bool was_first
= first
;
710 else if (need_percent
)
711 extra_offset
= LEVEL_OFFSET_STEP
;
713 folded_sign
= callchain_list__folded(chain
);
715 row
+= hist_browser__show_callchain_list(browser
, child
,
717 was_first
&& need_percent
,
718 offset
+ extra_offset
,
721 if (is_output_full(browser
, row
))
724 if (folded_sign
== '+')
729 if (is_output_full(browser
, row
))
734 return row
- first_row
;
737 static char *hist_browser__folded_callchain_str(struct hist_browser
*browser
,
738 struct callchain_list
*chain
,
739 char *value_str
, char *old_str
)
745 str
= callchain_list__sym_name(chain
, bf
, sizeof(bf
),
748 if (asprintf(&new, "%s%s%s", old_str
,
749 symbol_conf
.field_sep
?: ";", str
) < 0)
753 if (asprintf(&new, "%s %s", value_str
, str
) < 0)
756 if (asprintf(&new, "%s", str
) < 0)
763 static int hist_browser__show_callchain_folded(struct hist_browser
*browser
,
764 struct rb_root
*root
,
765 unsigned short row
, u64 total
,
766 print_callchain_entry_fn print
,
767 struct callchain_print_arg
*arg
,
768 check_output_full_fn is_output_full
)
770 struct rb_node
*node
;
771 int first_row
= row
, offset
= LEVEL_OFFSET_STEP
;
774 node
= rb_first(root
);
775 need_percent
= node
&& rb_next(node
);
778 struct callchain_node
*child
= rb_entry(node
, struct callchain_node
, rb_node
);
779 struct rb_node
*next
= rb_next(node
);
780 struct callchain_list
*chain
, *first_chain
= NULL
;
782 char *value_str
= NULL
, *value_str_alloc
= NULL
;
783 char *chain_str
= NULL
, *chain_str_alloc
= NULL
;
785 if (arg
->row_offset
!= 0) {
793 callchain_node__scnprintf_value(child
, buf
, sizeof(buf
), total
);
794 if (asprintf(&value_str
, "%s", buf
) < 0) {
795 value_str
= (char *)"<...>";
798 value_str_alloc
= value_str
;
801 list_for_each_entry(chain
, &child
->parent_val
, list
) {
802 chain_str
= hist_browser__folded_callchain_str(browser
,
803 chain
, value_str
, chain_str
);
809 if (chain_str
== NULL
) {
810 chain_str
= (char *)"Not enough memory!";
814 chain_str_alloc
= chain_str
;
817 list_for_each_entry(chain
, &child
->val
, list
) {
818 chain_str
= hist_browser__folded_callchain_str(browser
,
819 chain
, value_str
, chain_str
);
825 if (chain_str
== NULL
) {
826 chain_str
= (char *)"Not enough memory!";
830 chain_str_alloc
= chain_str
;
834 print(browser
, first_chain
, chain_str
, offset
, row
++, arg
);
835 free(value_str_alloc
);
836 free(chain_str_alloc
);
839 if (is_output_full(browser
, row
))
844 return row
- first_row
;
847 static int hist_browser__show_callchain(struct hist_browser
*browser
,
848 struct rb_root
*root
, int level
,
849 unsigned short row
, u64 total
,
850 print_callchain_entry_fn print
,
851 struct callchain_print_arg
*arg
,
852 check_output_full_fn is_output_full
)
854 struct rb_node
*node
;
855 int first_row
= row
, offset
= level
* LEVEL_OFFSET_STEP
;
859 node
= rb_first(root
);
860 need_percent
= node
&& rb_next(node
);
863 struct callchain_node
*child
= rb_entry(node
, struct callchain_node
, rb_node
);
864 struct rb_node
*next
= rb_next(node
);
865 struct callchain_list
*chain
;
866 char folded_sign
= ' ';
868 int extra_offset
= 0;
870 list_for_each_entry(chain
, &child
->val
, list
) {
871 bool was_first
= first
;
875 else if (need_percent
)
876 extra_offset
= LEVEL_OFFSET_STEP
;
878 folded_sign
= callchain_list__folded(chain
);
880 row
+= hist_browser__show_callchain_list(browser
, child
,
882 was_first
&& need_percent
,
883 offset
+ extra_offset
,
886 if (is_output_full(browser
, row
))
889 if (folded_sign
== '+')
893 if (folded_sign
== '-') {
894 const int new_level
= level
+ (extra_offset
? 2 : 1);
896 if (callchain_param
.mode
== CHAIN_GRAPH_REL
)
897 new_total
= child
->children_hit
;
901 row
+= hist_browser__show_callchain(browser
, &child
->rb_root
,
902 new_level
, row
, new_total
,
903 print
, arg
, is_output_full
);
905 if (is_output_full(browser
, row
))
910 return row
- first_row
;
914 struct ui_browser
*b
;
919 static int __hpp__slsmg_color_printf(struct perf_hpp
*hpp
, const char *fmt
, ...)
921 struct hpp_arg
*arg
= hpp
->ptr
;
927 len
= va_arg(args
, int);
928 percent
= va_arg(args
, double);
931 ui_browser__set_percent_color(arg
->b
, percent
, arg
->current_entry
);
933 ret
= scnprintf(hpp
->buf
, hpp
->size
, fmt
, len
, percent
);
934 ui_browser__printf(arg
->b
, "%s", hpp
->buf
);
936 advance_hpp(hpp
, ret
);
940 #define __HPP_COLOR_PERCENT_FN(_type, _field) \
941 static u64 __hpp_get_##_field(struct hist_entry *he) \
943 return he->stat._field; \
947 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
948 struct perf_hpp *hpp, \
949 struct hist_entry *he) \
951 return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%", \
952 __hpp__slsmg_color_printf, true); \
955 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
956 static u64 __hpp_get_acc_##_field(struct hist_entry *he) \
958 return he->stat_acc->_field; \
962 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
963 struct perf_hpp *hpp, \
964 struct hist_entry *he) \
966 if (!symbol_conf.cumulate_callchain) { \
967 struct hpp_arg *arg = hpp->ptr; \
968 int len = fmt->user_len ?: fmt->len; \
969 int ret = scnprintf(hpp->buf, hpp->size, \
970 "%*s", len, "N/A"); \
971 ui_browser__printf(arg->b, "%s", hpp->buf); \
975 return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field, \
976 " %*.2f%%", __hpp__slsmg_color_printf, true); \
979 __HPP_COLOR_PERCENT_FN(overhead
, period
)
980 __HPP_COLOR_PERCENT_FN(overhead_sys
, period_sys
)
981 __HPP_COLOR_PERCENT_FN(overhead_us
, period_us
)
982 __HPP_COLOR_PERCENT_FN(overhead_guest_sys
, period_guest_sys
)
983 __HPP_COLOR_PERCENT_FN(overhead_guest_us
, period_guest_us
)
984 __HPP_COLOR_ACC_PERCENT_FN(overhead_acc
, period
)
986 #undef __HPP_COLOR_PERCENT_FN
987 #undef __HPP_COLOR_ACC_PERCENT_FN
989 void hist_browser__init_hpp(void)
991 perf_hpp__format
[PERF_HPP__OVERHEAD
].color
=
992 hist_browser__hpp_color_overhead
;
993 perf_hpp__format
[PERF_HPP__OVERHEAD_SYS
].color
=
994 hist_browser__hpp_color_overhead_sys
;
995 perf_hpp__format
[PERF_HPP__OVERHEAD_US
].color
=
996 hist_browser__hpp_color_overhead_us
;
997 perf_hpp__format
[PERF_HPP__OVERHEAD_GUEST_SYS
].color
=
998 hist_browser__hpp_color_overhead_guest_sys
;
999 perf_hpp__format
[PERF_HPP__OVERHEAD_GUEST_US
].color
=
1000 hist_browser__hpp_color_overhead_guest_us
;
1001 perf_hpp__format
[PERF_HPP__OVERHEAD_ACC
].color
=
1002 hist_browser__hpp_color_overhead_acc
;
1005 static int hist_browser__show_entry(struct hist_browser
*browser
,
1006 struct hist_entry
*entry
,
1011 int width
= browser
->b
.width
;
1012 char folded_sign
= ' ';
1013 bool current_entry
= ui_browser__is_current_entry(&browser
->b
, row
);
1014 off_t row_offset
= entry
->row_offset
;
1016 struct perf_hpp_fmt
*fmt
;
1018 if (current_entry
) {
1019 browser
->he_selection
= entry
;
1020 browser
->selection
= &entry
->ms
;
1023 if (symbol_conf
.use_callchain
) {
1024 hist_entry__init_have_children(entry
);
1025 folded_sign
= hist_entry__folded(entry
);
1028 if (row_offset
== 0) {
1029 struct hpp_arg arg
= {
1031 .folded_sign
= folded_sign
,
1032 .current_entry
= current_entry
,
1034 struct perf_hpp hpp
= {
1041 hist_browser__gotorc(browser
, row
, 0);
1043 perf_hpp__for_each_format(fmt
) {
1044 if (perf_hpp__should_skip(fmt
, entry
->hists
) ||
1045 column
++ < browser
->b
.horiz_scroll
)
1048 if (current_entry
&& browser
->b
.navkeypressed
) {
1049 ui_browser__set_color(&browser
->b
,
1050 HE_COLORSET_SELECTED
);
1052 ui_browser__set_color(&browser
->b
,
1053 HE_COLORSET_NORMAL
);
1057 if (symbol_conf
.use_callchain
) {
1058 ui_browser__printf(&browser
->b
, "%c ", folded_sign
);
1063 ui_browser__printf(&browser
->b
, " ");
1068 width
-= fmt
->color(fmt
, &hpp
, entry
);
1070 width
-= fmt
->entry(fmt
, &hpp
, entry
);
1071 ui_browser__printf(&browser
->b
, "%s", s
);
1075 /* The scroll bar isn't being used */
1076 if (!browser
->b
.navkeypressed
)
1079 ui_browser__write_nstring(&browser
->b
, "", width
);
1086 if (folded_sign
== '-' && row
!= browser
->b
.rows
) {
1087 u64 total
= hists__total_period(entry
->hists
);
1088 struct callchain_print_arg arg
= {
1089 .row_offset
= row_offset
,
1090 .is_current_entry
= current_entry
,
1093 if (callchain_param
.mode
== CHAIN_GRAPH_REL
) {
1094 if (symbol_conf
.cumulate_callchain
)
1095 total
= entry
->stat_acc
->period
;
1097 total
= entry
->stat
.period
;
1100 if (callchain_param
.mode
== CHAIN_FLAT
) {
1101 printed
+= hist_browser__show_callchain_flat(browser
,
1102 &entry
->sorted_chain
, row
, total
,
1103 hist_browser__show_callchain_entry
, &arg
,
1104 hist_browser__check_output_full
);
1105 } else if (callchain_param
.mode
== CHAIN_FOLDED
) {
1106 printed
+= hist_browser__show_callchain_folded(browser
,
1107 &entry
->sorted_chain
, row
, total
,
1108 hist_browser__show_callchain_entry
, &arg
,
1109 hist_browser__check_output_full
);
1111 printed
+= hist_browser__show_callchain(browser
,
1112 &entry
->sorted_chain
, 1, row
, total
,
1113 hist_browser__show_callchain_entry
, &arg
,
1114 hist_browser__check_output_full
);
1117 if (arg
.is_current_entry
)
1118 browser
->he_selection
= entry
;
1124 static int advance_hpp_check(struct perf_hpp
*hpp
, int inc
)
1126 advance_hpp(hpp
, inc
);
1127 return hpp
->size
<= 0;
1130 static int hists_browser__scnprintf_headers(struct hist_browser
*browser
, char *buf
, size_t size
)
1132 struct hists
*hists
= browser
->hists
;
1133 struct perf_hpp dummy_hpp
= {
1137 struct perf_hpp_fmt
*fmt
;
1141 if (symbol_conf
.use_callchain
) {
1142 ret
= scnprintf(buf
, size
, " ");
1143 if (advance_hpp_check(&dummy_hpp
, ret
))
1147 perf_hpp__for_each_format(fmt
) {
1148 if (perf_hpp__should_skip(fmt
, hists
) || column
++ < browser
->b
.horiz_scroll
)
1151 ret
= fmt
->header(fmt
, &dummy_hpp
, hists_to_evsel(hists
));
1152 if (advance_hpp_check(&dummy_hpp
, ret
))
1155 ret
= scnprintf(dummy_hpp
.buf
, dummy_hpp
.size
, " ");
1156 if (advance_hpp_check(&dummy_hpp
, ret
))
1163 static void hist_browser__show_headers(struct hist_browser
*browser
)
1167 hists_browser__scnprintf_headers(browser
, headers
, sizeof(headers
));
1168 ui_browser__gotorc(&browser
->b
, 0, 0);
1169 ui_browser__set_color(&browser
->b
, HE_COLORSET_ROOT
);
1170 ui_browser__write_nstring(&browser
->b
, headers
, browser
->b
.width
+ 1);
1173 static void ui_browser__hists_init_top(struct ui_browser
*browser
)
1175 if (browser
->top
== NULL
) {
1176 struct hist_browser
*hb
;
1178 hb
= container_of(browser
, struct hist_browser
, b
);
1179 browser
->top
= rb_first(&hb
->hists
->entries
);
1183 static unsigned int hist_browser__refresh(struct ui_browser
*browser
)
1186 u16 header_offset
= 0;
1188 struct hist_browser
*hb
= container_of(browser
, struct hist_browser
, b
);
1190 if (hb
->show_headers
) {
1191 hist_browser__show_headers(hb
);
1195 ui_browser__hists_init_top(browser
);
1196 hb
->he_selection
= NULL
;
1197 hb
->selection
= NULL
;
1199 for (nd
= browser
->top
; nd
; nd
= rb_next(nd
)) {
1200 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1206 percent
= hist_entry__get_percent_limit(h
);
1207 if (percent
< hb
->min_pcnt
)
1210 row
+= hist_browser__show_entry(hb
, h
, row
);
1211 if (row
== browser
->rows
)
1215 return row
+ header_offset
;
1218 static struct rb_node
*hists__filter_entries(struct rb_node
*nd
,
1221 while (nd
!= NULL
) {
1222 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1223 float percent
= hist_entry__get_percent_limit(h
);
1225 if (!h
->filtered
&& percent
>= min_pcnt
)
1234 static struct rb_node
*hists__filter_prev_entries(struct rb_node
*nd
,
1237 while (nd
!= NULL
) {
1238 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1239 float percent
= hist_entry__get_percent_limit(h
);
1241 if (!h
->filtered
&& percent
>= min_pcnt
)
1250 static void ui_browser__hists_seek(struct ui_browser
*browser
,
1251 off_t offset
, int whence
)
1253 struct hist_entry
*h
;
1256 struct hist_browser
*hb
;
1258 hb
= container_of(browser
, struct hist_browser
, b
);
1260 if (browser
->nr_entries
== 0)
1263 ui_browser__hists_init_top(browser
);
1267 nd
= hists__filter_entries(rb_first(browser
->entries
),
1274 nd
= hists__filter_prev_entries(rb_last(browser
->entries
),
1283 * Moves not relative to the first visible entry invalidates its
1286 h
= rb_entry(browser
->top
, struct hist_entry
, rb_node
);
1290 * Here we have to check if nd is expanded (+), if it is we can't go
1291 * the next top level hist_entry, instead we must compute an offset of
1292 * what _not_ to show and not change the first visible entry.
1294 * This offset increments when we are going from top to bottom and
1295 * decreases when we're going from bottom to top.
1297 * As we don't have backpointers to the top level in the callchains
1298 * structure, we need to always print the whole hist_entry callchain,
1299 * skipping the first ones that are before the first visible entry
1300 * and stop when we printed enough lines to fill the screen.
1308 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1310 u16 remaining
= h
->nr_rows
- h
->row_offset
;
1311 if (offset
> remaining
) {
1312 offset
-= remaining
;
1315 h
->row_offset
+= offset
;
1321 nd
= hists__filter_entries(rb_next(nd
), hb
->min_pcnt
);
1326 } while (offset
!= 0);
1327 } else if (offset
< 0) {
1329 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1332 if (-offset
> h
->row_offset
) {
1333 offset
+= h
->row_offset
;
1336 h
->row_offset
+= offset
;
1342 if (-offset
> h
->nr_rows
) {
1343 offset
+= h
->nr_rows
;
1346 h
->row_offset
= h
->nr_rows
+ offset
;
1354 nd
= hists__filter_prev_entries(rb_prev(nd
),
1362 * Last unfiltered hist_entry, check if it is
1363 * unfolded, if it is then we should have
1364 * row_offset at its last entry.
1366 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1368 h
->row_offset
= h
->nr_rows
;
1375 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1380 static int hist_browser__fprintf_callchain(struct hist_browser
*browser
,
1381 struct hist_entry
*he
, FILE *fp
)
1383 u64 total
= hists__total_period(he
->hists
);
1384 struct callchain_print_arg arg
= {
1388 if (symbol_conf
.cumulate_callchain
)
1389 total
= he
->stat_acc
->period
;
1391 hist_browser__show_callchain(browser
, &he
->sorted_chain
, 1, 0, total
,
1392 hist_browser__fprintf_callchain_entry
, &arg
,
1393 hist_browser__check_dump_full
);
1397 static int hist_browser__fprintf_entry(struct hist_browser
*browser
,
1398 struct hist_entry
*he
, FILE *fp
)
1402 char folded_sign
= ' ';
1403 struct perf_hpp hpp
= {
1407 struct perf_hpp_fmt
*fmt
;
1411 if (symbol_conf
.use_callchain
)
1412 folded_sign
= hist_entry__folded(he
);
1414 if (symbol_conf
.use_callchain
)
1415 printed
+= fprintf(fp
, "%c ", folded_sign
);
1417 perf_hpp__for_each_format(fmt
) {
1418 if (perf_hpp__should_skip(fmt
, he
->hists
))
1422 ret
= scnprintf(hpp
.buf
, hpp
.size
, " ");
1423 advance_hpp(&hpp
, ret
);
1427 ret
= fmt
->entry(fmt
, &hpp
, he
);
1428 advance_hpp(&hpp
, ret
);
1430 printed
+= fprintf(fp
, "%s\n", rtrim(s
));
1432 if (folded_sign
== '-')
1433 printed
+= hist_browser__fprintf_callchain(browser
, he
, fp
);
1438 static int hist_browser__fprintf(struct hist_browser
*browser
, FILE *fp
)
1440 struct rb_node
*nd
= hists__filter_entries(rb_first(browser
->b
.entries
),
1445 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1447 printed
+= hist_browser__fprintf_entry(browser
, h
, fp
);
1448 nd
= hists__filter_entries(rb_next(nd
), browser
->min_pcnt
);
1454 static int hist_browser__dump(struct hist_browser
*browser
)
1460 scnprintf(filename
, sizeof(filename
), "perf.hist.%d", browser
->print_seq
);
1461 if (access(filename
, F_OK
))
1464 * XXX: Just an arbitrary lazy upper limit
1466 if (++browser
->print_seq
== 8192) {
1467 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1472 fp
= fopen(filename
, "w");
1475 const char *err
= strerror_r(errno
, bf
, sizeof(bf
));
1476 ui_helpline__fpush("Couldn't write to %s: %s", filename
, err
);
1480 ++browser
->print_seq
;
1481 hist_browser__fprintf(browser
, fp
);
1483 ui_helpline__fpush("%s written!", filename
);
1488 static struct hist_browser
*hist_browser__new(struct hists
*hists
,
1489 struct hist_browser_timer
*hbt
,
1490 struct perf_env
*env
)
1492 struct hist_browser
*browser
= zalloc(sizeof(*browser
));
1495 browser
->hists
= hists
;
1496 browser
->b
.refresh
= hist_browser__refresh
;
1497 browser
->b
.refresh_dimensions
= hist_browser__refresh_dimensions
;
1498 browser
->b
.seek
= ui_browser__hists_seek
;
1499 browser
->b
.use_navkeypressed
= true;
1500 browser
->show_headers
= symbol_conf
.show_hist_headers
;
1508 static void hist_browser__delete(struct hist_browser
*browser
)
1513 static struct hist_entry
*hist_browser__selected_entry(struct hist_browser
*browser
)
1515 return browser
->he_selection
;
1518 static struct thread
*hist_browser__selected_thread(struct hist_browser
*browser
)
1520 return browser
->he_selection
->thread
;
1523 /* Check whether the browser is for 'top' or 'report' */
1524 static inline bool is_report_browser(void *timer
)
1526 return timer
== NULL
;
1529 static int hists__browser_title(struct hists
*hists
,
1530 struct hist_browser_timer
*hbt
,
1531 char *bf
, size_t size
)
1535 const struct dso
*dso
= hists
->dso_filter
;
1536 const struct thread
*thread
= hists
->thread_filter
;
1537 int socket_id
= hists
->socket_filter
;
1538 unsigned long nr_samples
= hists
->stats
.nr_events
[PERF_RECORD_SAMPLE
];
1539 u64 nr_events
= hists
->stats
.total_period
;
1540 struct perf_evsel
*evsel
= hists_to_evsel(hists
);
1541 const char *ev_name
= perf_evsel__name(evsel
);
1543 size_t buflen
= sizeof(buf
);
1544 char ref
[30] = " show reference callgraph, ";
1545 bool enable_ref
= false;
1547 if (symbol_conf
.filter_relative
) {
1548 nr_samples
= hists
->stats
.nr_non_filtered_samples
;
1549 nr_events
= hists
->stats
.total_non_filtered_period
;
1552 if (perf_evsel__is_group_event(evsel
)) {
1553 struct perf_evsel
*pos
;
1555 perf_evsel__group_desc(evsel
, buf
, buflen
);
1558 for_each_group_member(pos
, evsel
) {
1559 struct hists
*pos_hists
= evsel__hists(pos
);
1561 if (symbol_conf
.filter_relative
) {
1562 nr_samples
+= pos_hists
->stats
.nr_non_filtered_samples
;
1563 nr_events
+= pos_hists
->stats
.total_non_filtered_period
;
1565 nr_samples
+= pos_hists
->stats
.nr_events
[PERF_RECORD_SAMPLE
];
1566 nr_events
+= pos_hists
->stats
.total_period
;
1571 if (symbol_conf
.show_ref_callgraph
&&
1572 strstr(ev_name
, "call-graph=no"))
1574 nr_samples
= convert_unit(nr_samples
, &unit
);
1575 printed
= scnprintf(bf
, size
,
1576 "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64
,
1577 nr_samples
, unit
, ev_name
, enable_ref
? ref
: " ", nr_events
);
1580 if (hists
->uid_filter_str
)
1581 printed
+= snprintf(bf
+ printed
, size
- printed
,
1582 ", UID: %s", hists
->uid_filter_str
);
1584 printed
+= scnprintf(bf
+ printed
, size
- printed
,
1586 (thread
->comm_set
? thread__comm_str(thread
) : ""),
1589 printed
+= scnprintf(bf
+ printed
, size
- printed
,
1590 ", DSO: %s", dso
->short_name
);
1592 printed
+= scnprintf(bf
+ printed
, size
- printed
,
1593 ", Processor Socket: %d", socket_id
);
1594 if (!is_report_browser(hbt
)) {
1595 struct perf_top
*top
= hbt
->arg
;
1598 printed
+= scnprintf(bf
+ printed
, size
- printed
, " [z]");
1604 static inline void free_popup_options(char **options
, int n
)
1608 for (i
= 0; i
< n
; ++i
)
1613 * Only runtime switching of perf data file will make "input_name" point
1614 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1615 * whether we need to call free() for current "input_name" during the switch.
1617 static bool is_input_name_malloced
= false;
1619 static int switch_data_file(void)
1621 char *pwd
, *options
[32], *abs_path
[32], *tmp
;
1623 int nr_options
= 0, choice
= -1, ret
= -1;
1624 struct dirent
*dent
;
1626 pwd
= getenv("PWD");
1630 pwd_dir
= opendir(pwd
);
1634 memset(options
, 0, sizeof(options
));
1635 memset(options
, 0, sizeof(abs_path
));
1637 while ((dent
= readdir(pwd_dir
))) {
1638 char path
[PATH_MAX
];
1640 char *name
= dent
->d_name
;
1643 if (!(dent
->d_type
== DT_REG
))
1646 snprintf(path
, sizeof(path
), "%s/%s", pwd
, name
);
1648 file
= fopen(path
, "r");
1652 if (fread(&magic
, 1, 8, file
) < 8)
1653 goto close_file_and_continue
;
1655 if (is_perf_magic(magic
)) {
1656 options
[nr_options
] = strdup(name
);
1657 if (!options
[nr_options
])
1658 goto close_file_and_continue
;
1660 abs_path
[nr_options
] = strdup(path
);
1661 if (!abs_path
[nr_options
]) {
1662 zfree(&options
[nr_options
]);
1663 ui__warning("Can't search all data files due to memory shortage.\n");
1671 close_file_and_continue
:
1673 if (nr_options
>= 32) {
1674 ui__warning("Too many perf data files in PWD!\n"
1675 "Only the first 32 files will be listed.\n");
1682 choice
= ui__popup_menu(nr_options
, options
);
1683 if (choice
< nr_options
&& choice
>= 0) {
1684 tmp
= strdup(abs_path
[choice
]);
1686 if (is_input_name_malloced
)
1687 free((void *)input_name
);
1689 is_input_name_malloced
= true;
1692 ui__warning("Data switch failed due to memory shortage!\n");
1696 free_popup_options(options
, nr_options
);
1697 free_popup_options(abs_path
, nr_options
);
1701 struct popup_action
{
1702 struct thread
*thread
;
1703 struct map_symbol ms
;
1706 int (*fn
)(struct hist_browser
*browser
, struct popup_action
*act
);
1710 do_annotate(struct hist_browser
*browser
, struct popup_action
*act
)
1712 struct perf_evsel
*evsel
;
1713 struct annotation
*notes
;
1714 struct hist_entry
*he
;
1717 if (!objdump_path
&& perf_env__lookup_objdump(browser
->env
))
1720 notes
= symbol__annotation(act
->ms
.sym
);
1724 evsel
= hists_to_evsel(browser
->hists
);
1725 err
= map_symbol__tui_annotate(&act
->ms
, evsel
, browser
->hbt
);
1726 he
= hist_browser__selected_entry(browser
);
1728 * offer option to annotate the other branch source or target
1729 * (if they exists) when returning from annotate
1731 if ((err
== 'q' || err
== CTRL('c')) && he
->branch_info
)
1734 ui_browser__update_nr_entries(&browser
->b
, browser
->hists
->nr_entries
);
1736 ui_browser__handle_resize(&browser
->b
);
1741 add_annotate_opt(struct hist_browser
*browser __maybe_unused
,
1742 struct popup_action
*act
, char **optstr
,
1743 struct map
*map
, struct symbol
*sym
)
1745 if (sym
== NULL
|| map
->dso
->annotate_warned
)
1748 if (asprintf(optstr
, "Annotate %s", sym
->name
) < 0)
1753 act
->fn
= do_annotate
;
1758 do_zoom_thread(struct hist_browser
*browser
, struct popup_action
*act
)
1760 struct thread
*thread
= act
->thread
;
1762 if (browser
->hists
->thread_filter
) {
1763 pstack__remove(browser
->pstack
, &browser
->hists
->thread_filter
);
1764 perf_hpp__set_elide(HISTC_THREAD
, false);
1765 thread__zput(browser
->hists
->thread_filter
);
1768 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
1769 thread
->comm_set
? thread__comm_str(thread
) : "",
1771 browser
->hists
->thread_filter
= thread__get(thread
);
1772 perf_hpp__set_elide(HISTC_THREAD
, false);
1773 pstack__push(browser
->pstack
, &browser
->hists
->thread_filter
);
1776 hists__filter_by_thread(browser
->hists
);
1777 hist_browser__reset(browser
);
1782 add_thread_opt(struct hist_browser
*browser
, struct popup_action
*act
,
1783 char **optstr
, struct thread
*thread
)
1788 if (asprintf(optstr
, "Zoom %s %s(%d) thread",
1789 browser
->hists
->thread_filter
? "out of" : "into",
1790 thread
->comm_set
? thread__comm_str(thread
) : "",
1794 act
->thread
= thread
;
1795 act
->fn
= do_zoom_thread
;
1800 do_zoom_dso(struct hist_browser
*browser
, struct popup_action
*act
)
1802 struct map
*map
= act
->ms
.map
;
1804 if (browser
->hists
->dso_filter
) {
1805 pstack__remove(browser
->pstack
, &browser
->hists
->dso_filter
);
1806 perf_hpp__set_elide(HISTC_DSO
, false);
1807 browser
->hists
->dso_filter
= NULL
;
1812 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
1813 __map__is_kernel(map
) ? "the Kernel" : map
->dso
->short_name
);
1814 browser
->hists
->dso_filter
= map
->dso
;
1815 perf_hpp__set_elide(HISTC_DSO
, true);
1816 pstack__push(browser
->pstack
, &browser
->hists
->dso_filter
);
1819 hists__filter_by_dso(browser
->hists
);
1820 hist_browser__reset(browser
);
1825 add_dso_opt(struct hist_browser
*browser
, struct popup_action
*act
,
1826 char **optstr
, struct map
*map
)
1831 if (asprintf(optstr
, "Zoom %s %s DSO",
1832 browser
->hists
->dso_filter
? "out of" : "into",
1833 __map__is_kernel(map
) ? "the Kernel" : map
->dso
->short_name
) < 0)
1837 act
->fn
= do_zoom_dso
;
1842 do_browse_map(struct hist_browser
*browser __maybe_unused
,
1843 struct popup_action
*act
)
1845 map__browse(act
->ms
.map
);
1850 add_map_opt(struct hist_browser
*browser __maybe_unused
,
1851 struct popup_action
*act
, char **optstr
, struct map
*map
)
1856 if (asprintf(optstr
, "Browse map details") < 0)
1860 act
->fn
= do_browse_map
;
1865 do_run_script(struct hist_browser
*browser __maybe_unused
,
1866 struct popup_action
*act
)
1868 char script_opt
[64];
1869 memset(script_opt
, 0, sizeof(script_opt
));
1872 scnprintf(script_opt
, sizeof(script_opt
), " -c %s ",
1873 thread__comm_str(act
->thread
));
1874 } else if (act
->ms
.sym
) {
1875 scnprintf(script_opt
, sizeof(script_opt
), " -S %s ",
1879 script_browse(script_opt
);
1884 add_script_opt(struct hist_browser
*browser __maybe_unused
,
1885 struct popup_action
*act
, char **optstr
,
1886 struct thread
*thread
, struct symbol
*sym
)
1889 if (asprintf(optstr
, "Run scripts for samples of thread [%s]",
1890 thread__comm_str(thread
)) < 0)
1893 if (asprintf(optstr
, "Run scripts for samples of symbol [%s]",
1897 if (asprintf(optstr
, "Run scripts for all samples") < 0)
1901 act
->thread
= thread
;
1903 act
->fn
= do_run_script
;
1908 do_switch_data(struct hist_browser
*browser __maybe_unused
,
1909 struct popup_action
*act __maybe_unused
)
1911 if (switch_data_file()) {
1912 ui__warning("Won't switch the data files due to\n"
1913 "no valid data file get selected!\n");
1917 return K_SWITCH_INPUT_DATA
;
1921 add_switch_opt(struct hist_browser
*browser
,
1922 struct popup_action
*act
, char **optstr
)
1924 if (!is_report_browser(browser
->hbt
))
1927 if (asprintf(optstr
, "Switch to another data file in PWD") < 0)
1930 act
->fn
= do_switch_data
;
1935 do_exit_browser(struct hist_browser
*browser __maybe_unused
,
1936 struct popup_action
*act __maybe_unused
)
1942 add_exit_opt(struct hist_browser
*browser __maybe_unused
,
1943 struct popup_action
*act
, char **optstr
)
1945 if (asprintf(optstr
, "Exit") < 0)
1948 act
->fn
= do_exit_browser
;
1953 do_zoom_socket(struct hist_browser
*browser
, struct popup_action
*act
)
1955 if (browser
->hists
->socket_filter
> -1) {
1956 pstack__remove(browser
->pstack
, &browser
->hists
->socket_filter
);
1957 browser
->hists
->socket_filter
= -1;
1958 perf_hpp__set_elide(HISTC_SOCKET
, false);
1960 browser
->hists
->socket_filter
= act
->socket
;
1961 perf_hpp__set_elide(HISTC_SOCKET
, true);
1962 pstack__push(browser
->pstack
, &browser
->hists
->socket_filter
);
1965 hists__filter_by_socket(browser
->hists
);
1966 hist_browser__reset(browser
);
1971 add_socket_opt(struct hist_browser
*browser
, struct popup_action
*act
,
1972 char **optstr
, int socket_id
)
1977 if (asprintf(optstr
, "Zoom %s Processor Socket %d",
1978 (browser
->hists
->socket_filter
> -1) ? "out of" : "into",
1982 act
->socket
= socket_id
;
1983 act
->fn
= do_zoom_socket
;
1987 static void hist_browser__update_nr_entries(struct hist_browser
*hb
)
1990 struct rb_node
*nd
= rb_first(&hb
->hists
->entries
);
1992 if (hb
->min_pcnt
== 0) {
1993 hb
->nr_non_filtered_entries
= hb
->hists
->nr_non_filtered_entries
;
1997 while ((nd
= hists__filter_entries(nd
, hb
->min_pcnt
)) != NULL
) {
2002 hb
->nr_non_filtered_entries
= nr_entries
;
2005 static int perf_evsel__hists_browse(struct perf_evsel
*evsel
, int nr_events
,
2006 const char *helpline
,
2008 struct hist_browser_timer
*hbt
,
2010 struct perf_env
*env
)
2012 struct hists
*hists
= evsel__hists(evsel
);
2013 struct hist_browser
*browser
= hist_browser__new(hists
, hbt
, env
);
2014 struct branch_info
*bi
;
2015 #define MAX_OPTIONS 16
2016 char *options
[MAX_OPTIONS
];
2017 struct popup_action actions
[MAX_OPTIONS
];
2021 int delay_secs
= hbt
? hbt
->refresh
: 0;
2022 struct perf_hpp_fmt
*fmt
;
2024 #define HIST_BROWSER_HELP_COMMON \
2025 "h/?/F1 Show this window\n" \
2027 "PGDN/SPACE Navigate\n" \
2028 "q/ESC/CTRL+C Exit browser\n\n" \
2029 "For multiple event sessions:\n\n" \
2030 "TAB/UNTAB Switch events\n\n" \
2031 "For symbolic views (--sort has sym):\n\n" \
2032 "ENTER Zoom into DSO/Threads & Annotate current symbol\n" \
2034 "a Annotate current symbol\n" \
2035 "C Collapse all callchains\n" \
2036 "d Zoom into current DSO\n" \
2037 "E Expand all callchains\n" \
2038 "F Toggle percentage of filtered entries\n" \
2039 "H Display column headers\n" \
2040 "m Display context menu\n" \
2041 "S Zoom into current Processor Socket\n" \
2043 /* help messages are sorted by lexical order of the hotkey */
2044 const char report_help
[] = HIST_BROWSER_HELP_COMMON
2045 "i Show header information\n"
2046 "P Print histograms to perf.hist.N\n"
2047 "r Run available scripts\n"
2048 "s Switch to another data file in PWD\n"
2049 "t Zoom into current Thread\n"
2050 "V Verbose (DSO names in callchains, etc)\n"
2051 "/ Filter symbol by name";
2052 const char top_help
[] = HIST_BROWSER_HELP_COMMON
2053 "P Print histograms to perf.hist.N\n"
2054 "t Zoom into current Thread\n"
2055 "V Verbose (DSO names in callchains, etc)\n"
2056 "z Toggle zeroing of samples\n"
2057 "f Enable/Disable events\n"
2058 "/ Filter symbol by name";
2060 if (browser
== NULL
)
2063 /* reset abort key so that it can get Ctrl-C as a key */
2065 SLang_init_tty(0, 0, 0);
2068 browser
->min_pcnt
= min_pcnt
;
2069 hist_browser__update_nr_entries(browser
);
2071 browser
->pstack
= pstack__new(3);
2072 if (browser
->pstack
== NULL
)
2075 ui_helpline__push(helpline
);
2077 memset(options
, 0, sizeof(options
));
2078 memset(actions
, 0, sizeof(actions
));
2080 perf_hpp__for_each_format(fmt
) {
2081 perf_hpp__reset_width(fmt
, hists
);
2083 * This is done just once, and activates the horizontal scrolling
2084 * code in the ui_browser code, it would be better to have a the
2085 * counter in the perf_hpp code, but I couldn't find doing it here
2086 * works, FIXME by setting this in hist_browser__new, for now, be
2089 ++browser
->b
.columns
;
2092 if (symbol_conf
.col_width_list_str
)
2093 perf_hpp__set_user_width(symbol_conf
.col_width_list_str
);
2096 struct thread
*thread
= NULL
;
2097 struct map
*map
= NULL
;
2103 key
= hist_browser__run(browser
, helpline
);
2105 if (browser
->he_selection
!= NULL
) {
2106 thread
= hist_browser__selected_thread(browser
);
2107 map
= browser
->selection
->map
;
2108 socked_id
= browser
->he_selection
->socket
;
2116 * Exit the browser, let hists__browser_tree
2117 * go to the next or previous
2119 goto out_free_stack
;
2121 if (!sort__has_sym
) {
2122 ui_browser__warning(&browser
->b
, delay_secs
* 2,
2123 "Annotation is only available for symbolic views, "
2124 "include \"sym*\" in --sort to use it.");
2128 if (browser
->selection
== NULL
||
2129 browser
->selection
->sym
== NULL
||
2130 browser
->selection
->map
->dso
->annotate_warned
)
2133 actions
->ms
.map
= browser
->selection
->map
;
2134 actions
->ms
.sym
= browser
->selection
->sym
;
2135 do_annotate(browser
, actions
);
2138 hist_browser__dump(browser
);
2141 actions
->ms
.map
= map
;
2142 do_zoom_dso(browser
, actions
);
2145 browser
->show_dso
= !browser
->show_dso
;
2148 actions
->thread
= thread
;
2149 do_zoom_thread(browser
, actions
);
2152 actions
->socket
= socked_id
;
2153 do_zoom_socket(browser
, actions
);
2156 if (ui_browser__input_window("Symbol to show",
2157 "Please enter the name of symbol you want to see.\n"
2158 "To remove the filter later, press / + ENTER.",
2159 buf
, "ENTER: OK, ESC: Cancel",
2160 delay_secs
* 2) == K_ENTER
) {
2161 hists
->symbol_filter_str
= *buf
? buf
: NULL
;
2162 hists__filter_by_symbol(hists
);
2163 hist_browser__reset(browser
);
2167 if (is_report_browser(hbt
)) {
2168 actions
->thread
= NULL
;
2169 actions
->ms
.sym
= NULL
;
2170 do_run_script(browser
, actions
);
2174 if (is_report_browser(hbt
)) {
2175 key
= do_switch_data(browser
, actions
);
2176 if (key
== K_SWITCH_INPUT_DATA
)
2177 goto out_free_stack
;
2181 /* env->arch is NULL for live-mode (i.e. perf top) */
2183 tui__header_window(env
);
2186 symbol_conf
.filter_relative
^= 1;
2189 if (!is_report_browser(hbt
)) {
2190 struct perf_top
*top
= hbt
->arg
;
2192 top
->zero
= !top
->zero
;
2198 ui_browser__help_window(&browser
->b
,
2199 is_report_browser(hbt
) ? report_help
: top_help
);
2210 if (pstack__empty(browser
->pstack
)) {
2212 * Go back to the perf_evsel_menu__run or other user
2215 goto out_free_stack
;
2218 ui_browser__dialog_yesno(&browser
->b
,
2219 "Do you really want to exit?"))
2220 goto out_free_stack
;
2224 top
= pstack__peek(browser
->pstack
);
2225 if (top
== &browser
->hists
->dso_filter
) {
2227 * No need to set actions->dso here since
2228 * it's just to remove the current filter.
2229 * Ditto for thread below.
2231 do_zoom_dso(browser
, actions
);
2232 } else if (top
== &browser
->hists
->thread_filter
) {
2233 do_zoom_thread(browser
, actions
);
2234 } else if (top
== &browser
->hists
->socket_filter
) {
2235 do_zoom_socket(browser
, actions
);
2241 goto out_free_stack
;
2243 if (!is_report_browser(hbt
)) {
2244 struct perf_top
*top
= hbt
->arg
;
2246 perf_evlist__toggle_enable(top
->evlist
);
2248 * No need to refresh, resort/decay histogram
2249 * entries if we are not collecting samples:
2251 if (top
->evlist
->enabled
) {
2252 helpline
= "Press 'f' to disable the events or 'h' to see other hotkeys";
2253 hbt
->refresh
= delay_secs
;
2255 helpline
= "Press 'f' again to re-enable the events";
2262 helpline
= "Press '?' for help on key bindings";
2267 goto add_exit_option
;
2269 if (browser
->selection
== NULL
)
2270 goto skip_annotation
;
2272 if (sort__mode
== SORT_MODE__BRANCH
) {
2273 bi
= browser
->he_selection
->branch_info
;
2276 goto skip_annotation
;
2278 nr_options
+= add_annotate_opt(browser
,
2279 &actions
[nr_options
],
2280 &options
[nr_options
],
2283 if (bi
->to
.sym
!= bi
->from
.sym
)
2284 nr_options
+= add_annotate_opt(browser
,
2285 &actions
[nr_options
],
2286 &options
[nr_options
],
2290 nr_options
+= add_annotate_opt(browser
,
2291 &actions
[nr_options
],
2292 &options
[nr_options
],
2293 browser
->selection
->map
,
2294 browser
->selection
->sym
);
2297 nr_options
+= add_thread_opt(browser
, &actions
[nr_options
],
2298 &options
[nr_options
], thread
);
2299 nr_options
+= add_dso_opt(browser
, &actions
[nr_options
],
2300 &options
[nr_options
], map
);
2301 nr_options
+= add_map_opt(browser
, &actions
[nr_options
],
2302 &options
[nr_options
],
2303 browser
->selection
?
2304 browser
->selection
->map
: NULL
);
2305 nr_options
+= add_socket_opt(browser
, &actions
[nr_options
],
2306 &options
[nr_options
],
2308 /* perf script support */
2309 if (browser
->he_selection
) {
2310 nr_options
+= add_script_opt(browser
,
2311 &actions
[nr_options
],
2312 &options
[nr_options
],
2315 * Note that browser->selection != NULL
2316 * when browser->he_selection is not NULL,
2317 * so we don't need to check browser->selection
2318 * before fetching browser->selection->sym like what
2319 * we do before fetching browser->selection->map.
2321 * See hist_browser__show_entry.
2323 nr_options
+= add_script_opt(browser
,
2324 &actions
[nr_options
],
2325 &options
[nr_options
],
2326 NULL
, browser
->selection
->sym
);
2328 nr_options
+= add_script_opt(browser
, &actions
[nr_options
],
2329 &options
[nr_options
], NULL
, NULL
);
2330 nr_options
+= add_switch_opt(browser
, &actions
[nr_options
],
2331 &options
[nr_options
]);
2333 nr_options
+= add_exit_opt(browser
, &actions
[nr_options
],
2334 &options
[nr_options
]);
2337 struct popup_action
*act
;
2339 choice
= ui__popup_menu(nr_options
, options
);
2340 if (choice
== -1 || choice
>= nr_options
)
2343 act
= &actions
[choice
];
2344 key
= act
->fn(browser
, act
);
2347 if (key
== K_SWITCH_INPUT_DATA
)
2351 pstack__delete(browser
->pstack
);
2353 hist_browser__delete(browser
);
2354 free_popup_options(options
, MAX_OPTIONS
);
2358 struct perf_evsel_menu
{
2359 struct ui_browser b
;
2360 struct perf_evsel
*selection
;
2361 bool lost_events
, lost_events_warned
;
2363 struct perf_env
*env
;
2366 static void perf_evsel_menu__write(struct ui_browser
*browser
,
2367 void *entry
, int row
)
2369 struct perf_evsel_menu
*menu
= container_of(browser
,
2370 struct perf_evsel_menu
, b
);
2371 struct perf_evsel
*evsel
= list_entry(entry
, struct perf_evsel
, node
);
2372 struct hists
*hists
= evsel__hists(evsel
);
2373 bool current_entry
= ui_browser__is_current_entry(browser
, row
);
2374 unsigned long nr_events
= hists
->stats
.nr_events
[PERF_RECORD_SAMPLE
];
2375 const char *ev_name
= perf_evsel__name(evsel
);
2377 const char *warn
= " ";
2380 ui_browser__set_color(browser
, current_entry
? HE_COLORSET_SELECTED
:
2381 HE_COLORSET_NORMAL
);
2383 if (perf_evsel__is_group_event(evsel
)) {
2384 struct perf_evsel
*pos
;
2386 ev_name
= perf_evsel__group_name(evsel
);
2388 for_each_group_member(pos
, evsel
) {
2389 struct hists
*pos_hists
= evsel__hists(pos
);
2390 nr_events
+= pos_hists
->stats
.nr_events
[PERF_RECORD_SAMPLE
];
2394 nr_events
= convert_unit(nr_events
, &unit
);
2395 printed
= scnprintf(bf
, sizeof(bf
), "%lu%c%s%s", nr_events
,
2396 unit
, unit
== ' ' ? "" : " ", ev_name
);
2397 ui_browser__printf(browser
, "%s", bf
);
2399 nr_events
= hists
->stats
.nr_events
[PERF_RECORD_LOST
];
2400 if (nr_events
!= 0) {
2401 menu
->lost_events
= true;
2403 ui_browser__set_color(browser
, HE_COLORSET_TOP
);
2404 nr_events
= convert_unit(nr_events
, &unit
);
2405 printed
+= scnprintf(bf
, sizeof(bf
), ": %ld%c%schunks LOST!",
2406 nr_events
, unit
, unit
== ' ' ? "" : " ");
2410 ui_browser__write_nstring(browser
, warn
, browser
->width
- printed
);
2413 menu
->selection
= evsel
;
2416 static int perf_evsel_menu__run(struct perf_evsel_menu
*menu
,
2417 int nr_events
, const char *help
,
2418 struct hist_browser_timer
*hbt
)
2420 struct perf_evlist
*evlist
= menu
->b
.priv
;
2421 struct perf_evsel
*pos
;
2422 const char *title
= "Available samples";
2423 int delay_secs
= hbt
? hbt
->refresh
: 0;
2426 if (ui_browser__show(&menu
->b
, title
,
2427 "ESC: exit, ENTER|->: Browse histograms") < 0)
2431 key
= ui_browser__run(&menu
->b
, delay_secs
);
2435 hbt
->timer(hbt
->arg
);
2437 if (!menu
->lost_events_warned
&& menu
->lost_events
) {
2438 ui_browser__warn_lost_events(&menu
->b
);
2439 menu
->lost_events_warned
= true;
2444 if (!menu
->selection
)
2446 pos
= menu
->selection
;
2448 perf_evlist__set_selected(evlist
, pos
);
2450 * Give the calling tool a chance to populate the non
2451 * default evsel resorted hists tree.
2454 hbt
->timer(hbt
->arg
);
2455 key
= perf_evsel__hists_browse(pos
, nr_events
, help
,
2459 ui_browser__show_title(&menu
->b
, title
);
2462 if (pos
->node
.next
== &evlist
->entries
)
2463 pos
= perf_evlist__first(evlist
);
2465 pos
= perf_evsel__next(pos
);
2468 if (pos
->node
.prev
== &evlist
->entries
)
2469 pos
= perf_evlist__last(evlist
);
2471 pos
= perf_evsel__prev(pos
);
2473 case K_SWITCH_INPUT_DATA
:
2484 if (!ui_browser__dialog_yesno(&menu
->b
,
2485 "Do you really want to exit?"))
2497 ui_browser__hide(&menu
->b
);
2501 static bool filter_group_entries(struct ui_browser
*browser __maybe_unused
,
2504 struct perf_evsel
*evsel
= list_entry(entry
, struct perf_evsel
, node
);
2506 if (symbol_conf
.event_group
&& !perf_evsel__is_group_leader(evsel
))
2512 static int __perf_evlist__tui_browse_hists(struct perf_evlist
*evlist
,
2513 int nr_entries
, const char *help
,
2514 struct hist_browser_timer
*hbt
,
2516 struct perf_env
*env
)
2518 struct perf_evsel
*pos
;
2519 struct perf_evsel_menu menu
= {
2521 .entries
= &evlist
->entries
,
2522 .refresh
= ui_browser__list_head_refresh
,
2523 .seek
= ui_browser__list_head_seek
,
2524 .write
= perf_evsel_menu__write
,
2525 .filter
= filter_group_entries
,
2526 .nr_entries
= nr_entries
,
2529 .min_pcnt
= min_pcnt
,
2533 ui_helpline__push("Press ESC to exit");
2535 evlist__for_each(evlist
, pos
) {
2536 const char *ev_name
= perf_evsel__name(pos
);
2537 size_t line_len
= strlen(ev_name
) + 7;
2539 if (menu
.b
.width
< line_len
)
2540 menu
.b
.width
= line_len
;
2543 return perf_evsel_menu__run(&menu
, nr_entries
, help
, hbt
);
2546 int perf_evlist__tui_browse_hists(struct perf_evlist
*evlist
, const char *help
,
2547 struct hist_browser_timer
*hbt
,
2549 struct perf_env
*env
)
2551 int nr_entries
= evlist
->nr_entries
;
2554 if (nr_entries
== 1) {
2555 struct perf_evsel
*first
= perf_evlist__first(evlist
);
2557 return perf_evsel__hists_browse(first
, nr_entries
, help
,
2558 false, hbt
, min_pcnt
,
2562 if (symbol_conf
.event_group
) {
2563 struct perf_evsel
*pos
;
2566 evlist__for_each(evlist
, pos
) {
2567 if (perf_evsel__is_group_leader(pos
))
2571 if (nr_entries
== 1)
2575 return __perf_evlist__tui_browse_hists(evlist
, nr_entries
, help
,
2576 hbt
, min_pcnt
, env
);