4 #include <linux/rbtree.h>
6 #include "../../util/evsel.h"
7 #include "../../util/evlist.h"
8 #include "../../util/hist.h"
9 #include "../../util/pstack.h"
10 #include "../../util/sort.h"
11 #include "../../util/util.h"
12 #include "../../util/top.h"
13 #include "../../arch/common.h"
15 #include "../browsers/hists.h"
16 #include "../helpline.h"
22 extern void hist_browser__init_hpp(void);
24 static int perf_evsel_browser_title(struct hist_browser
*browser
,
25 char *bf
, size_t size
);
26 static void hist_browser__update_nr_entries(struct hist_browser
*hb
);
28 static struct rb_node
*hists__filter_entries(struct rb_node
*nd
,
31 static bool hist_browser__has_filter(struct hist_browser
*hb
)
33 return hists__has_filter(hb
->hists
) || hb
->min_pcnt
|| symbol_conf
.has_filter
|| hb
->c2c_filter
;
36 static int hist_browser__get_folding(struct hist_browser
*browser
)
39 struct hists
*hists
= browser
->hists
;
40 int unfolded_rows
= 0;
42 for (nd
= rb_first(&hists
->entries
);
43 (nd
= hists__filter_entries(nd
, browser
->min_pcnt
)) != NULL
;
44 nd
= rb_hierarchy_next(nd
)) {
45 struct hist_entry
*he
=
46 rb_entry(nd
, struct hist_entry
, rb_node
);
48 if (he
->leaf
&& he
->unfolded
)
49 unfolded_rows
+= he
->nr_rows
;
54 static u32
hist_browser__nr_entries(struct hist_browser
*hb
)
58 if (symbol_conf
.report_hierarchy
)
59 nr_entries
= hb
->nr_hierarchy_entries
;
60 else if (hist_browser__has_filter(hb
))
61 nr_entries
= hb
->nr_non_filtered_entries
;
63 nr_entries
= hb
->hists
->nr_entries
;
65 hb
->nr_callchain_rows
= hist_browser__get_folding(hb
);
66 return nr_entries
+ hb
->nr_callchain_rows
;
69 static void hist_browser__update_rows(struct hist_browser
*hb
)
71 struct ui_browser
*browser
= &hb
->b
;
72 struct hists
*hists
= hb
->hists
;
73 struct perf_hpp_list
*hpp_list
= hists
->hpp_list
;
74 u16 header_offset
, index_row
;
76 header_offset
= hb
->show_headers
? hpp_list
->nr_header_lines
: 0;
77 browser
->rows
= browser
->height
- header_offset
;
79 * Verify if we were at the last line and that line isn't
80 * visibe because we now show the header line(s).
82 index_row
= browser
->index
- browser
->top_idx
;
83 if (index_row
>= browser
->rows
)
84 browser
->index
-= index_row
- browser
->rows
+ 1;
87 static void hist_browser__refresh_dimensions(struct ui_browser
*browser
)
89 struct hist_browser
*hb
= container_of(browser
, struct hist_browser
, b
);
91 /* 3 == +/- toggle symbol before actual hist_entry rendering */
92 browser
->width
= 3 + (hists__sort_list_width(hb
->hists
) + sizeof("[k]"));
94 * FIXME: Just keeping existing behaviour, but this really should be
95 * before updating browser->width, as it will invalidate the
96 * calculation above. Fix this and the fallout in another
99 ui_browser__refresh_dimensions(browser
);
100 hist_browser__update_rows(hb
);
103 static void hist_browser__gotorc(struct hist_browser
*browser
, int row
, int column
)
105 struct hists
*hists
= browser
->hists
;
106 struct perf_hpp_list
*hpp_list
= hists
->hpp_list
;
109 header_offset
= browser
->show_headers
? hpp_list
->nr_header_lines
: 0;
110 ui_browser__gotorc(&browser
->b
, row
+ header_offset
, column
);
113 static void hist_browser__reset(struct hist_browser
*browser
)
116 * The hists__remove_entry_filter() already folds non-filtered
117 * entries so we can assume it has 0 callchain rows.
119 browser
->nr_callchain_rows
= 0;
121 hist_browser__update_nr_entries(browser
);
122 browser
->b
.nr_entries
= hist_browser__nr_entries(browser
);
123 hist_browser__refresh_dimensions(&browser
->b
);
124 ui_browser__reset_index(&browser
->b
);
127 static char tree__folded_sign(bool unfolded
)
129 return unfolded
? '-' : '+';
132 static char hist_entry__folded(const struct hist_entry
*he
)
134 return he
->has_children
? tree__folded_sign(he
->unfolded
) : ' ';
137 static char callchain_list__folded(const struct callchain_list
*cl
)
139 return cl
->has_children
? tree__folded_sign(cl
->unfolded
) : ' ';
142 static void callchain_list__set_folding(struct callchain_list
*cl
, bool unfold
)
144 cl
->unfolded
= unfold
? cl
->has_children
: false;
147 static int callchain_node__count_rows_rb_tree(struct callchain_node
*node
)
152 for (nd
= rb_first(&node
->rb_root
); nd
; nd
= rb_next(nd
)) {
153 struct callchain_node
*child
= rb_entry(nd
, struct callchain_node
, rb_node
);
154 struct callchain_list
*chain
;
155 char folded_sign
= ' '; /* No children */
157 list_for_each_entry(chain
, &child
->val
, list
) {
159 /* We need this because we may not have children */
160 folded_sign
= callchain_list__folded(chain
);
161 if (folded_sign
== '+')
165 if (folded_sign
== '-') /* Have children and they're unfolded */
166 n
+= callchain_node__count_rows_rb_tree(child
);
172 static int callchain_node__count_flat_rows(struct callchain_node
*node
)
174 struct callchain_list
*chain
;
175 char folded_sign
= 0;
178 list_for_each_entry(chain
, &node
->parent_val
, list
) {
180 /* only check first chain list entry */
181 folded_sign
= callchain_list__folded(chain
);
182 if (folded_sign
== '+')
188 list_for_each_entry(chain
, &node
->val
, list
) {
190 /* node->parent_val list might be empty */
191 folded_sign
= callchain_list__folded(chain
);
192 if (folded_sign
== '+')
201 static int callchain_node__count_folded_rows(struct callchain_node
*node __maybe_unused
)
206 static int callchain_node__count_rows(struct callchain_node
*node
)
208 struct callchain_list
*chain
;
209 bool unfolded
= false;
212 if (callchain_param
.mode
== CHAIN_FLAT
)
213 return callchain_node__count_flat_rows(node
);
214 else if (callchain_param
.mode
== CHAIN_FOLDED
)
215 return callchain_node__count_folded_rows(node
);
217 list_for_each_entry(chain
, &node
->val
, list
) {
219 unfolded
= chain
->unfolded
;
223 n
+= callchain_node__count_rows_rb_tree(node
);
228 static int callchain__count_rows(struct rb_root
*chain
)
233 for (nd
= rb_first(chain
); nd
; nd
= rb_next(nd
)) {
234 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
235 n
+= callchain_node__count_rows(node
);
241 static int hierarchy_count_rows(struct hist_browser
*hb
, struct hist_entry
*he
,
242 bool include_children
)
245 struct rb_node
*node
;
246 struct hist_entry
*child
;
249 return callchain__count_rows(&he
->sorted_chain
);
251 if (he
->has_no_entry
)
254 node
= rb_first(&he
->hroot_out
);
258 child
= rb_entry(node
, struct hist_entry
, rb_node
);
259 percent
= hist_entry__get_percent_limit(child
);
261 if (!child
->filtered
&& percent
>= hb
->min_pcnt
) {
264 if (include_children
&& child
->unfolded
)
265 count
+= hierarchy_count_rows(hb
, child
, true);
268 node
= rb_next(node
);
273 static bool hist_entry__toggle_fold(struct hist_entry
*he
)
278 if (!he
->has_children
)
281 he
->unfolded
= !he
->unfolded
;
285 static bool callchain_list__toggle_fold(struct callchain_list
*cl
)
290 if (!cl
->has_children
)
293 cl
->unfolded
= !cl
->unfolded
;
297 static void callchain_node__init_have_children_rb_tree(struct callchain_node
*node
)
299 struct rb_node
*nd
= rb_first(&node
->rb_root
);
301 for (nd
= rb_first(&node
->rb_root
); nd
; nd
= rb_next(nd
)) {
302 struct callchain_node
*child
= rb_entry(nd
, struct callchain_node
, rb_node
);
303 struct callchain_list
*chain
;
306 list_for_each_entry(chain
, &child
->val
, list
) {
309 chain
->has_children
= chain
->list
.next
!= &child
->val
||
310 !RB_EMPTY_ROOT(&child
->rb_root
);
312 chain
->has_children
= chain
->list
.next
== &child
->val
&&
313 !RB_EMPTY_ROOT(&child
->rb_root
);
316 callchain_node__init_have_children_rb_tree(child
);
320 static void callchain_node__init_have_children(struct callchain_node
*node
,
323 struct callchain_list
*chain
;
325 chain
= list_entry(node
->val
.next
, struct callchain_list
, list
);
326 chain
->has_children
= has_sibling
;
328 if (!list_empty(&node
->val
)) {
329 chain
= list_entry(node
->val
.prev
, struct callchain_list
, list
);
330 chain
->has_children
= !RB_EMPTY_ROOT(&node
->rb_root
);
333 callchain_node__init_have_children_rb_tree(node
);
336 static void callchain__init_have_children(struct rb_root
*root
)
338 struct rb_node
*nd
= rb_first(root
);
339 bool has_sibling
= nd
&& rb_next(nd
);
341 for (nd
= rb_first(root
); nd
; nd
= rb_next(nd
)) {
342 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
343 callchain_node__init_have_children(node
, has_sibling
);
344 if (callchain_param
.mode
== CHAIN_FLAT
||
345 callchain_param
.mode
== CHAIN_FOLDED
)
346 callchain_node__make_parent_list(node
);
350 static void hist_entry__init_have_children(struct hist_entry
*he
)
352 if (he
->init_have_children
)
356 he
->has_children
= !RB_EMPTY_ROOT(&he
->sorted_chain
);
357 callchain__init_have_children(&he
->sorted_chain
);
359 he
->has_children
= !RB_EMPTY_ROOT(&he
->hroot_out
);
362 he
->init_have_children
= true;
365 static bool hist_browser__toggle_fold(struct hist_browser
*browser
)
367 struct hist_entry
*he
= browser
->he_selection
;
368 struct map_symbol
*ms
= browser
->selection
;
369 struct callchain_list
*cl
= container_of(ms
, struct callchain_list
, ms
);
376 has_children
= hist_entry__toggle_fold(he
);
378 has_children
= callchain_list__toggle_fold(cl
);
383 hist_entry__init_have_children(he
);
384 browser
->b
.nr_entries
-= he
->nr_rows
;
387 browser
->nr_callchain_rows
-= he
->nr_rows
;
389 browser
->nr_hierarchy_entries
-= he
->nr_rows
;
391 if (symbol_conf
.report_hierarchy
)
392 child_rows
= hierarchy_count_rows(browser
, he
, true);
396 he
->nr_rows
= callchain__count_rows(&he
->sorted_chain
);
398 he
->nr_rows
= hierarchy_count_rows(browser
, he
, false);
400 /* account grand children */
401 if (symbol_conf
.report_hierarchy
)
402 browser
->b
.nr_entries
+= child_rows
- he
->nr_rows
;
404 if (!he
->leaf
&& he
->nr_rows
== 0) {
405 he
->has_no_entry
= true;
409 if (symbol_conf
.report_hierarchy
)
410 browser
->b
.nr_entries
-= child_rows
- he
->nr_rows
;
412 if (he
->has_no_entry
)
413 he
->has_no_entry
= false;
418 browser
->b
.nr_entries
+= he
->nr_rows
;
421 browser
->nr_callchain_rows
+= he
->nr_rows
;
423 browser
->nr_hierarchy_entries
+= he
->nr_rows
;
428 /* If it doesn't have children, no toggling performed */
432 static int callchain_node__set_folding_rb_tree(struct callchain_node
*node
, bool unfold
)
437 for (nd
= rb_first(&node
->rb_root
); nd
; nd
= rb_next(nd
)) {
438 struct callchain_node
*child
= rb_entry(nd
, struct callchain_node
, rb_node
);
439 struct callchain_list
*chain
;
440 bool has_children
= false;
442 list_for_each_entry(chain
, &child
->val
, list
) {
444 callchain_list__set_folding(chain
, unfold
);
445 has_children
= chain
->has_children
;
449 n
+= callchain_node__set_folding_rb_tree(child
, unfold
);
455 static int callchain_node__set_folding(struct callchain_node
*node
, bool unfold
)
457 struct callchain_list
*chain
;
458 bool has_children
= false;
461 list_for_each_entry(chain
, &node
->val
, list
) {
463 callchain_list__set_folding(chain
, unfold
);
464 has_children
= chain
->has_children
;
468 n
+= callchain_node__set_folding_rb_tree(node
, unfold
);
473 static int callchain__set_folding(struct rb_root
*chain
, bool unfold
)
478 for (nd
= rb_first(chain
); nd
; nd
= rb_next(nd
)) {
479 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
480 n
+= callchain_node__set_folding(node
, unfold
);
486 static int hierarchy_set_folding(struct hist_browser
*hb
, struct hist_entry
*he
,
487 bool unfold __maybe_unused
)
491 struct hist_entry
*child
;
494 for (nd
= rb_first(&he
->hroot_out
); nd
; nd
= rb_next(nd
)) {
495 child
= rb_entry(nd
, struct hist_entry
, rb_node
);
496 percent
= hist_entry__get_percent_limit(child
);
497 if (!child
->filtered
&& percent
>= hb
->min_pcnt
)
504 static void __hist_entry__set_folding(struct hist_entry
*he
,
505 struct hist_browser
*hb
, bool unfold
)
507 hist_entry__init_have_children(he
);
508 he
->unfolded
= unfold
? he
->has_children
: false;
510 if (he
->has_children
) {
514 n
= callchain__set_folding(&he
->sorted_chain
, unfold
);
516 n
= hierarchy_set_folding(hb
, he
, unfold
);
518 he
->nr_rows
= unfold
? n
: 0;
523 static void hist_entry__set_folding(struct hist_entry
*he
,
524 struct hist_browser
*browser
, bool unfold
)
528 percent
= hist_entry__get_percent_limit(he
);
529 if (he
->filtered
|| percent
< browser
->min_pcnt
)
532 __hist_entry__set_folding(he
, browser
, unfold
);
534 if (!he
->depth
|| unfold
)
535 browser
->nr_hierarchy_entries
++;
537 browser
->nr_callchain_rows
+= he
->nr_rows
;
538 else if (unfold
&& !hist_entry__has_hierarchy_children(he
, browser
->min_pcnt
)) {
539 browser
->nr_hierarchy_entries
++;
540 he
->has_no_entry
= true;
543 he
->has_no_entry
= false;
547 __hist_browser__set_folding(struct hist_browser
*browser
, bool unfold
)
550 struct hist_entry
*he
;
552 nd
= rb_first(&browser
->hists
->entries
);
554 he
= rb_entry(nd
, struct hist_entry
, rb_node
);
556 /* set folding state even if it's currently folded */
557 nd
= __rb_hierarchy_next(nd
, HMD_FORCE_CHILD
);
559 hist_entry__set_folding(he
, browser
, unfold
);
563 static void hist_browser__set_folding(struct hist_browser
*browser
, bool unfold
)
565 browser
->nr_hierarchy_entries
= 0;
566 browser
->nr_callchain_rows
= 0;
567 __hist_browser__set_folding(browser
, unfold
);
569 browser
->b
.nr_entries
= hist_browser__nr_entries(browser
);
570 /* Go to the start, we may be way after valid entries after a collapse */
571 ui_browser__reset_index(&browser
->b
);
574 static void hist_browser__set_folding_selected(struct hist_browser
*browser
, bool unfold
)
576 if (!browser
->he_selection
)
579 hist_entry__set_folding(browser
->he_selection
, browser
, unfold
);
580 browser
->b
.nr_entries
= hist_browser__nr_entries(browser
);
583 static void ui_browser__warn_lost_events(struct ui_browser
*browser
)
585 ui_browser__warning(browser
, 4,
586 "Events are being lost, check IO/CPU overload!\n\n"
587 "You may want to run 'perf' using a RT scheduler policy:\n\n"
588 " perf top -r 80\n\n"
589 "Or reduce the sampling frequency.");
592 static int hist_browser__title(struct hist_browser
*browser
, char *bf
, size_t size
)
594 return browser
->title
? browser
->title(browser
, bf
, size
) : 0;
597 int hist_browser__run(struct hist_browser
*browser
, const char *help
)
601 struct hist_browser_timer
*hbt
= browser
->hbt
;
602 int delay_secs
= hbt
? hbt
->refresh
: 0;
604 browser
->b
.entries
= &browser
->hists
->entries
;
605 browser
->b
.nr_entries
= hist_browser__nr_entries(browser
);
607 hist_browser__title(browser
, title
, sizeof(title
));
609 if (ui_browser__show(&browser
->b
, title
, "%s", help
) < 0)
613 key
= ui_browser__run(&browser
->b
, delay_secs
);
618 hbt
->timer(hbt
->arg
);
620 if (hist_browser__has_filter(browser
) ||
621 symbol_conf
.report_hierarchy
)
622 hist_browser__update_nr_entries(browser
);
624 nr_entries
= hist_browser__nr_entries(browser
);
625 ui_browser__update_nr_entries(&browser
->b
, nr_entries
);
627 if (browser
->hists
->stats
.nr_lost_warned
!=
628 browser
->hists
->stats
.nr_events
[PERF_RECORD_LOST
]) {
629 browser
->hists
->stats
.nr_lost_warned
=
630 browser
->hists
->stats
.nr_events
[PERF_RECORD_LOST
];
631 ui_browser__warn_lost_events(&browser
->b
);
634 hist_browser__title(browser
, title
, sizeof(title
));
635 ui_browser__show_title(&browser
->b
, title
);
638 case 'D': { /* Debug */
640 struct hist_entry
*h
= rb_entry(browser
->b
.top
,
641 struct hist_entry
, rb_node
);
643 ui_helpline__fpush("%d: nr_ent=(%d,%d), rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
644 seq
++, browser
->b
.nr_entries
,
645 browser
->hists
->nr_entries
,
649 h
->row_offset
, h
->nr_rows
);
653 /* Collapse the whole world. */
654 hist_browser__set_folding(browser
, false);
657 /* Collapse the selected entry. */
658 hist_browser__set_folding_selected(browser
, false);
661 /* Expand the whole world. */
662 hist_browser__set_folding(browser
, true);
665 /* Expand the selected entry. */
666 hist_browser__set_folding_selected(browser
, true);
669 browser
->show_headers
= !browser
->show_headers
;
670 hist_browser__update_rows(browser
);
673 if (hist_browser__toggle_fold(browser
))
681 ui_browser__hide(&browser
->b
);
685 struct callchain_print_arg
{
686 /* for hists browser */
688 bool is_current_entry
;
695 typedef void (*print_callchain_entry_fn
)(struct hist_browser
*browser
,
696 struct callchain_list
*chain
,
697 const char *str
, int offset
,
699 struct callchain_print_arg
*arg
);
701 static void hist_browser__show_callchain_entry(struct hist_browser
*browser
,
702 struct callchain_list
*chain
,
703 const char *str
, int offset
,
705 struct callchain_print_arg
*arg
)
708 char folded_sign
= callchain_list__folded(chain
);
709 bool show_annotated
= browser
->show_dso
&& chain
->ms
.sym
&& symbol__annotation(chain
->ms
.sym
)->src
;
711 color
= HE_COLORSET_NORMAL
;
712 width
= browser
->b
.width
- (offset
+ 2);
713 if (ui_browser__is_current_entry(&browser
->b
, row
)) {
714 browser
->selection
= &chain
->ms
;
715 color
= HE_COLORSET_SELECTED
;
716 arg
->is_current_entry
= true;
719 ui_browser__set_color(&browser
->b
, color
);
720 hist_browser__gotorc(browser
, row
, 0);
721 ui_browser__write_nstring(&browser
->b
, " ", offset
);
722 ui_browser__printf(&browser
->b
, "%c", folded_sign
);
723 ui_browser__write_graph(&browser
->b
, show_annotated
? SLSMG_RARROW_CHAR
: ' ');
724 ui_browser__write_nstring(&browser
->b
, str
, width
);
727 static void hist_browser__fprintf_callchain_entry(struct hist_browser
*b __maybe_unused
,
728 struct callchain_list
*chain
,
729 const char *str
, int offset
,
730 unsigned short row __maybe_unused
,
731 struct callchain_print_arg
*arg
)
733 char folded_sign
= callchain_list__folded(chain
);
735 arg
->printed
+= fprintf(arg
->fp
, "%*s%c %s\n", offset
, " ",
739 typedef bool (*check_output_full_fn
)(struct hist_browser
*browser
,
742 static bool hist_browser__check_output_full(struct hist_browser
*browser
,
745 return browser
->b
.rows
== row
;
748 static bool hist_browser__check_dump_full(struct hist_browser
*browser __maybe_unused
,
749 unsigned short row __maybe_unused
)
754 #define LEVEL_OFFSET_STEP 3
756 static int hist_browser__show_callchain_list(struct hist_browser
*browser
,
757 struct callchain_node
*node
,
758 struct callchain_list
*chain
,
759 unsigned short row
, u64 total
,
760 bool need_percent
, int offset
,
761 print_callchain_entry_fn print
,
762 struct callchain_print_arg
*arg
)
764 char bf
[1024], *alloc_str
;
765 char buf
[64], *alloc_str2
;
768 if (arg
->row_offset
!= 0) {
776 str
= callchain_list__sym_name(chain
, bf
, sizeof(bf
),
779 if (symbol_conf
.show_branchflag_count
) {
781 callchain_list_counts__printf_value(node
, chain
, NULL
,
784 callchain_list_counts__printf_value(NULL
, chain
, NULL
,
787 if (asprintf(&alloc_str2
, "%s%s", str
, buf
) < 0)
788 str
= "Not enough memory!";
794 callchain_node__scnprintf_value(node
, buf
, sizeof(buf
),
797 if (asprintf(&alloc_str
, "%s %s", buf
, str
) < 0)
798 str
= "Not enough memory!";
803 print(browser
, chain
, str
, offset
, row
, arg
);
810 static bool check_percent_display(struct rb_node
*node
, u64 parent_total
)
812 struct callchain_node
*child
;
820 child
= rb_entry(node
, struct callchain_node
, rb_node
);
821 return callchain_cumul_hits(child
) != parent_total
;
824 static int hist_browser__show_callchain_flat(struct hist_browser
*browser
,
825 struct rb_root
*root
,
826 unsigned short row
, u64 total
,
828 print_callchain_entry_fn print
,
829 struct callchain_print_arg
*arg
,
830 check_output_full_fn is_output_full
)
832 struct rb_node
*node
;
833 int first_row
= row
, offset
= LEVEL_OFFSET_STEP
;
836 node
= rb_first(root
);
837 need_percent
= check_percent_display(node
, parent_total
);
840 struct callchain_node
*child
= rb_entry(node
, struct callchain_node
, rb_node
);
841 struct rb_node
*next
= rb_next(node
);
842 struct callchain_list
*chain
;
843 char folded_sign
= ' ';
845 int extra_offset
= 0;
847 list_for_each_entry(chain
, &child
->parent_val
, list
) {
848 bool was_first
= first
;
852 else if (need_percent
)
853 extra_offset
= LEVEL_OFFSET_STEP
;
855 folded_sign
= callchain_list__folded(chain
);
857 row
+= hist_browser__show_callchain_list(browser
, child
,
859 was_first
&& need_percent
,
860 offset
+ extra_offset
,
863 if (is_output_full(browser
, row
))
866 if (folded_sign
== '+')
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
== '+')
894 if (is_output_full(browser
, row
))
899 return row
- first_row
;
902 static char *hist_browser__folded_callchain_str(struct hist_browser
*browser
,
903 struct callchain_list
*chain
,
904 char *value_str
, char *old_str
)
910 str
= callchain_list__sym_name(chain
, bf
, sizeof(bf
),
913 if (asprintf(&new, "%s%s%s", old_str
,
914 symbol_conf
.field_sep
?: ";", str
) < 0)
918 if (asprintf(&new, "%s %s", value_str
, str
) < 0)
921 if (asprintf(&new, "%s", str
) < 0)
928 static int hist_browser__show_callchain_folded(struct hist_browser
*browser
,
929 struct rb_root
*root
,
930 unsigned short row
, u64 total
,
932 print_callchain_entry_fn print
,
933 struct callchain_print_arg
*arg
,
934 check_output_full_fn is_output_full
)
936 struct rb_node
*node
;
937 int first_row
= row
, offset
= LEVEL_OFFSET_STEP
;
940 node
= rb_first(root
);
941 need_percent
= check_percent_display(node
, parent_total
);
944 struct callchain_node
*child
= rb_entry(node
, struct callchain_node
, rb_node
);
945 struct rb_node
*next
= rb_next(node
);
946 struct callchain_list
*chain
, *first_chain
= NULL
;
948 char *value_str
= NULL
, *value_str_alloc
= NULL
;
949 char *chain_str
= NULL
, *chain_str_alloc
= NULL
;
951 if (arg
->row_offset
!= 0) {
959 callchain_node__scnprintf_value(child
, buf
, sizeof(buf
), total
);
960 if (asprintf(&value_str
, "%s", buf
) < 0) {
961 value_str
= (char *)"<...>";
964 value_str_alloc
= value_str
;
967 list_for_each_entry(chain
, &child
->parent_val
, list
) {
968 chain_str
= hist_browser__folded_callchain_str(browser
,
969 chain
, value_str
, chain_str
);
975 if (chain_str
== NULL
) {
976 chain_str
= (char *)"Not enough memory!";
980 chain_str_alloc
= chain_str
;
983 list_for_each_entry(chain
, &child
->val
, list
) {
984 chain_str
= hist_browser__folded_callchain_str(browser
,
985 chain
, value_str
, chain_str
);
991 if (chain_str
== NULL
) {
992 chain_str
= (char *)"Not enough memory!";
996 chain_str_alloc
= chain_str
;
1000 print(browser
, first_chain
, chain_str
, offset
, row
++, arg
);
1001 free(value_str_alloc
);
1002 free(chain_str_alloc
);
1005 if (is_output_full(browser
, row
))
1010 return row
- first_row
;
1013 static int hist_browser__show_callchain_graph(struct hist_browser
*browser
,
1014 struct rb_root
*root
, int level
,
1015 unsigned short row
, u64 total
,
1017 print_callchain_entry_fn print
,
1018 struct callchain_print_arg
*arg
,
1019 check_output_full_fn is_output_full
)
1021 struct rb_node
*node
;
1022 int first_row
= row
, offset
= level
* LEVEL_OFFSET_STEP
;
1024 u64 percent_total
= total
;
1026 if (callchain_param
.mode
== CHAIN_GRAPH_REL
)
1027 percent_total
= parent_total
;
1029 node
= rb_first(root
);
1030 need_percent
= check_percent_display(node
, parent_total
);
1033 struct callchain_node
*child
= rb_entry(node
, struct callchain_node
, rb_node
);
1034 struct rb_node
*next
= rb_next(node
);
1035 struct callchain_list
*chain
;
1036 char folded_sign
= ' ';
1038 int extra_offset
= 0;
1040 list_for_each_entry(chain
, &child
->val
, list
) {
1041 bool was_first
= first
;
1045 else if (need_percent
)
1046 extra_offset
= LEVEL_OFFSET_STEP
;
1048 folded_sign
= callchain_list__folded(chain
);
1050 row
+= hist_browser__show_callchain_list(browser
, child
,
1051 chain
, row
, percent_total
,
1052 was_first
&& need_percent
,
1053 offset
+ extra_offset
,
1056 if (is_output_full(browser
, row
))
1059 if (folded_sign
== '+')
1063 if (folded_sign
== '-') {
1064 const int new_level
= level
+ (extra_offset
? 2 : 1);
1066 row
+= hist_browser__show_callchain_graph(browser
, &child
->rb_root
,
1067 new_level
, row
, total
,
1068 child
->children_hit
,
1069 print
, arg
, is_output_full
);
1071 if (is_output_full(browser
, row
))
1076 return row
- first_row
;
1079 static int hist_browser__show_callchain(struct hist_browser
*browser
,
1080 struct hist_entry
*entry
, int level
,
1082 print_callchain_entry_fn print
,
1083 struct callchain_print_arg
*arg
,
1084 check_output_full_fn is_output_full
)
1086 u64 total
= hists__total_period(entry
->hists
);
1090 if (symbol_conf
.cumulate_callchain
)
1091 parent_total
= entry
->stat_acc
->period
;
1093 parent_total
= entry
->stat
.period
;
1095 if (callchain_param
.mode
== CHAIN_FLAT
) {
1096 printed
= hist_browser__show_callchain_flat(browser
,
1097 &entry
->sorted_chain
, row
,
1098 total
, parent_total
, print
, arg
,
1100 } else if (callchain_param
.mode
== CHAIN_FOLDED
) {
1101 printed
= hist_browser__show_callchain_folded(browser
,
1102 &entry
->sorted_chain
, row
,
1103 total
, parent_total
, print
, arg
,
1106 printed
= hist_browser__show_callchain_graph(browser
,
1107 &entry
->sorted_chain
, level
, row
,
1108 total
, parent_total
, print
, arg
,
1112 if (arg
->is_current_entry
)
1113 browser
->he_selection
= entry
;
1119 struct ui_browser
*b
;
1124 int __hpp__slsmg_color_printf(struct perf_hpp
*hpp
, const char *fmt
, ...)
1126 struct hpp_arg
*arg
= hpp
->ptr
;
1131 va_start(args
, fmt
);
1132 len
= va_arg(args
, int);
1133 percent
= va_arg(args
, double);
1136 ui_browser__set_percent_color(arg
->b
, percent
, arg
->current_entry
);
1138 ret
= scnprintf(hpp
->buf
, hpp
->size
, fmt
, len
, percent
);
1139 ui_browser__printf(arg
->b
, "%s", hpp
->buf
);
1144 #define __HPP_COLOR_PERCENT_FN(_type, _field) \
1145 static u64 __hpp_get_##_field(struct hist_entry *he) \
1147 return he->stat._field; \
1151 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
1152 struct perf_hpp *hpp, \
1153 struct hist_entry *he) \
1155 return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%", \
1156 __hpp__slsmg_color_printf, true); \
1159 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
1160 static u64 __hpp_get_acc_##_field(struct hist_entry *he) \
1162 return he->stat_acc->_field; \
1166 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
1167 struct perf_hpp *hpp, \
1168 struct hist_entry *he) \
1170 if (!symbol_conf.cumulate_callchain) { \
1171 struct hpp_arg *arg = hpp->ptr; \
1172 int len = fmt->user_len ?: fmt->len; \
1173 int ret = scnprintf(hpp->buf, hpp->size, \
1174 "%*s", len, "N/A"); \
1175 ui_browser__printf(arg->b, "%s", hpp->buf); \
1179 return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field, \
1180 " %*.2f%%", __hpp__slsmg_color_printf, true); \
1183 __HPP_COLOR_PERCENT_FN(overhead
, period
)
1184 __HPP_COLOR_PERCENT_FN(overhead_sys
, period_sys
)
1185 __HPP_COLOR_PERCENT_FN(overhead_us
, period_us
)
1186 __HPP_COLOR_PERCENT_FN(overhead_guest_sys
, period_guest_sys
)
1187 __HPP_COLOR_PERCENT_FN(overhead_guest_us
, period_guest_us
)
1188 __HPP_COLOR_ACC_PERCENT_FN(overhead_acc
, period
)
1190 #undef __HPP_COLOR_PERCENT_FN
1191 #undef __HPP_COLOR_ACC_PERCENT_FN
1193 void hist_browser__init_hpp(void)
1195 perf_hpp__format
[PERF_HPP__OVERHEAD
].color
=
1196 hist_browser__hpp_color_overhead
;
1197 perf_hpp__format
[PERF_HPP__OVERHEAD_SYS
].color
=
1198 hist_browser__hpp_color_overhead_sys
;
1199 perf_hpp__format
[PERF_HPP__OVERHEAD_US
].color
=
1200 hist_browser__hpp_color_overhead_us
;
1201 perf_hpp__format
[PERF_HPP__OVERHEAD_GUEST_SYS
].color
=
1202 hist_browser__hpp_color_overhead_guest_sys
;
1203 perf_hpp__format
[PERF_HPP__OVERHEAD_GUEST_US
].color
=
1204 hist_browser__hpp_color_overhead_guest_us
;
1205 perf_hpp__format
[PERF_HPP__OVERHEAD_ACC
].color
=
1206 hist_browser__hpp_color_overhead_acc
;
1209 static int hist_browser__show_entry(struct hist_browser
*browser
,
1210 struct hist_entry
*entry
,
1214 int width
= browser
->b
.width
;
1215 char folded_sign
= ' ';
1216 bool current_entry
= ui_browser__is_current_entry(&browser
->b
, row
);
1217 off_t row_offset
= entry
->row_offset
;
1219 struct perf_hpp_fmt
*fmt
;
1221 if (current_entry
) {
1222 browser
->he_selection
= entry
;
1223 browser
->selection
= &entry
->ms
;
1226 if (symbol_conf
.use_callchain
) {
1227 hist_entry__init_have_children(entry
);
1228 folded_sign
= hist_entry__folded(entry
);
1231 if (row_offset
== 0) {
1232 struct hpp_arg arg
= {
1234 .folded_sign
= folded_sign
,
1235 .current_entry
= current_entry
,
1239 hist_browser__gotorc(browser
, row
, 0);
1241 hists__for_each_format(browser
->hists
, fmt
) {
1243 struct perf_hpp hpp
= {
1249 if (perf_hpp__should_skip(fmt
, entry
->hists
) ||
1250 column
++ < browser
->b
.horiz_scroll
)
1253 if (current_entry
&& browser
->b
.navkeypressed
) {
1254 ui_browser__set_color(&browser
->b
,
1255 HE_COLORSET_SELECTED
);
1257 ui_browser__set_color(&browser
->b
,
1258 HE_COLORSET_NORMAL
);
1262 if (symbol_conf
.use_callchain
) {
1263 ui_browser__printf(&browser
->b
, "%c ", folded_sign
);
1268 ui_browser__printf(&browser
->b
, " ");
1273 int ret
= fmt
->color(fmt
, &hpp
, entry
);
1274 hist_entry__snprintf_alignment(entry
, &hpp
, fmt
, ret
);
1276 * fmt->color() already used ui_browser to
1277 * print the non alignment bits, skip it (+ret):
1279 ui_browser__printf(&browser
->b
, "%s", s
+ ret
);
1281 hist_entry__snprintf_alignment(entry
, &hpp
, fmt
, fmt
->entry(fmt
, &hpp
, entry
));
1282 ui_browser__printf(&browser
->b
, "%s", s
);
1284 width
-= hpp
.buf
- s
;
1287 /* The scroll bar isn't being used */
1288 if (!browser
->b
.navkeypressed
)
1291 ui_browser__write_nstring(&browser
->b
, "", width
);
1298 if (folded_sign
== '-' && row
!= browser
->b
.rows
) {
1299 struct callchain_print_arg arg
= {
1300 .row_offset
= row_offset
,
1301 .is_current_entry
= current_entry
,
1304 printed
+= hist_browser__show_callchain(browser
, entry
, 1, row
,
1305 hist_browser__show_callchain_entry
, &arg
,
1306 hist_browser__check_output_full
);
1312 static int hist_browser__show_hierarchy_entry(struct hist_browser
*browser
,
1313 struct hist_entry
*entry
,
1318 int width
= browser
->b
.width
;
1319 char folded_sign
= ' ';
1320 bool current_entry
= ui_browser__is_current_entry(&browser
->b
, row
);
1321 off_t row_offset
= entry
->row_offset
;
1323 struct perf_hpp_fmt
*fmt
;
1324 struct perf_hpp_list_node
*fmt_node
;
1325 struct hpp_arg arg
= {
1327 .current_entry
= current_entry
,
1330 int hierarchy_indent
= (entry
->hists
->nr_hpp_node
- 2) * HIERARCHY_INDENT
;
1332 if (current_entry
) {
1333 browser
->he_selection
= entry
;
1334 browser
->selection
= &entry
->ms
;
1337 hist_entry__init_have_children(entry
);
1338 folded_sign
= hist_entry__folded(entry
);
1339 arg
.folded_sign
= folded_sign
;
1341 if (entry
->leaf
&& row_offset
) {
1343 goto show_callchain
;
1346 hist_browser__gotorc(browser
, row
, 0);
1348 if (current_entry
&& browser
->b
.navkeypressed
)
1349 ui_browser__set_color(&browser
->b
, HE_COLORSET_SELECTED
);
1351 ui_browser__set_color(&browser
->b
, HE_COLORSET_NORMAL
);
1353 ui_browser__write_nstring(&browser
->b
, "", level
* HIERARCHY_INDENT
);
1354 width
-= level
* HIERARCHY_INDENT
;
1356 /* the first hpp_list_node is for overhead columns */
1357 fmt_node
= list_first_entry(&entry
->hists
->hpp_formats
,
1358 struct perf_hpp_list_node
, list
);
1359 perf_hpp_list__for_each_format(&fmt_node
->hpp
, fmt
) {
1361 struct perf_hpp hpp
= {
1367 if (perf_hpp__should_skip(fmt
, entry
->hists
) ||
1368 column
++ < browser
->b
.horiz_scroll
)
1371 if (current_entry
&& browser
->b
.navkeypressed
) {
1372 ui_browser__set_color(&browser
->b
,
1373 HE_COLORSET_SELECTED
);
1375 ui_browser__set_color(&browser
->b
,
1376 HE_COLORSET_NORMAL
);
1380 ui_browser__printf(&browser
->b
, "%c ", folded_sign
);
1384 ui_browser__printf(&browser
->b
, " ");
1389 int ret
= fmt
->color(fmt
, &hpp
, entry
);
1390 hist_entry__snprintf_alignment(entry
, &hpp
, fmt
, ret
);
1392 * fmt->color() already used ui_browser to
1393 * print the non alignment bits, skip it (+ret):
1395 ui_browser__printf(&browser
->b
, "%s", s
+ ret
);
1397 int ret
= fmt
->entry(fmt
, &hpp
, entry
);
1398 hist_entry__snprintf_alignment(entry
, &hpp
, fmt
, ret
);
1399 ui_browser__printf(&browser
->b
, "%s", s
);
1401 width
-= hpp
.buf
- s
;
1405 ui_browser__write_nstring(&browser
->b
, "", hierarchy_indent
);
1406 width
-= hierarchy_indent
;
1409 if (column
>= browser
->b
.horiz_scroll
) {
1411 struct perf_hpp hpp
= {
1417 if (current_entry
&& browser
->b
.navkeypressed
) {
1418 ui_browser__set_color(&browser
->b
,
1419 HE_COLORSET_SELECTED
);
1421 ui_browser__set_color(&browser
->b
,
1422 HE_COLORSET_NORMAL
);
1425 perf_hpp_list__for_each_format(entry
->hpp_list
, fmt
) {
1427 ui_browser__printf(&browser
->b
, "%c ", folded_sign
);
1430 ui_browser__write_nstring(&browser
->b
, "", 2);
1436 * No need to call hist_entry__snprintf_alignment()
1437 * since this fmt is always the last column in the
1441 width
-= fmt
->color(fmt
, &hpp
, entry
);
1445 width
-= fmt
->entry(fmt
, &hpp
, entry
);
1446 ui_browser__printf(&browser
->b
, "%s", ltrim(s
));
1448 while (isspace(s
[i
++]))
1454 /* The scroll bar isn't being used */
1455 if (!browser
->b
.navkeypressed
)
1458 ui_browser__write_nstring(&browser
->b
, "", width
);
1464 if (entry
->leaf
&& folded_sign
== '-' && row
!= browser
->b
.rows
) {
1465 struct callchain_print_arg carg
= {
1466 .row_offset
= row_offset
,
1469 printed
+= hist_browser__show_callchain(browser
, entry
,
1471 hist_browser__show_callchain_entry
, &carg
,
1472 hist_browser__check_output_full
);
1478 static int hist_browser__show_no_entry(struct hist_browser
*browser
,
1479 unsigned short row
, int level
)
1481 int width
= browser
->b
.width
;
1482 bool current_entry
= ui_browser__is_current_entry(&browser
->b
, row
);
1486 struct perf_hpp_fmt
*fmt
;
1487 struct perf_hpp_list_node
*fmt_node
;
1488 int indent
= browser
->hists
->nr_hpp_node
- 2;
1490 if (current_entry
) {
1491 browser
->he_selection
= NULL
;
1492 browser
->selection
= NULL
;
1495 hist_browser__gotorc(browser
, row
, 0);
1497 if (current_entry
&& browser
->b
.navkeypressed
)
1498 ui_browser__set_color(&browser
->b
, HE_COLORSET_SELECTED
);
1500 ui_browser__set_color(&browser
->b
, HE_COLORSET_NORMAL
);
1502 ui_browser__write_nstring(&browser
->b
, "", level
* HIERARCHY_INDENT
);
1503 width
-= level
* HIERARCHY_INDENT
;
1505 /* the first hpp_list_node is for overhead columns */
1506 fmt_node
= list_first_entry(&browser
->hists
->hpp_formats
,
1507 struct perf_hpp_list_node
, list
);
1508 perf_hpp_list__for_each_format(&fmt_node
->hpp
, fmt
) {
1509 if (perf_hpp__should_skip(fmt
, browser
->hists
) ||
1510 column
++ < browser
->b
.horiz_scroll
)
1513 ret
= fmt
->width(fmt
, NULL
, browser
->hists
);
1516 /* for folded sign */
1520 /* space between columns */
1524 ui_browser__write_nstring(&browser
->b
, "", ret
);
1528 ui_browser__write_nstring(&browser
->b
, "", indent
* HIERARCHY_INDENT
);
1529 width
-= indent
* HIERARCHY_INDENT
;
1531 if (column
>= browser
->b
.horiz_scroll
) {
1534 ret
= snprintf(buf
, sizeof(buf
), "no entry >= %.2f%%", browser
->min_pcnt
);
1535 ui_browser__printf(&browser
->b
, " %s", buf
);
1539 /* The scroll bar isn't being used */
1540 if (!browser
->b
.navkeypressed
)
1543 ui_browser__write_nstring(&browser
->b
, "", width
);
1547 static int advance_hpp_check(struct perf_hpp
*hpp
, int inc
)
1549 advance_hpp(hpp
, inc
);
1550 return hpp
->size
<= 0;
1554 hists_browser__scnprintf_headers(struct hist_browser
*browser
, char *buf
,
1555 size_t size
, int line
)
1557 struct hists
*hists
= browser
->hists
;
1558 struct perf_hpp dummy_hpp
= {
1562 struct perf_hpp_fmt
*fmt
;
1567 if (symbol_conf
.use_callchain
) {
1568 ret
= scnprintf(buf
, size
, " ");
1569 if (advance_hpp_check(&dummy_hpp
, ret
))
1573 hists__for_each_format(browser
->hists
, fmt
) {
1574 if (perf_hpp__should_skip(fmt
, hists
) || column
++ < browser
->b
.horiz_scroll
)
1577 ret
= fmt
->header(fmt
, &dummy_hpp
, hists
, line
, &span
);
1578 if (advance_hpp_check(&dummy_hpp
, ret
))
1584 ret
= scnprintf(dummy_hpp
.buf
, dummy_hpp
.size
, " ");
1585 if (advance_hpp_check(&dummy_hpp
, ret
))
1592 static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser
*browser
, char *buf
, size_t size
)
1594 struct hists
*hists
= browser
->hists
;
1595 struct perf_hpp dummy_hpp
= {
1599 struct perf_hpp_fmt
*fmt
;
1600 struct perf_hpp_list_node
*fmt_node
;
1603 int indent
= hists
->nr_hpp_node
- 2;
1604 bool first_node
, first_col
;
1606 ret
= scnprintf(buf
, size
, " ");
1607 if (advance_hpp_check(&dummy_hpp
, ret
))
1611 /* the first hpp_list_node is for overhead columns */
1612 fmt_node
= list_first_entry(&hists
->hpp_formats
,
1613 struct perf_hpp_list_node
, list
);
1614 perf_hpp_list__for_each_format(&fmt_node
->hpp
, fmt
) {
1615 if (column
++ < browser
->b
.horiz_scroll
)
1618 ret
= fmt
->header(fmt
, &dummy_hpp
, hists
, 0, NULL
);
1619 if (advance_hpp_check(&dummy_hpp
, ret
))
1622 ret
= scnprintf(dummy_hpp
.buf
, dummy_hpp
.size
, " ");
1623 if (advance_hpp_check(&dummy_hpp
, ret
))
1630 ret
= scnprintf(dummy_hpp
.buf
, dummy_hpp
.size
, "%*s",
1631 indent
* HIERARCHY_INDENT
, "");
1632 if (advance_hpp_check(&dummy_hpp
, ret
))
1637 list_for_each_entry_continue(fmt_node
, &hists
->hpp_formats
, list
) {
1639 ret
= scnprintf(dummy_hpp
.buf
, dummy_hpp
.size
, " / ");
1640 if (advance_hpp_check(&dummy_hpp
, ret
))
1646 perf_hpp_list__for_each_format(&fmt_node
->hpp
, fmt
) {
1649 if (perf_hpp__should_skip(fmt
, hists
))
1653 ret
= scnprintf(dummy_hpp
.buf
, dummy_hpp
.size
, "+");
1654 if (advance_hpp_check(&dummy_hpp
, ret
))
1659 ret
= fmt
->header(fmt
, &dummy_hpp
, hists
, 0, NULL
);
1660 dummy_hpp
.buf
[ret
] = '\0';
1662 start
= trim(dummy_hpp
.buf
);
1663 ret
= strlen(start
);
1665 if (start
!= dummy_hpp
.buf
)
1666 memmove(dummy_hpp
.buf
, start
, ret
+ 1);
1668 if (advance_hpp_check(&dummy_hpp
, ret
))
1676 static void hists_browser__hierarchy_headers(struct hist_browser
*browser
)
1680 hists_browser__scnprintf_hierarchy_headers(browser
, headers
,
1683 ui_browser__gotorc(&browser
->b
, 0, 0);
1684 ui_browser__set_color(&browser
->b
, HE_COLORSET_ROOT
);
1685 ui_browser__write_nstring(&browser
->b
, headers
, browser
->b
.width
+ 1);
1688 static void hists_browser__headers(struct hist_browser
*browser
)
1690 struct hists
*hists
= browser
->hists
;
1691 struct perf_hpp_list
*hpp_list
= hists
->hpp_list
;
1695 for (line
= 0; line
< hpp_list
->nr_header_lines
; line
++) {
1698 hists_browser__scnprintf_headers(browser
, headers
,
1699 sizeof(headers
), line
);
1701 ui_browser__gotorc(&browser
->b
, line
, 0);
1702 ui_browser__set_color(&browser
->b
, HE_COLORSET_ROOT
);
1703 ui_browser__write_nstring(&browser
->b
, headers
, browser
->b
.width
+ 1);
1707 static void hist_browser__show_headers(struct hist_browser
*browser
)
1709 if (symbol_conf
.report_hierarchy
)
1710 hists_browser__hierarchy_headers(browser
);
1712 hists_browser__headers(browser
);
1715 static void ui_browser__hists_init_top(struct ui_browser
*browser
)
1717 if (browser
->top
== NULL
) {
1718 struct hist_browser
*hb
;
1720 hb
= container_of(browser
, struct hist_browser
, b
);
1721 browser
->top
= rb_first(&hb
->hists
->entries
);
1725 static unsigned int hist_browser__refresh(struct ui_browser
*browser
)
1728 u16 header_offset
= 0;
1730 struct hist_browser
*hb
= container_of(browser
, struct hist_browser
, b
);
1731 struct hists
*hists
= hb
->hists
;
1733 if (hb
->show_headers
) {
1734 struct perf_hpp_list
*hpp_list
= hists
->hpp_list
;
1736 hist_browser__show_headers(hb
);
1737 header_offset
= hpp_list
->nr_header_lines
;
1740 ui_browser__hists_init_top(browser
);
1741 hb
->he_selection
= NULL
;
1742 hb
->selection
= NULL
;
1744 for (nd
= browser
->top
; nd
; nd
= rb_hierarchy_next(nd
)) {
1745 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1749 /* let it move to sibling */
1750 h
->unfolded
= false;
1754 percent
= hist_entry__get_percent_limit(h
);
1755 if (percent
< hb
->min_pcnt
)
1758 if (symbol_conf
.report_hierarchy
) {
1759 row
+= hist_browser__show_hierarchy_entry(hb
, h
, row
,
1761 if (row
== browser
->rows
)
1764 if (h
->has_no_entry
) {
1765 hist_browser__show_no_entry(hb
, row
, h
->depth
+ 1);
1769 row
+= hist_browser__show_entry(hb
, h
, row
);
1772 if (row
== browser
->rows
)
1776 return row
+ header_offset
;
1779 static struct rb_node
*hists__filter_entries(struct rb_node
*nd
,
1782 while (nd
!= NULL
) {
1783 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1784 float percent
= hist_entry__get_percent_limit(h
);
1786 if (!h
->filtered
&& percent
>= min_pcnt
)
1790 * If it's filtered, its all children also were filtered.
1791 * So move to sibling node.
1796 nd
= rb_hierarchy_next(nd
);
1802 static struct rb_node
*hists__filter_prev_entries(struct rb_node
*nd
,
1805 while (nd
!= NULL
) {
1806 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1807 float percent
= hist_entry__get_percent_limit(h
);
1809 if (!h
->filtered
&& percent
>= min_pcnt
)
1812 nd
= rb_hierarchy_prev(nd
);
1818 static void ui_browser__hists_seek(struct ui_browser
*browser
,
1819 off_t offset
, int whence
)
1821 struct hist_entry
*h
;
1824 struct hist_browser
*hb
;
1826 hb
= container_of(browser
, struct hist_browser
, b
);
1828 if (browser
->nr_entries
== 0)
1831 ui_browser__hists_init_top(browser
);
1835 nd
= hists__filter_entries(rb_first(browser
->entries
),
1842 nd
= rb_hierarchy_last(rb_last(browser
->entries
));
1843 nd
= hists__filter_prev_entries(nd
, hb
->min_pcnt
);
1851 * Moves not relative to the first visible entry invalidates its
1854 h
= rb_entry(browser
->top
, struct hist_entry
, rb_node
);
1858 * Here we have to check if nd is expanded (+), if it is we can't go
1859 * the next top level hist_entry, instead we must compute an offset of
1860 * what _not_ to show and not change the first visible entry.
1862 * This offset increments when we are going from top to bottom and
1863 * decreases when we're going from bottom to top.
1865 * As we don't have backpointers to the top level in the callchains
1866 * structure, we need to always print the whole hist_entry callchain,
1867 * skipping the first ones that are before the first visible entry
1868 * and stop when we printed enough lines to fill the screen.
1876 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1877 if (h
->unfolded
&& h
->leaf
) {
1878 u16 remaining
= h
->nr_rows
- h
->row_offset
;
1879 if (offset
> remaining
) {
1880 offset
-= remaining
;
1883 h
->row_offset
+= offset
;
1889 nd
= hists__filter_entries(rb_hierarchy_next(nd
),
1895 } while (offset
!= 0);
1896 } else if (offset
< 0) {
1898 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1899 if (h
->unfolded
&& h
->leaf
) {
1901 if (-offset
> h
->row_offset
) {
1902 offset
+= h
->row_offset
;
1905 h
->row_offset
+= offset
;
1911 if (-offset
> h
->nr_rows
) {
1912 offset
+= h
->nr_rows
;
1915 h
->row_offset
= h
->nr_rows
+ offset
;
1923 nd
= hists__filter_prev_entries(rb_hierarchy_prev(nd
),
1931 * Last unfiltered hist_entry, check if it is
1932 * unfolded, if it is then we should have
1933 * row_offset at its last entry.
1935 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1936 if (h
->unfolded
&& h
->leaf
)
1937 h
->row_offset
= h
->nr_rows
;
1944 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1949 static int hist_browser__fprintf_callchain(struct hist_browser
*browser
,
1950 struct hist_entry
*he
, FILE *fp
,
1953 struct callchain_print_arg arg
= {
1957 hist_browser__show_callchain(browser
, he
, level
, 0,
1958 hist_browser__fprintf_callchain_entry
, &arg
,
1959 hist_browser__check_dump_full
);
1963 static int hist_browser__fprintf_entry(struct hist_browser
*browser
,
1964 struct hist_entry
*he
, FILE *fp
)
1968 char folded_sign
= ' ';
1969 struct perf_hpp hpp
= {
1973 struct perf_hpp_fmt
*fmt
;
1977 if (symbol_conf
.use_callchain
) {
1978 folded_sign
= hist_entry__folded(he
);
1979 printed
+= fprintf(fp
, "%c ", folded_sign
);
1982 hists__for_each_format(browser
->hists
, fmt
) {
1983 if (perf_hpp__should_skip(fmt
, he
->hists
))
1987 ret
= scnprintf(hpp
.buf
, hpp
.size
, " ");
1988 advance_hpp(&hpp
, ret
);
1992 ret
= fmt
->entry(fmt
, &hpp
, he
);
1993 ret
= hist_entry__snprintf_alignment(he
, &hpp
, fmt
, ret
);
1994 advance_hpp(&hpp
, ret
);
1996 printed
+= fprintf(fp
, "%s\n", s
);
1998 if (folded_sign
== '-')
1999 printed
+= hist_browser__fprintf_callchain(browser
, he
, fp
, 1);
2005 static int hist_browser__fprintf_hierarchy_entry(struct hist_browser
*browser
,
2006 struct hist_entry
*he
,
2007 FILE *fp
, int level
)
2011 char folded_sign
= ' ';
2012 struct perf_hpp hpp
= {
2016 struct perf_hpp_fmt
*fmt
;
2017 struct perf_hpp_list_node
*fmt_node
;
2020 int hierarchy_indent
= (he
->hists
->nr_hpp_node
- 2) * HIERARCHY_INDENT
;
2022 printed
= fprintf(fp
, "%*s", level
* HIERARCHY_INDENT
, "");
2024 folded_sign
= hist_entry__folded(he
);
2025 printed
+= fprintf(fp
, "%c", folded_sign
);
2027 /* the first hpp_list_node is for overhead columns */
2028 fmt_node
= list_first_entry(&he
->hists
->hpp_formats
,
2029 struct perf_hpp_list_node
, list
);
2030 perf_hpp_list__for_each_format(&fmt_node
->hpp
, fmt
) {
2032 ret
= scnprintf(hpp
.buf
, hpp
.size
, " ");
2033 advance_hpp(&hpp
, ret
);
2037 ret
= fmt
->entry(fmt
, &hpp
, he
);
2038 advance_hpp(&hpp
, ret
);
2041 ret
= scnprintf(hpp
.buf
, hpp
.size
, "%*s", hierarchy_indent
, "");
2042 advance_hpp(&hpp
, ret
);
2044 perf_hpp_list__for_each_format(he
->hpp_list
, fmt
) {
2045 ret
= scnprintf(hpp
.buf
, hpp
.size
, " ");
2046 advance_hpp(&hpp
, ret
);
2048 ret
= fmt
->entry(fmt
, &hpp
, he
);
2049 advance_hpp(&hpp
, ret
);
2052 printed
+= fprintf(fp
, "%s\n", rtrim(s
));
2054 if (he
->leaf
&& folded_sign
== '-') {
2055 printed
+= hist_browser__fprintf_callchain(browser
, he
, fp
,
2062 static int hist_browser__fprintf(struct hist_browser
*browser
, FILE *fp
)
2064 struct rb_node
*nd
= hists__filter_entries(rb_first(browser
->b
.entries
),
2069 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
2071 if (symbol_conf
.report_hierarchy
) {
2072 printed
+= hist_browser__fprintf_hierarchy_entry(browser
,
2076 printed
+= hist_browser__fprintf_entry(browser
, h
, fp
);
2079 nd
= hists__filter_entries(rb_hierarchy_next(nd
),
2086 static int hist_browser__dump(struct hist_browser
*browser
)
2092 scnprintf(filename
, sizeof(filename
), "perf.hist.%d", browser
->print_seq
);
2093 if (access(filename
, F_OK
))
2096 * XXX: Just an arbitrary lazy upper limit
2098 if (++browser
->print_seq
== 8192) {
2099 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
2104 fp
= fopen(filename
, "w");
2107 const char *err
= str_error_r(errno
, bf
, sizeof(bf
));
2108 ui_helpline__fpush("Couldn't write to %s: %s", filename
, err
);
2112 ++browser
->print_seq
;
2113 hist_browser__fprintf(browser
, fp
);
2115 ui_helpline__fpush("%s written!", filename
);
2120 void hist_browser__init(struct hist_browser
*browser
,
2121 struct hists
*hists
)
2123 struct perf_hpp_fmt
*fmt
;
2125 browser
->hists
= hists
;
2126 browser
->b
.refresh
= hist_browser__refresh
;
2127 browser
->b
.refresh_dimensions
= hist_browser__refresh_dimensions
;
2128 browser
->b
.seek
= ui_browser__hists_seek
;
2129 browser
->b
.use_navkeypressed
= true;
2130 browser
->show_headers
= symbol_conf
.show_hist_headers
;
2132 if (symbol_conf
.report_hierarchy
) {
2133 struct perf_hpp_list_node
*fmt_node
;
2135 /* count overhead columns (in the first node) */
2136 fmt_node
= list_first_entry(&hists
->hpp_formats
,
2137 struct perf_hpp_list_node
, list
);
2138 perf_hpp_list__for_each_format(&fmt_node
->hpp
, fmt
)
2139 ++browser
->b
.columns
;
2141 /* add a single column for whole hierarchy sort keys*/
2142 ++browser
->b
.columns
;
2144 hists__for_each_format(hists
, fmt
)
2145 ++browser
->b
.columns
;
2148 hists__reset_column_width(hists
);
2151 struct hist_browser
*hist_browser__new(struct hists
*hists
)
2153 struct hist_browser
*browser
= zalloc(sizeof(*browser
));
2156 hist_browser__init(browser
, hists
);
2161 static struct hist_browser
*
2162 perf_evsel_browser__new(struct perf_evsel
*evsel
,
2163 struct hist_browser_timer
*hbt
,
2164 struct perf_env
*env
)
2166 struct hist_browser
*browser
= hist_browser__new(evsel__hists(evsel
));
2171 browser
->title
= perf_evsel_browser_title
;
2176 void hist_browser__delete(struct hist_browser
*browser
)
2181 static struct hist_entry
*hist_browser__selected_entry(struct hist_browser
*browser
)
2183 return browser
->he_selection
;
2186 static struct thread
*hist_browser__selected_thread(struct hist_browser
*browser
)
2188 return browser
->he_selection
->thread
;
2191 /* Check whether the browser is for 'top' or 'report' */
2192 static inline bool is_report_browser(void *timer
)
2194 return timer
== NULL
;
2197 static int perf_evsel_browser_title(struct hist_browser
*browser
,
2198 char *bf
, size_t size
)
2200 struct hist_browser_timer
*hbt
= browser
->hbt
;
2201 struct hists
*hists
= browser
->hists
;
2204 const struct dso
*dso
= hists
->dso_filter
;
2205 const struct thread
*thread
= hists
->thread_filter
;
2206 int socket_id
= hists
->socket_filter
;
2207 unsigned long nr_samples
= hists
->stats
.nr_events
[PERF_RECORD_SAMPLE
];
2208 u64 nr_events
= hists
->stats
.total_period
;
2209 struct perf_evsel
*evsel
= hists_to_evsel(hists
);
2210 const char *ev_name
= perf_evsel__name(evsel
);
2212 size_t buflen
= sizeof(buf
);
2213 char ref
[30] = " show reference callgraph, ";
2214 bool enable_ref
= false;
2216 if (symbol_conf
.filter_relative
) {
2217 nr_samples
= hists
->stats
.nr_non_filtered_samples
;
2218 nr_events
= hists
->stats
.total_non_filtered_period
;
2221 if (perf_evsel__is_group_event(evsel
)) {
2222 struct perf_evsel
*pos
;
2224 perf_evsel__group_desc(evsel
, buf
, buflen
);
2227 for_each_group_member(pos
, evsel
) {
2228 struct hists
*pos_hists
= evsel__hists(pos
);
2230 if (symbol_conf
.filter_relative
) {
2231 nr_samples
+= pos_hists
->stats
.nr_non_filtered_samples
;
2232 nr_events
+= pos_hists
->stats
.total_non_filtered_period
;
2234 nr_samples
+= pos_hists
->stats
.nr_events
[PERF_RECORD_SAMPLE
];
2235 nr_events
+= pos_hists
->stats
.total_period
;
2240 if (symbol_conf
.show_ref_callgraph
&&
2241 strstr(ev_name
, "call-graph=no"))
2243 nr_samples
= convert_unit(nr_samples
, &unit
);
2244 printed
= scnprintf(bf
, size
,
2245 "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64
,
2246 nr_samples
, unit
, ev_name
, enable_ref
? ref
: " ", nr_events
);
2249 if (hists
->uid_filter_str
)
2250 printed
+= snprintf(bf
+ printed
, size
- printed
,
2251 ", UID: %s", hists
->uid_filter_str
);
2253 if (hists__has(hists
, thread
)) {
2254 printed
+= scnprintf(bf
+ printed
, size
- printed
,
2256 (thread
->comm_set
? thread__comm_str(thread
) : ""),
2259 printed
+= scnprintf(bf
+ printed
, size
- printed
,
2261 (thread
->comm_set
? thread__comm_str(thread
) : ""));
2265 printed
+= scnprintf(bf
+ printed
, size
- printed
,
2266 ", DSO: %s", dso
->short_name
);
2268 printed
+= scnprintf(bf
+ printed
, size
- printed
,
2269 ", Processor Socket: %d", socket_id
);
2270 if (!is_report_browser(hbt
)) {
2271 struct perf_top
*top
= hbt
->arg
;
2274 printed
+= scnprintf(bf
+ printed
, size
- printed
, " [z]");
2280 static inline void free_popup_options(char **options
, int n
)
2284 for (i
= 0; i
< n
; ++i
)
2289 * Only runtime switching of perf data file will make "input_name" point
2290 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
2291 * whether we need to call free() for current "input_name" during the switch.
2293 static bool is_input_name_malloced
= false;
2295 static int switch_data_file(void)
2297 char *pwd
, *options
[32], *abs_path
[32], *tmp
;
2299 int nr_options
= 0, choice
= -1, ret
= -1;
2300 struct dirent
*dent
;
2302 pwd
= getenv("PWD");
2306 pwd_dir
= opendir(pwd
);
2310 memset(options
, 0, sizeof(options
));
2311 memset(abs_path
, 0, sizeof(abs_path
));
2313 while ((dent
= readdir(pwd_dir
))) {
2314 char path
[PATH_MAX
];
2316 char *name
= dent
->d_name
;
2319 if (!(dent
->d_type
== DT_REG
))
2322 snprintf(path
, sizeof(path
), "%s/%s", pwd
, name
);
2324 file
= fopen(path
, "r");
2328 if (fread(&magic
, 1, 8, file
) < 8)
2329 goto close_file_and_continue
;
2331 if (is_perf_magic(magic
)) {
2332 options
[nr_options
] = strdup(name
);
2333 if (!options
[nr_options
])
2334 goto close_file_and_continue
;
2336 abs_path
[nr_options
] = strdup(path
);
2337 if (!abs_path
[nr_options
]) {
2338 zfree(&options
[nr_options
]);
2339 ui__warning("Can't search all data files due to memory shortage.\n");
2347 close_file_and_continue
:
2349 if (nr_options
>= 32) {
2350 ui__warning("Too many perf data files in PWD!\n"
2351 "Only the first 32 files will be listed.\n");
2358 choice
= ui__popup_menu(nr_options
, options
);
2359 if (choice
< nr_options
&& choice
>= 0) {
2360 tmp
= strdup(abs_path
[choice
]);
2362 if (is_input_name_malloced
)
2363 free((void *)input_name
);
2365 is_input_name_malloced
= true;
2368 ui__warning("Data switch failed due to memory shortage!\n");
2372 free_popup_options(options
, nr_options
);
2373 free_popup_options(abs_path
, nr_options
);
2377 struct popup_action
{
2378 struct thread
*thread
;
2379 struct map_symbol ms
;
2382 int (*fn
)(struct hist_browser
*browser
, struct popup_action
*act
);
2386 do_annotate(struct hist_browser
*browser
, struct popup_action
*act
)
2388 struct perf_evsel
*evsel
;
2389 struct annotation
*notes
;
2390 struct hist_entry
*he
;
2393 if (!objdump_path
&& perf_env__lookup_objdump(browser
->env
))
2396 notes
= symbol__annotation(act
->ms
.sym
);
2400 evsel
= hists_to_evsel(browser
->hists
);
2401 err
= map_symbol__tui_annotate(&act
->ms
, evsel
, browser
->hbt
);
2402 he
= hist_browser__selected_entry(browser
);
2404 * offer option to annotate the other branch source or target
2405 * (if they exists) when returning from annotate
2407 if ((err
== 'q' || err
== CTRL('c')) && he
->branch_info
)
2410 ui_browser__update_nr_entries(&browser
->b
, browser
->hists
->nr_entries
);
2412 ui_browser__handle_resize(&browser
->b
);
2417 add_annotate_opt(struct hist_browser
*browser __maybe_unused
,
2418 struct popup_action
*act
, char **optstr
,
2419 struct map
*map
, struct symbol
*sym
)
2421 if (sym
== NULL
|| map
->dso
->annotate_warned
)
2424 if (asprintf(optstr
, "Annotate %s", sym
->name
) < 0)
2429 act
->fn
= do_annotate
;
2434 do_zoom_thread(struct hist_browser
*browser
, struct popup_action
*act
)
2436 struct thread
*thread
= act
->thread
;
2438 if ((!hists__has(browser
->hists
, thread
) &&
2439 !hists__has(browser
->hists
, comm
)) || thread
== NULL
)
2442 if (browser
->hists
->thread_filter
) {
2443 pstack__remove(browser
->pstack
, &browser
->hists
->thread_filter
);
2444 perf_hpp__set_elide(HISTC_THREAD
, false);
2445 thread__zput(browser
->hists
->thread_filter
);
2448 if (hists__has(browser
->hists
, thread
)) {
2449 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
2450 thread
->comm_set
? thread__comm_str(thread
) : "",
2453 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
2454 thread
->comm_set
? thread__comm_str(thread
) : "");
2457 browser
->hists
->thread_filter
= thread__get(thread
);
2458 perf_hpp__set_elide(HISTC_THREAD
, false);
2459 pstack__push(browser
->pstack
, &browser
->hists
->thread_filter
);
2462 hists__filter_by_thread(browser
->hists
);
2463 hist_browser__reset(browser
);
2468 add_thread_opt(struct hist_browser
*browser
, struct popup_action
*act
,
2469 char **optstr
, struct thread
*thread
)
2473 if ((!hists__has(browser
->hists
, thread
) &&
2474 !hists__has(browser
->hists
, comm
)) || thread
== NULL
)
2477 if (hists__has(browser
->hists
, thread
)) {
2478 ret
= asprintf(optstr
, "Zoom %s %s(%d) thread",
2479 browser
->hists
->thread_filter
? "out of" : "into",
2480 thread
->comm_set
? thread__comm_str(thread
) : "",
2483 ret
= asprintf(optstr
, "Zoom %s %s thread",
2484 browser
->hists
->thread_filter
? "out of" : "into",
2485 thread
->comm_set
? thread__comm_str(thread
) : "");
2490 act
->thread
= thread
;
2491 act
->fn
= do_zoom_thread
;
2496 do_zoom_dso(struct hist_browser
*browser
, struct popup_action
*act
)
2498 struct map
*map
= act
->ms
.map
;
2500 if (!hists__has(browser
->hists
, dso
) || map
== NULL
)
2503 if (browser
->hists
->dso_filter
) {
2504 pstack__remove(browser
->pstack
, &browser
->hists
->dso_filter
);
2505 perf_hpp__set_elide(HISTC_DSO
, false);
2506 browser
->hists
->dso_filter
= NULL
;
2509 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
2510 __map__is_kernel(map
) ? "the Kernel" : map
->dso
->short_name
);
2511 browser
->hists
->dso_filter
= map
->dso
;
2512 perf_hpp__set_elide(HISTC_DSO
, true);
2513 pstack__push(browser
->pstack
, &browser
->hists
->dso_filter
);
2516 hists__filter_by_dso(browser
->hists
);
2517 hist_browser__reset(browser
);
2522 add_dso_opt(struct hist_browser
*browser
, struct popup_action
*act
,
2523 char **optstr
, struct map
*map
)
2525 if (!hists__has(browser
->hists
, dso
) || map
== NULL
)
2528 if (asprintf(optstr
, "Zoom %s %s DSO",
2529 browser
->hists
->dso_filter
? "out of" : "into",
2530 __map__is_kernel(map
) ? "the Kernel" : map
->dso
->short_name
) < 0)
2534 act
->fn
= do_zoom_dso
;
2539 do_browse_map(struct hist_browser
*browser __maybe_unused
,
2540 struct popup_action
*act
)
2542 map__browse(act
->ms
.map
);
2547 add_map_opt(struct hist_browser
*browser
,
2548 struct popup_action
*act
, char **optstr
, struct map
*map
)
2550 if (!hists__has(browser
->hists
, dso
) || map
== NULL
)
2553 if (asprintf(optstr
, "Browse map details") < 0)
2557 act
->fn
= do_browse_map
;
2562 do_run_script(struct hist_browser
*browser __maybe_unused
,
2563 struct popup_action
*act
)
2565 char script_opt
[64];
2566 memset(script_opt
, 0, sizeof(script_opt
));
2569 scnprintf(script_opt
, sizeof(script_opt
), " -c %s ",
2570 thread__comm_str(act
->thread
));
2571 } else if (act
->ms
.sym
) {
2572 scnprintf(script_opt
, sizeof(script_opt
), " -S %s ",
2576 script_browse(script_opt
);
2581 add_script_opt(struct hist_browser
*browser __maybe_unused
,
2582 struct popup_action
*act
, char **optstr
,
2583 struct thread
*thread
, struct symbol
*sym
)
2586 if (asprintf(optstr
, "Run scripts for samples of thread [%s]",
2587 thread__comm_str(thread
)) < 0)
2590 if (asprintf(optstr
, "Run scripts for samples of symbol [%s]",
2594 if (asprintf(optstr
, "Run scripts for all samples") < 0)
2598 act
->thread
= thread
;
2600 act
->fn
= do_run_script
;
2605 do_switch_data(struct hist_browser
*browser __maybe_unused
,
2606 struct popup_action
*act __maybe_unused
)
2608 if (switch_data_file()) {
2609 ui__warning("Won't switch the data files due to\n"
2610 "no valid data file get selected!\n");
2614 return K_SWITCH_INPUT_DATA
;
2618 add_switch_opt(struct hist_browser
*browser
,
2619 struct popup_action
*act
, char **optstr
)
2621 if (!is_report_browser(browser
->hbt
))
2624 if (asprintf(optstr
, "Switch to another data file in PWD") < 0)
2627 act
->fn
= do_switch_data
;
2632 do_exit_browser(struct hist_browser
*browser __maybe_unused
,
2633 struct popup_action
*act __maybe_unused
)
2639 add_exit_opt(struct hist_browser
*browser __maybe_unused
,
2640 struct popup_action
*act
, char **optstr
)
2642 if (asprintf(optstr
, "Exit") < 0)
2645 act
->fn
= do_exit_browser
;
2650 do_zoom_socket(struct hist_browser
*browser
, struct popup_action
*act
)
2652 if (!hists__has(browser
->hists
, socket
) || act
->socket
< 0)
2655 if (browser
->hists
->socket_filter
> -1) {
2656 pstack__remove(browser
->pstack
, &browser
->hists
->socket_filter
);
2657 browser
->hists
->socket_filter
= -1;
2658 perf_hpp__set_elide(HISTC_SOCKET
, false);
2660 browser
->hists
->socket_filter
= act
->socket
;
2661 perf_hpp__set_elide(HISTC_SOCKET
, true);
2662 pstack__push(browser
->pstack
, &browser
->hists
->socket_filter
);
2665 hists__filter_by_socket(browser
->hists
);
2666 hist_browser__reset(browser
);
2671 add_socket_opt(struct hist_browser
*browser
, struct popup_action
*act
,
2672 char **optstr
, int socket_id
)
2674 if (!hists__has(browser
->hists
, socket
) || socket_id
< 0)
2677 if (asprintf(optstr
, "Zoom %s Processor Socket %d",
2678 (browser
->hists
->socket_filter
> -1) ? "out of" : "into",
2682 act
->socket
= socket_id
;
2683 act
->fn
= do_zoom_socket
;
2687 static void hist_browser__update_nr_entries(struct hist_browser
*hb
)
2690 struct rb_node
*nd
= rb_first(&hb
->hists
->entries
);
2692 if (hb
->min_pcnt
== 0 && !symbol_conf
.report_hierarchy
) {
2693 hb
->nr_non_filtered_entries
= hb
->hists
->nr_non_filtered_entries
;
2697 while ((nd
= hists__filter_entries(nd
, hb
->min_pcnt
)) != NULL
) {
2699 nd
= rb_hierarchy_next(nd
);
2702 hb
->nr_non_filtered_entries
= nr_entries
;
2703 hb
->nr_hierarchy_entries
= nr_entries
;
2706 static void hist_browser__update_percent_limit(struct hist_browser
*hb
,
2709 struct hist_entry
*he
;
2710 struct rb_node
*nd
= rb_first(&hb
->hists
->entries
);
2711 u64 total
= hists__total_period(hb
->hists
);
2712 u64 min_callchain_hits
= total
* (percent
/ 100);
2714 hb
->min_pcnt
= callchain_param
.min_percent
= percent
;
2716 while ((nd
= hists__filter_entries(nd
, hb
->min_pcnt
)) != NULL
) {
2717 he
= rb_entry(nd
, struct hist_entry
, rb_node
);
2719 if (he
->has_no_entry
) {
2720 he
->has_no_entry
= false;
2724 if (!he
->leaf
|| !symbol_conf
.use_callchain
)
2727 if (callchain_param
.mode
== CHAIN_GRAPH_REL
) {
2728 total
= he
->stat
.period
;
2730 if (symbol_conf
.cumulate_callchain
)
2731 total
= he
->stat_acc
->period
;
2733 min_callchain_hits
= total
* (percent
/ 100);
2736 callchain_param
.sort(&he
->sorted_chain
, he
->callchain
,
2737 min_callchain_hits
, &callchain_param
);
2740 nd
= __rb_hierarchy_next(nd
, HMD_FORCE_CHILD
);
2742 /* force to re-evaluate folding state of callchains */
2743 he
->init_have_children
= false;
2744 hist_entry__set_folding(he
, hb
, false);
2748 static int perf_evsel__hists_browse(struct perf_evsel
*evsel
, int nr_events
,
2749 const char *helpline
,
2751 struct hist_browser_timer
*hbt
,
2753 struct perf_env
*env
)
2755 struct hists
*hists
= evsel__hists(evsel
);
2756 struct hist_browser
*browser
= perf_evsel_browser__new(evsel
, hbt
, env
);
2757 struct branch_info
*bi
;
2758 #define MAX_OPTIONS 16
2759 char *options
[MAX_OPTIONS
];
2760 struct popup_action actions
[MAX_OPTIONS
];
2764 int delay_secs
= hbt
? hbt
->refresh
: 0;
2766 #define HIST_BROWSER_HELP_COMMON \
2767 "h/?/F1 Show this window\n" \
2769 "PGDN/SPACE Navigate\n" \
2770 "q/ESC/CTRL+C Exit browser\n\n" \
2771 "For multiple event sessions:\n\n" \
2772 "TAB/UNTAB Switch events\n\n" \
2773 "For symbolic views (--sort has sym):\n\n" \
2774 "ENTER Zoom into DSO/Threads & Annotate current symbol\n" \
2776 "a Annotate current symbol\n" \
2777 "C Collapse all callchains\n" \
2778 "d Zoom into current DSO\n" \
2779 "E Expand all callchains\n" \
2780 "F Toggle percentage of filtered entries\n" \
2781 "H Display column headers\n" \
2782 "L Change percent limit\n" \
2783 "m Display context menu\n" \
2784 "S Zoom into current Processor Socket\n" \
2786 /* help messages are sorted by lexical order of the hotkey */
2787 const char report_help
[] = HIST_BROWSER_HELP_COMMON
2788 "i Show header information\n"
2789 "P Print histograms to perf.hist.N\n"
2790 "r Run available scripts\n"
2791 "s Switch to another data file in PWD\n"
2792 "t Zoom into current Thread\n"
2793 "V Verbose (DSO names in callchains, etc)\n"
2794 "/ Filter symbol by name";
2795 const char top_help
[] = HIST_BROWSER_HELP_COMMON
2796 "P Print histograms to perf.hist.N\n"
2797 "t Zoom into current Thread\n"
2798 "V Verbose (DSO names in callchains, etc)\n"
2799 "z Toggle zeroing of samples\n"
2800 "f Enable/Disable events\n"
2801 "/ Filter symbol by name";
2803 if (browser
== NULL
)
2806 /* reset abort key so that it can get Ctrl-C as a key */
2808 SLang_init_tty(0, 0, 0);
2811 browser
->min_pcnt
= min_pcnt
;
2812 hist_browser__update_nr_entries(browser
);
2814 browser
->pstack
= pstack__new(3);
2815 if (browser
->pstack
== NULL
)
2818 ui_helpline__push(helpline
);
2820 memset(options
, 0, sizeof(options
));
2821 memset(actions
, 0, sizeof(actions
));
2823 if (symbol_conf
.col_width_list_str
)
2824 perf_hpp__set_user_width(symbol_conf
.col_width_list_str
);
2827 struct thread
*thread
= NULL
;
2828 struct map
*map
= NULL
;
2834 key
= hist_browser__run(browser
, helpline
);
2836 if (browser
->he_selection
!= NULL
) {
2837 thread
= hist_browser__selected_thread(browser
);
2838 map
= browser
->selection
->map
;
2839 socked_id
= browser
->he_selection
->socket
;
2847 * Exit the browser, let hists__browser_tree
2848 * go to the next or previous
2850 goto out_free_stack
;
2852 if (!hists__has(hists
, sym
)) {
2853 ui_browser__warning(&browser
->b
, delay_secs
* 2,
2854 "Annotation is only available for symbolic views, "
2855 "include \"sym*\" in --sort to use it.");
2859 if (browser
->selection
== NULL
||
2860 browser
->selection
->sym
== NULL
||
2861 browser
->selection
->map
->dso
->annotate_warned
)
2864 actions
->ms
.map
= browser
->selection
->map
;
2865 actions
->ms
.sym
= browser
->selection
->sym
;
2866 do_annotate(browser
, actions
);
2869 hist_browser__dump(browser
);
2872 actions
->ms
.map
= map
;
2873 do_zoom_dso(browser
, actions
);
2876 verbose
= (verbose
+ 1) % 4;
2877 browser
->show_dso
= verbose
> 0;
2878 ui_helpline__fpush("Verbosity level set to %d\n",
2882 actions
->thread
= thread
;
2883 do_zoom_thread(browser
, actions
);
2886 actions
->socket
= socked_id
;
2887 do_zoom_socket(browser
, actions
);
2890 if (ui_browser__input_window("Symbol to show",
2891 "Please enter the name of symbol you want to see.\n"
2892 "To remove the filter later, press / + ENTER.",
2893 buf
, "ENTER: OK, ESC: Cancel",
2894 delay_secs
* 2) == K_ENTER
) {
2895 hists
->symbol_filter_str
= *buf
? buf
: NULL
;
2896 hists__filter_by_symbol(hists
);
2897 hist_browser__reset(browser
);
2901 if (is_report_browser(hbt
)) {
2902 actions
->thread
= NULL
;
2903 actions
->ms
.sym
= NULL
;
2904 do_run_script(browser
, actions
);
2908 if (is_report_browser(hbt
)) {
2909 key
= do_switch_data(browser
, actions
);
2910 if (key
== K_SWITCH_INPUT_DATA
)
2911 goto out_free_stack
;
2915 /* env->arch is NULL for live-mode (i.e. perf top) */
2917 tui__header_window(env
);
2920 symbol_conf
.filter_relative
^= 1;
2923 if (!is_report_browser(hbt
)) {
2924 struct perf_top
*top
= hbt
->arg
;
2926 top
->zero
= !top
->zero
;
2930 if (ui_browser__input_window("Percent Limit",
2931 "Please enter the value you want to hide entries under that percent.",
2932 buf
, "ENTER: OK, ESC: Cancel",
2933 delay_secs
* 2) == K_ENTER
) {
2935 double new_percent
= strtod(buf
, &end
);
2937 if (new_percent
< 0 || new_percent
> 100) {
2938 ui_browser__warning(&browser
->b
, delay_secs
* 2,
2939 "Invalid percent: %.2f", new_percent
);
2943 hist_browser__update_percent_limit(browser
, new_percent
);
2944 hist_browser__reset(browser
);
2950 ui_browser__help_window(&browser
->b
,
2951 is_report_browser(hbt
) ? report_help
: top_help
);
2962 if (pstack__empty(browser
->pstack
)) {
2964 * Go back to the perf_evsel_menu__run or other user
2967 goto out_free_stack
;
2970 ui_browser__dialog_yesno(&browser
->b
,
2971 "Do you really want to exit?"))
2972 goto out_free_stack
;
2976 top
= pstack__peek(browser
->pstack
);
2977 if (top
== &browser
->hists
->dso_filter
) {
2979 * No need to set actions->dso here since
2980 * it's just to remove the current filter.
2981 * Ditto for thread below.
2983 do_zoom_dso(browser
, actions
);
2984 } else if (top
== &browser
->hists
->thread_filter
) {
2985 do_zoom_thread(browser
, actions
);
2986 } else if (top
== &browser
->hists
->socket_filter
) {
2987 do_zoom_socket(browser
, actions
);
2993 goto out_free_stack
;
2995 if (!is_report_browser(hbt
)) {
2996 struct perf_top
*top
= hbt
->arg
;
2998 perf_evlist__toggle_enable(top
->evlist
);
3000 * No need to refresh, resort/decay histogram
3001 * entries if we are not collecting samples:
3003 if (top
->evlist
->enabled
) {
3004 helpline
= "Press 'f' to disable the events or 'h' to see other hotkeys";
3005 hbt
->refresh
= delay_secs
;
3007 helpline
= "Press 'f' again to re-enable the events";
3014 helpline
= "Press '?' for help on key bindings";
3018 if (!hists__has(hists
, sym
) || browser
->selection
== NULL
)
3019 goto skip_annotation
;
3021 if (sort__mode
== SORT_MODE__BRANCH
) {
3022 bi
= browser
->he_selection
->branch_info
;
3025 goto skip_annotation
;
3027 nr_options
+= add_annotate_opt(browser
,
3028 &actions
[nr_options
],
3029 &options
[nr_options
],
3032 if (bi
->to
.sym
!= bi
->from
.sym
)
3033 nr_options
+= add_annotate_opt(browser
,
3034 &actions
[nr_options
],
3035 &options
[nr_options
],
3039 nr_options
+= add_annotate_opt(browser
,
3040 &actions
[nr_options
],
3041 &options
[nr_options
],
3042 browser
->selection
->map
,
3043 browser
->selection
->sym
);
3046 nr_options
+= add_thread_opt(browser
, &actions
[nr_options
],
3047 &options
[nr_options
], thread
);
3048 nr_options
+= add_dso_opt(browser
, &actions
[nr_options
],
3049 &options
[nr_options
], map
);
3050 nr_options
+= add_map_opt(browser
, &actions
[nr_options
],
3051 &options
[nr_options
],
3052 browser
->selection
?
3053 browser
->selection
->map
: NULL
);
3054 nr_options
+= add_socket_opt(browser
, &actions
[nr_options
],
3055 &options
[nr_options
],
3057 /* perf script support */
3058 if (!is_report_browser(hbt
))
3059 goto skip_scripting
;
3061 if (browser
->he_selection
) {
3062 if (hists__has(hists
, thread
) && thread
) {
3063 nr_options
+= add_script_opt(browser
,
3064 &actions
[nr_options
],
3065 &options
[nr_options
],
3069 * Note that browser->selection != NULL
3070 * when browser->he_selection is not NULL,
3071 * so we don't need to check browser->selection
3072 * before fetching browser->selection->sym like what
3073 * we do before fetching browser->selection->map.
3075 * See hist_browser__show_entry.
3077 if (hists__has(hists
, sym
) && browser
->selection
->sym
) {
3078 nr_options
+= add_script_opt(browser
,
3079 &actions
[nr_options
],
3080 &options
[nr_options
],
3081 NULL
, browser
->selection
->sym
);
3084 nr_options
+= add_script_opt(browser
, &actions
[nr_options
],
3085 &options
[nr_options
], NULL
, NULL
);
3086 nr_options
+= add_switch_opt(browser
, &actions
[nr_options
],
3087 &options
[nr_options
]);
3089 nr_options
+= add_exit_opt(browser
, &actions
[nr_options
],
3090 &options
[nr_options
]);
3093 struct popup_action
*act
;
3095 choice
= ui__popup_menu(nr_options
, options
);
3096 if (choice
== -1 || choice
>= nr_options
)
3099 act
= &actions
[choice
];
3100 key
= act
->fn(browser
, act
);
3103 if (key
== K_SWITCH_INPUT_DATA
)
3107 pstack__delete(browser
->pstack
);
3109 hist_browser__delete(browser
);
3110 free_popup_options(options
, MAX_OPTIONS
);
3114 struct perf_evsel_menu
{
3115 struct ui_browser b
;
3116 struct perf_evsel
*selection
;
3117 bool lost_events
, lost_events_warned
;
3119 struct perf_env
*env
;
3122 static void perf_evsel_menu__write(struct ui_browser
*browser
,
3123 void *entry
, int row
)
3125 struct perf_evsel_menu
*menu
= container_of(browser
,
3126 struct perf_evsel_menu
, b
);
3127 struct perf_evsel
*evsel
= list_entry(entry
, struct perf_evsel
, node
);
3128 struct hists
*hists
= evsel__hists(evsel
);
3129 bool current_entry
= ui_browser__is_current_entry(browser
, row
);
3130 unsigned long nr_events
= hists
->stats
.nr_events
[PERF_RECORD_SAMPLE
];
3131 const char *ev_name
= perf_evsel__name(evsel
);
3133 const char *warn
= " ";
3136 ui_browser__set_color(browser
, current_entry
? HE_COLORSET_SELECTED
:
3137 HE_COLORSET_NORMAL
);
3139 if (perf_evsel__is_group_event(evsel
)) {
3140 struct perf_evsel
*pos
;
3142 ev_name
= perf_evsel__group_name(evsel
);
3144 for_each_group_member(pos
, evsel
) {
3145 struct hists
*pos_hists
= evsel__hists(pos
);
3146 nr_events
+= pos_hists
->stats
.nr_events
[PERF_RECORD_SAMPLE
];
3150 nr_events
= convert_unit(nr_events
, &unit
);
3151 printed
= scnprintf(bf
, sizeof(bf
), "%lu%c%s%s", nr_events
,
3152 unit
, unit
== ' ' ? "" : " ", ev_name
);
3153 ui_browser__printf(browser
, "%s", bf
);
3155 nr_events
= hists
->stats
.nr_events
[PERF_RECORD_LOST
];
3156 if (nr_events
!= 0) {
3157 menu
->lost_events
= true;
3159 ui_browser__set_color(browser
, HE_COLORSET_TOP
);
3160 nr_events
= convert_unit(nr_events
, &unit
);
3161 printed
+= scnprintf(bf
, sizeof(bf
), ": %ld%c%schunks LOST!",
3162 nr_events
, unit
, unit
== ' ' ? "" : " ");
3166 ui_browser__write_nstring(browser
, warn
, browser
->width
- printed
);
3169 menu
->selection
= evsel
;
3172 static int perf_evsel_menu__run(struct perf_evsel_menu
*menu
,
3173 int nr_events
, const char *help
,
3174 struct hist_browser_timer
*hbt
)
3176 struct perf_evlist
*evlist
= menu
->b
.priv
;
3177 struct perf_evsel
*pos
;
3178 const char *title
= "Available samples";
3179 int delay_secs
= hbt
? hbt
->refresh
: 0;
3182 if (ui_browser__show(&menu
->b
, title
,
3183 "ESC: exit, ENTER|->: Browse histograms") < 0)
3187 key
= ui_browser__run(&menu
->b
, delay_secs
);
3191 hbt
->timer(hbt
->arg
);
3193 if (!menu
->lost_events_warned
&& menu
->lost_events
) {
3194 ui_browser__warn_lost_events(&menu
->b
);
3195 menu
->lost_events_warned
= true;
3200 if (!menu
->selection
)
3202 pos
= menu
->selection
;
3204 perf_evlist__set_selected(evlist
, pos
);
3206 * Give the calling tool a chance to populate the non
3207 * default evsel resorted hists tree.
3210 hbt
->timer(hbt
->arg
);
3211 key
= perf_evsel__hists_browse(pos
, nr_events
, help
,
3215 ui_browser__show_title(&menu
->b
, title
);
3218 if (pos
->node
.next
== &evlist
->entries
)
3219 pos
= perf_evlist__first(evlist
);
3221 pos
= perf_evsel__next(pos
);
3224 if (pos
->node
.prev
== &evlist
->entries
)
3225 pos
= perf_evlist__last(evlist
);
3227 pos
= perf_evsel__prev(pos
);
3229 case K_SWITCH_INPUT_DATA
:
3240 if (!ui_browser__dialog_yesno(&menu
->b
,
3241 "Do you really want to exit?"))
3253 ui_browser__hide(&menu
->b
);
3257 static bool filter_group_entries(struct ui_browser
*browser __maybe_unused
,
3260 struct perf_evsel
*evsel
= list_entry(entry
, struct perf_evsel
, node
);
3262 if (symbol_conf
.event_group
&& !perf_evsel__is_group_leader(evsel
))
3268 static int __perf_evlist__tui_browse_hists(struct perf_evlist
*evlist
,
3269 int nr_entries
, const char *help
,
3270 struct hist_browser_timer
*hbt
,
3272 struct perf_env
*env
)
3274 struct perf_evsel
*pos
;
3275 struct perf_evsel_menu menu
= {
3277 .entries
= &evlist
->entries
,
3278 .refresh
= ui_browser__list_head_refresh
,
3279 .seek
= ui_browser__list_head_seek
,
3280 .write
= perf_evsel_menu__write
,
3281 .filter
= filter_group_entries
,
3282 .nr_entries
= nr_entries
,
3285 .min_pcnt
= min_pcnt
,
3289 ui_helpline__push("Press ESC to exit");
3291 evlist__for_each_entry(evlist
, pos
) {
3292 const char *ev_name
= perf_evsel__name(pos
);
3293 size_t line_len
= strlen(ev_name
) + 7;
3295 if (menu
.b
.width
< line_len
)
3296 menu
.b
.width
= line_len
;
3299 return perf_evsel_menu__run(&menu
, nr_entries
, help
, hbt
);
3302 int perf_evlist__tui_browse_hists(struct perf_evlist
*evlist
, const char *help
,
3303 struct hist_browser_timer
*hbt
,
3305 struct perf_env
*env
)
3307 int nr_entries
= evlist
->nr_entries
;
3310 if (nr_entries
== 1) {
3311 struct perf_evsel
*first
= perf_evlist__first(evlist
);
3313 return perf_evsel__hists_browse(first
, nr_entries
, help
,
3314 false, hbt
, min_pcnt
,
3318 if (symbol_conf
.event_group
) {
3319 struct perf_evsel
*pos
;
3322 evlist__for_each_entry(evlist
, pos
) {
3323 if (perf_evsel__is_group_leader(pos
))
3327 if (nr_entries
== 1)
3331 return __perf_evlist__tui_browse_hists(evlist
, nr_entries
, help
,
3332 hbt
, min_pcnt
, env
);