2 #include "../libslang.h"
5 #include <linux/rbtree.h>
7 #include "../../util/evsel.h"
8 #include "../../util/evlist.h"
9 #include "../../util/hist.h"
10 #include "../../util/pstack.h"
11 #include "../../util/sort.h"
12 #include "../../util/util.h"
13 #include "../../util/top.h"
14 #include "../../arch/common.h"
16 #include "../browser.h"
17 #include "../helpline.h"
26 struct hist_entry
*he_selection
;
27 struct map_symbol
*selection
;
28 struct hist_browser_timer
*hbt
;
29 struct pstack
*pstack
;
30 struct perf_session_env
*env
;
35 u64 nr_non_filtered_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
;
63 struct hist_entry
*he
=
64 rb_entry(nd
, struct hist_entry
, rb_node
);
67 unfolded_rows
+= he
->nr_rows
;
72 static u32
hist_browser__nr_entries(struct hist_browser
*hb
)
76 if (hist_browser__has_filter(hb
))
77 nr_entries
= hb
->nr_non_filtered_entries
;
79 nr_entries
= hb
->hists
->nr_entries
;
81 hb
->nr_callchain_rows
= hist_browser__get_folding(hb
);
82 return nr_entries
+ hb
->nr_callchain_rows
;
85 static void hist_browser__update_rows(struct hist_browser
*hb
)
87 struct ui_browser
*browser
= &hb
->b
;
88 u16 header_offset
= hb
->show_headers
? 1 : 0, index_row
;
90 browser
->rows
= browser
->height
- header_offset
;
92 * Verify if we were at the last line and that line isn't
93 * visibe because we now show the header line(s).
95 index_row
= browser
->index
- browser
->top_idx
;
96 if (index_row
>= browser
->rows
)
97 browser
->index
-= index_row
- browser
->rows
+ 1;
100 static void hist_browser__refresh_dimensions(struct ui_browser
*browser
)
102 struct hist_browser
*hb
= container_of(browser
, struct hist_browser
, b
);
104 /* 3 == +/- toggle symbol before actual hist_entry rendering */
105 browser
->width
= 3 + (hists__sort_list_width(hb
->hists
) + sizeof("[k]"));
107 * FIXME: Just keeping existing behaviour, but this really should be
108 * before updating browser->width, as it will invalidate the
109 * calculation above. Fix this and the fallout in another
112 ui_browser__refresh_dimensions(browser
);
113 hist_browser__update_rows(hb
);
116 static void hist_browser__gotorc(struct hist_browser
*browser
, int row
, int column
)
118 u16 header_offset
= browser
->show_headers
? 1 : 0;
120 ui_browser__gotorc(&browser
->b
, row
+ header_offset
, column
);
123 static void hist_browser__reset(struct hist_browser
*browser
)
126 * The hists__remove_entry_filter() already folds non-filtered
127 * entries so we can assume it has 0 callchain rows.
129 browser
->nr_callchain_rows
= 0;
131 hist_browser__update_nr_entries(browser
);
132 browser
->b
.nr_entries
= hist_browser__nr_entries(browser
);
133 hist_browser__refresh_dimensions(&browser
->b
);
134 ui_browser__reset_index(&browser
->b
);
137 static char tree__folded_sign(bool unfolded
)
139 return unfolded
? '-' : '+';
142 static char hist_entry__folded(const struct hist_entry
*he
)
144 return he
->has_children
? tree__folded_sign(he
->unfolded
) : ' ';
147 static char callchain_list__folded(const struct callchain_list
*cl
)
149 return cl
->has_children
? tree__folded_sign(cl
->unfolded
) : ' ';
152 static void callchain_list__set_folding(struct callchain_list
*cl
, bool unfold
)
154 cl
->unfolded
= unfold
? cl
->has_children
: false;
157 static int callchain_node__count_rows_rb_tree(struct callchain_node
*node
)
162 for (nd
= rb_first(&node
->rb_root
); nd
; nd
= rb_next(nd
)) {
163 struct callchain_node
*child
= rb_entry(nd
, struct callchain_node
, rb_node
);
164 struct callchain_list
*chain
;
165 char folded_sign
= ' '; /* No children */
167 list_for_each_entry(chain
, &child
->val
, list
) {
169 /* We need this because we may not have children */
170 folded_sign
= callchain_list__folded(chain
);
171 if (folded_sign
== '+')
175 if (folded_sign
== '-') /* Have children and they're unfolded */
176 n
+= callchain_node__count_rows_rb_tree(child
);
182 static int callchain_node__count_rows(struct callchain_node
*node
)
184 struct callchain_list
*chain
;
185 bool unfolded
= false;
188 list_for_each_entry(chain
, &node
->val
, list
) {
190 unfolded
= chain
->unfolded
;
194 n
+= callchain_node__count_rows_rb_tree(node
);
199 static int callchain__count_rows(struct rb_root
*chain
)
204 for (nd
= rb_first(chain
); nd
; nd
= rb_next(nd
)) {
205 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
206 n
+= callchain_node__count_rows(node
);
212 static bool hist_entry__toggle_fold(struct hist_entry
*he
)
217 if (!he
->has_children
)
220 he
->unfolded
= !he
->unfolded
;
224 static bool callchain_list__toggle_fold(struct callchain_list
*cl
)
229 if (!cl
->has_children
)
232 cl
->unfolded
= !cl
->unfolded
;
236 static void callchain_node__init_have_children_rb_tree(struct callchain_node
*node
)
238 struct rb_node
*nd
= rb_first(&node
->rb_root
);
240 for (nd
= rb_first(&node
->rb_root
); nd
; nd
= rb_next(nd
)) {
241 struct callchain_node
*child
= rb_entry(nd
, struct callchain_node
, rb_node
);
242 struct callchain_list
*chain
;
245 list_for_each_entry(chain
, &child
->val
, list
) {
248 chain
->has_children
= chain
->list
.next
!= &child
->val
||
249 !RB_EMPTY_ROOT(&child
->rb_root
);
251 chain
->has_children
= chain
->list
.next
== &child
->val
&&
252 !RB_EMPTY_ROOT(&child
->rb_root
);
255 callchain_node__init_have_children_rb_tree(child
);
259 static void callchain_node__init_have_children(struct callchain_node
*node
,
262 struct callchain_list
*chain
;
264 chain
= list_entry(node
->val
.next
, struct callchain_list
, list
);
265 chain
->has_children
= has_sibling
;
267 if (!list_empty(&node
->val
)) {
268 chain
= list_entry(node
->val
.prev
, struct callchain_list
, list
);
269 chain
->has_children
= !RB_EMPTY_ROOT(&node
->rb_root
);
272 callchain_node__init_have_children_rb_tree(node
);
275 static void callchain__init_have_children(struct rb_root
*root
)
277 struct rb_node
*nd
= rb_first(root
);
278 bool has_sibling
= nd
&& rb_next(nd
);
280 for (nd
= rb_first(root
); nd
; nd
= rb_next(nd
)) {
281 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
282 callchain_node__init_have_children(node
, has_sibling
);
286 static void hist_entry__init_have_children(struct hist_entry
*he
)
288 if (!he
->init_have_children
) {
289 he
->has_children
= !RB_EMPTY_ROOT(&he
->sorted_chain
);
290 callchain__init_have_children(&he
->sorted_chain
);
291 he
->init_have_children
= true;
295 static bool hist_browser__toggle_fold(struct hist_browser
*browser
)
297 struct hist_entry
*he
= browser
->he_selection
;
298 struct map_symbol
*ms
= browser
->selection
;
299 struct callchain_list
*cl
= container_of(ms
, struct callchain_list
, ms
);
303 has_children
= hist_entry__toggle_fold(he
);
305 has_children
= callchain_list__toggle_fold(cl
);
308 hist_entry__init_have_children(he
);
309 browser
->b
.nr_entries
-= he
->nr_rows
;
310 browser
->nr_callchain_rows
-= he
->nr_rows
;
313 he
->nr_rows
= callchain__count_rows(&he
->sorted_chain
);
317 browser
->b
.nr_entries
+= he
->nr_rows
;
318 browser
->nr_callchain_rows
+= he
->nr_rows
;
323 /* If it doesn't have children, no toggling performed */
327 static int callchain_node__set_folding_rb_tree(struct callchain_node
*node
, bool unfold
)
332 for (nd
= rb_first(&node
->rb_root
); nd
; nd
= rb_next(nd
)) {
333 struct callchain_node
*child
= rb_entry(nd
, struct callchain_node
, rb_node
);
334 struct callchain_list
*chain
;
335 bool has_children
= false;
337 list_for_each_entry(chain
, &child
->val
, list
) {
339 callchain_list__set_folding(chain
, unfold
);
340 has_children
= chain
->has_children
;
344 n
+= callchain_node__set_folding_rb_tree(child
, unfold
);
350 static int callchain_node__set_folding(struct callchain_node
*node
, bool unfold
)
352 struct callchain_list
*chain
;
353 bool has_children
= false;
356 list_for_each_entry(chain
, &node
->val
, list
) {
358 callchain_list__set_folding(chain
, unfold
);
359 has_children
= chain
->has_children
;
363 n
+= callchain_node__set_folding_rb_tree(node
, unfold
);
368 static int callchain__set_folding(struct rb_root
*chain
, bool unfold
)
373 for (nd
= rb_first(chain
); nd
; nd
= rb_next(nd
)) {
374 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
375 n
+= callchain_node__set_folding(node
, unfold
);
381 static void hist_entry__set_folding(struct hist_entry
*he
, bool unfold
)
383 hist_entry__init_have_children(he
);
384 he
->unfolded
= unfold
? he
->has_children
: false;
386 if (he
->has_children
) {
387 int n
= callchain__set_folding(&he
->sorted_chain
, unfold
);
388 he
->nr_rows
= unfold
? n
: 0;
394 __hist_browser__set_folding(struct hist_browser
*browser
, bool unfold
)
397 struct hists
*hists
= browser
->hists
;
399 for (nd
= rb_first(&hists
->entries
);
400 (nd
= hists__filter_entries(nd
, browser
->min_pcnt
)) != NULL
;
402 struct hist_entry
*he
= rb_entry(nd
, struct hist_entry
, rb_node
);
403 hist_entry__set_folding(he
, unfold
);
404 browser
->nr_callchain_rows
+= he
->nr_rows
;
408 static void hist_browser__set_folding(struct hist_browser
*browser
, bool unfold
)
410 browser
->nr_callchain_rows
= 0;
411 __hist_browser__set_folding(browser
, unfold
);
413 browser
->b
.nr_entries
= hist_browser__nr_entries(browser
);
414 /* Go to the start, we may be way after valid entries after a collapse */
415 ui_browser__reset_index(&browser
->b
);
418 static void ui_browser__warn_lost_events(struct ui_browser
*browser
)
420 ui_browser__warning(browser
, 4,
421 "Events are being lost, check IO/CPU overload!\n\n"
422 "You may want to run 'perf' using a RT scheduler policy:\n\n"
423 " perf top -r 80\n\n"
424 "Or reduce the sampling frequency.");
427 static int hist_browser__run(struct hist_browser
*browser
, const char *help
)
431 struct hist_browser_timer
*hbt
= browser
->hbt
;
432 int delay_secs
= hbt
? hbt
->refresh
: 0;
434 browser
->b
.entries
= &browser
->hists
->entries
;
435 browser
->b
.nr_entries
= hist_browser__nr_entries(browser
);
437 hists__browser_title(browser
->hists
, hbt
, title
, sizeof(title
));
439 if (ui_browser__show(&browser
->b
, title
, help
) < 0)
443 key
= ui_browser__run(&browser
->b
, delay_secs
);
448 hbt
->timer(hbt
->arg
);
450 if (hist_browser__has_filter(browser
))
451 hist_browser__update_nr_entries(browser
);
453 nr_entries
= hist_browser__nr_entries(browser
);
454 ui_browser__update_nr_entries(&browser
->b
, nr_entries
);
456 if (browser
->hists
->stats
.nr_lost_warned
!=
457 browser
->hists
->stats
.nr_events
[PERF_RECORD_LOST
]) {
458 browser
->hists
->stats
.nr_lost_warned
=
459 browser
->hists
->stats
.nr_events
[PERF_RECORD_LOST
];
460 ui_browser__warn_lost_events(&browser
->b
);
463 hists__browser_title(browser
->hists
,
464 hbt
, title
, sizeof(title
));
465 ui_browser__show_title(&browser
->b
, title
);
468 case 'D': { /* Debug */
470 struct hist_entry
*h
= rb_entry(browser
->b
.top
,
471 struct hist_entry
, rb_node
);
473 ui_helpline__fpush("%d: nr_ent=(%d,%d), rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
474 seq
++, browser
->b
.nr_entries
,
475 browser
->hists
->nr_entries
,
479 h
->row_offset
, h
->nr_rows
);
483 /* Collapse the whole world. */
484 hist_browser__set_folding(browser
, false);
487 /* Expand the whole world. */
488 hist_browser__set_folding(browser
, true);
491 browser
->show_headers
= !browser
->show_headers
;
492 hist_browser__update_rows(browser
);
495 if (hist_browser__toggle_fold(browser
))
503 ui_browser__hide(&browser
->b
);
507 struct callchain_print_arg
{
508 /* for hists browser */
510 bool is_current_entry
;
517 typedef void (*print_callchain_entry_fn
)(struct hist_browser
*browser
,
518 struct callchain_list
*chain
,
519 const char *str
, int offset
,
521 struct callchain_print_arg
*arg
);
523 static void hist_browser__show_callchain_entry(struct hist_browser
*browser
,
524 struct callchain_list
*chain
,
525 const char *str
, int offset
,
527 struct callchain_print_arg
*arg
)
530 char folded_sign
= callchain_list__folded(chain
);
531 bool show_annotated
= browser
->show_dso
&& chain
->ms
.sym
&& symbol__annotation(chain
->ms
.sym
)->src
;
533 color
= HE_COLORSET_NORMAL
;
534 width
= browser
->b
.width
- (offset
+ 2);
535 if (ui_browser__is_current_entry(&browser
->b
, row
)) {
536 browser
->selection
= &chain
->ms
;
537 color
= HE_COLORSET_SELECTED
;
538 arg
->is_current_entry
= true;
541 ui_browser__set_color(&browser
->b
, color
);
542 hist_browser__gotorc(browser
, row
, 0);
543 slsmg_write_nstring(" ", offset
);
544 slsmg_printf("%c", folded_sign
);
545 ui_browser__write_graph(&browser
->b
, show_annotated
? SLSMG_RARROW_CHAR
: ' ');
546 slsmg_write_nstring(str
, width
);
549 static void hist_browser__fprintf_callchain_entry(struct hist_browser
*b __maybe_unused
,
550 struct callchain_list
*chain
,
551 const char *str
, int offset
,
552 unsigned short row __maybe_unused
,
553 struct callchain_print_arg
*arg
)
555 char folded_sign
= callchain_list__folded(chain
);
557 arg
->printed
+= fprintf(arg
->fp
, "%*s%c %s\n", offset
, " ",
561 typedef bool (*check_output_full_fn
)(struct hist_browser
*browser
,
564 static bool hist_browser__check_output_full(struct hist_browser
*browser
,
567 return browser
->b
.rows
== row
;
570 static bool hist_browser__check_dump_full(struct hist_browser
*browser __maybe_unused
,
571 unsigned short row __maybe_unused
)
576 #define LEVEL_OFFSET_STEP 3
578 static int hist_browser__show_callchain(struct hist_browser
*browser
,
579 struct rb_root
*root
, int level
,
580 unsigned short row
, u64 total
,
581 print_callchain_entry_fn print
,
582 struct callchain_print_arg
*arg
,
583 check_output_full_fn is_output_full
)
585 struct rb_node
*node
;
586 int first_row
= row
, offset
= level
* LEVEL_OFFSET_STEP
;
590 node
= rb_first(root
);
591 need_percent
= node
&& rb_next(node
);
594 struct callchain_node
*child
= rb_entry(node
, struct callchain_node
, rb_node
);
595 struct rb_node
*next
= rb_next(node
);
596 u64 cumul
= callchain_cumul_hits(child
);
597 struct callchain_list
*chain
;
598 char folded_sign
= ' ';
600 int extra_offset
= 0;
602 list_for_each_entry(chain
, &child
->val
, list
) {
603 char bf
[1024], *alloc_str
;
605 bool was_first
= first
;
609 else if (need_percent
)
610 extra_offset
= LEVEL_OFFSET_STEP
;
612 folded_sign
= callchain_list__folded(chain
);
613 if (arg
->row_offset
!= 0) {
619 str
= callchain_list__sym_name(chain
, bf
, sizeof(bf
),
622 if (was_first
&& need_percent
) {
623 double percent
= cumul
* 100.0 / total
;
625 if (asprintf(&alloc_str
, "%2.2f%% %s", percent
, str
) < 0)
626 str
= "Not enough memory!";
631 print(browser
, chain
, str
, offset
+ extra_offset
, row
, arg
);
635 if (is_output_full(browser
, ++row
))
638 if (folded_sign
== '+')
642 if (folded_sign
== '-') {
643 const int new_level
= level
+ (extra_offset
? 2 : 1);
645 if (callchain_param
.mode
== CHAIN_GRAPH_REL
)
646 new_total
= child
->children_hit
;
650 row
+= hist_browser__show_callchain(browser
, &child
->rb_root
,
651 new_level
, row
, new_total
,
652 print
, arg
, is_output_full
);
654 if (is_output_full(browser
, row
))
659 return row
- first_row
;
663 struct ui_browser
*b
;
668 static int __hpp__slsmg_color_printf(struct perf_hpp
*hpp
, const char *fmt
, ...)
670 struct hpp_arg
*arg
= hpp
->ptr
;
676 len
= va_arg(args
, int);
677 percent
= va_arg(args
, double);
680 ui_browser__set_percent_color(arg
->b
, percent
, arg
->current_entry
);
682 ret
= scnprintf(hpp
->buf
, hpp
->size
, fmt
, len
, percent
);
683 slsmg_printf("%s", hpp
->buf
);
685 advance_hpp(hpp
, ret
);
689 #define __HPP_COLOR_PERCENT_FN(_type, _field) \
690 static u64 __hpp_get_##_field(struct hist_entry *he) \
692 return he->stat._field; \
696 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
697 struct perf_hpp *hpp, \
698 struct hist_entry *he) \
700 return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%", \
701 __hpp__slsmg_color_printf, true); \
704 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
705 static u64 __hpp_get_acc_##_field(struct hist_entry *he) \
707 return he->stat_acc->_field; \
711 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
712 struct perf_hpp *hpp, \
713 struct hist_entry *he) \
715 if (!symbol_conf.cumulate_callchain) { \
716 int len = fmt->user_len ?: fmt->len; \
717 int ret = scnprintf(hpp->buf, hpp->size, \
718 "%*s", len, "N/A"); \
719 slsmg_printf("%s", hpp->buf); \
723 return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field, \
724 " %*.2f%%", __hpp__slsmg_color_printf, true); \
727 __HPP_COLOR_PERCENT_FN(overhead
, period
)
728 __HPP_COLOR_PERCENT_FN(overhead_sys
, period_sys
)
729 __HPP_COLOR_PERCENT_FN(overhead_us
, period_us
)
730 __HPP_COLOR_PERCENT_FN(overhead_guest_sys
, period_guest_sys
)
731 __HPP_COLOR_PERCENT_FN(overhead_guest_us
, period_guest_us
)
732 __HPP_COLOR_ACC_PERCENT_FN(overhead_acc
, period
)
734 #undef __HPP_COLOR_PERCENT_FN
735 #undef __HPP_COLOR_ACC_PERCENT_FN
737 void hist_browser__init_hpp(void)
739 perf_hpp__format
[PERF_HPP__OVERHEAD
].color
=
740 hist_browser__hpp_color_overhead
;
741 perf_hpp__format
[PERF_HPP__OVERHEAD_SYS
].color
=
742 hist_browser__hpp_color_overhead_sys
;
743 perf_hpp__format
[PERF_HPP__OVERHEAD_US
].color
=
744 hist_browser__hpp_color_overhead_us
;
745 perf_hpp__format
[PERF_HPP__OVERHEAD_GUEST_SYS
].color
=
746 hist_browser__hpp_color_overhead_guest_sys
;
747 perf_hpp__format
[PERF_HPP__OVERHEAD_GUEST_US
].color
=
748 hist_browser__hpp_color_overhead_guest_us
;
749 perf_hpp__format
[PERF_HPP__OVERHEAD_ACC
].color
=
750 hist_browser__hpp_color_overhead_acc
;
753 static int hist_browser__show_entry(struct hist_browser
*browser
,
754 struct hist_entry
*entry
,
759 int width
= browser
->b
.width
;
760 char folded_sign
= ' ';
761 bool current_entry
= ui_browser__is_current_entry(&browser
->b
, row
);
762 off_t row_offset
= entry
->row_offset
;
764 struct perf_hpp_fmt
*fmt
;
767 browser
->he_selection
= entry
;
768 browser
->selection
= &entry
->ms
;
771 if (symbol_conf
.use_callchain
) {
772 hist_entry__init_have_children(entry
);
773 folded_sign
= hist_entry__folded(entry
);
776 if (row_offset
== 0) {
777 struct hpp_arg arg
= {
779 .folded_sign
= folded_sign
,
780 .current_entry
= current_entry
,
782 struct perf_hpp hpp
= {
788 hist_browser__gotorc(browser
, row
, 0);
790 perf_hpp__for_each_format(fmt
) {
791 if (perf_hpp__should_skip(fmt
))
794 if (current_entry
&& browser
->b
.navkeypressed
) {
795 ui_browser__set_color(&browser
->b
,
796 HE_COLORSET_SELECTED
);
798 ui_browser__set_color(&browser
->b
,
803 if (symbol_conf
.use_callchain
) {
804 slsmg_printf("%c ", folded_sign
);
814 width
-= fmt
->color(fmt
, &hpp
, entry
);
816 width
-= fmt
->entry(fmt
, &hpp
, entry
);
817 slsmg_printf("%s", s
);
821 /* The scroll bar isn't being used */
822 if (!browser
->b
.navkeypressed
)
825 slsmg_write_nstring("", width
);
832 if (folded_sign
== '-' && row
!= browser
->b
.rows
) {
833 u64 total
= hists__total_period(entry
->hists
);
834 struct callchain_print_arg arg
= {
835 .row_offset
= row_offset
,
836 .is_current_entry
= current_entry
,
839 if (callchain_param
.mode
== CHAIN_GRAPH_REL
) {
840 if (symbol_conf
.cumulate_callchain
)
841 total
= entry
->stat_acc
->period
;
843 total
= entry
->stat
.period
;
846 printed
+= hist_browser__show_callchain(browser
,
847 &entry
->sorted_chain
, 1, row
, total
,
848 hist_browser__show_callchain_entry
, &arg
,
849 hist_browser__check_output_full
);
851 if (arg
.is_current_entry
)
852 browser
->he_selection
= entry
;
858 static int advance_hpp_check(struct perf_hpp
*hpp
, int inc
)
860 advance_hpp(hpp
, inc
);
861 return hpp
->size
<= 0;
864 static int hists__scnprintf_headers(char *buf
, size_t size
, struct hists
*hists
)
866 struct perf_hpp dummy_hpp
= {
870 struct perf_hpp_fmt
*fmt
;
873 if (symbol_conf
.use_callchain
) {
874 ret
= scnprintf(buf
, size
, " ");
875 if (advance_hpp_check(&dummy_hpp
, ret
))
879 perf_hpp__for_each_format(fmt
) {
880 if (perf_hpp__should_skip(fmt
))
883 ret
= fmt
->header(fmt
, &dummy_hpp
, hists_to_evsel(hists
));
884 if (advance_hpp_check(&dummy_hpp
, ret
))
887 ret
= scnprintf(dummy_hpp
.buf
, dummy_hpp
.size
, " ");
888 if (advance_hpp_check(&dummy_hpp
, ret
))
895 static void hist_browser__show_headers(struct hist_browser
*browser
)
899 hists__scnprintf_headers(headers
, sizeof(headers
), browser
->hists
);
900 ui_browser__gotorc(&browser
->b
, 0, 0);
901 ui_browser__set_color(&browser
->b
, HE_COLORSET_ROOT
);
902 slsmg_write_nstring(headers
, browser
->b
.width
+ 1);
905 static void ui_browser__hists_init_top(struct ui_browser
*browser
)
907 if (browser
->top
== NULL
) {
908 struct hist_browser
*hb
;
910 hb
= container_of(browser
, struct hist_browser
, b
);
911 browser
->top
= rb_first(&hb
->hists
->entries
);
915 static unsigned int hist_browser__refresh(struct ui_browser
*browser
)
918 u16 header_offset
= 0;
920 struct hist_browser
*hb
= container_of(browser
, struct hist_browser
, b
);
922 if (hb
->show_headers
) {
923 hist_browser__show_headers(hb
);
927 ui_browser__hists_init_top(browser
);
929 for (nd
= browser
->top
; nd
; nd
= rb_next(nd
)) {
930 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
936 percent
= hist_entry__get_percent_limit(h
);
937 if (percent
< hb
->min_pcnt
)
940 row
+= hist_browser__show_entry(hb
, h
, row
);
941 if (row
== browser
->rows
)
945 return row
+ header_offset
;
948 static struct rb_node
*hists__filter_entries(struct rb_node
*nd
,
952 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
953 float percent
= hist_entry__get_percent_limit(h
);
955 if (!h
->filtered
&& percent
>= min_pcnt
)
964 static struct rb_node
*hists__filter_prev_entries(struct rb_node
*nd
,
968 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
969 float percent
= hist_entry__get_percent_limit(h
);
971 if (!h
->filtered
&& percent
>= min_pcnt
)
980 static void ui_browser__hists_seek(struct ui_browser
*browser
,
981 off_t offset
, int whence
)
983 struct hist_entry
*h
;
986 struct hist_browser
*hb
;
988 hb
= container_of(browser
, struct hist_browser
, b
);
990 if (browser
->nr_entries
== 0)
993 ui_browser__hists_init_top(browser
);
997 nd
= hists__filter_entries(rb_first(browser
->entries
),
1004 nd
= hists__filter_prev_entries(rb_last(browser
->entries
),
1013 * Moves not relative to the first visible entry invalidates its
1016 h
= rb_entry(browser
->top
, struct hist_entry
, rb_node
);
1020 * Here we have to check if nd is expanded (+), if it is we can't go
1021 * the next top level hist_entry, instead we must compute an offset of
1022 * what _not_ to show and not change the first visible entry.
1024 * This offset increments when we are going from top to bottom and
1025 * decreases when we're going from bottom to top.
1027 * As we don't have backpointers to the top level in the callchains
1028 * structure, we need to always print the whole hist_entry callchain,
1029 * skipping the first ones that are before the first visible entry
1030 * and stop when we printed enough lines to fill the screen.
1035 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1037 u16 remaining
= h
->nr_rows
- h
->row_offset
;
1038 if (offset
> remaining
) {
1039 offset
-= remaining
;
1042 h
->row_offset
+= offset
;
1048 nd
= hists__filter_entries(rb_next(nd
), hb
->min_pcnt
);
1053 } while (offset
!= 0);
1054 } else if (offset
< 0) {
1056 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1059 if (-offset
> h
->row_offset
) {
1060 offset
+= h
->row_offset
;
1063 h
->row_offset
+= offset
;
1069 if (-offset
> h
->nr_rows
) {
1070 offset
+= h
->nr_rows
;
1073 h
->row_offset
= h
->nr_rows
+ offset
;
1081 nd
= hists__filter_prev_entries(rb_prev(nd
),
1089 * Last unfiltered hist_entry, check if it is
1090 * unfolded, if it is then we should have
1091 * row_offset at its last entry.
1093 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1095 h
->row_offset
= h
->nr_rows
;
1102 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1107 static int hist_browser__fprintf_callchain(struct hist_browser
*browser
,
1108 struct hist_entry
*he
, FILE *fp
)
1110 u64 total
= hists__total_period(he
->hists
);
1111 struct callchain_print_arg arg
= {
1115 if (symbol_conf
.cumulate_callchain
)
1116 total
= he
->stat_acc
->period
;
1118 hist_browser__show_callchain(browser
, &he
->sorted_chain
, 1, 0, total
,
1119 hist_browser__fprintf_callchain_entry
, &arg
,
1120 hist_browser__check_dump_full
);
1124 static int hist_browser__fprintf_entry(struct hist_browser
*browser
,
1125 struct hist_entry
*he
, FILE *fp
)
1129 char folded_sign
= ' ';
1130 struct perf_hpp hpp
= {
1134 struct perf_hpp_fmt
*fmt
;
1138 if (symbol_conf
.use_callchain
)
1139 folded_sign
= hist_entry__folded(he
);
1141 if (symbol_conf
.use_callchain
)
1142 printed
+= fprintf(fp
, "%c ", folded_sign
);
1144 perf_hpp__for_each_format(fmt
) {
1145 if (perf_hpp__should_skip(fmt
))
1149 ret
= scnprintf(hpp
.buf
, hpp
.size
, " ");
1150 advance_hpp(&hpp
, ret
);
1154 ret
= fmt
->entry(fmt
, &hpp
, he
);
1155 advance_hpp(&hpp
, ret
);
1157 printed
+= fprintf(fp
, "%s\n", rtrim(s
));
1159 if (folded_sign
== '-')
1160 printed
+= hist_browser__fprintf_callchain(browser
, he
, fp
);
1165 static int hist_browser__fprintf(struct hist_browser
*browser
, FILE *fp
)
1167 struct rb_node
*nd
= hists__filter_entries(rb_first(browser
->b
.entries
),
1172 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1174 printed
+= hist_browser__fprintf_entry(browser
, h
, fp
);
1175 nd
= hists__filter_entries(rb_next(nd
), browser
->min_pcnt
);
1181 static int hist_browser__dump(struct hist_browser
*browser
)
1187 scnprintf(filename
, sizeof(filename
), "perf.hist.%d", browser
->print_seq
);
1188 if (access(filename
, F_OK
))
1191 * XXX: Just an arbitrary lazy upper limit
1193 if (++browser
->print_seq
== 8192) {
1194 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1199 fp
= fopen(filename
, "w");
1202 const char *err
= strerror_r(errno
, bf
, sizeof(bf
));
1203 ui_helpline__fpush("Couldn't write to %s: %s", filename
, err
);
1207 ++browser
->print_seq
;
1208 hist_browser__fprintf(browser
, fp
);
1210 ui_helpline__fpush("%s written!", filename
);
1215 static struct hist_browser
*hist_browser__new(struct hists
*hists
,
1216 struct hist_browser_timer
*hbt
,
1217 struct perf_session_env
*env
)
1219 struct hist_browser
*browser
= zalloc(sizeof(*browser
));
1222 browser
->hists
= hists
;
1223 browser
->b
.refresh
= hist_browser__refresh
;
1224 browser
->b
.refresh_dimensions
= hist_browser__refresh_dimensions
;
1225 browser
->b
.seek
= ui_browser__hists_seek
;
1226 browser
->b
.use_navkeypressed
= true;
1227 browser
->show_headers
= symbol_conf
.show_hist_headers
;
1235 static void hist_browser__delete(struct hist_browser
*browser
)
1240 static struct hist_entry
*hist_browser__selected_entry(struct hist_browser
*browser
)
1242 return browser
->he_selection
;
1245 static struct thread
*hist_browser__selected_thread(struct hist_browser
*browser
)
1247 return browser
->he_selection
->thread
;
1250 /* Check whether the browser is for 'top' or 'report' */
1251 static inline bool is_report_browser(void *timer
)
1253 return timer
== NULL
;
1256 static int hists__browser_title(struct hists
*hists
,
1257 struct hist_browser_timer
*hbt
,
1258 char *bf
, size_t size
)
1262 const struct dso
*dso
= hists
->dso_filter
;
1263 const struct thread
*thread
= hists
->thread_filter
;
1264 unsigned long nr_samples
= hists
->stats
.nr_events
[PERF_RECORD_SAMPLE
];
1265 u64 nr_events
= hists
->stats
.total_period
;
1266 struct perf_evsel
*evsel
= hists_to_evsel(hists
);
1267 const char *ev_name
= perf_evsel__name(evsel
);
1269 size_t buflen
= sizeof(buf
);
1271 if (symbol_conf
.filter_relative
) {
1272 nr_samples
= hists
->stats
.nr_non_filtered_samples
;
1273 nr_events
= hists
->stats
.total_non_filtered_period
;
1276 if (perf_evsel__is_group_event(evsel
)) {
1277 struct perf_evsel
*pos
;
1279 perf_evsel__group_desc(evsel
, buf
, buflen
);
1282 for_each_group_member(pos
, evsel
) {
1283 struct hists
*pos_hists
= evsel__hists(pos
);
1285 if (symbol_conf
.filter_relative
) {
1286 nr_samples
+= pos_hists
->stats
.nr_non_filtered_samples
;
1287 nr_events
+= pos_hists
->stats
.total_non_filtered_period
;
1289 nr_samples
+= pos_hists
->stats
.nr_events
[PERF_RECORD_SAMPLE
];
1290 nr_events
+= pos_hists
->stats
.total_period
;
1295 nr_samples
= convert_unit(nr_samples
, &unit
);
1296 printed
= scnprintf(bf
, size
,
1297 "Samples: %lu%c of event '%s', Event count (approx.): %" PRIu64
,
1298 nr_samples
, unit
, ev_name
, nr_events
);
1301 if (hists
->uid_filter_str
)
1302 printed
+= snprintf(bf
+ printed
, size
- printed
,
1303 ", UID: %s", hists
->uid_filter_str
);
1305 printed
+= scnprintf(bf
+ printed
, size
- printed
,
1307 (thread
->comm_set
? thread__comm_str(thread
) : ""),
1310 printed
+= scnprintf(bf
+ printed
, size
- printed
,
1311 ", DSO: %s", dso
->short_name
);
1312 if (!is_report_browser(hbt
)) {
1313 struct perf_top
*top
= hbt
->arg
;
1316 printed
+= scnprintf(bf
+ printed
, size
- printed
, " [z]");
1322 static inline void free_popup_options(char **options
, int n
)
1326 for (i
= 0; i
< n
; ++i
)
1331 * Only runtime switching of perf data file will make "input_name" point
1332 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1333 * whether we need to call free() for current "input_name" during the switch.
1335 static bool is_input_name_malloced
= false;
1337 static int switch_data_file(void)
1339 char *pwd
, *options
[32], *abs_path
[32], *tmp
;
1341 int nr_options
= 0, choice
= -1, ret
= -1;
1342 struct dirent
*dent
;
1344 pwd
= getenv("PWD");
1348 pwd_dir
= opendir(pwd
);
1352 memset(options
, 0, sizeof(options
));
1353 memset(options
, 0, sizeof(abs_path
));
1355 while ((dent
= readdir(pwd_dir
))) {
1356 char path
[PATH_MAX
];
1358 char *name
= dent
->d_name
;
1361 if (!(dent
->d_type
== DT_REG
))
1364 snprintf(path
, sizeof(path
), "%s/%s", pwd
, name
);
1366 file
= fopen(path
, "r");
1370 if (fread(&magic
, 1, 8, file
) < 8)
1371 goto close_file_and_continue
;
1373 if (is_perf_magic(magic
)) {
1374 options
[nr_options
] = strdup(name
);
1375 if (!options
[nr_options
])
1376 goto close_file_and_continue
;
1378 abs_path
[nr_options
] = strdup(path
);
1379 if (!abs_path
[nr_options
]) {
1380 zfree(&options
[nr_options
]);
1381 ui__warning("Can't search all data files due to memory shortage.\n");
1389 close_file_and_continue
:
1391 if (nr_options
>= 32) {
1392 ui__warning("Too many perf data files in PWD!\n"
1393 "Only the first 32 files will be listed.\n");
1400 choice
= ui__popup_menu(nr_options
, options
);
1401 if (choice
< nr_options
&& choice
>= 0) {
1402 tmp
= strdup(abs_path
[choice
]);
1404 if (is_input_name_malloced
)
1405 free((void *)input_name
);
1407 is_input_name_malloced
= true;
1410 ui__warning("Data switch failed due to memory shortage!\n");
1414 free_popup_options(options
, nr_options
);
1415 free_popup_options(abs_path
, nr_options
);
1419 struct popup_action
{
1420 struct thread
*thread
;
1422 struct map_symbol ms
;
1424 int (*fn
)(struct hist_browser
*browser
, struct popup_action
*act
);
1428 do_annotate(struct hist_browser
*browser
, struct popup_action
*act
)
1430 struct perf_evsel
*evsel
;
1431 struct annotation
*notes
;
1432 struct hist_entry
*he
;
1435 if (!objdump_path
&& perf_session_env__lookup_objdump(browser
->env
))
1438 notes
= symbol__annotation(act
->ms
.sym
);
1442 evsel
= hists_to_evsel(browser
->hists
);
1443 err
= map_symbol__tui_annotate(&act
->ms
, evsel
, browser
->hbt
);
1444 he
= hist_browser__selected_entry(browser
);
1446 * offer option to annotate the other branch source or target
1447 * (if they exists) when returning from annotate
1449 if ((err
== 'q' || err
== CTRL('c')) && he
->branch_info
)
1452 ui_browser__update_nr_entries(&browser
->b
, browser
->hists
->nr_entries
);
1454 ui_browser__handle_resize(&browser
->b
);
1459 add_annotate_opt(struct hist_browser
*browser __maybe_unused
,
1460 struct popup_action
*act
, char **optstr
,
1461 struct map
*map
, struct symbol
*sym
)
1463 if (sym
== NULL
|| map
->dso
->annotate_warned
)
1466 if (asprintf(optstr
, "Annotate %s", sym
->name
) < 0)
1471 act
->fn
= do_annotate
;
1476 do_zoom_thread(struct hist_browser
*browser
, struct popup_action
*act
)
1478 struct thread
*thread
= act
->thread
;
1480 if (browser
->hists
->thread_filter
) {
1481 pstack__remove(browser
->pstack
, &browser
->hists
->thread_filter
);
1482 perf_hpp__set_elide(HISTC_THREAD
, false);
1483 thread__zput(browser
->hists
->thread_filter
);
1486 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1487 thread
->comm_set
? thread__comm_str(thread
) : "",
1489 browser
->hists
->thread_filter
= thread__get(thread
);
1490 perf_hpp__set_elide(HISTC_THREAD
, false);
1491 pstack__push(browser
->pstack
, &browser
->hists
->thread_filter
);
1494 hists__filter_by_thread(browser
->hists
);
1495 hist_browser__reset(browser
);
1500 add_thread_opt(struct hist_browser
*browser
, struct popup_action
*act
,
1501 char **optstr
, struct thread
*thread
)
1506 if (asprintf(optstr
, "Zoom %s %s(%d) thread",
1507 browser
->hists
->thread_filter
? "out of" : "into",
1508 thread
->comm_set
? thread__comm_str(thread
) : "",
1512 act
->thread
= thread
;
1513 act
->fn
= do_zoom_thread
;
1518 do_zoom_dso(struct hist_browser
*browser
, struct popup_action
*act
)
1520 struct dso
*dso
= act
->dso
;
1522 if (browser
->hists
->dso_filter
) {
1523 pstack__remove(browser
->pstack
, &browser
->hists
->dso_filter
);
1524 perf_hpp__set_elide(HISTC_DSO
, false);
1525 browser
->hists
->dso_filter
= NULL
;
1530 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1531 dso
->kernel
? "the Kernel" : dso
->short_name
);
1532 browser
->hists
->dso_filter
= dso
;
1533 perf_hpp__set_elide(HISTC_DSO
, true);
1534 pstack__push(browser
->pstack
, &browser
->hists
->dso_filter
);
1537 hists__filter_by_dso(browser
->hists
);
1538 hist_browser__reset(browser
);
1543 add_dso_opt(struct hist_browser
*browser
, struct popup_action
*act
,
1544 char **optstr
, struct dso
*dso
)
1549 if (asprintf(optstr
, "Zoom %s %s DSO",
1550 browser
->hists
->dso_filter
? "out of" : "into",
1551 dso
->kernel
? "the Kernel" : dso
->short_name
) < 0)
1555 act
->fn
= do_zoom_dso
;
1560 do_browse_map(struct hist_browser
*browser __maybe_unused
,
1561 struct popup_action
*act
)
1563 map__browse(act
->ms
.map
);
1568 add_map_opt(struct hist_browser
*browser __maybe_unused
,
1569 struct popup_action
*act
, char **optstr
, struct map
*map
)
1574 if (asprintf(optstr
, "Browse map details") < 0)
1578 act
->fn
= do_browse_map
;
1583 do_run_script(struct hist_browser
*browser __maybe_unused
,
1584 struct popup_action
*act
)
1586 char script_opt
[64];
1587 memset(script_opt
, 0, sizeof(script_opt
));
1590 scnprintf(script_opt
, sizeof(script_opt
), " -c %s ",
1591 thread__comm_str(act
->thread
));
1592 } else if (act
->ms
.sym
) {
1593 scnprintf(script_opt
, sizeof(script_opt
), " -S %s ",
1597 script_browse(script_opt
);
1602 add_script_opt(struct hist_browser
*browser __maybe_unused
,
1603 struct popup_action
*act
, char **optstr
,
1604 struct thread
*thread
, struct symbol
*sym
)
1607 if (asprintf(optstr
, "Run scripts for samples of thread [%s]",
1608 thread__comm_str(thread
)) < 0)
1611 if (asprintf(optstr
, "Run scripts for samples of symbol [%s]",
1615 if (asprintf(optstr
, "Run scripts for all samples") < 0)
1619 act
->thread
= thread
;
1621 act
->fn
= do_run_script
;
1626 do_switch_data(struct hist_browser
*browser __maybe_unused
,
1627 struct popup_action
*act __maybe_unused
)
1629 if (switch_data_file()) {
1630 ui__warning("Won't switch the data files due to\n"
1631 "no valid data file get selected!\n");
1635 return K_SWITCH_INPUT_DATA
;
1639 add_switch_opt(struct hist_browser
*browser
,
1640 struct popup_action
*act
, char **optstr
)
1642 if (!is_report_browser(browser
->hbt
))
1645 if (asprintf(optstr
, "Switch to another data file in PWD") < 0)
1648 act
->fn
= do_switch_data
;
1653 do_exit_browser(struct hist_browser
*browser __maybe_unused
,
1654 struct popup_action
*act __maybe_unused
)
1660 add_exit_opt(struct hist_browser
*browser __maybe_unused
,
1661 struct popup_action
*act
, char **optstr
)
1663 if (asprintf(optstr
, "Exit") < 0)
1666 act
->fn
= do_exit_browser
;
1670 static void hist_browser__update_nr_entries(struct hist_browser
*hb
)
1673 struct rb_node
*nd
= rb_first(&hb
->hists
->entries
);
1675 if (hb
->min_pcnt
== 0) {
1676 hb
->nr_non_filtered_entries
= hb
->hists
->nr_non_filtered_entries
;
1680 while ((nd
= hists__filter_entries(nd
, hb
->min_pcnt
)) != NULL
) {
1685 hb
->nr_non_filtered_entries
= nr_entries
;
1688 static int perf_evsel__hists_browse(struct perf_evsel
*evsel
, int nr_events
,
1689 const char *helpline
,
1691 struct hist_browser_timer
*hbt
,
1693 struct perf_session_env
*env
)
1695 struct hists
*hists
= evsel__hists(evsel
);
1696 struct hist_browser
*browser
= hist_browser__new(hists
, hbt
, env
);
1697 struct branch_info
*bi
;
1698 #define MAX_OPTIONS 16
1699 char *options
[MAX_OPTIONS
];
1700 struct popup_action actions
[MAX_OPTIONS
];
1704 int delay_secs
= hbt
? hbt
->refresh
: 0;
1705 struct perf_hpp_fmt
*fmt
;
1707 #define HIST_BROWSER_HELP_COMMON \
1708 "h/?/F1 Show this window\n" \
1710 "PGDN/SPACE Navigate\n" \
1711 "q/ESC/CTRL+C Exit browser\n\n" \
1712 "For multiple event sessions:\n\n" \
1713 "TAB/UNTAB Switch events\n\n" \
1714 "For symbolic views (--sort has sym):\n\n" \
1715 "-> Zoom into DSO/Threads & Annotate current symbol\n" \
1717 "a Annotate current symbol\n" \
1718 "C Collapse all callchains\n" \
1719 "d Zoom into current DSO\n" \
1720 "E Expand all callchains\n" \
1721 "F Toggle percentage of filtered entries\n" \
1722 "H Display column headers\n" \
1724 /* help messages are sorted by lexical order of the hotkey */
1725 const char report_help
[] = HIST_BROWSER_HELP_COMMON
1726 "i Show header information\n"
1727 "P Print histograms to perf.hist.N\n"
1728 "r Run available scripts\n"
1729 "s Switch to another data file in PWD\n"
1730 "t Zoom into current Thread\n"
1731 "V Verbose (DSO names in callchains, etc)\n"
1732 "/ Filter symbol by name";
1733 const char top_help
[] = HIST_BROWSER_HELP_COMMON
1734 "P Print histograms to perf.hist.N\n"
1735 "t Zoom into current Thread\n"
1736 "V Verbose (DSO names in callchains, etc)\n"
1737 "z Toggle zeroing of samples\n"
1738 "f Enable/Disable events\n"
1739 "/ Filter symbol by name";
1741 if (browser
== NULL
)
1744 /* reset abort key so that it can get Ctrl-C as a key */
1746 SLang_init_tty(0, 0, 0);
1749 browser
->min_pcnt
= min_pcnt
;
1750 hist_browser__update_nr_entries(browser
);
1753 browser
->pstack
= pstack__new(2);
1754 if (browser
->pstack
== NULL
)
1757 ui_helpline__push(helpline
);
1759 memset(options
, 0, sizeof(options
));
1760 memset(actions
, 0, sizeof(actions
));
1762 perf_hpp__for_each_format(fmt
)
1763 perf_hpp__reset_width(fmt
, hists
);
1765 if (symbol_conf
.col_width_list_str
)
1766 perf_hpp__set_user_width(symbol_conf
.col_width_list_str
);
1769 struct thread
*thread
= NULL
;
1770 struct dso
*dso
= NULL
;
1775 key
= hist_browser__run(browser
, helpline
);
1777 if (browser
->he_selection
!= NULL
) {
1778 thread
= hist_browser__selected_thread(browser
);
1779 dso
= browser
->selection
->map
? browser
->selection
->map
->dso
: NULL
;
1787 * Exit the browser, let hists__browser_tree
1788 * go to the next or previous
1790 goto out_free_stack
;
1792 if (!sort__has_sym
) {
1793 ui_browser__warning(&browser
->b
, delay_secs
* 2,
1794 "Annotation is only available for symbolic views, "
1795 "include \"sym*\" in --sort to use it.");
1799 if (browser
->selection
== NULL
||
1800 browser
->selection
->sym
== NULL
||
1801 browser
->selection
->map
->dso
->annotate_warned
)
1804 actions
->ms
.map
= browser
->selection
->map
;
1805 actions
->ms
.sym
= browser
->selection
->sym
;
1806 do_annotate(browser
, actions
);
1809 hist_browser__dump(browser
);
1813 do_zoom_dso(browser
, actions
);
1816 browser
->show_dso
= !browser
->show_dso
;
1819 actions
->thread
= thread
;
1820 do_zoom_thread(browser
, actions
);
1823 if (ui_browser__input_window("Symbol to show",
1824 "Please enter the name of symbol you want to see",
1825 buf
, "ENTER: OK, ESC: Cancel",
1826 delay_secs
* 2) == K_ENTER
) {
1827 hists
->symbol_filter_str
= *buf
? buf
: NULL
;
1828 hists__filter_by_symbol(hists
);
1829 hist_browser__reset(browser
);
1833 if (is_report_browser(hbt
)) {
1834 actions
->thread
= NULL
;
1835 actions
->ms
.sym
= NULL
;
1836 do_run_script(browser
, actions
);
1840 if (is_report_browser(hbt
)) {
1841 key
= do_switch_data(browser
, actions
);
1842 if (key
== K_SWITCH_INPUT_DATA
)
1843 goto out_free_stack
;
1847 /* env->arch is NULL for live-mode (i.e. perf top) */
1849 tui__header_window(env
);
1852 symbol_conf
.filter_relative
^= 1;
1855 if (!is_report_browser(hbt
)) {
1856 struct perf_top
*top
= hbt
->arg
;
1858 top
->zero
= !top
->zero
;
1864 ui_browser__help_window(&browser
->b
,
1865 is_report_browser(hbt
) ? report_help
: top_help
);
1874 if (pstack__empty(browser
->pstack
)) {
1876 * Go back to the perf_evsel_menu__run or other user
1879 goto out_free_stack
;
1882 top
= pstack__peek(browser
->pstack
);
1883 if (top
== &browser
->hists
->dso_filter
) {
1885 * No need to set actions->dso here since
1886 * it's just to remove the current filter.
1887 * Ditto for thread below.
1889 do_zoom_dso(browser
, actions
);
1891 if (top
== &browser
->hists
->thread_filter
)
1892 do_zoom_thread(browser
, actions
);
1897 !ui_browser__dialog_yesno(&browser
->b
,
1898 "Do you really want to exit?"))
1903 goto out_free_stack
;
1905 if (!is_report_browser(hbt
)) {
1906 struct perf_top
*top
= hbt
->arg
;
1908 perf_evlist__toggle_enable(top
->evlist
);
1910 * No need to refresh, resort/decay histogram
1911 * entries if we are not collecting samples:
1913 if (top
->evlist
->enabled
) {
1914 helpline
= "Press 'f' to disable the events or 'h' to see other hotkeys";
1915 hbt
->refresh
= delay_secs
;
1917 helpline
= "Press 'f' again to re-enable the events";
1924 helpline
= "Press '?' for help on key bindings";
1929 goto add_exit_option
;
1931 if (browser
->selection
== NULL
)
1932 goto skip_annotation
;
1934 if (sort__mode
== SORT_MODE__BRANCH
) {
1935 bi
= browser
->he_selection
->branch_info
;
1938 goto skip_annotation
;
1940 nr_options
+= add_annotate_opt(browser
,
1941 &actions
[nr_options
],
1942 &options
[nr_options
],
1945 if (bi
->to
.sym
!= bi
->from
.sym
)
1946 nr_options
+= add_annotate_opt(browser
,
1947 &actions
[nr_options
],
1948 &options
[nr_options
],
1952 nr_options
+= add_annotate_opt(browser
,
1953 &actions
[nr_options
],
1954 &options
[nr_options
],
1955 browser
->selection
->map
,
1956 browser
->selection
->sym
);
1959 nr_options
+= add_thread_opt(browser
, &actions
[nr_options
],
1960 &options
[nr_options
], thread
);
1961 nr_options
+= add_dso_opt(browser
, &actions
[nr_options
],
1962 &options
[nr_options
], dso
);
1963 nr_options
+= add_map_opt(browser
, &actions
[nr_options
],
1964 &options
[nr_options
],
1965 browser
->selection
->map
);
1967 /* perf script support */
1968 if (browser
->he_selection
) {
1969 nr_options
+= add_script_opt(browser
,
1970 &actions
[nr_options
],
1971 &options
[nr_options
],
1973 nr_options
+= add_script_opt(browser
,
1974 &actions
[nr_options
],
1975 &options
[nr_options
],
1976 NULL
, browser
->selection
->sym
);
1978 nr_options
+= add_script_opt(browser
, &actions
[nr_options
],
1979 &options
[nr_options
], NULL
, NULL
);
1980 nr_options
+= add_switch_opt(browser
, &actions
[nr_options
],
1981 &options
[nr_options
]);
1983 nr_options
+= add_exit_opt(browser
, &actions
[nr_options
],
1984 &options
[nr_options
]);
1987 struct popup_action
*act
;
1989 choice
= ui__popup_menu(nr_options
, options
);
1990 if (choice
== -1 || choice
>= nr_options
)
1993 act
= &actions
[choice
];
1994 key
= act
->fn(browser
, act
);
1997 if (key
== K_SWITCH_INPUT_DATA
)
2001 pstack__delete(browser
->pstack
);
2003 hist_browser__delete(browser
);
2004 free_popup_options(options
, MAX_OPTIONS
);
2008 struct perf_evsel_menu
{
2009 struct ui_browser b
;
2010 struct perf_evsel
*selection
;
2011 bool lost_events
, lost_events_warned
;
2013 struct perf_session_env
*env
;
2016 static void perf_evsel_menu__write(struct ui_browser
*browser
,
2017 void *entry
, int row
)
2019 struct perf_evsel_menu
*menu
= container_of(browser
,
2020 struct perf_evsel_menu
, b
);
2021 struct perf_evsel
*evsel
= list_entry(entry
, struct perf_evsel
, node
);
2022 struct hists
*hists
= evsel__hists(evsel
);
2023 bool current_entry
= ui_browser__is_current_entry(browser
, row
);
2024 unsigned long nr_events
= hists
->stats
.nr_events
[PERF_RECORD_SAMPLE
];
2025 const char *ev_name
= perf_evsel__name(evsel
);
2027 const char *warn
= " ";
2030 ui_browser__set_color(browser
, current_entry
? HE_COLORSET_SELECTED
:
2031 HE_COLORSET_NORMAL
);
2033 if (perf_evsel__is_group_event(evsel
)) {
2034 struct perf_evsel
*pos
;
2036 ev_name
= perf_evsel__group_name(evsel
);
2038 for_each_group_member(pos
, evsel
) {
2039 struct hists
*pos_hists
= evsel__hists(pos
);
2040 nr_events
+= pos_hists
->stats
.nr_events
[PERF_RECORD_SAMPLE
];
2044 nr_events
= convert_unit(nr_events
, &unit
);
2045 printed
= scnprintf(bf
, sizeof(bf
), "%lu%c%s%s", nr_events
,
2046 unit
, unit
== ' ' ? "" : " ", ev_name
);
2047 slsmg_printf("%s", bf
);
2049 nr_events
= hists
->stats
.nr_events
[PERF_RECORD_LOST
];
2050 if (nr_events
!= 0) {
2051 menu
->lost_events
= true;
2053 ui_browser__set_color(browser
, HE_COLORSET_TOP
);
2054 nr_events
= convert_unit(nr_events
, &unit
);
2055 printed
+= scnprintf(bf
, sizeof(bf
), ": %ld%c%schunks LOST!",
2056 nr_events
, unit
, unit
== ' ' ? "" : " ");
2060 slsmg_write_nstring(warn
, browser
->width
- printed
);
2063 menu
->selection
= evsel
;
2066 static int perf_evsel_menu__run(struct perf_evsel_menu
*menu
,
2067 int nr_events
, const char *help
,
2068 struct hist_browser_timer
*hbt
)
2070 struct perf_evlist
*evlist
= menu
->b
.priv
;
2071 struct perf_evsel
*pos
;
2072 const char *title
= "Available samples";
2073 int delay_secs
= hbt
? hbt
->refresh
: 0;
2076 if (ui_browser__show(&menu
->b
, title
,
2077 "ESC: exit, ENTER|->: Browse histograms") < 0)
2081 key
= ui_browser__run(&menu
->b
, delay_secs
);
2085 hbt
->timer(hbt
->arg
);
2087 if (!menu
->lost_events_warned
&& menu
->lost_events
) {
2088 ui_browser__warn_lost_events(&menu
->b
);
2089 menu
->lost_events_warned
= true;
2094 if (!menu
->selection
)
2096 pos
= menu
->selection
;
2098 perf_evlist__set_selected(evlist
, pos
);
2100 * Give the calling tool a chance to populate the non
2101 * default evsel resorted hists tree.
2104 hbt
->timer(hbt
->arg
);
2105 key
= perf_evsel__hists_browse(pos
, nr_events
, help
,
2109 ui_browser__show_title(&menu
->b
, title
);
2112 if (pos
->node
.next
== &evlist
->entries
)
2113 pos
= perf_evlist__first(evlist
);
2115 pos
= perf_evsel__next(pos
);
2118 if (pos
->node
.prev
== &evlist
->entries
)
2119 pos
= perf_evlist__last(evlist
);
2121 pos
= perf_evsel__prev(pos
);
2124 if (!ui_browser__dialog_yesno(&menu
->b
,
2125 "Do you really want to exit?"))
2128 case K_SWITCH_INPUT_DATA
:
2138 if (!ui_browser__dialog_yesno(&menu
->b
,
2139 "Do you really want to exit?"))
2151 ui_browser__hide(&menu
->b
);
2155 static bool filter_group_entries(struct ui_browser
*browser __maybe_unused
,
2158 struct perf_evsel
*evsel
= list_entry(entry
, struct perf_evsel
, node
);
2160 if (symbol_conf
.event_group
&& !perf_evsel__is_group_leader(evsel
))
2166 static int __perf_evlist__tui_browse_hists(struct perf_evlist
*evlist
,
2167 int nr_entries
, const char *help
,
2168 struct hist_browser_timer
*hbt
,
2170 struct perf_session_env
*env
)
2172 struct perf_evsel
*pos
;
2173 struct perf_evsel_menu menu
= {
2175 .entries
= &evlist
->entries
,
2176 .refresh
= ui_browser__list_head_refresh
,
2177 .seek
= ui_browser__list_head_seek
,
2178 .write
= perf_evsel_menu__write
,
2179 .filter
= filter_group_entries
,
2180 .nr_entries
= nr_entries
,
2183 .min_pcnt
= min_pcnt
,
2187 ui_helpline__push("Press ESC to exit");
2189 evlist__for_each(evlist
, pos
) {
2190 const char *ev_name
= perf_evsel__name(pos
);
2191 size_t line_len
= strlen(ev_name
) + 7;
2193 if (menu
.b
.width
< line_len
)
2194 menu
.b
.width
= line_len
;
2197 return perf_evsel_menu__run(&menu
, nr_entries
, help
, hbt
);
2200 int perf_evlist__tui_browse_hists(struct perf_evlist
*evlist
, const char *help
,
2201 struct hist_browser_timer
*hbt
,
2203 struct perf_session_env
*env
)
2205 int nr_entries
= evlist
->nr_entries
;
2208 if (nr_entries
== 1) {
2209 struct perf_evsel
*first
= perf_evlist__first(evlist
);
2211 return perf_evsel__hists_browse(first
, nr_entries
, help
,
2212 false, hbt
, min_pcnt
,
2216 if (symbol_conf
.event_group
) {
2217 struct perf_evsel
*pos
;
2220 evlist__for_each(evlist
, pos
) {
2221 if (perf_evsel__is_group_leader(pos
))
2225 if (nr_entries
== 1)
2229 return __perf_evlist__tui_browse_hists(evlist
, nr_entries
, help
,
2230 hbt
, min_pcnt
, env
);