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
;
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
)
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
,
440 "Press '?' for help on key bindings") < 0)
444 key
= ui_browser__run(&browser
->b
, delay_secs
);
449 hbt
->timer(hbt
->arg
);
451 if (hist_browser__has_filter(browser
))
452 hist_browser__update_nr_entries(browser
);
454 nr_entries
= hist_browser__nr_entries(browser
);
455 ui_browser__update_nr_entries(&browser
->b
, nr_entries
);
457 if (browser
->hists
->stats
.nr_lost_warned
!=
458 browser
->hists
->stats
.nr_events
[PERF_RECORD_LOST
]) {
459 browser
->hists
->stats
.nr_lost_warned
=
460 browser
->hists
->stats
.nr_events
[PERF_RECORD_LOST
];
461 ui_browser__warn_lost_events(&browser
->b
);
464 hists__browser_title(browser
->hists
,
465 hbt
, title
, sizeof(title
));
466 ui_browser__show_title(&browser
->b
, title
);
469 case 'D': { /* Debug */
471 struct hist_entry
*h
= rb_entry(browser
->b
.top
,
472 struct hist_entry
, rb_node
);
474 ui_helpline__fpush("%d: nr_ent=(%d,%d), rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
475 seq
++, browser
->b
.nr_entries
,
476 browser
->hists
->nr_entries
,
480 h
->row_offset
, h
->nr_rows
);
484 /* Collapse the whole world. */
485 hist_browser__set_folding(browser
, false);
488 /* Expand the whole world. */
489 hist_browser__set_folding(browser
, true);
492 browser
->show_headers
= !browser
->show_headers
;
493 hist_browser__update_rows(browser
);
496 if (hist_browser__toggle_fold(browser
))
504 ui_browser__hide(&browser
->b
);
508 struct callchain_print_arg
{
509 /* for hists browser */
511 bool is_current_entry
;
518 typedef void (*print_callchain_entry_fn
)(struct hist_browser
*browser
,
519 struct callchain_list
*chain
,
520 const char *str
, int offset
,
522 struct callchain_print_arg
*arg
);
524 static void hist_browser__show_callchain_entry(struct hist_browser
*browser
,
525 struct callchain_list
*chain
,
526 const char *str
, int offset
,
528 struct callchain_print_arg
*arg
)
531 char folded_sign
= callchain_list__folded(chain
);
532 bool show_annotated
= browser
->show_dso
&& chain
->ms
.sym
&& symbol__annotation(chain
->ms
.sym
)->src
;
534 color
= HE_COLORSET_NORMAL
;
535 width
= browser
->b
.width
- (offset
+ 2);
536 if (ui_browser__is_current_entry(&browser
->b
, row
)) {
537 browser
->selection
= &chain
->ms
;
538 color
= HE_COLORSET_SELECTED
;
539 arg
->is_current_entry
= true;
542 ui_browser__set_color(&browser
->b
, color
);
543 hist_browser__gotorc(browser
, row
, 0);
544 slsmg_write_nstring(" ", offset
);
545 slsmg_printf("%c", folded_sign
);
546 ui_browser__write_graph(&browser
->b
, show_annotated
? SLSMG_RARROW_CHAR
: ' ');
547 slsmg_write_nstring(str
, width
);
550 static void hist_browser__fprintf_callchain_entry(struct hist_browser
*b __maybe_unused
,
551 struct callchain_list
*chain
,
552 const char *str
, int offset
,
553 unsigned short row __maybe_unused
,
554 struct callchain_print_arg
*arg
)
556 char folded_sign
= callchain_list__folded(chain
);
558 arg
->printed
+= fprintf(arg
->fp
, "%*s%c %s\n", offset
, " ",
562 typedef bool (*check_output_full_fn
)(struct hist_browser
*browser
,
565 static bool hist_browser__check_output_full(struct hist_browser
*browser
,
568 return browser
->b
.rows
== row
;
571 static bool hist_browser__check_dump_full(struct hist_browser
*browser __maybe_unused
,
572 unsigned short row __maybe_unused
)
577 #define LEVEL_OFFSET_STEP 3
579 static int hist_browser__show_callchain(struct hist_browser
*browser
,
580 struct rb_root
*root
, int level
,
581 unsigned short row
, u64 total
,
582 print_callchain_entry_fn print
,
583 struct callchain_print_arg
*arg
,
584 check_output_full_fn is_output_full
)
586 struct rb_node
*node
;
587 int first_row
= row
, offset
= level
* LEVEL_OFFSET_STEP
;
591 node
= rb_first(root
);
592 need_percent
= node
&& rb_next(node
);
595 struct callchain_node
*child
= rb_entry(node
, struct callchain_node
, rb_node
);
596 struct rb_node
*next
= rb_next(node
);
597 u64 cumul
= callchain_cumul_hits(child
);
598 struct callchain_list
*chain
;
599 char folded_sign
= ' ';
601 int extra_offset
= 0;
603 list_for_each_entry(chain
, &child
->val
, list
) {
604 char bf
[1024], *alloc_str
;
606 bool was_first
= first
;
610 else if (need_percent
)
611 extra_offset
= LEVEL_OFFSET_STEP
;
613 folded_sign
= callchain_list__folded(chain
);
614 if (arg
->row_offset
!= 0) {
620 str
= callchain_list__sym_name(chain
, bf
, sizeof(bf
),
623 if (was_first
&& need_percent
) {
624 double percent
= cumul
* 100.0 / total
;
626 if (asprintf(&alloc_str
, "%2.2f%% %s", percent
, str
) < 0)
627 str
= "Not enough memory!";
632 print(browser
, chain
, str
, offset
+ extra_offset
, row
, arg
);
636 if (is_output_full(browser
, ++row
))
639 if (folded_sign
== '+')
643 if (folded_sign
== '-') {
644 const int new_level
= level
+ (extra_offset
? 2 : 1);
646 if (callchain_param
.mode
== CHAIN_GRAPH_REL
)
647 new_total
= child
->children_hit
;
651 row
+= hist_browser__show_callchain(browser
, &child
->rb_root
,
652 new_level
, row
, new_total
,
653 print
, arg
, is_output_full
);
655 if (is_output_full(browser
, row
))
660 return row
- first_row
;
664 struct ui_browser
*b
;
669 static int __hpp__slsmg_color_printf(struct perf_hpp
*hpp
, const char *fmt
, ...)
671 struct hpp_arg
*arg
= hpp
->ptr
;
677 len
= va_arg(args
, int);
678 percent
= va_arg(args
, double);
681 ui_browser__set_percent_color(arg
->b
, percent
, arg
->current_entry
);
683 ret
= scnprintf(hpp
->buf
, hpp
->size
, fmt
, len
, percent
);
684 slsmg_printf("%s", hpp
->buf
);
686 advance_hpp(hpp
, ret
);
690 #define __HPP_COLOR_PERCENT_FN(_type, _field) \
691 static u64 __hpp_get_##_field(struct hist_entry *he) \
693 return he->stat._field; \
697 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
698 struct perf_hpp *hpp, \
699 struct hist_entry *he) \
701 return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%", \
702 __hpp__slsmg_color_printf, true); \
705 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
706 static u64 __hpp_get_acc_##_field(struct hist_entry *he) \
708 return he->stat_acc->_field; \
712 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
713 struct perf_hpp *hpp, \
714 struct hist_entry *he) \
716 if (!symbol_conf.cumulate_callchain) { \
717 int len = fmt->user_len ?: fmt->len; \
718 int ret = scnprintf(hpp->buf, hpp->size, \
719 "%*s", len, "N/A"); \
720 slsmg_printf("%s", hpp->buf); \
724 return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field, \
725 " %*.2f%%", __hpp__slsmg_color_printf, true); \
728 __HPP_COLOR_PERCENT_FN(overhead
, period
)
729 __HPP_COLOR_PERCENT_FN(overhead_sys
, period_sys
)
730 __HPP_COLOR_PERCENT_FN(overhead_us
, period_us
)
731 __HPP_COLOR_PERCENT_FN(overhead_guest_sys
, period_guest_sys
)
732 __HPP_COLOR_PERCENT_FN(overhead_guest_us
, period_guest_us
)
733 __HPP_COLOR_ACC_PERCENT_FN(overhead_acc
, period
)
735 #undef __HPP_COLOR_PERCENT_FN
736 #undef __HPP_COLOR_ACC_PERCENT_FN
738 void hist_browser__init_hpp(void)
740 perf_hpp__format
[PERF_HPP__OVERHEAD
].color
=
741 hist_browser__hpp_color_overhead
;
742 perf_hpp__format
[PERF_HPP__OVERHEAD_SYS
].color
=
743 hist_browser__hpp_color_overhead_sys
;
744 perf_hpp__format
[PERF_HPP__OVERHEAD_US
].color
=
745 hist_browser__hpp_color_overhead_us
;
746 perf_hpp__format
[PERF_HPP__OVERHEAD_GUEST_SYS
].color
=
747 hist_browser__hpp_color_overhead_guest_sys
;
748 perf_hpp__format
[PERF_HPP__OVERHEAD_GUEST_US
].color
=
749 hist_browser__hpp_color_overhead_guest_us
;
750 perf_hpp__format
[PERF_HPP__OVERHEAD_ACC
].color
=
751 hist_browser__hpp_color_overhead_acc
;
754 static int hist_browser__show_entry(struct hist_browser
*browser
,
755 struct hist_entry
*entry
,
760 int width
= browser
->b
.width
;
761 char folded_sign
= ' ';
762 bool current_entry
= ui_browser__is_current_entry(&browser
->b
, row
);
763 off_t row_offset
= entry
->row_offset
;
765 struct perf_hpp_fmt
*fmt
;
768 browser
->he_selection
= entry
;
769 browser
->selection
= &entry
->ms
;
772 if (symbol_conf
.use_callchain
) {
773 hist_entry__init_have_children(entry
);
774 folded_sign
= hist_entry__folded(entry
);
777 if (row_offset
== 0) {
778 struct hpp_arg arg
= {
780 .folded_sign
= folded_sign
,
781 .current_entry
= current_entry
,
783 struct perf_hpp hpp
= {
789 hist_browser__gotorc(browser
, row
, 0);
791 perf_hpp__for_each_format(fmt
) {
792 if (perf_hpp__should_skip(fmt
))
795 if (current_entry
&& browser
->b
.navkeypressed
) {
796 ui_browser__set_color(&browser
->b
,
797 HE_COLORSET_SELECTED
);
799 ui_browser__set_color(&browser
->b
,
804 if (symbol_conf
.use_callchain
) {
805 slsmg_printf("%c ", folded_sign
);
815 width
-= fmt
->color(fmt
, &hpp
, entry
);
817 width
-= fmt
->entry(fmt
, &hpp
, entry
);
818 slsmg_printf("%s", s
);
822 /* The scroll bar isn't being used */
823 if (!browser
->b
.navkeypressed
)
826 slsmg_write_nstring("", width
);
833 if (folded_sign
== '-' && row
!= browser
->b
.rows
) {
834 u64 total
= hists__total_period(entry
->hists
);
835 struct callchain_print_arg arg
= {
836 .row_offset
= row_offset
,
837 .is_current_entry
= current_entry
,
840 if (callchain_param
.mode
== CHAIN_GRAPH_REL
) {
841 if (symbol_conf
.cumulate_callchain
)
842 total
= entry
->stat_acc
->period
;
844 total
= entry
->stat
.period
;
847 printed
+= hist_browser__show_callchain(browser
,
848 &entry
->sorted_chain
, 1, row
, total
,
849 hist_browser__show_callchain_entry
, &arg
,
850 hist_browser__check_output_full
);
852 if (arg
.is_current_entry
)
853 browser
->he_selection
= entry
;
859 static int advance_hpp_check(struct perf_hpp
*hpp
, int inc
)
861 advance_hpp(hpp
, inc
);
862 return hpp
->size
<= 0;
865 static int hists__scnprintf_headers(char *buf
, size_t size
, struct hists
*hists
)
867 struct perf_hpp dummy_hpp
= {
871 struct perf_hpp_fmt
*fmt
;
874 if (symbol_conf
.use_callchain
) {
875 ret
= scnprintf(buf
, size
, " ");
876 if (advance_hpp_check(&dummy_hpp
, ret
))
880 perf_hpp__for_each_format(fmt
) {
881 if (perf_hpp__should_skip(fmt
))
884 ret
= fmt
->header(fmt
, &dummy_hpp
, hists_to_evsel(hists
));
885 if (advance_hpp_check(&dummy_hpp
, ret
))
888 ret
= scnprintf(dummy_hpp
.buf
, dummy_hpp
.size
, " ");
889 if (advance_hpp_check(&dummy_hpp
, ret
))
896 static void hist_browser__show_headers(struct hist_browser
*browser
)
900 hists__scnprintf_headers(headers
, sizeof(headers
), browser
->hists
);
901 ui_browser__gotorc(&browser
->b
, 0, 0);
902 ui_browser__set_color(&browser
->b
, HE_COLORSET_ROOT
);
903 slsmg_write_nstring(headers
, browser
->b
.width
+ 1);
906 static void ui_browser__hists_init_top(struct ui_browser
*browser
)
908 if (browser
->top
== NULL
) {
909 struct hist_browser
*hb
;
911 hb
= container_of(browser
, struct hist_browser
, b
);
912 browser
->top
= rb_first(&hb
->hists
->entries
);
916 static unsigned int hist_browser__refresh(struct ui_browser
*browser
)
919 u16 header_offset
= 0;
921 struct hist_browser
*hb
= container_of(browser
, struct hist_browser
, b
);
923 if (hb
->show_headers
) {
924 hist_browser__show_headers(hb
);
928 ui_browser__hists_init_top(browser
);
930 for (nd
= browser
->top
; nd
; nd
= rb_next(nd
)) {
931 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
937 percent
= hist_entry__get_percent_limit(h
);
938 if (percent
< hb
->min_pcnt
)
941 row
+= hist_browser__show_entry(hb
, h
, row
);
942 if (row
== browser
->rows
)
946 return row
+ header_offset
;
949 static struct rb_node
*hists__filter_entries(struct rb_node
*nd
,
953 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
954 float percent
= hist_entry__get_percent_limit(h
);
956 if (!h
->filtered
&& percent
>= min_pcnt
)
965 static struct rb_node
*hists__filter_prev_entries(struct rb_node
*nd
,
969 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
970 float percent
= hist_entry__get_percent_limit(h
);
972 if (!h
->filtered
&& percent
>= min_pcnt
)
981 static void ui_browser__hists_seek(struct ui_browser
*browser
,
982 off_t offset
, int whence
)
984 struct hist_entry
*h
;
987 struct hist_browser
*hb
;
989 hb
= container_of(browser
, struct hist_browser
, b
);
991 if (browser
->nr_entries
== 0)
994 ui_browser__hists_init_top(browser
);
998 nd
= hists__filter_entries(rb_first(browser
->entries
),
1005 nd
= hists__filter_prev_entries(rb_last(browser
->entries
),
1014 * Moves not relative to the first visible entry invalidates its
1017 h
= rb_entry(browser
->top
, struct hist_entry
, rb_node
);
1021 * Here we have to check if nd is expanded (+), if it is we can't go
1022 * the next top level hist_entry, instead we must compute an offset of
1023 * what _not_ to show and not change the first visible entry.
1025 * This offset increments when we are going from top to bottom and
1026 * decreases when we're going from bottom to top.
1028 * As we don't have backpointers to the top level in the callchains
1029 * structure, we need to always print the whole hist_entry callchain,
1030 * skipping the first ones that are before the first visible entry
1031 * and stop when we printed enough lines to fill the screen.
1036 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1038 u16 remaining
= h
->nr_rows
- h
->row_offset
;
1039 if (offset
> remaining
) {
1040 offset
-= remaining
;
1043 h
->row_offset
+= offset
;
1049 nd
= hists__filter_entries(rb_next(nd
), hb
->min_pcnt
);
1054 } while (offset
!= 0);
1055 } else if (offset
< 0) {
1057 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1060 if (-offset
> h
->row_offset
) {
1061 offset
+= h
->row_offset
;
1064 h
->row_offset
+= offset
;
1070 if (-offset
> h
->nr_rows
) {
1071 offset
+= h
->nr_rows
;
1074 h
->row_offset
= h
->nr_rows
+ offset
;
1082 nd
= hists__filter_prev_entries(rb_prev(nd
),
1090 * Last unfiltered hist_entry, check if it is
1091 * unfolded, if it is then we should have
1092 * row_offset at its last entry.
1094 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1096 h
->row_offset
= h
->nr_rows
;
1103 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1108 static int hist_browser__fprintf_callchain(struct hist_browser
*browser
,
1109 struct hist_entry
*he
, FILE *fp
)
1111 u64 total
= hists__total_period(he
->hists
);
1112 struct callchain_print_arg arg
= {
1116 if (symbol_conf
.cumulate_callchain
)
1117 total
= he
->stat_acc
->period
;
1119 hist_browser__show_callchain(browser
, &he
->sorted_chain
, 1, 0, total
,
1120 hist_browser__fprintf_callchain_entry
, &arg
,
1121 hist_browser__check_dump_full
);
1125 static int hist_browser__fprintf_entry(struct hist_browser
*browser
,
1126 struct hist_entry
*he
, FILE *fp
)
1130 char folded_sign
= ' ';
1131 struct perf_hpp hpp
= {
1135 struct perf_hpp_fmt
*fmt
;
1139 if (symbol_conf
.use_callchain
)
1140 folded_sign
= hist_entry__folded(he
);
1142 if (symbol_conf
.use_callchain
)
1143 printed
+= fprintf(fp
, "%c ", folded_sign
);
1145 perf_hpp__for_each_format(fmt
) {
1146 if (perf_hpp__should_skip(fmt
))
1150 ret
= scnprintf(hpp
.buf
, hpp
.size
, " ");
1151 advance_hpp(&hpp
, ret
);
1155 ret
= fmt
->entry(fmt
, &hpp
, he
);
1156 advance_hpp(&hpp
, ret
);
1158 printed
+= fprintf(fp
, "%s\n", rtrim(s
));
1160 if (folded_sign
== '-')
1161 printed
+= hist_browser__fprintf_callchain(browser
, he
, fp
);
1166 static int hist_browser__fprintf(struct hist_browser
*browser
, FILE *fp
)
1168 struct rb_node
*nd
= hists__filter_entries(rb_first(browser
->b
.entries
),
1173 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1175 printed
+= hist_browser__fprintf_entry(browser
, h
, fp
);
1176 nd
= hists__filter_entries(rb_next(nd
), browser
->min_pcnt
);
1182 static int hist_browser__dump(struct hist_browser
*browser
)
1188 scnprintf(filename
, sizeof(filename
), "perf.hist.%d", browser
->print_seq
);
1189 if (access(filename
, F_OK
))
1192 * XXX: Just an arbitrary lazy upper limit
1194 if (++browser
->print_seq
== 8192) {
1195 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1200 fp
= fopen(filename
, "w");
1203 const char *err
= strerror_r(errno
, bf
, sizeof(bf
));
1204 ui_helpline__fpush("Couldn't write to %s: %s", filename
, err
);
1208 ++browser
->print_seq
;
1209 hist_browser__fprintf(browser
, fp
);
1211 ui_helpline__fpush("%s written!", filename
);
1216 static struct hist_browser
*hist_browser__new(struct hists
*hists
,
1217 struct hist_browser_timer
*hbt
,
1218 struct perf_session_env
*env
)
1220 struct hist_browser
*browser
= zalloc(sizeof(*browser
));
1223 browser
->hists
= hists
;
1224 browser
->b
.refresh
= hist_browser__refresh
;
1225 browser
->b
.refresh_dimensions
= hist_browser__refresh_dimensions
;
1226 browser
->b
.seek
= ui_browser__hists_seek
;
1227 browser
->b
.use_navkeypressed
= true;
1228 browser
->show_headers
= symbol_conf
.show_hist_headers
;
1236 static void hist_browser__delete(struct hist_browser
*browser
)
1241 static struct hist_entry
*hist_browser__selected_entry(struct hist_browser
*browser
)
1243 return browser
->he_selection
;
1246 static struct thread
*hist_browser__selected_thread(struct hist_browser
*browser
)
1248 return browser
->he_selection
->thread
;
1251 /* Check whether the browser is for 'top' or 'report' */
1252 static inline bool is_report_browser(void *timer
)
1254 return timer
== NULL
;
1257 static int hists__browser_title(struct hists
*hists
,
1258 struct hist_browser_timer
*hbt
,
1259 char *bf
, size_t size
)
1263 const struct dso
*dso
= hists
->dso_filter
;
1264 const struct thread
*thread
= hists
->thread_filter
;
1265 unsigned long nr_samples
= hists
->stats
.nr_events
[PERF_RECORD_SAMPLE
];
1266 u64 nr_events
= hists
->stats
.total_period
;
1267 struct perf_evsel
*evsel
= hists_to_evsel(hists
);
1268 const char *ev_name
= perf_evsel__name(evsel
);
1270 size_t buflen
= sizeof(buf
);
1272 if (symbol_conf
.filter_relative
) {
1273 nr_samples
= hists
->stats
.nr_non_filtered_samples
;
1274 nr_events
= hists
->stats
.total_non_filtered_period
;
1277 if (perf_evsel__is_group_event(evsel
)) {
1278 struct perf_evsel
*pos
;
1280 perf_evsel__group_desc(evsel
, buf
, buflen
);
1283 for_each_group_member(pos
, evsel
) {
1284 struct hists
*pos_hists
= evsel__hists(pos
);
1286 if (symbol_conf
.filter_relative
) {
1287 nr_samples
+= pos_hists
->stats
.nr_non_filtered_samples
;
1288 nr_events
+= pos_hists
->stats
.total_non_filtered_period
;
1290 nr_samples
+= pos_hists
->stats
.nr_events
[PERF_RECORD_SAMPLE
];
1291 nr_events
+= pos_hists
->stats
.total_period
;
1296 nr_samples
= convert_unit(nr_samples
, &unit
);
1297 printed
= scnprintf(bf
, size
,
1298 "Samples: %lu%c of event '%s', Event count (approx.): %" PRIu64
,
1299 nr_samples
, unit
, ev_name
, nr_events
);
1302 if (hists
->uid_filter_str
)
1303 printed
+= snprintf(bf
+ printed
, size
- printed
,
1304 ", UID: %s", hists
->uid_filter_str
);
1306 printed
+= scnprintf(bf
+ printed
, size
- printed
,
1308 (thread
->comm_set
? thread__comm_str(thread
) : ""),
1311 printed
+= scnprintf(bf
+ printed
, size
- printed
,
1312 ", DSO: %s", dso
->short_name
);
1313 if (!is_report_browser(hbt
)) {
1314 struct perf_top
*top
= hbt
->arg
;
1317 printed
+= scnprintf(bf
+ printed
, size
- printed
, " [z]");
1323 static inline void free_popup_options(char **options
, int n
)
1327 for (i
= 0; i
< n
; ++i
)
1332 * Only runtime switching of perf data file will make "input_name" point
1333 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1334 * whether we need to call free() for current "input_name" during the switch.
1336 static bool is_input_name_malloced
= false;
1338 static int switch_data_file(void)
1340 char *pwd
, *options
[32], *abs_path
[32], *tmp
;
1342 int nr_options
= 0, choice
= -1, ret
= -1;
1343 struct dirent
*dent
;
1345 pwd
= getenv("PWD");
1349 pwd_dir
= opendir(pwd
);
1353 memset(options
, 0, sizeof(options
));
1354 memset(options
, 0, sizeof(abs_path
));
1356 while ((dent
= readdir(pwd_dir
))) {
1357 char path
[PATH_MAX
];
1359 char *name
= dent
->d_name
;
1362 if (!(dent
->d_type
== DT_REG
))
1365 snprintf(path
, sizeof(path
), "%s/%s", pwd
, name
);
1367 file
= fopen(path
, "r");
1371 if (fread(&magic
, 1, 8, file
) < 8)
1372 goto close_file_and_continue
;
1374 if (is_perf_magic(magic
)) {
1375 options
[nr_options
] = strdup(name
);
1376 if (!options
[nr_options
])
1377 goto close_file_and_continue
;
1379 abs_path
[nr_options
] = strdup(path
);
1380 if (!abs_path
[nr_options
]) {
1381 zfree(&options
[nr_options
]);
1382 ui__warning("Can't search all data files due to memory shortage.\n");
1390 close_file_and_continue
:
1392 if (nr_options
>= 32) {
1393 ui__warning("Too many perf data files in PWD!\n"
1394 "Only the first 32 files will be listed.\n");
1401 choice
= ui__popup_menu(nr_options
, options
);
1402 if (choice
< nr_options
&& choice
>= 0) {
1403 tmp
= strdup(abs_path
[choice
]);
1405 if (is_input_name_malloced
)
1406 free((void *)input_name
);
1408 is_input_name_malloced
= true;
1411 ui__warning("Data switch failed due to memory shortage!\n");
1415 free_popup_options(options
, nr_options
);
1416 free_popup_options(abs_path
, nr_options
);
1420 struct popup_action
{
1421 struct thread
*thread
;
1423 struct map_symbol ms
;
1425 int (*fn
)(struct hist_browser
*browser
, struct popup_action
*act
);
1429 do_annotate(struct hist_browser
*browser
, struct popup_action
*act
)
1431 struct perf_evsel
*evsel
;
1432 struct annotation
*notes
;
1433 struct hist_entry
*he
;
1436 if (!objdump_path
&& perf_session_env__lookup_objdump(browser
->env
))
1439 notes
= symbol__annotation(act
->ms
.sym
);
1443 evsel
= hists_to_evsel(browser
->hists
);
1444 err
= map_symbol__tui_annotate(&act
->ms
, evsel
, browser
->hbt
);
1445 he
= hist_browser__selected_entry(browser
);
1447 * offer option to annotate the other branch source or target
1448 * (if they exists) when returning from annotate
1450 if ((err
== 'q' || err
== CTRL('c')) && he
->branch_info
)
1453 ui_browser__update_nr_entries(&browser
->b
, browser
->hists
->nr_entries
);
1455 ui_browser__handle_resize(&browser
->b
);
1460 add_annotate_opt(struct hist_browser
*browser __maybe_unused
,
1461 struct popup_action
*act
, char **optstr
,
1462 struct map
*map
, struct symbol
*sym
)
1464 if (sym
== NULL
|| map
->dso
->annotate_warned
)
1467 if (asprintf(optstr
, "Annotate %s", sym
->name
) < 0)
1472 act
->fn
= do_annotate
;
1477 do_zoom_thread(struct hist_browser
*browser
, struct popup_action
*act
)
1479 struct thread
*thread
= act
->thread
;
1481 if (browser
->hists
->thread_filter
) {
1482 pstack__remove(browser
->pstack
, &browser
->hists
->thread_filter
);
1483 perf_hpp__set_elide(HISTC_THREAD
, false);
1484 thread__zput(browser
->hists
->thread_filter
);
1487 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1488 thread
->comm_set
? thread__comm_str(thread
) : "",
1490 browser
->hists
->thread_filter
= thread__get(thread
);
1491 perf_hpp__set_elide(HISTC_THREAD
, false);
1492 pstack__push(browser
->pstack
, &browser
->hists
->thread_filter
);
1495 hists__filter_by_thread(browser
->hists
);
1496 hist_browser__reset(browser
);
1501 add_thread_opt(struct hist_browser
*browser
, struct popup_action
*act
,
1502 char **optstr
, struct thread
*thread
)
1507 if (asprintf(optstr
, "Zoom %s %s(%d) thread",
1508 browser
->hists
->thread_filter
? "out of" : "into",
1509 thread
->comm_set
? thread__comm_str(thread
) : "",
1513 act
->thread
= thread
;
1514 act
->fn
= do_zoom_thread
;
1519 do_zoom_dso(struct hist_browser
*browser
, struct popup_action
*act
)
1521 struct dso
*dso
= act
->dso
;
1523 if (browser
->hists
->dso_filter
) {
1524 pstack__remove(browser
->pstack
, &browser
->hists
->dso_filter
);
1525 perf_hpp__set_elide(HISTC_DSO
, false);
1526 browser
->hists
->dso_filter
= NULL
;
1531 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1532 dso
->kernel
? "the Kernel" : dso
->short_name
);
1533 browser
->hists
->dso_filter
= dso
;
1534 perf_hpp__set_elide(HISTC_DSO
, true);
1535 pstack__push(browser
->pstack
, &browser
->hists
->dso_filter
);
1538 hists__filter_by_dso(browser
->hists
);
1539 hist_browser__reset(browser
);
1544 add_dso_opt(struct hist_browser
*browser
, struct popup_action
*act
,
1545 char **optstr
, struct dso
*dso
)
1550 if (asprintf(optstr
, "Zoom %s %s DSO",
1551 browser
->hists
->dso_filter
? "out of" : "into",
1552 dso
->kernel
? "the Kernel" : dso
->short_name
) < 0)
1556 act
->fn
= do_zoom_dso
;
1561 do_browse_map(struct hist_browser
*browser __maybe_unused
,
1562 struct popup_action
*act
)
1564 map__browse(act
->ms
.map
);
1569 add_map_opt(struct hist_browser
*browser __maybe_unused
,
1570 struct popup_action
*act
, char **optstr
, struct map
*map
)
1575 if (asprintf(optstr
, "Browse map details") < 0)
1579 act
->fn
= do_browse_map
;
1584 do_run_script(struct hist_browser
*browser __maybe_unused
,
1585 struct popup_action
*act
)
1587 char script_opt
[64];
1588 memset(script_opt
, 0, sizeof(script_opt
));
1591 scnprintf(script_opt
, sizeof(script_opt
), " -c %s ",
1592 thread__comm_str(act
->thread
));
1593 } else if (act
->ms
.sym
) {
1594 scnprintf(script_opt
, sizeof(script_opt
), " -S %s ",
1598 script_browse(script_opt
);
1603 add_script_opt(struct hist_browser
*browser __maybe_unused
,
1604 struct popup_action
*act
, char **optstr
,
1605 struct thread
*thread
, struct symbol
*sym
)
1608 if (asprintf(optstr
, "Run scripts for samples of thread [%s]",
1609 thread__comm_str(thread
)) < 0)
1612 if (asprintf(optstr
, "Run scripts for samples of symbol [%s]",
1616 if (asprintf(optstr
, "Run scripts for all samples") < 0)
1620 act
->thread
= thread
;
1622 act
->fn
= do_run_script
;
1627 do_switch_data(struct hist_browser
*browser __maybe_unused
,
1628 struct popup_action
*act __maybe_unused
)
1630 if (switch_data_file()) {
1631 ui__warning("Won't switch the data files due to\n"
1632 "no valid data file get selected!\n");
1636 return K_SWITCH_INPUT_DATA
;
1640 add_switch_opt(struct hist_browser
*browser
,
1641 struct popup_action
*act
, char **optstr
)
1643 if (!is_report_browser(browser
->hbt
))
1646 if (asprintf(optstr
, "Switch to another data file in PWD") < 0)
1649 act
->fn
= do_switch_data
;
1654 do_exit_browser(struct hist_browser
*browser __maybe_unused
,
1655 struct popup_action
*act __maybe_unused
)
1661 add_exit_opt(struct hist_browser
*browser __maybe_unused
,
1662 struct popup_action
*act
, char **optstr
)
1664 if (asprintf(optstr
, "Exit") < 0)
1667 act
->fn
= do_exit_browser
;
1671 static void hist_browser__update_nr_entries(struct hist_browser
*hb
)
1674 struct rb_node
*nd
= rb_first(&hb
->hists
->entries
);
1676 if (hb
->min_pcnt
== 0) {
1677 hb
->nr_non_filtered_entries
= hb
->hists
->nr_non_filtered_entries
;
1681 while ((nd
= hists__filter_entries(nd
, hb
->min_pcnt
)) != NULL
) {
1686 hb
->nr_non_filtered_entries
= nr_entries
;
1689 static int perf_evsel__hists_browse(struct perf_evsel
*evsel
, int nr_events
,
1690 const char *helpline
,
1692 struct hist_browser_timer
*hbt
,
1694 struct perf_session_env
*env
)
1696 struct hists
*hists
= evsel__hists(evsel
);
1697 struct hist_browser
*browser
= hist_browser__new(hists
, hbt
, env
);
1698 struct branch_info
*bi
;
1699 #define MAX_OPTIONS 16
1700 char *options
[MAX_OPTIONS
];
1701 struct popup_action actions
[MAX_OPTIONS
];
1705 int delay_secs
= hbt
? hbt
->refresh
: 0;
1706 struct perf_hpp_fmt
*fmt
;
1708 #define HIST_BROWSER_HELP_COMMON \
1709 "h/?/F1 Show this window\n" \
1711 "PGDN/SPACE Navigate\n" \
1712 "q/ESC/CTRL+C Exit browser\n\n" \
1713 "For multiple event sessions:\n\n" \
1714 "TAB/UNTAB Switch events\n\n" \
1715 "For symbolic views (--sort has sym):\n\n" \
1716 "-> Zoom into DSO/Threads & Annotate current symbol\n" \
1718 "a Annotate current symbol\n" \
1719 "C Collapse all callchains\n" \
1720 "d Zoom into current DSO\n" \
1721 "E Expand all callchains\n" \
1722 "F Toggle percentage of filtered entries\n" \
1723 "H Display column headers\n" \
1725 /* help messages are sorted by lexical order of the hotkey */
1726 const char report_help
[] = HIST_BROWSER_HELP_COMMON
1727 "i Show header information\n"
1728 "P Print histograms to perf.hist.N\n"
1729 "r Run available scripts\n"
1730 "s Switch to another data file in PWD\n"
1731 "t Zoom into current Thread\n"
1732 "V Verbose (DSO names in callchains, etc)\n"
1733 "/ Filter symbol by name";
1734 const char top_help
[] = HIST_BROWSER_HELP_COMMON
1735 "P Print histograms to perf.hist.N\n"
1736 "t Zoom into current Thread\n"
1737 "V Verbose (DSO names in callchains, etc)\n"
1738 "z Toggle zeroing of samples\n"
1739 "f Enable/Disable events\n"
1740 "/ Filter symbol by name";
1742 if (browser
== NULL
)
1745 /* reset abort key so that it can get Ctrl-C as a key */
1747 SLang_init_tty(0, 0, 0);
1750 browser
->min_pcnt
= min_pcnt
;
1751 hist_browser__update_nr_entries(browser
);
1754 browser
->pstack
= pstack__new(2);
1755 if (browser
->pstack
== NULL
)
1758 ui_helpline__push(helpline
);
1760 memset(options
, 0, sizeof(options
));
1761 memset(actions
, 0, sizeof(actions
));
1763 perf_hpp__for_each_format(fmt
)
1764 perf_hpp__reset_width(fmt
, hists
);
1766 if (symbol_conf
.col_width_list_str
)
1767 perf_hpp__set_user_width(symbol_conf
.col_width_list_str
);
1770 struct thread
*thread
= NULL
;
1771 struct dso
*dso
= NULL
;
1776 key
= hist_browser__run(browser
);
1778 if (browser
->he_selection
!= NULL
) {
1779 thread
= hist_browser__selected_thread(browser
);
1780 dso
= browser
->selection
->map
? browser
->selection
->map
->dso
: NULL
;
1788 * Exit the browser, let hists__browser_tree
1789 * go to the next or previous
1791 goto out_free_stack
;
1793 if (!sort__has_sym
) {
1794 ui_browser__warning(&browser
->b
, delay_secs
* 2,
1795 "Annotation is only available for symbolic views, "
1796 "include \"sym*\" in --sort to use it.");
1800 if (browser
->selection
== NULL
||
1801 browser
->selection
->sym
== NULL
||
1802 browser
->selection
->map
->dso
->annotate_warned
)
1805 actions
->ms
.map
= browser
->selection
->map
;
1806 actions
->ms
.sym
= browser
->selection
->sym
;
1807 do_annotate(browser
, actions
);
1810 hist_browser__dump(browser
);
1814 do_zoom_dso(browser
, actions
);
1817 browser
->show_dso
= !browser
->show_dso
;
1820 actions
->thread
= thread
;
1821 do_zoom_thread(browser
, actions
);
1824 if (ui_browser__input_window("Symbol to show",
1825 "Please enter the name of symbol you want to see",
1826 buf
, "ENTER: OK, ESC: Cancel",
1827 delay_secs
* 2) == K_ENTER
) {
1828 hists
->symbol_filter_str
= *buf
? buf
: NULL
;
1829 hists__filter_by_symbol(hists
);
1830 hist_browser__reset(browser
);
1834 if (is_report_browser(hbt
)) {
1835 actions
->thread
= NULL
;
1836 actions
->ms
.sym
= NULL
;
1837 do_run_script(browser
, actions
);
1841 if (is_report_browser(hbt
)) {
1842 key
= do_switch_data(browser
, actions
);
1843 if (key
== K_SWITCH_INPUT_DATA
)
1844 goto out_free_stack
;
1848 /* env->arch is NULL for live-mode (i.e. perf top) */
1850 tui__header_window(env
);
1853 symbol_conf
.filter_relative
^= 1;
1856 if (!is_report_browser(hbt
)) {
1857 struct perf_top
*top
= hbt
->arg
;
1859 top
->zero
= !top
->zero
;
1865 ui_browser__help_window(&browser
->b
,
1866 is_report_browser(hbt
) ? report_help
: top_help
);
1875 if (pstack__empty(browser
->pstack
)) {
1877 * Go back to the perf_evsel_menu__run or other user
1880 goto out_free_stack
;
1883 top
= pstack__peek(browser
->pstack
);
1884 if (top
== &browser
->hists
->dso_filter
) {
1886 * No need to set actions->dso here since
1887 * it's just to remove the current filter.
1888 * Ditto for thread below.
1890 do_zoom_dso(browser
, actions
);
1892 if (top
== &browser
->hists
->thread_filter
)
1893 do_zoom_thread(browser
, actions
);
1898 !ui_browser__dialog_yesno(&browser
->b
,
1899 "Do you really want to exit?"))
1904 goto out_free_stack
;
1906 if (is_report_browser(hbt
))
1908 goto out_free_stack
;
1914 goto add_exit_option
;
1916 if (browser
->selection
== NULL
)
1917 goto skip_annotation
;
1919 if (sort__mode
== SORT_MODE__BRANCH
) {
1920 bi
= browser
->he_selection
->branch_info
;
1923 goto skip_annotation
;
1925 nr_options
+= add_annotate_opt(browser
,
1926 &actions
[nr_options
],
1927 &options
[nr_options
],
1930 if (bi
->to
.sym
!= bi
->from
.sym
)
1931 nr_options
+= add_annotate_opt(browser
,
1932 &actions
[nr_options
],
1933 &options
[nr_options
],
1937 nr_options
+= add_annotate_opt(browser
,
1938 &actions
[nr_options
],
1939 &options
[nr_options
],
1940 browser
->selection
->map
,
1941 browser
->selection
->sym
);
1944 nr_options
+= add_thread_opt(browser
, &actions
[nr_options
],
1945 &options
[nr_options
], thread
);
1946 nr_options
+= add_dso_opt(browser
, &actions
[nr_options
],
1947 &options
[nr_options
], dso
);
1948 nr_options
+= add_map_opt(browser
, &actions
[nr_options
],
1949 &options
[nr_options
],
1950 browser
->selection
->map
);
1952 /* perf script support */
1953 if (browser
->he_selection
) {
1954 nr_options
+= add_script_opt(browser
,
1955 &actions
[nr_options
],
1956 &options
[nr_options
],
1958 nr_options
+= add_script_opt(browser
,
1959 &actions
[nr_options
],
1960 &options
[nr_options
],
1961 NULL
, browser
->selection
->sym
);
1963 nr_options
+= add_script_opt(browser
, &actions
[nr_options
],
1964 &options
[nr_options
], NULL
, NULL
);
1965 nr_options
+= add_switch_opt(browser
, &actions
[nr_options
],
1966 &options
[nr_options
]);
1968 nr_options
+= add_exit_opt(browser
, &actions
[nr_options
],
1969 &options
[nr_options
]);
1972 struct popup_action
*act
;
1974 choice
= ui__popup_menu(nr_options
, options
);
1975 if (choice
== -1 || choice
>= nr_options
)
1978 act
= &actions
[choice
];
1979 key
= act
->fn(browser
, act
);
1982 if (key
== K_SWITCH_INPUT_DATA
)
1986 pstack__delete(browser
->pstack
);
1988 hist_browser__delete(browser
);
1989 free_popup_options(options
, MAX_OPTIONS
);
1993 struct perf_evsel_menu
{
1994 struct ui_browser b
;
1995 struct perf_evsel
*selection
;
1996 bool lost_events
, lost_events_warned
;
1998 struct perf_session_env
*env
;
2001 static void perf_evsel_menu__write(struct ui_browser
*browser
,
2002 void *entry
, int row
)
2004 struct perf_evsel_menu
*menu
= container_of(browser
,
2005 struct perf_evsel_menu
, b
);
2006 struct perf_evsel
*evsel
= list_entry(entry
, struct perf_evsel
, node
);
2007 struct hists
*hists
= evsel__hists(evsel
);
2008 bool current_entry
= ui_browser__is_current_entry(browser
, row
);
2009 unsigned long nr_events
= hists
->stats
.nr_events
[PERF_RECORD_SAMPLE
];
2010 const char *ev_name
= perf_evsel__name(evsel
);
2012 const char *warn
= " ";
2015 ui_browser__set_color(browser
, current_entry
? HE_COLORSET_SELECTED
:
2016 HE_COLORSET_NORMAL
);
2018 if (perf_evsel__is_group_event(evsel
)) {
2019 struct perf_evsel
*pos
;
2021 ev_name
= perf_evsel__group_name(evsel
);
2023 for_each_group_member(pos
, evsel
) {
2024 struct hists
*pos_hists
= evsel__hists(pos
);
2025 nr_events
+= pos_hists
->stats
.nr_events
[PERF_RECORD_SAMPLE
];
2029 nr_events
= convert_unit(nr_events
, &unit
);
2030 printed
= scnprintf(bf
, sizeof(bf
), "%lu%c%s%s", nr_events
,
2031 unit
, unit
== ' ' ? "" : " ", ev_name
);
2032 slsmg_printf("%s", bf
);
2034 nr_events
= hists
->stats
.nr_events
[PERF_RECORD_LOST
];
2035 if (nr_events
!= 0) {
2036 menu
->lost_events
= true;
2038 ui_browser__set_color(browser
, HE_COLORSET_TOP
);
2039 nr_events
= convert_unit(nr_events
, &unit
);
2040 printed
+= scnprintf(bf
, sizeof(bf
), ": %ld%c%schunks LOST!",
2041 nr_events
, unit
, unit
== ' ' ? "" : " ");
2045 slsmg_write_nstring(warn
, browser
->width
- printed
);
2048 menu
->selection
= evsel
;
2051 static int perf_evsel_menu__run(struct perf_evsel_menu
*menu
,
2052 int nr_events
, const char *help
,
2053 struct hist_browser_timer
*hbt
)
2055 struct perf_evlist
*evlist
= menu
->b
.priv
;
2056 struct perf_evsel
*pos
;
2057 const char *title
= "Available samples";
2058 int delay_secs
= hbt
? hbt
->refresh
: 0;
2061 if (ui_browser__show(&menu
->b
, title
,
2062 "ESC: exit, ENTER|->: Browse histograms") < 0)
2066 key
= ui_browser__run(&menu
->b
, delay_secs
);
2070 hbt
->timer(hbt
->arg
);
2072 if (!menu
->lost_events_warned
&& menu
->lost_events
) {
2073 ui_browser__warn_lost_events(&menu
->b
);
2074 menu
->lost_events_warned
= true;
2079 if (!menu
->selection
)
2081 pos
= menu
->selection
;
2083 perf_evlist__set_selected(evlist
, pos
);
2085 * Give the calling tool a chance to populate the non
2086 * default evsel resorted hists tree.
2089 hbt
->timer(hbt
->arg
);
2090 key
= perf_evsel__hists_browse(pos
, nr_events
, help
,
2094 ui_browser__show_title(&menu
->b
, title
);
2097 if (pos
->node
.next
== &evlist
->entries
)
2098 pos
= perf_evlist__first(evlist
);
2100 pos
= perf_evsel__next(pos
);
2103 if (pos
->node
.prev
== &evlist
->entries
)
2104 pos
= perf_evlist__last(evlist
);
2106 pos
= perf_evsel__prev(pos
);
2109 if (!ui_browser__dialog_yesno(&menu
->b
,
2110 "Do you really want to exit?"))
2113 case K_SWITCH_INPUT_DATA
:
2123 if (!ui_browser__dialog_yesno(&menu
->b
,
2124 "Do you really want to exit?"))
2136 ui_browser__hide(&menu
->b
);
2140 static bool filter_group_entries(struct ui_browser
*browser __maybe_unused
,
2143 struct perf_evsel
*evsel
= list_entry(entry
, struct perf_evsel
, node
);
2145 if (symbol_conf
.event_group
&& !perf_evsel__is_group_leader(evsel
))
2151 static int __perf_evlist__tui_browse_hists(struct perf_evlist
*evlist
,
2152 int nr_entries
, const char *help
,
2153 struct hist_browser_timer
*hbt
,
2155 struct perf_session_env
*env
)
2157 struct perf_evsel
*pos
;
2158 struct perf_evsel_menu menu
= {
2160 .entries
= &evlist
->entries
,
2161 .refresh
= ui_browser__list_head_refresh
,
2162 .seek
= ui_browser__list_head_seek
,
2163 .write
= perf_evsel_menu__write
,
2164 .filter
= filter_group_entries
,
2165 .nr_entries
= nr_entries
,
2168 .min_pcnt
= min_pcnt
,
2172 ui_helpline__push("Press ESC to exit");
2174 evlist__for_each(evlist
, pos
) {
2175 const char *ev_name
= perf_evsel__name(pos
);
2176 size_t line_len
= strlen(ev_name
) + 7;
2178 if (menu
.b
.width
< line_len
)
2179 menu
.b
.width
= line_len
;
2182 return perf_evsel_menu__run(&menu
, nr_entries
, help
, hbt
);
2185 int perf_evlist__tui_browse_hists(struct perf_evlist
*evlist
, const char *help
,
2186 struct hist_browser_timer
*hbt
,
2188 struct perf_session_env
*env
)
2190 int nr_entries
= evlist
->nr_entries
;
2193 if (nr_entries
== 1) {
2194 struct perf_evsel
*first
= perf_evlist__first(evlist
);
2196 return perf_evsel__hists_browse(first
, nr_entries
, help
,
2197 false, hbt
, min_pcnt
,
2201 if (symbol_conf
.event_group
) {
2202 struct perf_evsel
*pos
;
2205 evlist__for_each(evlist
, pos
) {
2206 if (perf_evsel__is_group_leader(pos
))
2210 if (nr_entries
== 1)
2214 return __perf_evlist__tui_browse_hists(evlist
, nr_entries
, help
,
2215 hbt
, min_pcnt
, env
);