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_hierarchy_entries
;
36 u64 nr_callchain_rows
;
39 extern void hist_browser__init_hpp(void);
41 static int hists__browser_title(struct hists
*hists
,
42 struct hist_browser_timer
*hbt
,
43 char *bf
, size_t size
);
44 static void hist_browser__update_nr_entries(struct hist_browser
*hb
);
46 static struct rb_node
*hists__filter_entries(struct rb_node
*nd
,
49 static bool hist_browser__has_filter(struct hist_browser
*hb
)
51 return hists__has_filter(hb
->hists
) || hb
->min_pcnt
|| symbol_conf
.has_filter
;
54 static int hist_browser__get_folding(struct hist_browser
*browser
)
57 struct hists
*hists
= browser
->hists
;
58 int unfolded_rows
= 0;
60 for (nd
= rb_first(&hists
->entries
);
61 (nd
= hists__filter_entries(nd
, browser
->min_pcnt
)) != NULL
;
62 nd
= rb_hierarchy_next(nd
)) {
63 struct hist_entry
*he
=
64 rb_entry(nd
, struct hist_entry
, rb_node
);
66 if (he
->leaf
&& he
->unfolded
)
67 unfolded_rows
+= he
->nr_rows
;
72 static u32
hist_browser__nr_entries(struct hist_browser
*hb
)
76 if (symbol_conf
.report_hierarchy
)
77 nr_entries
= hb
->nr_hierarchy_entries
;
78 else if (hist_browser__has_filter(hb
))
79 nr_entries
= hb
->nr_non_filtered_entries
;
81 nr_entries
= hb
->hists
->nr_entries
;
83 hb
->nr_callchain_rows
= hist_browser__get_folding(hb
);
84 return nr_entries
+ hb
->nr_callchain_rows
;
87 static void hist_browser__update_rows(struct hist_browser
*hb
)
89 struct ui_browser
*browser
= &hb
->b
;
90 u16 header_offset
= hb
->show_headers
? 1 : 0, index_row
;
92 browser
->rows
= browser
->height
- header_offset
;
94 * Verify if we were at the last line and that line isn't
95 * visibe because we now show the header line(s).
97 index_row
= browser
->index
- browser
->top_idx
;
98 if (index_row
>= browser
->rows
)
99 browser
->index
-= index_row
- browser
->rows
+ 1;
102 static void hist_browser__refresh_dimensions(struct ui_browser
*browser
)
104 struct hist_browser
*hb
= container_of(browser
, struct hist_browser
, b
);
106 /* 3 == +/- toggle symbol before actual hist_entry rendering */
107 browser
->width
= 3 + (hists__sort_list_width(hb
->hists
) + sizeof("[k]"));
109 * FIXME: Just keeping existing behaviour, but this really should be
110 * before updating browser->width, as it will invalidate the
111 * calculation above. Fix this and the fallout in another
114 ui_browser__refresh_dimensions(browser
);
115 hist_browser__update_rows(hb
);
118 static void hist_browser__gotorc(struct hist_browser
*browser
, int row
, int column
)
120 u16 header_offset
= browser
->show_headers
? 1 : 0;
122 ui_browser__gotorc(&browser
->b
, row
+ header_offset
, column
);
125 static void hist_browser__reset(struct hist_browser
*browser
)
128 * The hists__remove_entry_filter() already folds non-filtered
129 * entries so we can assume it has 0 callchain rows.
131 browser
->nr_callchain_rows
= 0;
133 hist_browser__update_nr_entries(browser
);
134 browser
->b
.nr_entries
= hist_browser__nr_entries(browser
);
135 hist_browser__refresh_dimensions(&browser
->b
);
136 ui_browser__reset_index(&browser
->b
);
139 static char tree__folded_sign(bool unfolded
)
141 return unfolded
? '-' : '+';
144 static char hist_entry__folded(const struct hist_entry
*he
)
146 return he
->has_children
? tree__folded_sign(he
->unfolded
) : ' ';
149 static char callchain_list__folded(const struct callchain_list
*cl
)
151 return cl
->has_children
? tree__folded_sign(cl
->unfolded
) : ' ';
154 static void callchain_list__set_folding(struct callchain_list
*cl
, bool unfold
)
156 cl
->unfolded
= unfold
? cl
->has_children
: false;
159 static int callchain_node__count_rows_rb_tree(struct callchain_node
*node
)
164 for (nd
= rb_first(&node
->rb_root
); nd
; nd
= rb_next(nd
)) {
165 struct callchain_node
*child
= rb_entry(nd
, struct callchain_node
, rb_node
);
166 struct callchain_list
*chain
;
167 char folded_sign
= ' '; /* No children */
169 list_for_each_entry(chain
, &child
->val
, list
) {
171 /* We need this because we may not have children */
172 folded_sign
= callchain_list__folded(chain
);
173 if (folded_sign
== '+')
177 if (folded_sign
== '-') /* Have children and they're unfolded */
178 n
+= callchain_node__count_rows_rb_tree(child
);
184 static int callchain_node__count_flat_rows(struct callchain_node
*node
)
186 struct callchain_list
*chain
;
187 char folded_sign
= 0;
190 list_for_each_entry(chain
, &node
->parent_val
, list
) {
192 /* only check first chain list entry */
193 folded_sign
= callchain_list__folded(chain
);
194 if (folded_sign
== '+')
200 list_for_each_entry(chain
, &node
->val
, list
) {
202 /* node->parent_val list might be empty */
203 folded_sign
= callchain_list__folded(chain
);
204 if (folded_sign
== '+')
213 static int callchain_node__count_folded_rows(struct callchain_node
*node __maybe_unused
)
218 static int callchain_node__count_rows(struct callchain_node
*node
)
220 struct callchain_list
*chain
;
221 bool unfolded
= false;
224 if (callchain_param
.mode
== CHAIN_FLAT
)
225 return callchain_node__count_flat_rows(node
);
226 else if (callchain_param
.mode
== CHAIN_FOLDED
)
227 return callchain_node__count_folded_rows(node
);
229 list_for_each_entry(chain
, &node
->val
, list
) {
231 unfolded
= chain
->unfolded
;
235 n
+= callchain_node__count_rows_rb_tree(node
);
240 static int callchain__count_rows(struct rb_root
*chain
)
245 for (nd
= rb_first(chain
); nd
; nd
= rb_next(nd
)) {
246 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
247 n
+= callchain_node__count_rows(node
);
253 static int hierarchy_count_rows(struct hist_browser
*hb
, struct hist_entry
*he
,
254 bool include_children
)
257 struct rb_node
*node
;
258 struct hist_entry
*child
;
261 return callchain__count_rows(&he
->sorted_chain
);
263 node
= rb_first(&he
->hroot_out
);
267 child
= rb_entry(node
, struct hist_entry
, rb_node
);
268 percent
= hist_entry__get_percent_limit(child
);
270 if (!child
->filtered
&& percent
>= hb
->min_pcnt
) {
273 if (include_children
&& child
->unfolded
)
274 count
+= hierarchy_count_rows(hb
, child
, true);
277 node
= rb_next(node
);
282 static bool hist_entry__toggle_fold(struct hist_entry
*he
)
287 if (!he
->has_children
)
290 he
->unfolded
= !he
->unfolded
;
294 static bool callchain_list__toggle_fold(struct callchain_list
*cl
)
299 if (!cl
->has_children
)
302 cl
->unfolded
= !cl
->unfolded
;
306 static void callchain_node__init_have_children_rb_tree(struct callchain_node
*node
)
308 struct rb_node
*nd
= rb_first(&node
->rb_root
);
310 for (nd
= rb_first(&node
->rb_root
); nd
; nd
= rb_next(nd
)) {
311 struct callchain_node
*child
= rb_entry(nd
, struct callchain_node
, rb_node
);
312 struct callchain_list
*chain
;
315 list_for_each_entry(chain
, &child
->val
, list
) {
318 chain
->has_children
= chain
->list
.next
!= &child
->val
||
319 !RB_EMPTY_ROOT(&child
->rb_root
);
321 chain
->has_children
= chain
->list
.next
== &child
->val
&&
322 !RB_EMPTY_ROOT(&child
->rb_root
);
325 callchain_node__init_have_children_rb_tree(child
);
329 static void callchain_node__init_have_children(struct callchain_node
*node
,
332 struct callchain_list
*chain
;
334 chain
= list_entry(node
->val
.next
, struct callchain_list
, list
);
335 chain
->has_children
= has_sibling
;
337 if (node
->val
.next
!= node
->val
.prev
) {
338 chain
= list_entry(node
->val
.prev
, struct callchain_list
, list
);
339 chain
->has_children
= !RB_EMPTY_ROOT(&node
->rb_root
);
342 callchain_node__init_have_children_rb_tree(node
);
345 static void callchain__init_have_children(struct rb_root
*root
)
347 struct rb_node
*nd
= rb_first(root
);
348 bool has_sibling
= nd
&& rb_next(nd
);
350 for (nd
= rb_first(root
); nd
; nd
= rb_next(nd
)) {
351 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
352 callchain_node__init_have_children(node
, has_sibling
);
353 if (callchain_param
.mode
== CHAIN_FLAT
||
354 callchain_param
.mode
== CHAIN_FOLDED
)
355 callchain_node__make_parent_list(node
);
359 static void hist_entry__init_have_children(struct hist_entry
*he
)
361 if (he
->init_have_children
)
365 he
->has_children
= !RB_EMPTY_ROOT(&he
->sorted_chain
);
366 callchain__init_have_children(&he
->sorted_chain
);
368 he
->has_children
= !RB_EMPTY_ROOT(&he
->hroot_out
);
371 he
->init_have_children
= true;
374 static bool hist_browser__toggle_fold(struct hist_browser
*browser
)
376 struct hist_entry
*he
= browser
->he_selection
;
377 struct map_symbol
*ms
= browser
->selection
;
378 struct callchain_list
*cl
= container_of(ms
, struct callchain_list
, ms
);
385 has_children
= hist_entry__toggle_fold(he
);
387 has_children
= callchain_list__toggle_fold(cl
);
392 hist_entry__init_have_children(he
);
393 browser
->b
.nr_entries
-= he
->nr_rows
;
396 browser
->nr_callchain_rows
-= he
->nr_rows
;
398 browser
->nr_hierarchy_entries
-= he
->nr_rows
;
400 if (symbol_conf
.report_hierarchy
)
401 child_rows
= hierarchy_count_rows(browser
, he
, true);
405 he
->nr_rows
= callchain__count_rows(&he
->sorted_chain
);
407 he
->nr_rows
= hierarchy_count_rows(browser
, he
, false);
409 /* account grand children */
410 if (symbol_conf
.report_hierarchy
)
411 browser
->b
.nr_entries
+= child_rows
- he
->nr_rows
;
413 if (symbol_conf
.report_hierarchy
)
414 browser
->b
.nr_entries
-= child_rows
- he
->nr_rows
;
419 browser
->b
.nr_entries
+= he
->nr_rows
;
422 browser
->nr_callchain_rows
+= he
->nr_rows
;
424 browser
->nr_hierarchy_entries
+= he
->nr_rows
;
429 /* If it doesn't have children, no toggling performed */
433 static int callchain_node__set_folding_rb_tree(struct callchain_node
*node
, bool unfold
)
438 for (nd
= rb_first(&node
->rb_root
); nd
; nd
= rb_next(nd
)) {
439 struct callchain_node
*child
= rb_entry(nd
, struct callchain_node
, rb_node
);
440 struct callchain_list
*chain
;
441 bool has_children
= false;
443 list_for_each_entry(chain
, &child
->val
, list
) {
445 callchain_list__set_folding(chain
, unfold
);
446 has_children
= chain
->has_children
;
450 n
+= callchain_node__set_folding_rb_tree(child
, unfold
);
456 static int callchain_node__set_folding(struct callchain_node
*node
, bool unfold
)
458 struct callchain_list
*chain
;
459 bool has_children
= false;
462 list_for_each_entry(chain
, &node
->val
, list
) {
464 callchain_list__set_folding(chain
, unfold
);
465 has_children
= chain
->has_children
;
469 n
+= callchain_node__set_folding_rb_tree(node
, unfold
);
474 static int callchain__set_folding(struct rb_root
*chain
, bool unfold
)
479 for (nd
= rb_first(chain
); nd
; nd
= rb_next(nd
)) {
480 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
481 n
+= callchain_node__set_folding(node
, unfold
);
487 static int hierarchy_set_folding(struct hist_browser
*hb
, struct hist_entry
*he
,
488 bool unfold __maybe_unused
)
492 struct hist_entry
*child
;
495 for (nd
= rb_first(&he
->hroot_out
); nd
; nd
= rb_next(nd
)) {
496 child
= rb_entry(nd
, struct hist_entry
, rb_node
);
497 percent
= hist_entry__get_percent_limit(child
);
498 if (!child
->filtered
&& percent
>= hb
->min_pcnt
)
505 static void hist_entry__set_folding(struct hist_entry
*he
,
506 struct hist_browser
*hb
, bool unfold
)
508 hist_entry__init_have_children(he
);
509 he
->unfolded
= unfold
? he
->has_children
: false;
511 if (he
->has_children
) {
515 n
= callchain__set_folding(&he
->sorted_chain
, unfold
);
517 n
= hierarchy_set_folding(hb
, he
, unfold
);
519 he
->nr_rows
= unfold
? n
: 0;
525 __hist_browser__set_folding(struct hist_browser
*browser
, bool unfold
)
528 struct hist_entry
*he
;
531 nd
= rb_first(&browser
->hists
->entries
);
533 he
= rb_entry(nd
, struct hist_entry
, rb_node
);
535 /* set folding state even if it's currently folded */
536 nd
= __rb_hierarchy_next(nd
, HMD_FORCE_CHILD
);
538 hist_entry__set_folding(he
, browser
, unfold
);
540 percent
= hist_entry__get_percent_limit(he
);
541 if (he
->filtered
|| percent
< browser
->min_pcnt
)
544 if (!he
->depth
|| unfold
)
545 browser
->nr_hierarchy_entries
++;
547 browser
->nr_callchain_rows
+= he
->nr_rows
;
551 static void hist_browser__set_folding(struct hist_browser
*browser
, bool unfold
)
553 browser
->nr_hierarchy_entries
= 0;
554 browser
->nr_callchain_rows
= 0;
555 __hist_browser__set_folding(browser
, unfold
);
557 browser
->b
.nr_entries
= hist_browser__nr_entries(browser
);
558 /* Go to the start, we may be way after valid entries after a collapse */
559 ui_browser__reset_index(&browser
->b
);
562 static void ui_browser__warn_lost_events(struct ui_browser
*browser
)
564 ui_browser__warning(browser
, 4,
565 "Events are being lost, check IO/CPU overload!\n\n"
566 "You may want to run 'perf' using a RT scheduler policy:\n\n"
567 " perf top -r 80\n\n"
568 "Or reduce the sampling frequency.");
571 static int hist_browser__run(struct hist_browser
*browser
, const char *help
)
575 struct hist_browser_timer
*hbt
= browser
->hbt
;
576 int delay_secs
= hbt
? hbt
->refresh
: 0;
578 browser
->b
.entries
= &browser
->hists
->entries
;
579 browser
->b
.nr_entries
= hist_browser__nr_entries(browser
);
581 hists__browser_title(browser
->hists
, hbt
, title
, sizeof(title
));
583 if (ui_browser__show(&browser
->b
, title
, "%s", help
) < 0)
587 key
= ui_browser__run(&browser
->b
, delay_secs
);
592 hbt
->timer(hbt
->arg
);
594 if (hist_browser__has_filter(browser
))
595 hist_browser__update_nr_entries(browser
);
597 nr_entries
= hist_browser__nr_entries(browser
);
598 ui_browser__update_nr_entries(&browser
->b
, nr_entries
);
600 if (browser
->hists
->stats
.nr_lost_warned
!=
601 browser
->hists
->stats
.nr_events
[PERF_RECORD_LOST
]) {
602 browser
->hists
->stats
.nr_lost_warned
=
603 browser
->hists
->stats
.nr_events
[PERF_RECORD_LOST
];
604 ui_browser__warn_lost_events(&browser
->b
);
607 hists__browser_title(browser
->hists
,
608 hbt
, title
, sizeof(title
));
609 ui_browser__show_title(&browser
->b
, title
);
612 case 'D': { /* Debug */
614 struct hist_entry
*h
= rb_entry(browser
->b
.top
,
615 struct hist_entry
, rb_node
);
617 ui_helpline__fpush("%d: nr_ent=(%d,%d), rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
618 seq
++, browser
->b
.nr_entries
,
619 browser
->hists
->nr_entries
,
623 h
->row_offset
, h
->nr_rows
);
627 /* Collapse the whole world. */
628 hist_browser__set_folding(browser
, false);
631 /* Expand the whole world. */
632 hist_browser__set_folding(browser
, true);
635 browser
->show_headers
= !browser
->show_headers
;
636 hist_browser__update_rows(browser
);
639 if (hist_browser__toggle_fold(browser
))
647 ui_browser__hide(&browser
->b
);
651 struct callchain_print_arg
{
652 /* for hists browser */
654 bool is_current_entry
;
661 typedef void (*print_callchain_entry_fn
)(struct hist_browser
*browser
,
662 struct callchain_list
*chain
,
663 const char *str
, int offset
,
665 struct callchain_print_arg
*arg
);
667 static void hist_browser__show_callchain_entry(struct hist_browser
*browser
,
668 struct callchain_list
*chain
,
669 const char *str
, int offset
,
671 struct callchain_print_arg
*arg
)
674 char folded_sign
= callchain_list__folded(chain
);
675 bool show_annotated
= browser
->show_dso
&& chain
->ms
.sym
&& symbol__annotation(chain
->ms
.sym
)->src
;
677 color
= HE_COLORSET_NORMAL
;
678 width
= browser
->b
.width
- (offset
+ 2);
679 if (ui_browser__is_current_entry(&browser
->b
, row
)) {
680 browser
->selection
= &chain
->ms
;
681 color
= HE_COLORSET_SELECTED
;
682 arg
->is_current_entry
= true;
685 ui_browser__set_color(&browser
->b
, color
);
686 hist_browser__gotorc(browser
, row
, 0);
687 ui_browser__write_nstring(&browser
->b
, " ", offset
);
688 ui_browser__printf(&browser
->b
, "%c", folded_sign
);
689 ui_browser__write_graph(&browser
->b
, show_annotated
? SLSMG_RARROW_CHAR
: ' ');
690 ui_browser__write_nstring(&browser
->b
, str
, width
);
693 static void hist_browser__fprintf_callchain_entry(struct hist_browser
*b __maybe_unused
,
694 struct callchain_list
*chain
,
695 const char *str
, int offset
,
696 unsigned short row __maybe_unused
,
697 struct callchain_print_arg
*arg
)
699 char folded_sign
= callchain_list__folded(chain
);
701 arg
->printed
+= fprintf(arg
->fp
, "%*s%c %s\n", offset
, " ",
705 typedef bool (*check_output_full_fn
)(struct hist_browser
*browser
,
708 static bool hist_browser__check_output_full(struct hist_browser
*browser
,
711 return browser
->b
.rows
== row
;
714 static bool hist_browser__check_dump_full(struct hist_browser
*browser __maybe_unused
,
715 unsigned short row __maybe_unused
)
720 #define LEVEL_OFFSET_STEP 3
722 static int hist_browser__show_callchain_list(struct hist_browser
*browser
,
723 struct callchain_node
*node
,
724 struct callchain_list
*chain
,
725 unsigned short row
, u64 total
,
726 bool need_percent
, int offset
,
727 print_callchain_entry_fn print
,
728 struct callchain_print_arg
*arg
)
730 char bf
[1024], *alloc_str
;
733 if (arg
->row_offset
!= 0) {
739 str
= callchain_list__sym_name(chain
, bf
, sizeof(bf
),
745 callchain_node__scnprintf_value(node
, buf
, sizeof(buf
),
748 if (asprintf(&alloc_str
, "%s %s", buf
, str
) < 0)
749 str
= "Not enough memory!";
754 print(browser
, chain
, str
, offset
, row
, arg
);
760 static bool check_percent_display(struct rb_node
*node
, u64 parent_total
)
762 struct callchain_node
*child
;
770 child
= rb_entry(node
, struct callchain_node
, rb_node
);
771 return callchain_cumul_hits(child
) != parent_total
;
774 static int hist_browser__show_callchain_flat(struct hist_browser
*browser
,
775 struct rb_root
*root
,
776 unsigned short row
, u64 total
,
778 print_callchain_entry_fn print
,
779 struct callchain_print_arg
*arg
,
780 check_output_full_fn is_output_full
)
782 struct rb_node
*node
;
783 int first_row
= row
, offset
= LEVEL_OFFSET_STEP
;
786 node
= rb_first(root
);
787 need_percent
= check_percent_display(node
, parent_total
);
790 struct callchain_node
*child
= rb_entry(node
, struct callchain_node
, rb_node
);
791 struct rb_node
*next
= rb_next(node
);
792 struct callchain_list
*chain
;
793 char folded_sign
= ' ';
795 int extra_offset
= 0;
797 list_for_each_entry(chain
, &child
->parent_val
, list
) {
798 bool was_first
= first
;
802 else if (need_percent
)
803 extra_offset
= LEVEL_OFFSET_STEP
;
805 folded_sign
= callchain_list__folded(chain
);
807 row
+= hist_browser__show_callchain_list(browser
, child
,
809 was_first
&& need_percent
,
810 offset
+ extra_offset
,
813 if (is_output_full(browser
, row
))
816 if (folded_sign
== '+')
820 list_for_each_entry(chain
, &child
->val
, list
) {
821 bool was_first
= first
;
825 else if (need_percent
)
826 extra_offset
= LEVEL_OFFSET_STEP
;
828 folded_sign
= callchain_list__folded(chain
);
830 row
+= hist_browser__show_callchain_list(browser
, child
,
832 was_first
&& need_percent
,
833 offset
+ extra_offset
,
836 if (is_output_full(browser
, row
))
839 if (folded_sign
== '+')
844 if (is_output_full(browser
, row
))
849 return row
- first_row
;
852 static char *hist_browser__folded_callchain_str(struct hist_browser
*browser
,
853 struct callchain_list
*chain
,
854 char *value_str
, char *old_str
)
860 str
= callchain_list__sym_name(chain
, bf
, sizeof(bf
),
863 if (asprintf(&new, "%s%s%s", old_str
,
864 symbol_conf
.field_sep
?: ";", str
) < 0)
868 if (asprintf(&new, "%s %s", value_str
, str
) < 0)
871 if (asprintf(&new, "%s", str
) < 0)
878 static int hist_browser__show_callchain_folded(struct hist_browser
*browser
,
879 struct rb_root
*root
,
880 unsigned short row
, u64 total
,
882 print_callchain_entry_fn print
,
883 struct callchain_print_arg
*arg
,
884 check_output_full_fn is_output_full
)
886 struct rb_node
*node
;
887 int first_row
= row
, offset
= LEVEL_OFFSET_STEP
;
890 node
= rb_first(root
);
891 need_percent
= check_percent_display(node
, parent_total
);
894 struct callchain_node
*child
= rb_entry(node
, struct callchain_node
, rb_node
);
895 struct rb_node
*next
= rb_next(node
);
896 struct callchain_list
*chain
, *first_chain
= NULL
;
898 char *value_str
= NULL
, *value_str_alloc
= NULL
;
899 char *chain_str
= NULL
, *chain_str_alloc
= NULL
;
901 if (arg
->row_offset
!= 0) {
909 callchain_node__scnprintf_value(child
, buf
, sizeof(buf
), total
);
910 if (asprintf(&value_str
, "%s", buf
) < 0) {
911 value_str
= (char *)"<...>";
914 value_str_alloc
= value_str
;
917 list_for_each_entry(chain
, &child
->parent_val
, list
) {
918 chain_str
= hist_browser__folded_callchain_str(browser
,
919 chain
, value_str
, chain_str
);
925 if (chain_str
== NULL
) {
926 chain_str
= (char *)"Not enough memory!";
930 chain_str_alloc
= chain_str
;
933 list_for_each_entry(chain
, &child
->val
, list
) {
934 chain_str
= hist_browser__folded_callchain_str(browser
,
935 chain
, value_str
, chain_str
);
941 if (chain_str
== NULL
) {
942 chain_str
= (char *)"Not enough memory!";
946 chain_str_alloc
= chain_str
;
950 print(browser
, first_chain
, chain_str
, offset
, row
++, arg
);
951 free(value_str_alloc
);
952 free(chain_str_alloc
);
955 if (is_output_full(browser
, row
))
960 return row
- first_row
;
963 static int hist_browser__show_callchain_graph(struct hist_browser
*browser
,
964 struct rb_root
*root
, int level
,
965 unsigned short row
, u64 total
,
967 print_callchain_entry_fn print
,
968 struct callchain_print_arg
*arg
,
969 check_output_full_fn is_output_full
)
971 struct rb_node
*node
;
972 int first_row
= row
, offset
= level
* LEVEL_OFFSET_STEP
;
974 u64 percent_total
= total
;
976 if (callchain_param
.mode
== CHAIN_GRAPH_REL
)
977 percent_total
= parent_total
;
979 node
= rb_first(root
);
980 need_percent
= check_percent_display(node
, parent_total
);
983 struct callchain_node
*child
= rb_entry(node
, struct callchain_node
, rb_node
);
984 struct rb_node
*next
= rb_next(node
);
985 struct callchain_list
*chain
;
986 char folded_sign
= ' ';
988 int extra_offset
= 0;
990 list_for_each_entry(chain
, &child
->val
, list
) {
991 bool was_first
= first
;
995 else if (need_percent
)
996 extra_offset
= LEVEL_OFFSET_STEP
;
998 folded_sign
= callchain_list__folded(chain
);
1000 row
+= hist_browser__show_callchain_list(browser
, child
,
1001 chain
, row
, percent_total
,
1002 was_first
&& need_percent
,
1003 offset
+ extra_offset
,
1006 if (is_output_full(browser
, row
))
1009 if (folded_sign
== '+')
1013 if (folded_sign
== '-') {
1014 const int new_level
= level
+ (extra_offset
? 2 : 1);
1016 row
+= hist_browser__show_callchain_graph(browser
, &child
->rb_root
,
1017 new_level
, row
, total
,
1018 child
->children_hit
,
1019 print
, arg
, is_output_full
);
1021 if (is_output_full(browser
, row
))
1026 return row
- first_row
;
1029 static int hist_browser__show_callchain(struct hist_browser
*browser
,
1030 struct hist_entry
*entry
, int level
,
1032 print_callchain_entry_fn print
,
1033 struct callchain_print_arg
*arg
,
1034 check_output_full_fn is_output_full
)
1036 u64 total
= hists__total_period(entry
->hists
);
1040 if (symbol_conf
.cumulate_callchain
)
1041 parent_total
= entry
->stat_acc
->period
;
1043 parent_total
= entry
->stat
.period
;
1045 if (callchain_param
.mode
== CHAIN_FLAT
) {
1046 printed
= hist_browser__show_callchain_flat(browser
,
1047 &entry
->sorted_chain
, row
,
1048 total
, parent_total
, print
, arg
,
1050 } else if (callchain_param
.mode
== CHAIN_FOLDED
) {
1051 printed
= hist_browser__show_callchain_folded(browser
,
1052 &entry
->sorted_chain
, row
,
1053 total
, parent_total
, print
, arg
,
1056 printed
= hist_browser__show_callchain_graph(browser
,
1057 &entry
->sorted_chain
, level
, row
,
1058 total
, parent_total
, print
, arg
,
1062 if (arg
->is_current_entry
)
1063 browser
->he_selection
= entry
;
1069 struct ui_browser
*b
;
1074 static int __hpp__slsmg_color_printf(struct perf_hpp
*hpp
, const char *fmt
, ...)
1076 struct hpp_arg
*arg
= hpp
->ptr
;
1081 va_start(args
, fmt
);
1082 len
= va_arg(args
, int);
1083 percent
= va_arg(args
, double);
1086 ui_browser__set_percent_color(arg
->b
, percent
, arg
->current_entry
);
1088 ret
= scnprintf(hpp
->buf
, hpp
->size
, fmt
, len
, percent
);
1089 ui_browser__printf(arg
->b
, "%s", hpp
->buf
);
1091 advance_hpp(hpp
, ret
);
1095 #define __HPP_COLOR_PERCENT_FN(_type, _field) \
1096 static u64 __hpp_get_##_field(struct hist_entry *he) \
1098 return he->stat._field; \
1102 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
1103 struct perf_hpp *hpp, \
1104 struct hist_entry *he) \
1106 return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%", \
1107 __hpp__slsmg_color_printf, true); \
1110 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
1111 static u64 __hpp_get_acc_##_field(struct hist_entry *he) \
1113 return he->stat_acc->_field; \
1117 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
1118 struct perf_hpp *hpp, \
1119 struct hist_entry *he) \
1121 if (!symbol_conf.cumulate_callchain) { \
1122 struct hpp_arg *arg = hpp->ptr; \
1123 int len = fmt->user_len ?: fmt->len; \
1124 int ret = scnprintf(hpp->buf, hpp->size, \
1125 "%*s", len, "N/A"); \
1126 ui_browser__printf(arg->b, "%s", hpp->buf); \
1130 return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field, \
1131 " %*.2f%%", __hpp__slsmg_color_printf, true); \
1134 __HPP_COLOR_PERCENT_FN(overhead
, period
)
1135 __HPP_COLOR_PERCENT_FN(overhead_sys
, period_sys
)
1136 __HPP_COLOR_PERCENT_FN(overhead_us
, period_us
)
1137 __HPP_COLOR_PERCENT_FN(overhead_guest_sys
, period_guest_sys
)
1138 __HPP_COLOR_PERCENT_FN(overhead_guest_us
, period_guest_us
)
1139 __HPP_COLOR_ACC_PERCENT_FN(overhead_acc
, period
)
1141 #undef __HPP_COLOR_PERCENT_FN
1142 #undef __HPP_COLOR_ACC_PERCENT_FN
1144 void hist_browser__init_hpp(void)
1146 perf_hpp__format
[PERF_HPP__OVERHEAD
].color
=
1147 hist_browser__hpp_color_overhead
;
1148 perf_hpp__format
[PERF_HPP__OVERHEAD_SYS
].color
=
1149 hist_browser__hpp_color_overhead_sys
;
1150 perf_hpp__format
[PERF_HPP__OVERHEAD_US
].color
=
1151 hist_browser__hpp_color_overhead_us
;
1152 perf_hpp__format
[PERF_HPP__OVERHEAD_GUEST_SYS
].color
=
1153 hist_browser__hpp_color_overhead_guest_sys
;
1154 perf_hpp__format
[PERF_HPP__OVERHEAD_GUEST_US
].color
=
1155 hist_browser__hpp_color_overhead_guest_us
;
1156 perf_hpp__format
[PERF_HPP__OVERHEAD_ACC
].color
=
1157 hist_browser__hpp_color_overhead_acc
;
1160 static int hist_browser__show_entry(struct hist_browser
*browser
,
1161 struct hist_entry
*entry
,
1165 int width
= browser
->b
.width
;
1166 char folded_sign
= ' ';
1167 bool current_entry
= ui_browser__is_current_entry(&browser
->b
, row
);
1168 off_t row_offset
= entry
->row_offset
;
1170 struct perf_hpp_fmt
*fmt
;
1172 if (current_entry
) {
1173 browser
->he_selection
= entry
;
1174 browser
->selection
= &entry
->ms
;
1177 if (symbol_conf
.use_callchain
) {
1178 hist_entry__init_have_children(entry
);
1179 folded_sign
= hist_entry__folded(entry
);
1182 if (row_offset
== 0) {
1183 struct hpp_arg arg
= {
1185 .folded_sign
= folded_sign
,
1186 .current_entry
= current_entry
,
1190 hist_browser__gotorc(browser
, row
, 0);
1192 hists__for_each_format(browser
->hists
, fmt
) {
1194 struct perf_hpp hpp
= {
1200 if (perf_hpp__should_skip(fmt
, entry
->hists
) ||
1201 column
++ < browser
->b
.horiz_scroll
)
1204 if (current_entry
&& browser
->b
.navkeypressed
) {
1205 ui_browser__set_color(&browser
->b
,
1206 HE_COLORSET_SELECTED
);
1208 ui_browser__set_color(&browser
->b
,
1209 HE_COLORSET_NORMAL
);
1213 if (symbol_conf
.use_callchain
) {
1214 ui_browser__printf(&browser
->b
, "%c ", folded_sign
);
1219 ui_browser__printf(&browser
->b
, " ");
1224 int ret
= fmt
->color(fmt
, &hpp
, entry
);
1225 hist_entry__snprintf_alignment(entry
, &hpp
, fmt
, ret
);
1227 * fmt->color() already used ui_browser to
1228 * print the non alignment bits, skip it (+ret):
1230 ui_browser__printf(&browser
->b
, "%s", s
+ ret
);
1232 hist_entry__snprintf_alignment(entry
, &hpp
, fmt
, fmt
->entry(fmt
, &hpp
, entry
));
1233 ui_browser__printf(&browser
->b
, "%s", s
);
1235 width
-= hpp
.buf
- s
;
1238 /* The scroll bar isn't being used */
1239 if (!browser
->b
.navkeypressed
)
1242 ui_browser__write_nstring(&browser
->b
, "", width
);
1249 if (folded_sign
== '-' && row
!= browser
->b
.rows
) {
1250 struct callchain_print_arg arg
= {
1251 .row_offset
= row_offset
,
1252 .is_current_entry
= current_entry
,
1255 printed
+= hist_browser__show_callchain(browser
, entry
, 1, row
,
1256 hist_browser__show_callchain_entry
, &arg
,
1257 hist_browser__check_output_full
);
1263 static int hist_browser__show_hierarchy_entry(struct hist_browser
*browser
,
1264 struct hist_entry
*entry
,
1266 int level
, int nr_sort_keys
)
1269 int width
= browser
->b
.width
;
1270 char folded_sign
= ' ';
1271 bool current_entry
= ui_browser__is_current_entry(&browser
->b
, row
);
1272 off_t row_offset
= entry
->row_offset
;
1274 struct perf_hpp_fmt
*fmt
;
1275 struct hpp_arg arg
= {
1277 .current_entry
= current_entry
,
1280 int hierarchy_indent
= (nr_sort_keys
- 1) * HIERARCHY_INDENT
;
1282 if (current_entry
) {
1283 browser
->he_selection
= entry
;
1284 browser
->selection
= &entry
->ms
;
1287 hist_entry__init_have_children(entry
);
1288 folded_sign
= hist_entry__folded(entry
);
1289 arg
.folded_sign
= folded_sign
;
1291 if (entry
->leaf
&& row_offset
) {
1293 goto show_callchain
;
1296 hist_browser__gotorc(browser
, row
, 0);
1298 if (current_entry
&& browser
->b
.navkeypressed
)
1299 ui_browser__set_color(&browser
->b
, HE_COLORSET_SELECTED
);
1301 ui_browser__set_color(&browser
->b
, HE_COLORSET_NORMAL
);
1303 ui_browser__write_nstring(&browser
->b
, "", level
* HIERARCHY_INDENT
);
1304 width
-= level
* HIERARCHY_INDENT
;
1306 hists__for_each_format(entry
->hists
, fmt
) {
1308 struct perf_hpp hpp
= {
1314 if (perf_hpp__should_skip(fmt
, entry
->hists
) ||
1315 column
++ < browser
->b
.horiz_scroll
)
1318 if (perf_hpp__is_sort_entry(fmt
) ||
1319 perf_hpp__is_dynamic_entry(fmt
))
1322 if (current_entry
&& browser
->b
.navkeypressed
) {
1323 ui_browser__set_color(&browser
->b
,
1324 HE_COLORSET_SELECTED
);
1326 ui_browser__set_color(&browser
->b
,
1327 HE_COLORSET_NORMAL
);
1331 ui_browser__printf(&browser
->b
, "%c", folded_sign
);
1335 ui_browser__printf(&browser
->b
, " ");
1340 int ret
= fmt
->color(fmt
, &hpp
, entry
);
1341 hist_entry__snprintf_alignment(entry
, &hpp
, fmt
, ret
);
1343 * fmt->color() already used ui_browser to
1344 * print the non alignment bits, skip it (+ret):
1346 ui_browser__printf(&browser
->b
, "%s", s
+ ret
);
1348 int ret
= fmt
->entry(fmt
, &hpp
, entry
);
1349 hist_entry__snprintf_alignment(entry
, &hpp
, fmt
, ret
);
1350 ui_browser__printf(&browser
->b
, "%s", s
);
1352 width
-= hpp
.buf
- s
;
1355 ui_browser__write_nstring(&browser
->b
, "", hierarchy_indent
);
1356 width
-= hierarchy_indent
;
1358 if (column
>= browser
->b
.horiz_scroll
) {
1360 struct perf_hpp hpp
= {
1366 if (current_entry
&& browser
->b
.navkeypressed
) {
1367 ui_browser__set_color(&browser
->b
,
1368 HE_COLORSET_SELECTED
);
1370 ui_browser__set_color(&browser
->b
,
1371 HE_COLORSET_NORMAL
);
1374 ui_browser__write_nstring(&browser
->b
, "", 2);
1378 * No need to call hist_entry__snprintf_alignment()
1379 * since this fmt is always the last column in the
1384 width
-= fmt
->color(fmt
, &hpp
, entry
);
1386 width
-= fmt
->entry(fmt
, &hpp
, entry
);
1387 ui_browser__printf(&browser
->b
, "%s", s
);
1391 /* The scroll bar isn't being used */
1392 if (!browser
->b
.navkeypressed
)
1395 ui_browser__write_nstring(&browser
->b
, "", width
);
1401 if (entry
->leaf
&& folded_sign
== '-' && row
!= browser
->b
.rows
) {
1402 struct callchain_print_arg carg
= {
1403 .row_offset
= row_offset
,
1406 printed
+= hist_browser__show_callchain(browser
, entry
,
1408 hist_browser__show_callchain_entry
, &carg
,
1409 hist_browser__check_output_full
);
1415 static int advance_hpp_check(struct perf_hpp
*hpp
, int inc
)
1417 advance_hpp(hpp
, inc
);
1418 return hpp
->size
<= 0;
1421 static int hists_browser__scnprintf_headers(struct hist_browser
*browser
, char *buf
, size_t size
)
1423 struct hists
*hists
= browser
->hists
;
1424 struct perf_hpp dummy_hpp
= {
1428 struct perf_hpp_fmt
*fmt
;
1432 if (symbol_conf
.use_callchain
) {
1433 ret
= scnprintf(buf
, size
, " ");
1434 if (advance_hpp_check(&dummy_hpp
, ret
))
1438 hists__for_each_format(browser
->hists
, fmt
) {
1439 if (perf_hpp__should_skip(fmt
, hists
) || column
++ < browser
->b
.horiz_scroll
)
1442 ret
= fmt
->header(fmt
, &dummy_hpp
, hists_to_evsel(hists
));
1443 if (advance_hpp_check(&dummy_hpp
, ret
))
1446 ret
= scnprintf(dummy_hpp
.buf
, dummy_hpp
.size
, " ");
1447 if (advance_hpp_check(&dummy_hpp
, ret
))
1454 static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser
*browser
, char *buf
, size_t size
)
1456 struct hists
*hists
= browser
->hists
;
1457 struct perf_hpp dummy_hpp
= {
1461 struct perf_hpp_fmt
*fmt
;
1464 int nr_sort_keys
= hists
->hpp_list
->nr_sort_keys
;
1467 ret
= scnprintf(buf
, size
, " ");
1468 if (advance_hpp_check(&dummy_hpp
, ret
))
1471 hists__for_each_format(hists
, fmt
) {
1472 if (column
++ < browser
->b
.horiz_scroll
)
1475 if (perf_hpp__is_sort_entry(fmt
) || perf_hpp__is_dynamic_entry(fmt
))
1478 ret
= fmt
->header(fmt
, &dummy_hpp
, hists_to_evsel(hists
));
1479 if (advance_hpp_check(&dummy_hpp
, ret
))
1482 ret
= scnprintf(dummy_hpp
.buf
, dummy_hpp
.size
, " ");
1483 if (advance_hpp_check(&dummy_hpp
, ret
))
1487 ret
= scnprintf(dummy_hpp
.buf
, dummy_hpp
.size
, "%*s",
1488 (nr_sort_keys
- 1) * HIERARCHY_INDENT
, "");
1489 if (advance_hpp_check(&dummy_hpp
, ret
))
1492 hists__for_each_format(hists
, fmt
) {
1493 if (!perf_hpp__is_sort_entry(fmt
) && !perf_hpp__is_dynamic_entry(fmt
))
1495 if (perf_hpp__should_skip(fmt
, hists
))
1501 ret
= scnprintf(dummy_hpp
.buf
, dummy_hpp
.size
, " / ");
1502 if (advance_hpp_check(&dummy_hpp
, ret
))
1506 ret
= fmt
->header(fmt
, &dummy_hpp
, hists_to_evsel(hists
));
1507 dummy_hpp
.buf
[ret
] = '\0';
1508 rtrim(dummy_hpp
.buf
);
1510 ret
= strlen(dummy_hpp
.buf
);
1511 if (advance_hpp_check(&dummy_hpp
, ret
))
1518 static void hist_browser__show_headers(struct hist_browser
*browser
)
1522 if (symbol_conf
.report_hierarchy
)
1523 hists_browser__scnprintf_hierarchy_headers(browser
, headers
,
1526 hists_browser__scnprintf_headers(browser
, headers
,
1528 ui_browser__gotorc(&browser
->b
, 0, 0);
1529 ui_browser__set_color(&browser
->b
, HE_COLORSET_ROOT
);
1530 ui_browser__write_nstring(&browser
->b
, headers
, browser
->b
.width
+ 1);
1533 static void ui_browser__hists_init_top(struct ui_browser
*browser
)
1535 if (browser
->top
== NULL
) {
1536 struct hist_browser
*hb
;
1538 hb
= container_of(browser
, struct hist_browser
, b
);
1539 browser
->top
= rb_first(&hb
->hists
->entries
);
1543 static unsigned int hist_browser__refresh(struct ui_browser
*browser
)
1546 u16 header_offset
= 0;
1548 struct hist_browser
*hb
= container_of(browser
, struct hist_browser
, b
);
1549 int nr_sort
= hb
->hists
->hpp_list
->nr_sort_keys
;
1551 if (hb
->show_headers
) {
1552 hist_browser__show_headers(hb
);
1556 ui_browser__hists_init_top(browser
);
1557 hb
->he_selection
= NULL
;
1558 hb
->selection
= NULL
;
1560 for (nd
= browser
->top
; nd
; nd
= rb_hierarchy_next(nd
)) {
1561 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1565 /* let it move to sibling */
1566 h
->unfolded
= false;
1570 percent
= hist_entry__get_percent_limit(h
);
1571 if (percent
< hb
->min_pcnt
)
1574 if (symbol_conf
.report_hierarchy
) {
1575 row
+= hist_browser__show_hierarchy_entry(hb
, h
, row
,
1579 row
+= hist_browser__show_entry(hb
, h
, row
);
1582 if (row
== browser
->rows
)
1586 return row
+ header_offset
;
1589 static struct rb_node
*hists__filter_entries(struct rb_node
*nd
,
1592 while (nd
!= NULL
) {
1593 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1594 float percent
= hist_entry__get_percent_limit(h
);
1596 if (!h
->filtered
&& percent
>= min_pcnt
)
1600 * If it's filtered, its all children also were filtered.
1601 * So move to sibling node.
1606 nd
= rb_hierarchy_next(nd
);
1612 static struct rb_node
*hists__filter_prev_entries(struct rb_node
*nd
,
1615 while (nd
!= NULL
) {
1616 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1617 float percent
= hist_entry__get_percent_limit(h
);
1619 if (!h
->filtered
&& percent
>= min_pcnt
)
1622 nd
= rb_hierarchy_prev(nd
);
1628 static void ui_browser__hists_seek(struct ui_browser
*browser
,
1629 off_t offset
, int whence
)
1631 struct hist_entry
*h
;
1634 struct hist_browser
*hb
;
1636 hb
= container_of(browser
, struct hist_browser
, b
);
1638 if (browser
->nr_entries
== 0)
1641 ui_browser__hists_init_top(browser
);
1645 nd
= hists__filter_entries(rb_first(browser
->entries
),
1652 nd
= rb_hierarchy_last(rb_last(browser
->entries
));
1653 nd
= hists__filter_prev_entries(nd
, hb
->min_pcnt
);
1661 * Moves not relative to the first visible entry invalidates its
1664 h
= rb_entry(browser
->top
, struct hist_entry
, rb_node
);
1668 * Here we have to check if nd is expanded (+), if it is we can't go
1669 * the next top level hist_entry, instead we must compute an offset of
1670 * what _not_ to show and not change the first visible entry.
1672 * This offset increments when we are going from top to bottom and
1673 * decreases when we're going from bottom to top.
1675 * As we don't have backpointers to the top level in the callchains
1676 * structure, we need to always print the whole hist_entry callchain,
1677 * skipping the first ones that are before the first visible entry
1678 * and stop when we printed enough lines to fill the screen.
1686 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1687 if (h
->unfolded
&& h
->leaf
) {
1688 u16 remaining
= h
->nr_rows
- h
->row_offset
;
1689 if (offset
> remaining
) {
1690 offset
-= remaining
;
1693 h
->row_offset
+= offset
;
1699 nd
= hists__filter_entries(rb_hierarchy_next(nd
),
1705 } while (offset
!= 0);
1706 } else if (offset
< 0) {
1708 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1709 if (h
->unfolded
&& h
->leaf
) {
1711 if (-offset
> h
->row_offset
) {
1712 offset
+= h
->row_offset
;
1715 h
->row_offset
+= offset
;
1721 if (-offset
> h
->nr_rows
) {
1722 offset
+= h
->nr_rows
;
1725 h
->row_offset
= h
->nr_rows
+ offset
;
1733 nd
= hists__filter_prev_entries(rb_hierarchy_prev(nd
),
1741 * Last unfiltered hist_entry, check if it is
1742 * unfolded, if it is then we should have
1743 * row_offset at its last entry.
1745 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1746 if (h
->unfolded
&& h
->leaf
)
1747 h
->row_offset
= h
->nr_rows
;
1754 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1759 static int hist_browser__fprintf_callchain(struct hist_browser
*browser
,
1760 struct hist_entry
*he
, FILE *fp
,
1763 struct callchain_print_arg arg
= {
1767 hist_browser__show_callchain(browser
, he
, level
, 0,
1768 hist_browser__fprintf_callchain_entry
, &arg
,
1769 hist_browser__check_dump_full
);
1773 static int hist_browser__fprintf_entry(struct hist_browser
*browser
,
1774 struct hist_entry
*he
, FILE *fp
)
1778 char folded_sign
= ' ';
1779 struct perf_hpp hpp
= {
1783 struct perf_hpp_fmt
*fmt
;
1787 if (symbol_conf
.use_callchain
)
1788 folded_sign
= hist_entry__folded(he
);
1790 if (symbol_conf
.use_callchain
)
1791 printed
+= fprintf(fp
, "%c ", folded_sign
);
1793 hists__for_each_format(browser
->hists
, fmt
) {
1794 if (perf_hpp__should_skip(fmt
, he
->hists
))
1798 ret
= scnprintf(hpp
.buf
, hpp
.size
, " ");
1799 advance_hpp(&hpp
, ret
);
1803 ret
= fmt
->entry(fmt
, &hpp
, he
);
1804 ret
= hist_entry__snprintf_alignment(he
, &hpp
, fmt
, ret
);
1805 advance_hpp(&hpp
, ret
);
1807 printed
+= fprintf(fp
, "%s\n", s
);
1809 if (folded_sign
== '-')
1810 printed
+= hist_browser__fprintf_callchain(browser
, he
, fp
, 1);
1816 static int hist_browser__fprintf_hierarchy_entry(struct hist_browser
*browser
,
1817 struct hist_entry
*he
,
1818 FILE *fp
, int level
,
1823 char folded_sign
= ' ';
1824 struct perf_hpp hpp
= {
1828 struct perf_hpp_fmt
*fmt
;
1831 int hierarchy_indent
= (nr_sort_keys
+ 1) * HIERARCHY_INDENT
;
1833 printed
= fprintf(fp
, "%*s", level
* HIERARCHY_INDENT
, "");
1835 folded_sign
= hist_entry__folded(he
);
1836 printed
+= fprintf(fp
, "%c", folded_sign
);
1838 hists__for_each_format(he
->hists
, fmt
) {
1839 if (perf_hpp__should_skip(fmt
, he
->hists
))
1842 if (perf_hpp__is_sort_entry(fmt
) ||
1843 perf_hpp__is_dynamic_entry(fmt
))
1847 ret
= scnprintf(hpp
.buf
, hpp
.size
, " ");
1848 advance_hpp(&hpp
, ret
);
1852 ret
= fmt
->entry(fmt
, &hpp
, he
);
1853 advance_hpp(&hpp
, ret
);
1856 ret
= scnprintf(hpp
.buf
, hpp
.size
, "%*s", hierarchy_indent
, "");
1857 advance_hpp(&hpp
, ret
);
1860 ret
= fmt
->entry(fmt
, &hpp
, he
);
1861 advance_hpp(&hpp
, ret
);
1863 printed
+= fprintf(fp
, "%s\n", rtrim(s
));
1865 if (he
->leaf
&& folded_sign
== '-') {
1866 printed
+= hist_browser__fprintf_callchain(browser
, he
, fp
,
1873 static int hist_browser__fprintf(struct hist_browser
*browser
, FILE *fp
)
1875 struct rb_node
*nd
= hists__filter_entries(rb_first(browser
->b
.entries
),
1878 int nr_sort
= browser
->hists
->hpp_list
->nr_sort_keys
;
1881 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1883 if (symbol_conf
.report_hierarchy
) {
1884 printed
+= hist_browser__fprintf_hierarchy_entry(browser
,
1889 printed
+= hist_browser__fprintf_entry(browser
, h
, fp
);
1892 nd
= hists__filter_entries(rb_hierarchy_next(nd
),
1899 static int hist_browser__dump(struct hist_browser
*browser
)
1905 scnprintf(filename
, sizeof(filename
), "perf.hist.%d", browser
->print_seq
);
1906 if (access(filename
, F_OK
))
1909 * XXX: Just an arbitrary lazy upper limit
1911 if (++browser
->print_seq
== 8192) {
1912 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1917 fp
= fopen(filename
, "w");
1920 const char *err
= strerror_r(errno
, bf
, sizeof(bf
));
1921 ui_helpline__fpush("Couldn't write to %s: %s", filename
, err
);
1925 ++browser
->print_seq
;
1926 hist_browser__fprintf(browser
, fp
);
1928 ui_helpline__fpush("%s written!", filename
);
1933 static struct hist_browser
*hist_browser__new(struct hists
*hists
,
1934 struct hist_browser_timer
*hbt
,
1935 struct perf_env
*env
)
1937 struct hist_browser
*browser
= zalloc(sizeof(*browser
));
1940 browser
->hists
= hists
;
1941 browser
->b
.refresh
= hist_browser__refresh
;
1942 browser
->b
.refresh_dimensions
= hist_browser__refresh_dimensions
;
1943 browser
->b
.seek
= ui_browser__hists_seek
;
1944 browser
->b
.use_navkeypressed
= true;
1945 browser
->show_headers
= symbol_conf
.show_hist_headers
;
1953 static void hist_browser__delete(struct hist_browser
*browser
)
1958 static struct hist_entry
*hist_browser__selected_entry(struct hist_browser
*browser
)
1960 return browser
->he_selection
;
1963 static struct thread
*hist_browser__selected_thread(struct hist_browser
*browser
)
1965 return browser
->he_selection
->thread
;
1968 /* Check whether the browser is for 'top' or 'report' */
1969 static inline bool is_report_browser(void *timer
)
1971 return timer
== NULL
;
1974 static int hists__browser_title(struct hists
*hists
,
1975 struct hist_browser_timer
*hbt
,
1976 char *bf
, size_t size
)
1980 const struct dso
*dso
= hists
->dso_filter
;
1981 const struct thread
*thread
= hists
->thread_filter
;
1982 int socket_id
= hists
->socket_filter
;
1983 unsigned long nr_samples
= hists
->stats
.nr_events
[PERF_RECORD_SAMPLE
];
1984 u64 nr_events
= hists
->stats
.total_period
;
1985 struct perf_evsel
*evsel
= hists_to_evsel(hists
);
1986 const char *ev_name
= perf_evsel__name(evsel
);
1988 size_t buflen
= sizeof(buf
);
1989 char ref
[30] = " show reference callgraph, ";
1990 bool enable_ref
= false;
1992 if (symbol_conf
.filter_relative
) {
1993 nr_samples
= hists
->stats
.nr_non_filtered_samples
;
1994 nr_events
= hists
->stats
.total_non_filtered_period
;
1997 if (perf_evsel__is_group_event(evsel
)) {
1998 struct perf_evsel
*pos
;
2000 perf_evsel__group_desc(evsel
, buf
, buflen
);
2003 for_each_group_member(pos
, evsel
) {
2004 struct hists
*pos_hists
= evsel__hists(pos
);
2006 if (symbol_conf
.filter_relative
) {
2007 nr_samples
+= pos_hists
->stats
.nr_non_filtered_samples
;
2008 nr_events
+= pos_hists
->stats
.total_non_filtered_period
;
2010 nr_samples
+= pos_hists
->stats
.nr_events
[PERF_RECORD_SAMPLE
];
2011 nr_events
+= pos_hists
->stats
.total_period
;
2016 if (symbol_conf
.show_ref_callgraph
&&
2017 strstr(ev_name
, "call-graph=no"))
2019 nr_samples
= convert_unit(nr_samples
, &unit
);
2020 printed
= scnprintf(bf
, size
,
2021 "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64
,
2022 nr_samples
, unit
, ev_name
, enable_ref
? ref
: " ", nr_events
);
2025 if (hists
->uid_filter_str
)
2026 printed
+= snprintf(bf
+ printed
, size
- printed
,
2027 ", UID: %s", hists
->uid_filter_str
);
2029 printed
+= scnprintf(bf
+ printed
, size
- printed
,
2031 (thread
->comm_set
? thread__comm_str(thread
) : ""),
2034 printed
+= scnprintf(bf
+ printed
, size
- printed
,
2035 ", DSO: %s", dso
->short_name
);
2037 printed
+= scnprintf(bf
+ printed
, size
- printed
,
2038 ", Processor Socket: %d", socket_id
);
2039 if (!is_report_browser(hbt
)) {
2040 struct perf_top
*top
= hbt
->arg
;
2043 printed
+= scnprintf(bf
+ printed
, size
- printed
, " [z]");
2049 static inline void free_popup_options(char **options
, int n
)
2053 for (i
= 0; i
< n
; ++i
)
2058 * Only runtime switching of perf data file will make "input_name" point
2059 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
2060 * whether we need to call free() for current "input_name" during the switch.
2062 static bool is_input_name_malloced
= false;
2064 static int switch_data_file(void)
2066 char *pwd
, *options
[32], *abs_path
[32], *tmp
;
2068 int nr_options
= 0, choice
= -1, ret
= -1;
2069 struct dirent
*dent
;
2071 pwd
= getenv("PWD");
2075 pwd_dir
= opendir(pwd
);
2079 memset(options
, 0, sizeof(options
));
2080 memset(options
, 0, sizeof(abs_path
));
2082 while ((dent
= readdir(pwd_dir
))) {
2083 char path
[PATH_MAX
];
2085 char *name
= dent
->d_name
;
2088 if (!(dent
->d_type
== DT_REG
))
2091 snprintf(path
, sizeof(path
), "%s/%s", pwd
, name
);
2093 file
= fopen(path
, "r");
2097 if (fread(&magic
, 1, 8, file
) < 8)
2098 goto close_file_and_continue
;
2100 if (is_perf_magic(magic
)) {
2101 options
[nr_options
] = strdup(name
);
2102 if (!options
[nr_options
])
2103 goto close_file_and_continue
;
2105 abs_path
[nr_options
] = strdup(path
);
2106 if (!abs_path
[nr_options
]) {
2107 zfree(&options
[nr_options
]);
2108 ui__warning("Can't search all data files due to memory shortage.\n");
2116 close_file_and_continue
:
2118 if (nr_options
>= 32) {
2119 ui__warning("Too many perf data files in PWD!\n"
2120 "Only the first 32 files will be listed.\n");
2127 choice
= ui__popup_menu(nr_options
, options
);
2128 if (choice
< nr_options
&& choice
>= 0) {
2129 tmp
= strdup(abs_path
[choice
]);
2131 if (is_input_name_malloced
)
2132 free((void *)input_name
);
2134 is_input_name_malloced
= true;
2137 ui__warning("Data switch failed due to memory shortage!\n");
2141 free_popup_options(options
, nr_options
);
2142 free_popup_options(abs_path
, nr_options
);
2146 struct popup_action
{
2147 struct thread
*thread
;
2148 struct map_symbol ms
;
2151 int (*fn
)(struct hist_browser
*browser
, struct popup_action
*act
);
2155 do_annotate(struct hist_browser
*browser
, struct popup_action
*act
)
2157 struct perf_evsel
*evsel
;
2158 struct annotation
*notes
;
2159 struct hist_entry
*he
;
2162 if (!objdump_path
&& perf_env__lookup_objdump(browser
->env
))
2165 notes
= symbol__annotation(act
->ms
.sym
);
2169 evsel
= hists_to_evsel(browser
->hists
);
2170 err
= map_symbol__tui_annotate(&act
->ms
, evsel
, browser
->hbt
);
2171 he
= hist_browser__selected_entry(browser
);
2173 * offer option to annotate the other branch source or target
2174 * (if they exists) when returning from annotate
2176 if ((err
== 'q' || err
== CTRL('c')) && he
->branch_info
)
2179 ui_browser__update_nr_entries(&browser
->b
, browser
->hists
->nr_entries
);
2181 ui_browser__handle_resize(&browser
->b
);
2186 add_annotate_opt(struct hist_browser
*browser __maybe_unused
,
2187 struct popup_action
*act
, char **optstr
,
2188 struct map
*map
, struct symbol
*sym
)
2190 if (sym
== NULL
|| map
->dso
->annotate_warned
)
2193 if (asprintf(optstr
, "Annotate %s", sym
->name
) < 0)
2198 act
->fn
= do_annotate
;
2203 do_zoom_thread(struct hist_browser
*browser
, struct popup_action
*act
)
2205 struct thread
*thread
= act
->thread
;
2207 if (browser
->hists
->thread_filter
) {
2208 pstack__remove(browser
->pstack
, &browser
->hists
->thread_filter
);
2209 perf_hpp__set_elide(HISTC_THREAD
, false);
2210 thread__zput(browser
->hists
->thread_filter
);
2213 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
2214 thread
->comm_set
? thread__comm_str(thread
) : "",
2216 browser
->hists
->thread_filter
= thread__get(thread
);
2217 perf_hpp__set_elide(HISTC_THREAD
, false);
2218 pstack__push(browser
->pstack
, &browser
->hists
->thread_filter
);
2221 hists__filter_by_thread(browser
->hists
);
2222 hist_browser__reset(browser
);
2227 add_thread_opt(struct hist_browser
*browser
, struct popup_action
*act
,
2228 char **optstr
, struct thread
*thread
)
2230 if (!sort__has_thread
|| thread
== NULL
)
2233 if (asprintf(optstr
, "Zoom %s %s(%d) thread",
2234 browser
->hists
->thread_filter
? "out of" : "into",
2235 thread
->comm_set
? thread__comm_str(thread
) : "",
2239 act
->thread
= thread
;
2240 act
->fn
= do_zoom_thread
;
2245 do_zoom_dso(struct hist_browser
*browser
, struct popup_action
*act
)
2247 struct map
*map
= act
->ms
.map
;
2249 if (browser
->hists
->dso_filter
) {
2250 pstack__remove(browser
->pstack
, &browser
->hists
->dso_filter
);
2251 perf_hpp__set_elide(HISTC_DSO
, false);
2252 browser
->hists
->dso_filter
= NULL
;
2257 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
2258 __map__is_kernel(map
) ? "the Kernel" : map
->dso
->short_name
);
2259 browser
->hists
->dso_filter
= map
->dso
;
2260 perf_hpp__set_elide(HISTC_DSO
, true);
2261 pstack__push(browser
->pstack
, &browser
->hists
->dso_filter
);
2264 hists__filter_by_dso(browser
->hists
);
2265 hist_browser__reset(browser
);
2270 add_dso_opt(struct hist_browser
*browser
, struct popup_action
*act
,
2271 char **optstr
, struct map
*map
)
2273 if (!sort__has_dso
|| map
== NULL
)
2276 if (asprintf(optstr
, "Zoom %s %s DSO",
2277 browser
->hists
->dso_filter
? "out of" : "into",
2278 __map__is_kernel(map
) ? "the Kernel" : map
->dso
->short_name
) < 0)
2282 act
->fn
= do_zoom_dso
;
2287 do_browse_map(struct hist_browser
*browser __maybe_unused
,
2288 struct popup_action
*act
)
2290 map__browse(act
->ms
.map
);
2295 add_map_opt(struct hist_browser
*browser __maybe_unused
,
2296 struct popup_action
*act
, char **optstr
, struct map
*map
)
2298 if (!sort__has_dso
|| map
== NULL
)
2301 if (asprintf(optstr
, "Browse map details") < 0)
2305 act
->fn
= do_browse_map
;
2310 do_run_script(struct hist_browser
*browser __maybe_unused
,
2311 struct popup_action
*act
)
2313 char script_opt
[64];
2314 memset(script_opt
, 0, sizeof(script_opt
));
2317 scnprintf(script_opt
, sizeof(script_opt
), " -c %s ",
2318 thread__comm_str(act
->thread
));
2319 } else if (act
->ms
.sym
) {
2320 scnprintf(script_opt
, sizeof(script_opt
), " -S %s ",
2324 script_browse(script_opt
);
2329 add_script_opt(struct hist_browser
*browser __maybe_unused
,
2330 struct popup_action
*act
, char **optstr
,
2331 struct thread
*thread
, struct symbol
*sym
)
2334 if (asprintf(optstr
, "Run scripts for samples of thread [%s]",
2335 thread__comm_str(thread
)) < 0)
2338 if (asprintf(optstr
, "Run scripts for samples of symbol [%s]",
2342 if (asprintf(optstr
, "Run scripts for all samples") < 0)
2346 act
->thread
= thread
;
2348 act
->fn
= do_run_script
;
2353 do_switch_data(struct hist_browser
*browser __maybe_unused
,
2354 struct popup_action
*act __maybe_unused
)
2356 if (switch_data_file()) {
2357 ui__warning("Won't switch the data files due to\n"
2358 "no valid data file get selected!\n");
2362 return K_SWITCH_INPUT_DATA
;
2366 add_switch_opt(struct hist_browser
*browser
,
2367 struct popup_action
*act
, char **optstr
)
2369 if (!is_report_browser(browser
->hbt
))
2372 if (asprintf(optstr
, "Switch to another data file in PWD") < 0)
2375 act
->fn
= do_switch_data
;
2380 do_exit_browser(struct hist_browser
*browser __maybe_unused
,
2381 struct popup_action
*act __maybe_unused
)
2387 add_exit_opt(struct hist_browser
*browser __maybe_unused
,
2388 struct popup_action
*act
, char **optstr
)
2390 if (asprintf(optstr
, "Exit") < 0)
2393 act
->fn
= do_exit_browser
;
2398 do_zoom_socket(struct hist_browser
*browser
, struct popup_action
*act
)
2400 if (browser
->hists
->socket_filter
> -1) {
2401 pstack__remove(browser
->pstack
, &browser
->hists
->socket_filter
);
2402 browser
->hists
->socket_filter
= -1;
2403 perf_hpp__set_elide(HISTC_SOCKET
, false);
2405 browser
->hists
->socket_filter
= act
->socket
;
2406 perf_hpp__set_elide(HISTC_SOCKET
, true);
2407 pstack__push(browser
->pstack
, &browser
->hists
->socket_filter
);
2410 hists__filter_by_socket(browser
->hists
);
2411 hist_browser__reset(browser
);
2416 add_socket_opt(struct hist_browser
*browser
, struct popup_action
*act
,
2417 char **optstr
, int socket_id
)
2419 if (!sort__has_socket
|| socket_id
< 0)
2422 if (asprintf(optstr
, "Zoom %s Processor Socket %d",
2423 (browser
->hists
->socket_filter
> -1) ? "out of" : "into",
2427 act
->socket
= socket_id
;
2428 act
->fn
= do_zoom_socket
;
2432 static void hist_browser__update_nr_entries(struct hist_browser
*hb
)
2435 struct rb_node
*nd
= rb_first(&hb
->hists
->entries
);
2437 if (hb
->min_pcnt
== 0 && !symbol_conf
.report_hierarchy
) {
2438 hb
->nr_non_filtered_entries
= hb
->hists
->nr_non_filtered_entries
;
2442 while ((nd
= hists__filter_entries(nd
, hb
->min_pcnt
)) != NULL
) {
2444 nd
= rb_hierarchy_next(nd
);
2447 hb
->nr_non_filtered_entries
= nr_entries
;
2448 hb
->nr_hierarchy_entries
= nr_entries
;
2451 static void hist_browser__update_percent_limit(struct hist_browser
*hb
,
2454 struct hist_entry
*he
;
2455 struct rb_node
*nd
= rb_first(&hb
->hists
->entries
);
2456 u64 total
= hists__total_period(hb
->hists
);
2457 u64 min_callchain_hits
= total
* (percent
/ 100);
2459 hb
->min_pcnt
= callchain_param
.min_percent
= percent
;
2461 while ((nd
= hists__filter_entries(nd
, hb
->min_pcnt
)) != NULL
) {
2462 he
= rb_entry(nd
, struct hist_entry
, rb_node
);
2464 if (!he
->leaf
|| !symbol_conf
.use_callchain
)
2467 if (callchain_param
.mode
== CHAIN_GRAPH_REL
) {
2468 total
= he
->stat
.period
;
2470 if (symbol_conf
.cumulate_callchain
)
2471 total
= he
->stat_acc
->period
;
2473 min_callchain_hits
= total
* (percent
/ 100);
2476 callchain_param
.sort(&he
->sorted_chain
, he
->callchain
,
2477 min_callchain_hits
, &callchain_param
);
2480 nd
= __rb_hierarchy_next(nd
, HMD_FORCE_CHILD
);
2482 /* force to re-evaluate folding state of callchains */
2483 he
->init_have_children
= false;
2484 hist_entry__set_folding(he
, hb
, false);
2488 static int perf_evsel__hists_browse(struct perf_evsel
*evsel
, int nr_events
,
2489 const char *helpline
,
2491 struct hist_browser_timer
*hbt
,
2493 struct perf_env
*env
)
2495 struct hists
*hists
= evsel__hists(evsel
);
2496 struct hist_browser
*browser
= hist_browser__new(hists
, hbt
, env
);
2497 struct branch_info
*bi
;
2498 #define MAX_OPTIONS 16
2499 char *options
[MAX_OPTIONS
];
2500 struct popup_action actions
[MAX_OPTIONS
];
2504 int delay_secs
= hbt
? hbt
->refresh
: 0;
2505 struct perf_hpp_fmt
*fmt
;
2507 #define HIST_BROWSER_HELP_COMMON \
2508 "h/?/F1 Show this window\n" \
2510 "PGDN/SPACE Navigate\n" \
2511 "q/ESC/CTRL+C Exit browser\n\n" \
2512 "For multiple event sessions:\n\n" \
2513 "TAB/UNTAB Switch events\n\n" \
2514 "For symbolic views (--sort has sym):\n\n" \
2515 "ENTER Zoom into DSO/Threads & Annotate current symbol\n" \
2517 "a Annotate current symbol\n" \
2518 "C Collapse all callchains\n" \
2519 "d Zoom into current DSO\n" \
2520 "E Expand all callchains\n" \
2521 "F Toggle percentage of filtered entries\n" \
2522 "H Display column headers\n" \
2523 "L Change percent limit\n" \
2524 "m Display context menu\n" \
2525 "S Zoom into current Processor Socket\n" \
2527 /* help messages are sorted by lexical order of the hotkey */
2528 const char report_help
[] = HIST_BROWSER_HELP_COMMON
2529 "i Show header information\n"
2530 "P Print histograms to perf.hist.N\n"
2531 "r Run available scripts\n"
2532 "s Switch to another data file in PWD\n"
2533 "t Zoom into current Thread\n"
2534 "V Verbose (DSO names in callchains, etc)\n"
2535 "/ Filter symbol by name";
2536 const char top_help
[] = HIST_BROWSER_HELP_COMMON
2537 "P Print histograms to perf.hist.N\n"
2538 "t Zoom into current Thread\n"
2539 "V Verbose (DSO names in callchains, etc)\n"
2540 "z Toggle zeroing of samples\n"
2541 "f Enable/Disable events\n"
2542 "/ Filter symbol by name";
2544 if (browser
== NULL
)
2547 /* reset abort key so that it can get Ctrl-C as a key */
2549 SLang_init_tty(0, 0, 0);
2552 browser
->min_pcnt
= min_pcnt
;
2553 hist_browser__update_nr_entries(browser
);
2555 browser
->pstack
= pstack__new(3);
2556 if (browser
->pstack
== NULL
)
2559 ui_helpline__push(helpline
);
2561 memset(options
, 0, sizeof(options
));
2562 memset(actions
, 0, sizeof(actions
));
2564 hists__for_each_format(browser
->hists
, fmt
) {
2565 perf_hpp__reset_width(fmt
, hists
);
2567 * This is done just once, and activates the horizontal scrolling
2568 * code in the ui_browser code, it would be better to have a the
2569 * counter in the perf_hpp code, but I couldn't find doing it here
2570 * works, FIXME by setting this in hist_browser__new, for now, be
2573 ++browser
->b
.columns
;
2576 if (symbol_conf
.col_width_list_str
)
2577 perf_hpp__set_user_width(symbol_conf
.col_width_list_str
);
2580 struct thread
*thread
= NULL
;
2581 struct map
*map
= NULL
;
2587 key
= hist_browser__run(browser
, helpline
);
2589 if (browser
->he_selection
!= NULL
) {
2590 thread
= hist_browser__selected_thread(browser
);
2591 map
= browser
->selection
->map
;
2592 socked_id
= browser
->he_selection
->socket
;
2600 * Exit the browser, let hists__browser_tree
2601 * go to the next or previous
2603 goto out_free_stack
;
2605 if (!sort__has_sym
) {
2606 ui_browser__warning(&browser
->b
, delay_secs
* 2,
2607 "Annotation is only available for symbolic views, "
2608 "include \"sym*\" in --sort to use it.");
2612 if (browser
->selection
== NULL
||
2613 browser
->selection
->sym
== NULL
||
2614 browser
->selection
->map
->dso
->annotate_warned
)
2617 actions
->ms
.map
= browser
->selection
->map
;
2618 actions
->ms
.sym
= browser
->selection
->sym
;
2619 do_annotate(browser
, actions
);
2622 hist_browser__dump(browser
);
2625 actions
->ms
.map
= map
;
2626 do_zoom_dso(browser
, actions
);
2629 browser
->show_dso
= !browser
->show_dso
;
2632 actions
->thread
= thread
;
2633 do_zoom_thread(browser
, actions
);
2636 actions
->socket
= socked_id
;
2637 do_zoom_socket(browser
, actions
);
2640 if (ui_browser__input_window("Symbol to show",
2641 "Please enter the name of symbol you want to see.\n"
2642 "To remove the filter later, press / + ENTER.",
2643 buf
, "ENTER: OK, ESC: Cancel",
2644 delay_secs
* 2) == K_ENTER
) {
2645 hists
->symbol_filter_str
= *buf
? buf
: NULL
;
2646 hists__filter_by_symbol(hists
);
2647 hist_browser__reset(browser
);
2651 if (is_report_browser(hbt
)) {
2652 actions
->thread
= NULL
;
2653 actions
->ms
.sym
= NULL
;
2654 do_run_script(browser
, actions
);
2658 if (is_report_browser(hbt
)) {
2659 key
= do_switch_data(browser
, actions
);
2660 if (key
== K_SWITCH_INPUT_DATA
)
2661 goto out_free_stack
;
2665 /* env->arch is NULL for live-mode (i.e. perf top) */
2667 tui__header_window(env
);
2670 symbol_conf
.filter_relative
^= 1;
2673 if (!is_report_browser(hbt
)) {
2674 struct perf_top
*top
= hbt
->arg
;
2676 top
->zero
= !top
->zero
;
2680 if (ui_browser__input_window("Percent Limit",
2681 "Please enter the value you want to hide entries under that percent.",
2682 buf
, "ENTER: OK, ESC: Cancel",
2683 delay_secs
* 2) == K_ENTER
) {
2685 double new_percent
= strtod(buf
, &end
);
2687 if (new_percent
< 0 || new_percent
> 100) {
2688 ui_browser__warning(&browser
->b
, delay_secs
* 2,
2689 "Invalid percent: %.2f", new_percent
);
2693 hist_browser__update_percent_limit(browser
, new_percent
);
2694 hist_browser__reset(browser
);
2700 ui_browser__help_window(&browser
->b
,
2701 is_report_browser(hbt
) ? report_help
: top_help
);
2712 if (pstack__empty(browser
->pstack
)) {
2714 * Go back to the perf_evsel_menu__run or other user
2717 goto out_free_stack
;
2720 ui_browser__dialog_yesno(&browser
->b
,
2721 "Do you really want to exit?"))
2722 goto out_free_stack
;
2726 top
= pstack__peek(browser
->pstack
);
2727 if (top
== &browser
->hists
->dso_filter
) {
2729 * No need to set actions->dso here since
2730 * it's just to remove the current filter.
2731 * Ditto for thread below.
2733 do_zoom_dso(browser
, actions
);
2734 } else if (top
== &browser
->hists
->thread_filter
) {
2735 do_zoom_thread(browser
, actions
);
2736 } else if (top
== &browser
->hists
->socket_filter
) {
2737 do_zoom_socket(browser
, actions
);
2743 goto out_free_stack
;
2745 if (!is_report_browser(hbt
)) {
2746 struct perf_top
*top
= hbt
->arg
;
2748 perf_evlist__toggle_enable(top
->evlist
);
2750 * No need to refresh, resort/decay histogram
2751 * entries if we are not collecting samples:
2753 if (top
->evlist
->enabled
) {
2754 helpline
= "Press 'f' to disable the events or 'h' to see other hotkeys";
2755 hbt
->refresh
= delay_secs
;
2757 helpline
= "Press 'f' again to re-enable the events";
2764 helpline
= "Press '?' for help on key bindings";
2768 if (!sort__has_sym
|| browser
->selection
== NULL
)
2769 goto skip_annotation
;
2771 if (sort__mode
== SORT_MODE__BRANCH
) {
2772 bi
= browser
->he_selection
->branch_info
;
2775 goto skip_annotation
;
2777 nr_options
+= add_annotate_opt(browser
,
2778 &actions
[nr_options
],
2779 &options
[nr_options
],
2782 if (bi
->to
.sym
!= bi
->from
.sym
)
2783 nr_options
+= add_annotate_opt(browser
,
2784 &actions
[nr_options
],
2785 &options
[nr_options
],
2789 nr_options
+= add_annotate_opt(browser
,
2790 &actions
[nr_options
],
2791 &options
[nr_options
],
2792 browser
->selection
->map
,
2793 browser
->selection
->sym
);
2796 nr_options
+= add_thread_opt(browser
, &actions
[nr_options
],
2797 &options
[nr_options
], thread
);
2798 nr_options
+= add_dso_opt(browser
, &actions
[nr_options
],
2799 &options
[nr_options
], map
);
2800 nr_options
+= add_map_opt(browser
, &actions
[nr_options
],
2801 &options
[nr_options
],
2802 browser
->selection
?
2803 browser
->selection
->map
: NULL
);
2804 nr_options
+= add_socket_opt(browser
, &actions
[nr_options
],
2805 &options
[nr_options
],
2807 /* perf script support */
2808 if (!is_report_browser(hbt
))
2809 goto skip_scripting
;
2811 if (browser
->he_selection
) {
2812 if (sort__has_thread
&& thread
) {
2813 nr_options
+= add_script_opt(browser
,
2814 &actions
[nr_options
],
2815 &options
[nr_options
],
2819 * Note that browser->selection != NULL
2820 * when browser->he_selection is not NULL,
2821 * so we don't need to check browser->selection
2822 * before fetching browser->selection->sym like what
2823 * we do before fetching browser->selection->map.
2825 * See hist_browser__show_entry.
2827 if (sort__has_sym
&& browser
->selection
->sym
) {
2828 nr_options
+= add_script_opt(browser
,
2829 &actions
[nr_options
],
2830 &options
[nr_options
],
2831 NULL
, browser
->selection
->sym
);
2834 nr_options
+= add_script_opt(browser
, &actions
[nr_options
],
2835 &options
[nr_options
], NULL
, NULL
);
2836 nr_options
+= add_switch_opt(browser
, &actions
[nr_options
],
2837 &options
[nr_options
]);
2839 nr_options
+= add_exit_opt(browser
, &actions
[nr_options
],
2840 &options
[nr_options
]);
2843 struct popup_action
*act
;
2845 choice
= ui__popup_menu(nr_options
, options
);
2846 if (choice
== -1 || choice
>= nr_options
)
2849 act
= &actions
[choice
];
2850 key
= act
->fn(browser
, act
);
2853 if (key
== K_SWITCH_INPUT_DATA
)
2857 pstack__delete(browser
->pstack
);
2859 hist_browser__delete(browser
);
2860 free_popup_options(options
, MAX_OPTIONS
);
2864 struct perf_evsel_menu
{
2865 struct ui_browser b
;
2866 struct perf_evsel
*selection
;
2867 bool lost_events
, lost_events_warned
;
2869 struct perf_env
*env
;
2872 static void perf_evsel_menu__write(struct ui_browser
*browser
,
2873 void *entry
, int row
)
2875 struct perf_evsel_menu
*menu
= container_of(browser
,
2876 struct perf_evsel_menu
, b
);
2877 struct perf_evsel
*evsel
= list_entry(entry
, struct perf_evsel
, node
);
2878 struct hists
*hists
= evsel__hists(evsel
);
2879 bool current_entry
= ui_browser__is_current_entry(browser
, row
);
2880 unsigned long nr_events
= hists
->stats
.nr_events
[PERF_RECORD_SAMPLE
];
2881 const char *ev_name
= perf_evsel__name(evsel
);
2883 const char *warn
= " ";
2886 ui_browser__set_color(browser
, current_entry
? HE_COLORSET_SELECTED
:
2887 HE_COLORSET_NORMAL
);
2889 if (perf_evsel__is_group_event(evsel
)) {
2890 struct perf_evsel
*pos
;
2892 ev_name
= perf_evsel__group_name(evsel
);
2894 for_each_group_member(pos
, evsel
) {
2895 struct hists
*pos_hists
= evsel__hists(pos
);
2896 nr_events
+= pos_hists
->stats
.nr_events
[PERF_RECORD_SAMPLE
];
2900 nr_events
= convert_unit(nr_events
, &unit
);
2901 printed
= scnprintf(bf
, sizeof(bf
), "%lu%c%s%s", nr_events
,
2902 unit
, unit
== ' ' ? "" : " ", ev_name
);
2903 ui_browser__printf(browser
, "%s", bf
);
2905 nr_events
= hists
->stats
.nr_events
[PERF_RECORD_LOST
];
2906 if (nr_events
!= 0) {
2907 menu
->lost_events
= true;
2909 ui_browser__set_color(browser
, HE_COLORSET_TOP
);
2910 nr_events
= convert_unit(nr_events
, &unit
);
2911 printed
+= scnprintf(bf
, sizeof(bf
), ": %ld%c%schunks LOST!",
2912 nr_events
, unit
, unit
== ' ' ? "" : " ");
2916 ui_browser__write_nstring(browser
, warn
, browser
->width
- printed
);
2919 menu
->selection
= evsel
;
2922 static int perf_evsel_menu__run(struct perf_evsel_menu
*menu
,
2923 int nr_events
, const char *help
,
2924 struct hist_browser_timer
*hbt
)
2926 struct perf_evlist
*evlist
= menu
->b
.priv
;
2927 struct perf_evsel
*pos
;
2928 const char *title
= "Available samples";
2929 int delay_secs
= hbt
? hbt
->refresh
: 0;
2932 if (ui_browser__show(&menu
->b
, title
,
2933 "ESC: exit, ENTER|->: Browse histograms") < 0)
2937 key
= ui_browser__run(&menu
->b
, delay_secs
);
2941 hbt
->timer(hbt
->arg
);
2943 if (!menu
->lost_events_warned
&& menu
->lost_events
) {
2944 ui_browser__warn_lost_events(&menu
->b
);
2945 menu
->lost_events_warned
= true;
2950 if (!menu
->selection
)
2952 pos
= menu
->selection
;
2954 perf_evlist__set_selected(evlist
, pos
);
2956 * Give the calling tool a chance to populate the non
2957 * default evsel resorted hists tree.
2960 hbt
->timer(hbt
->arg
);
2961 key
= perf_evsel__hists_browse(pos
, nr_events
, help
,
2965 ui_browser__show_title(&menu
->b
, title
);
2968 if (pos
->node
.next
== &evlist
->entries
)
2969 pos
= perf_evlist__first(evlist
);
2971 pos
= perf_evsel__next(pos
);
2974 if (pos
->node
.prev
== &evlist
->entries
)
2975 pos
= perf_evlist__last(evlist
);
2977 pos
= perf_evsel__prev(pos
);
2979 case K_SWITCH_INPUT_DATA
:
2990 if (!ui_browser__dialog_yesno(&menu
->b
,
2991 "Do you really want to exit?"))
3003 ui_browser__hide(&menu
->b
);
3007 static bool filter_group_entries(struct ui_browser
*browser __maybe_unused
,
3010 struct perf_evsel
*evsel
= list_entry(entry
, struct perf_evsel
, node
);
3012 if (symbol_conf
.event_group
&& !perf_evsel__is_group_leader(evsel
))
3018 static int __perf_evlist__tui_browse_hists(struct perf_evlist
*evlist
,
3019 int nr_entries
, const char *help
,
3020 struct hist_browser_timer
*hbt
,
3022 struct perf_env
*env
)
3024 struct perf_evsel
*pos
;
3025 struct perf_evsel_menu menu
= {
3027 .entries
= &evlist
->entries
,
3028 .refresh
= ui_browser__list_head_refresh
,
3029 .seek
= ui_browser__list_head_seek
,
3030 .write
= perf_evsel_menu__write
,
3031 .filter
= filter_group_entries
,
3032 .nr_entries
= nr_entries
,
3035 .min_pcnt
= min_pcnt
,
3039 ui_helpline__push("Press ESC to exit");
3041 evlist__for_each(evlist
, pos
) {
3042 const char *ev_name
= perf_evsel__name(pos
);
3043 size_t line_len
= strlen(ev_name
) + 7;
3045 if (menu
.b
.width
< line_len
)
3046 menu
.b
.width
= line_len
;
3049 return perf_evsel_menu__run(&menu
, nr_entries
, help
, hbt
);
3052 int perf_evlist__tui_browse_hists(struct perf_evlist
*evlist
, const char *help
,
3053 struct hist_browser_timer
*hbt
,
3055 struct perf_env
*env
)
3057 int nr_entries
= evlist
->nr_entries
;
3060 if (nr_entries
== 1) {
3061 struct perf_evsel
*first
= perf_evlist__first(evlist
);
3063 return perf_evsel__hists_browse(first
, nr_entries
, help
,
3064 false, hbt
, min_pcnt
,
3068 if (symbol_conf
.event_group
) {
3069 struct perf_evsel
*pos
;
3072 evlist__for_each(evlist
, pos
) {
3073 if (perf_evsel__is_group_leader(pos
))
3077 if (nr_entries
== 1)
3081 return __perf_evlist__tui_browse_hists(evlist
, nr_entries
, help
,
3082 hbt
, min_pcnt
, env
);