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 "../../arch/common.h"
15 #include "../browser.h"
16 #include "../helpline.h"
24 struct hist_entry
*he_selection
;
25 struct map_symbol
*selection
;
29 u64 nr_non_filtered_entries
;
30 u64 nr_callchain_rows
;
33 extern void hist_browser__init_hpp(void);
35 static int hists__browser_title(struct hists
*hists
, char *bf
, size_t size
,
37 static void hist_browser__update_nr_entries(struct hist_browser
*hb
);
39 static struct rb_node
*hists__filter_entries(struct rb_node
*nd
,
43 static bool hist_browser__has_filter(struct hist_browser
*hb
)
45 return hists__has_filter(hb
->hists
) || hb
->min_pcnt
;
48 static u32
hist_browser__nr_entries(struct hist_browser
*hb
)
52 if (hist_browser__has_filter(hb
))
53 nr_entries
= hb
->nr_non_filtered_entries
;
55 nr_entries
= hb
->hists
->nr_entries
;
57 return nr_entries
+ hb
->nr_callchain_rows
;
60 static void hist_browser__refresh_dimensions(struct hist_browser
*browser
)
62 /* 3 == +/- toggle symbol before actual hist_entry rendering */
63 browser
->b
.width
= 3 + (hists__sort_list_width(browser
->hists
) +
67 static void hist_browser__reset(struct hist_browser
*browser
)
70 * The hists__remove_entry_filter() already folds non-filtered
71 * entries so we can assume it has 0 callchain rows.
73 browser
->nr_callchain_rows
= 0;
75 hist_browser__update_nr_entries(browser
);
76 browser
->b
.nr_entries
= hist_browser__nr_entries(browser
);
77 hist_browser__refresh_dimensions(browser
);
78 ui_browser__reset_index(&browser
->b
);
81 static char tree__folded_sign(bool unfolded
)
83 return unfolded
? '-' : '+';
86 static char map_symbol__folded(const struct map_symbol
*ms
)
88 return ms
->has_children
? tree__folded_sign(ms
->unfolded
) : ' ';
91 static char hist_entry__folded(const struct hist_entry
*he
)
93 return map_symbol__folded(&he
->ms
);
96 static char callchain_list__folded(const struct callchain_list
*cl
)
98 return map_symbol__folded(&cl
->ms
);
101 static void map_symbol__set_folding(struct map_symbol
*ms
, bool unfold
)
103 ms
->unfolded
= unfold
? ms
->has_children
: false;
106 static int callchain_node__count_rows_rb_tree(struct callchain_node
*node
)
111 for (nd
= rb_first(&node
->rb_root
); nd
; nd
= rb_next(nd
)) {
112 struct callchain_node
*child
= rb_entry(nd
, struct callchain_node
, rb_node
);
113 struct callchain_list
*chain
;
114 char folded_sign
= ' '; /* No children */
116 list_for_each_entry(chain
, &child
->val
, list
) {
118 /* We need this because we may not have children */
119 folded_sign
= callchain_list__folded(chain
);
120 if (folded_sign
== '+')
124 if (folded_sign
== '-') /* Have children and they're unfolded */
125 n
+= callchain_node__count_rows_rb_tree(child
);
131 static int callchain_node__count_rows(struct callchain_node
*node
)
133 struct callchain_list
*chain
;
134 bool unfolded
= false;
137 list_for_each_entry(chain
, &node
->val
, list
) {
139 unfolded
= chain
->ms
.unfolded
;
143 n
+= callchain_node__count_rows_rb_tree(node
);
148 static int callchain__count_rows(struct rb_root
*chain
)
153 for (nd
= rb_first(chain
); nd
; nd
= rb_next(nd
)) {
154 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
155 n
+= callchain_node__count_rows(node
);
161 static bool map_symbol__toggle_fold(struct map_symbol
*ms
)
166 if (!ms
->has_children
)
169 ms
->unfolded
= !ms
->unfolded
;
173 static void callchain_node__init_have_children_rb_tree(struct callchain_node
*node
)
175 struct rb_node
*nd
= rb_first(&node
->rb_root
);
177 for (nd
= rb_first(&node
->rb_root
); nd
; nd
= rb_next(nd
)) {
178 struct callchain_node
*child
= rb_entry(nd
, struct callchain_node
, rb_node
);
179 struct callchain_list
*chain
;
182 list_for_each_entry(chain
, &child
->val
, list
) {
185 chain
->ms
.has_children
= chain
->list
.next
!= &child
->val
||
186 !RB_EMPTY_ROOT(&child
->rb_root
);
188 chain
->ms
.has_children
= chain
->list
.next
== &child
->val
&&
189 !RB_EMPTY_ROOT(&child
->rb_root
);
192 callchain_node__init_have_children_rb_tree(child
);
196 static void callchain_node__init_have_children(struct callchain_node
*node
)
198 struct callchain_list
*chain
;
200 list_for_each_entry(chain
, &node
->val
, list
)
201 chain
->ms
.has_children
= !RB_EMPTY_ROOT(&node
->rb_root
);
203 callchain_node__init_have_children_rb_tree(node
);
206 static void callchain__init_have_children(struct rb_root
*root
)
210 for (nd
= rb_first(root
); nd
; nd
= rb_next(nd
)) {
211 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
212 callchain_node__init_have_children(node
);
216 static void hist_entry__init_have_children(struct hist_entry
*he
)
218 if (!he
->init_have_children
) {
219 he
->ms
.has_children
= !RB_EMPTY_ROOT(&he
->sorted_chain
);
220 callchain__init_have_children(&he
->sorted_chain
);
221 he
->init_have_children
= true;
225 static bool hist_browser__toggle_fold(struct hist_browser
*browser
)
227 if (map_symbol__toggle_fold(browser
->selection
)) {
228 struct hist_entry
*he
= browser
->he_selection
;
230 hist_entry__init_have_children(he
);
231 browser
->b
.nr_entries
-= he
->nr_rows
;
232 browser
->nr_callchain_rows
-= he
->nr_rows
;
235 he
->nr_rows
= callchain__count_rows(&he
->sorted_chain
);
239 browser
->b
.nr_entries
+= he
->nr_rows
;
240 browser
->nr_callchain_rows
+= he
->nr_rows
;
245 /* If it doesn't have children, no toggling performed */
249 static int callchain_node__set_folding_rb_tree(struct callchain_node
*node
, bool unfold
)
254 for (nd
= rb_first(&node
->rb_root
); nd
; nd
= rb_next(nd
)) {
255 struct callchain_node
*child
= rb_entry(nd
, struct callchain_node
, rb_node
);
256 struct callchain_list
*chain
;
257 bool has_children
= false;
259 list_for_each_entry(chain
, &child
->val
, list
) {
261 map_symbol__set_folding(&chain
->ms
, unfold
);
262 has_children
= chain
->ms
.has_children
;
266 n
+= callchain_node__set_folding_rb_tree(child
, unfold
);
272 static int callchain_node__set_folding(struct callchain_node
*node
, bool unfold
)
274 struct callchain_list
*chain
;
275 bool has_children
= false;
278 list_for_each_entry(chain
, &node
->val
, list
) {
280 map_symbol__set_folding(&chain
->ms
, unfold
);
281 has_children
= chain
->ms
.has_children
;
285 n
+= callchain_node__set_folding_rb_tree(node
, unfold
);
290 static int callchain__set_folding(struct rb_root
*chain
, bool unfold
)
295 for (nd
= rb_first(chain
); nd
; nd
= rb_next(nd
)) {
296 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
297 n
+= callchain_node__set_folding(node
, unfold
);
303 static void hist_entry__set_folding(struct hist_entry
*he
, bool unfold
)
305 hist_entry__init_have_children(he
);
306 map_symbol__set_folding(&he
->ms
, unfold
);
308 if (he
->ms
.has_children
) {
309 int n
= callchain__set_folding(&he
->sorted_chain
, unfold
);
310 he
->nr_rows
= unfold
? n
: 0;
316 __hist_browser__set_folding(struct hist_browser
*browser
, bool unfold
)
319 struct hists
*hists
= browser
->hists
;
321 for (nd
= rb_first(&hists
->entries
);
322 (nd
= hists__filter_entries(nd
, hists
, browser
->min_pcnt
)) != NULL
;
324 struct hist_entry
*he
= rb_entry(nd
, struct hist_entry
, rb_node
);
325 hist_entry__set_folding(he
, unfold
);
326 browser
->nr_callchain_rows
+= he
->nr_rows
;
330 static void hist_browser__set_folding(struct hist_browser
*browser
, bool unfold
)
332 browser
->nr_callchain_rows
= 0;
333 __hist_browser__set_folding(browser
, unfold
);
335 browser
->b
.nr_entries
= hist_browser__nr_entries(browser
);
336 /* Go to the start, we may be way after valid entries after a collapse */
337 ui_browser__reset_index(&browser
->b
);
340 static void ui_browser__warn_lost_events(struct ui_browser
*browser
)
342 ui_browser__warning(browser
, 4,
343 "Events are being lost, check IO/CPU overload!\n\n"
344 "You may want to run 'perf' using a RT scheduler policy:\n\n"
345 " perf top -r 80\n\n"
346 "Or reduce the sampling frequency.");
349 static int hist_browser__run(struct hist_browser
*browser
, const char *ev_name
,
350 struct hist_browser_timer
*hbt
)
354 int delay_secs
= hbt
? hbt
->refresh
: 0;
356 browser
->b
.entries
= &browser
->hists
->entries
;
357 browser
->b
.nr_entries
= hist_browser__nr_entries(browser
);
359 hist_browser__refresh_dimensions(browser
);
360 hists__browser_title(browser
->hists
, title
, sizeof(title
), ev_name
);
362 if (ui_browser__show(&browser
->b
, title
,
363 "Press '?' for help on key bindings") < 0)
367 key
= ui_browser__run(&browser
->b
, delay_secs
);
372 hbt
->timer(hbt
->arg
);
374 if (hist_browser__has_filter(browser
))
375 hist_browser__update_nr_entries(browser
);
377 nr_entries
= hist_browser__nr_entries(browser
);
378 ui_browser__update_nr_entries(&browser
->b
, nr_entries
);
380 if (browser
->hists
->stats
.nr_lost_warned
!=
381 browser
->hists
->stats
.nr_events
[PERF_RECORD_LOST
]) {
382 browser
->hists
->stats
.nr_lost_warned
=
383 browser
->hists
->stats
.nr_events
[PERF_RECORD_LOST
];
384 ui_browser__warn_lost_events(&browser
->b
);
387 hists__browser_title(browser
->hists
, title
, sizeof(title
), ev_name
);
388 ui_browser__show_title(&browser
->b
, title
);
391 case 'D': { /* Debug */
393 struct hist_entry
*h
= rb_entry(browser
->b
.top
,
394 struct hist_entry
, rb_node
);
396 ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
397 seq
++, browser
->b
.nr_entries
,
398 browser
->hists
->nr_entries
,
402 h
->row_offset
, h
->nr_rows
);
406 /* Collapse the whole world. */
407 hist_browser__set_folding(browser
, false);
410 /* Expand the whole world. */
411 hist_browser__set_folding(browser
, true);
414 if (hist_browser__toggle_fold(browser
))
422 ui_browser__hide(&browser
->b
);
426 static char *callchain_list__sym_name(struct callchain_list
*cl
,
427 char *bf
, size_t bfsize
, bool show_dso
)
432 printed
= scnprintf(bf
, bfsize
, "%s", cl
->ms
.sym
->name
);
434 printed
= scnprintf(bf
, bfsize
, "%#" PRIx64
, cl
->ip
);
437 scnprintf(bf
+ printed
, bfsize
- printed
, " %s",
438 cl
->ms
.map
? cl
->ms
.map
->dso
->short_name
: "unknown");
443 #define LEVEL_OFFSET_STEP 3
445 static int hist_browser__show_callchain_node_rb_tree(struct hist_browser
*browser
,
446 struct callchain_node
*chain_node
,
447 u64 total
, int level
,
450 bool *is_current_entry
)
452 struct rb_node
*node
;
453 int first_row
= row
, width
, offset
= level
* LEVEL_OFFSET_STEP
;
454 u64 new_total
, remaining
;
456 if (callchain_param
.mode
== CHAIN_GRAPH_REL
)
457 new_total
= chain_node
->children_hit
;
461 remaining
= new_total
;
462 node
= rb_first(&chain_node
->rb_root
);
464 struct callchain_node
*child
= rb_entry(node
, struct callchain_node
, rb_node
);
465 struct rb_node
*next
= rb_next(node
);
466 u64 cumul
= callchain_cumul_hits(child
);
467 struct callchain_list
*chain
;
468 char folded_sign
= ' ';
470 int extra_offset
= 0;
474 list_for_each_entry(chain
, &child
->val
, list
) {
475 char bf
[1024], *alloc_str
;
478 bool was_first
= first
;
483 extra_offset
= LEVEL_OFFSET_STEP
;
485 folded_sign
= callchain_list__folded(chain
);
486 if (*row_offset
!= 0) {
492 str
= callchain_list__sym_name(chain
, bf
, sizeof(bf
),
495 double percent
= cumul
* 100.0 / new_total
;
497 if (asprintf(&alloc_str
, "%2.2f%% %s", percent
, str
) < 0)
498 str
= "Not enough memory!";
503 color
= HE_COLORSET_NORMAL
;
504 width
= browser
->b
.width
- (offset
+ extra_offset
+ 2);
505 if (ui_browser__is_current_entry(&browser
->b
, row
)) {
506 browser
->selection
= &chain
->ms
;
507 color
= HE_COLORSET_SELECTED
;
508 *is_current_entry
= true;
511 ui_browser__set_color(&browser
->b
, color
);
512 ui_browser__gotorc(&browser
->b
, row
, 0);
513 slsmg_write_nstring(" ", offset
+ extra_offset
);
514 slsmg_printf("%c ", folded_sign
);
515 slsmg_write_nstring(str
, width
);
518 if (++row
== browser
->b
.height
)
521 if (folded_sign
== '+')
525 if (folded_sign
== '-') {
526 const int new_level
= level
+ (extra_offset
? 2 : 1);
527 row
+= hist_browser__show_callchain_node_rb_tree(browser
, child
, new_total
,
528 new_level
, row
, row_offset
,
531 if (row
== browser
->b
.height
)
536 return row
- first_row
;
539 static int hist_browser__show_callchain_node(struct hist_browser
*browser
,
540 struct callchain_node
*node
,
541 int level
, unsigned short row
,
543 bool *is_current_entry
)
545 struct callchain_list
*chain
;
547 offset
= level
* LEVEL_OFFSET_STEP
,
548 width
= browser
->b
.width
- offset
;
549 char folded_sign
= ' ';
551 list_for_each_entry(chain
, &node
->val
, list
) {
555 folded_sign
= callchain_list__folded(chain
);
557 if (*row_offset
!= 0) {
562 color
= HE_COLORSET_NORMAL
;
563 if (ui_browser__is_current_entry(&browser
->b
, row
)) {
564 browser
->selection
= &chain
->ms
;
565 color
= HE_COLORSET_SELECTED
;
566 *is_current_entry
= true;
569 s
= callchain_list__sym_name(chain
, bf
, sizeof(bf
),
571 ui_browser__gotorc(&browser
->b
, row
, 0);
572 ui_browser__set_color(&browser
->b
, color
);
573 slsmg_write_nstring(" ", offset
);
574 slsmg_printf("%c ", folded_sign
);
575 slsmg_write_nstring(s
, width
- 2);
577 if (++row
== browser
->b
.height
)
581 if (folded_sign
== '-')
582 row
+= hist_browser__show_callchain_node_rb_tree(browser
, node
,
583 browser
->hists
->stats
.total_period
,
588 return row
- first_row
;
591 static int hist_browser__show_callchain(struct hist_browser
*browser
,
592 struct rb_root
*chain
,
593 int level
, unsigned short row
,
595 bool *is_current_entry
)
600 for (nd
= rb_first(chain
); nd
; nd
= rb_next(nd
)) {
601 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
603 row
+= hist_browser__show_callchain_node(browser
, node
, level
,
606 if (row
== browser
->b
.height
)
610 return row
- first_row
;
614 struct ui_browser
*b
;
619 static int __hpp__slsmg_color_printf(struct perf_hpp
*hpp
, const char *fmt
, ...)
621 struct hpp_arg
*arg
= hpp
->ptr
;
627 percent
= va_arg(args
, double);
630 ui_browser__set_percent_color(arg
->b
, percent
, arg
->current_entry
);
632 ret
= scnprintf(hpp
->buf
, hpp
->size
, fmt
, percent
);
633 slsmg_printf("%s", hpp
->buf
);
635 advance_hpp(hpp
, ret
);
639 #define __HPP_COLOR_PERCENT_FN(_type, _field) \
640 static u64 __hpp_get_##_field(struct hist_entry *he) \
642 return he->stat._field; \
646 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,\
647 struct perf_hpp *hpp, \
648 struct hist_entry *he) \
650 return __hpp__fmt(hpp, he, __hpp_get_##_field, " %6.2f%%", \
651 __hpp__slsmg_color_printf, true); \
654 __HPP_COLOR_PERCENT_FN(overhead
, period
)
655 __HPP_COLOR_PERCENT_FN(overhead_sys
, period_sys
)
656 __HPP_COLOR_PERCENT_FN(overhead_us
, period_us
)
657 __HPP_COLOR_PERCENT_FN(overhead_guest_sys
, period_guest_sys
)
658 __HPP_COLOR_PERCENT_FN(overhead_guest_us
, period_guest_us
)
660 #undef __HPP_COLOR_PERCENT_FN
662 void hist_browser__init_hpp(void)
664 perf_hpp__format
[PERF_HPP__OVERHEAD
].color
=
665 hist_browser__hpp_color_overhead
;
666 perf_hpp__format
[PERF_HPP__OVERHEAD_SYS
].color
=
667 hist_browser__hpp_color_overhead_sys
;
668 perf_hpp__format
[PERF_HPP__OVERHEAD_US
].color
=
669 hist_browser__hpp_color_overhead_us
;
670 perf_hpp__format
[PERF_HPP__OVERHEAD_GUEST_SYS
].color
=
671 hist_browser__hpp_color_overhead_guest_sys
;
672 perf_hpp__format
[PERF_HPP__OVERHEAD_GUEST_US
].color
=
673 hist_browser__hpp_color_overhead_guest_us
;
676 static int hist_browser__show_entry(struct hist_browser
*browser
,
677 struct hist_entry
*entry
,
682 int width
= browser
->b
.width
;
683 char folded_sign
= ' ';
684 bool current_entry
= ui_browser__is_current_entry(&browser
->b
, row
);
685 off_t row_offset
= entry
->row_offset
;
687 struct perf_hpp_fmt
*fmt
;
690 browser
->he_selection
= entry
;
691 browser
->selection
= &entry
->ms
;
694 if (symbol_conf
.use_callchain
) {
695 hist_entry__init_have_children(entry
);
696 folded_sign
= hist_entry__folded(entry
);
699 if (row_offset
== 0) {
700 struct hpp_arg arg
= {
702 .folded_sign
= folded_sign
,
703 .current_entry
= current_entry
,
705 struct perf_hpp hpp
= {
711 ui_browser__gotorc(&browser
->b
, row
, 0);
713 perf_hpp__for_each_format(fmt
) {
714 if (current_entry
&& browser
->b
.navkeypressed
) {
715 ui_browser__set_color(&browser
->b
,
716 HE_COLORSET_SELECTED
);
718 ui_browser__set_color(&browser
->b
,
723 if (symbol_conf
.use_callchain
) {
724 slsmg_printf("%c ", folded_sign
);
734 width
-= fmt
->color(fmt
, &hpp
, entry
);
736 width
-= fmt
->entry(fmt
, &hpp
, entry
);
737 slsmg_printf("%s", s
);
741 /* The scroll bar isn't being used */
742 if (!browser
->b
.navkeypressed
)
745 slsmg_write_nstring("", width
);
752 if (folded_sign
== '-' && row
!= browser
->b
.height
) {
753 printed
+= hist_browser__show_callchain(browser
, &entry
->sorted_chain
,
757 browser
->he_selection
= entry
;
763 static void ui_browser__hists_init_top(struct ui_browser
*browser
)
765 if (browser
->top
== NULL
) {
766 struct hist_browser
*hb
;
768 hb
= container_of(browser
, struct hist_browser
, b
);
769 browser
->top
= rb_first(&hb
->hists
->entries
);
773 static unsigned int hist_browser__refresh(struct ui_browser
*browser
)
777 struct hist_browser
*hb
= container_of(browser
, struct hist_browser
, b
);
779 ui_browser__hists_init_top(browser
);
781 for (nd
= browser
->top
; nd
; nd
= rb_next(nd
)) {
782 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
783 u64 total
= hists__total_period(h
->hists
);
790 percent
= h
->stat
.period
* 100.0 / total
;
792 if (percent
< hb
->min_pcnt
)
795 row
+= hist_browser__show_entry(hb
, h
, row
);
796 if (row
== browser
->height
)
803 static struct rb_node
*hists__filter_entries(struct rb_node
*nd
,
808 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
809 u64 total
= hists__total_period(hists
);
813 percent
= h
->stat
.period
* 100.0 / total
;
815 if (percent
< min_pcnt
)
827 static struct rb_node
*hists__filter_prev_entries(struct rb_node
*nd
,
832 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
833 u64 total
= hists__total_period(hists
);
837 percent
= h
->stat
.period
* 100.0 / total
;
839 if (!h
->filtered
&& percent
>= min_pcnt
)
848 static void ui_browser__hists_seek(struct ui_browser
*browser
,
849 off_t offset
, int whence
)
851 struct hist_entry
*h
;
854 struct hist_browser
*hb
;
856 hb
= container_of(browser
, struct hist_browser
, b
);
858 if (browser
->nr_entries
== 0)
861 ui_browser__hists_init_top(browser
);
865 nd
= hists__filter_entries(rb_first(browser
->entries
),
866 hb
->hists
, hb
->min_pcnt
);
872 nd
= hists__filter_prev_entries(rb_last(browser
->entries
),
873 hb
->hists
, hb
->min_pcnt
);
881 * Moves not relative to the first visible entry invalidates its
884 h
= rb_entry(browser
->top
, struct hist_entry
, rb_node
);
888 * Here we have to check if nd is expanded (+), if it is we can't go
889 * the next top level hist_entry, instead we must compute an offset of
890 * what _not_ to show and not change the first visible entry.
892 * This offset increments when we are going from top to bottom and
893 * decreases when we're going from bottom to top.
895 * As we don't have backpointers to the top level in the callchains
896 * structure, we need to always print the whole hist_entry callchain,
897 * skipping the first ones that are before the first visible entry
898 * and stop when we printed enough lines to fill the screen.
903 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
904 if (h
->ms
.unfolded
) {
905 u16 remaining
= h
->nr_rows
- h
->row_offset
;
906 if (offset
> remaining
) {
910 h
->row_offset
+= offset
;
916 nd
= hists__filter_entries(rb_next(nd
), hb
->hists
,
922 } while (offset
!= 0);
923 } else if (offset
< 0) {
925 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
926 if (h
->ms
.unfolded
) {
928 if (-offset
> h
->row_offset
) {
929 offset
+= h
->row_offset
;
932 h
->row_offset
+= offset
;
938 if (-offset
> h
->nr_rows
) {
939 offset
+= h
->nr_rows
;
942 h
->row_offset
= h
->nr_rows
+ offset
;
950 nd
= hists__filter_prev_entries(rb_prev(nd
), hb
->hists
,
958 * Last unfiltered hist_entry, check if it is
959 * unfolded, if it is then we should have
960 * row_offset at its last entry.
962 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
964 h
->row_offset
= h
->nr_rows
;
971 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
976 static int hist_browser__fprintf_callchain_node_rb_tree(struct hist_browser
*browser
,
977 struct callchain_node
*chain_node
,
978 u64 total
, int level
,
981 struct rb_node
*node
;
982 int offset
= level
* LEVEL_OFFSET_STEP
;
983 u64 new_total
, remaining
;
986 if (callchain_param
.mode
== CHAIN_GRAPH_REL
)
987 new_total
= chain_node
->children_hit
;
991 remaining
= new_total
;
992 node
= rb_first(&chain_node
->rb_root
);
994 struct callchain_node
*child
= rb_entry(node
, struct callchain_node
, rb_node
);
995 struct rb_node
*next
= rb_next(node
);
996 u64 cumul
= callchain_cumul_hits(child
);
997 struct callchain_list
*chain
;
998 char folded_sign
= ' ';
1000 int extra_offset
= 0;
1004 list_for_each_entry(chain
, &child
->val
, list
) {
1005 char bf
[1024], *alloc_str
;
1007 bool was_first
= first
;
1012 extra_offset
= LEVEL_OFFSET_STEP
;
1014 folded_sign
= callchain_list__folded(chain
);
1017 str
= callchain_list__sym_name(chain
, bf
, sizeof(bf
),
1020 double percent
= cumul
* 100.0 / new_total
;
1022 if (asprintf(&alloc_str
, "%2.2f%% %s", percent
, str
) < 0)
1023 str
= "Not enough memory!";
1028 printed
+= fprintf(fp
, "%*s%c %s\n", offset
+ extra_offset
, " ", folded_sign
, str
);
1030 if (folded_sign
== '+')
1034 if (folded_sign
== '-') {
1035 const int new_level
= level
+ (extra_offset
? 2 : 1);
1036 printed
+= hist_browser__fprintf_callchain_node_rb_tree(browser
, child
, new_total
,
1046 static int hist_browser__fprintf_callchain_node(struct hist_browser
*browser
,
1047 struct callchain_node
*node
,
1048 int level
, FILE *fp
)
1050 struct callchain_list
*chain
;
1051 int offset
= level
* LEVEL_OFFSET_STEP
;
1052 char folded_sign
= ' ';
1055 list_for_each_entry(chain
, &node
->val
, list
) {
1058 folded_sign
= callchain_list__folded(chain
);
1059 s
= callchain_list__sym_name(chain
, bf
, sizeof(bf
), browser
->show_dso
);
1060 printed
+= fprintf(fp
, "%*s%c %s\n", offset
, " ", folded_sign
, s
);
1063 if (folded_sign
== '-')
1064 printed
+= hist_browser__fprintf_callchain_node_rb_tree(browser
, node
,
1065 browser
->hists
->stats
.total_period
,
1070 static int hist_browser__fprintf_callchain(struct hist_browser
*browser
,
1071 struct rb_root
*chain
, int level
, FILE *fp
)
1076 for (nd
= rb_first(chain
); nd
; nd
= rb_next(nd
)) {
1077 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
1079 printed
+= hist_browser__fprintf_callchain_node(browser
, node
, level
, fp
);
1085 static int hist_browser__fprintf_entry(struct hist_browser
*browser
,
1086 struct hist_entry
*he
, FILE *fp
)
1090 char folded_sign
= ' ';
1091 struct perf_hpp hpp
= {
1095 struct perf_hpp_fmt
*fmt
;
1099 if (symbol_conf
.use_callchain
)
1100 folded_sign
= hist_entry__folded(he
);
1102 if (symbol_conf
.use_callchain
)
1103 printed
+= fprintf(fp
, "%c ", folded_sign
);
1105 perf_hpp__for_each_format(fmt
) {
1107 ret
= scnprintf(hpp
.buf
, hpp
.size
, " ");
1108 advance_hpp(&hpp
, ret
);
1112 ret
= fmt
->entry(fmt
, &hpp
, he
);
1113 advance_hpp(&hpp
, ret
);
1115 printed
+= fprintf(fp
, "%s\n", rtrim(s
));
1117 if (folded_sign
== '-')
1118 printed
+= hist_browser__fprintf_callchain(browser
, &he
->sorted_chain
, 1, fp
);
1123 static int hist_browser__fprintf(struct hist_browser
*browser
, FILE *fp
)
1125 struct rb_node
*nd
= hists__filter_entries(rb_first(browser
->b
.entries
),
1131 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1133 printed
+= hist_browser__fprintf_entry(browser
, h
, fp
);
1134 nd
= hists__filter_entries(rb_next(nd
), browser
->hists
,
1141 static int hist_browser__dump(struct hist_browser
*browser
)
1147 scnprintf(filename
, sizeof(filename
), "perf.hist.%d", browser
->print_seq
);
1148 if (access(filename
, F_OK
))
1151 * XXX: Just an arbitrary lazy upper limit
1153 if (++browser
->print_seq
== 8192) {
1154 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1159 fp
= fopen(filename
, "w");
1162 const char *err
= strerror_r(errno
, bf
, sizeof(bf
));
1163 ui_helpline__fpush("Couldn't write to %s: %s", filename
, err
);
1167 ++browser
->print_seq
;
1168 hist_browser__fprintf(browser
, fp
);
1170 ui_helpline__fpush("%s written!", filename
);
1175 static struct hist_browser
*hist_browser__new(struct hists
*hists
)
1177 struct hist_browser
*browser
= zalloc(sizeof(*browser
));
1180 browser
->hists
= hists
;
1181 browser
->b
.refresh
= hist_browser__refresh
;
1182 browser
->b
.seek
= ui_browser__hists_seek
;
1183 browser
->b
.use_navkeypressed
= true;
1189 static void hist_browser__delete(struct hist_browser
*browser
)
1194 static struct hist_entry
*hist_browser__selected_entry(struct hist_browser
*browser
)
1196 return browser
->he_selection
;
1199 static struct thread
*hist_browser__selected_thread(struct hist_browser
*browser
)
1201 return browser
->he_selection
->thread
;
1204 static int hists__browser_title(struct hists
*hists
, char *bf
, size_t size
,
1205 const char *ev_name
)
1209 const struct dso
*dso
= hists
->dso_filter
;
1210 const struct thread
*thread
= hists
->thread_filter
;
1211 unsigned long nr_samples
= hists
->stats
.nr_events
[PERF_RECORD_SAMPLE
];
1212 u64 nr_events
= hists
->stats
.total_period
;
1213 struct perf_evsel
*evsel
= hists_to_evsel(hists
);
1215 size_t buflen
= sizeof(buf
);
1217 if (symbol_conf
.filter_relative
) {
1218 nr_samples
= hists
->stats
.nr_non_filtered_samples
;
1219 nr_events
= hists
->stats
.total_non_filtered_period
;
1222 if (perf_evsel__is_group_event(evsel
)) {
1223 struct perf_evsel
*pos
;
1225 perf_evsel__group_desc(evsel
, buf
, buflen
);
1228 for_each_group_member(pos
, evsel
) {
1229 if (symbol_conf
.filter_relative
) {
1230 nr_samples
+= pos
->hists
.stats
.nr_non_filtered_samples
;
1231 nr_events
+= pos
->hists
.stats
.total_non_filtered_period
;
1233 nr_samples
+= pos
->hists
.stats
.nr_events
[PERF_RECORD_SAMPLE
];
1234 nr_events
+= pos
->hists
.stats
.total_period
;
1239 nr_samples
= convert_unit(nr_samples
, &unit
);
1240 printed
= scnprintf(bf
, size
,
1241 "Samples: %lu%c of event '%s', Event count (approx.): %lu",
1242 nr_samples
, unit
, ev_name
, nr_events
);
1245 if (hists
->uid_filter_str
)
1246 printed
+= snprintf(bf
+ printed
, size
- printed
,
1247 ", UID: %s", hists
->uid_filter_str
);
1249 printed
+= scnprintf(bf
+ printed
, size
- printed
,
1251 (thread
->comm_set
? thread__comm_str(thread
) : ""),
1254 printed
+= scnprintf(bf
+ printed
, size
- printed
,
1255 ", DSO: %s", dso
->short_name
);
1259 static inline void free_popup_options(char **options
, int n
)
1263 for (i
= 0; i
< n
; ++i
)
1267 /* Check whether the browser is for 'top' or 'report' */
1268 static inline bool is_report_browser(void *timer
)
1270 return timer
== NULL
;
1274 * Only runtime switching of perf data file will make "input_name" point
1275 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1276 * whether we need to call free() for current "input_name" during the switch.
1278 static bool is_input_name_malloced
= false;
1280 static int switch_data_file(void)
1282 char *pwd
, *options
[32], *abs_path
[32], *tmp
;
1284 int nr_options
= 0, choice
= -1, ret
= -1;
1285 struct dirent
*dent
;
1287 pwd
= getenv("PWD");
1291 pwd_dir
= opendir(pwd
);
1295 memset(options
, 0, sizeof(options
));
1296 memset(options
, 0, sizeof(abs_path
));
1298 while ((dent
= readdir(pwd_dir
))) {
1299 char path
[PATH_MAX
];
1301 char *name
= dent
->d_name
;
1304 if (!(dent
->d_type
== DT_REG
))
1307 snprintf(path
, sizeof(path
), "%s/%s", pwd
, name
);
1309 file
= fopen(path
, "r");
1313 if (fread(&magic
, 1, 8, file
) < 8)
1314 goto close_file_and_continue
;
1316 if (is_perf_magic(magic
)) {
1317 options
[nr_options
] = strdup(name
);
1318 if (!options
[nr_options
])
1319 goto close_file_and_continue
;
1321 abs_path
[nr_options
] = strdup(path
);
1322 if (!abs_path
[nr_options
]) {
1323 zfree(&options
[nr_options
]);
1324 ui__warning("Can't search all data files due to memory shortage.\n");
1332 close_file_and_continue
:
1334 if (nr_options
>= 32) {
1335 ui__warning("Too many perf data files in PWD!\n"
1336 "Only the first 32 files will be listed.\n");
1343 choice
= ui__popup_menu(nr_options
, options
);
1344 if (choice
< nr_options
&& choice
>= 0) {
1345 tmp
= strdup(abs_path
[choice
]);
1347 if (is_input_name_malloced
)
1348 free((void *)input_name
);
1350 is_input_name_malloced
= true;
1353 ui__warning("Data switch failed due to memory shortage!\n");
1357 free_popup_options(options
, nr_options
);
1358 free_popup_options(abs_path
, nr_options
);
1362 static void hist_browser__update_nr_entries(struct hist_browser
*hb
)
1365 struct rb_node
*nd
= rb_first(&hb
->hists
->entries
);
1367 if (hb
->min_pcnt
== 0) {
1368 hb
->nr_non_filtered_entries
= hb
->hists
->nr_non_filtered_entries
;
1372 while ((nd
= hists__filter_entries(nd
, hb
->hists
,
1373 hb
->min_pcnt
)) != NULL
) {
1378 hb
->nr_non_filtered_entries
= nr_entries
;
1381 static int perf_evsel__hists_browse(struct perf_evsel
*evsel
, int nr_events
,
1382 const char *helpline
, const char *ev_name
,
1384 struct hist_browser_timer
*hbt
,
1386 struct perf_session_env
*env
)
1388 struct hists
*hists
= &evsel
->hists
;
1389 struct hist_browser
*browser
= hist_browser__new(hists
);
1390 struct branch_info
*bi
;
1391 struct pstack
*fstack
;
1396 char script_opt
[64];
1397 int delay_secs
= hbt
? hbt
->refresh
: 0;
1399 #define HIST_BROWSER_HELP_COMMON \
1400 "h/?/F1 Show this window\n" \
1402 "PGDN/SPACE Navigate\n" \
1403 "q/ESC/CTRL+C Exit browser\n\n" \
1404 "For multiple event sessions:\n\n" \
1405 "TAB/UNTAB Switch events\n\n" \
1406 "For symbolic views (--sort has sym):\n\n" \
1407 "-> Zoom into DSO/Threads & Annotate current symbol\n" \
1409 "a Annotate current symbol\n" \
1410 "C Collapse all callchains\n" \
1411 "d Zoom into current DSO\n" \
1412 "E Expand all callchains\n" \
1413 "F Toggle percentage of filtered entries\n" \
1415 /* help messages are sorted by lexical order of the hotkey */
1416 const char report_help
[] = HIST_BROWSER_HELP_COMMON
1417 "i Show header information\n"
1418 "P Print histograms to perf.hist.N\n"
1419 "r Run available scripts\n"
1420 "s Switch to another data file in PWD\n"
1421 "t Zoom into current Thread\n"
1422 "V Verbose (DSO names in callchains, etc)\n"
1423 "/ Filter symbol by name";
1424 const char top_help
[] = HIST_BROWSER_HELP_COMMON
1425 "P Print histograms to perf.hist.N\n"
1426 "t Zoom into current Thread\n"
1427 "V Verbose (DSO names in callchains, etc)\n"
1428 "/ Filter symbol by name";
1430 if (browser
== NULL
)
1434 browser
->min_pcnt
= min_pcnt
;
1435 hist_browser__update_nr_entries(browser
);
1438 fstack
= pstack__new(2);
1442 ui_helpline__push(helpline
);
1444 memset(options
, 0, sizeof(options
));
1447 const struct thread
*thread
= NULL
;
1448 const struct dso
*dso
= NULL
;
1450 annotate
= -2, zoom_dso
= -2, zoom_thread
= -2,
1451 annotate_f
= -2, annotate_t
= -2, browse_map
= -2;
1452 int scripts_comm
= -2, scripts_symbol
= -2,
1453 scripts_all
= -2, switch_data
= -2;
1457 key
= hist_browser__run(browser
, ev_name
, hbt
);
1459 if (browser
->he_selection
!= NULL
) {
1460 thread
= hist_browser__selected_thread(browser
);
1461 dso
= browser
->selection
->map
? browser
->selection
->map
->dso
: NULL
;
1469 * Exit the browser, let hists__browser_tree
1470 * go to the next or previous
1472 goto out_free_stack
;
1474 if (!sort__has_sym
) {
1475 ui_browser__warning(&browser
->b
, delay_secs
* 2,
1476 "Annotation is only available for symbolic views, "
1477 "include \"sym*\" in --sort to use it.");
1481 if (browser
->selection
== NULL
||
1482 browser
->selection
->sym
== NULL
||
1483 browser
->selection
->map
->dso
->annotate_warned
)
1487 hist_browser__dump(browser
);
1492 browser
->show_dso
= !browser
->show_dso
;
1497 if (ui_browser__input_window("Symbol to show",
1498 "Please enter the name of symbol you want to see",
1499 buf
, "ENTER: OK, ESC: Cancel",
1500 delay_secs
* 2) == K_ENTER
) {
1501 hists
->symbol_filter_str
= *buf
? buf
: NULL
;
1502 hists__filter_by_symbol(hists
);
1503 hist_browser__reset(browser
);
1507 if (is_report_browser(hbt
))
1511 if (is_report_browser(hbt
))
1512 goto do_data_switch
;
1515 /* env->arch is NULL for live-mode (i.e. perf top) */
1517 tui__header_window(env
);
1520 symbol_conf
.filter_relative
^= 1;
1525 ui_browser__help_window(&browser
->b
,
1526 is_report_browser(hbt
) ? report_help
: top_help
);
1535 if (pstack__empty(fstack
)) {
1537 * Go back to the perf_evsel_menu__run or other user
1540 goto out_free_stack
;
1543 top
= pstack__pop(fstack
);
1544 if (top
== &browser
->hists
->dso_filter
)
1546 if (top
== &browser
->hists
->thread_filter
)
1547 goto zoom_out_thread
;
1552 !ui_browser__dialog_yesno(&browser
->b
,
1553 "Do you really want to exit?"))
1558 goto out_free_stack
;
1564 goto add_exit_option
;
1566 if (sort__mode
== SORT_MODE__BRANCH
) {
1567 bi
= browser
->he_selection
->branch_info
;
1568 if (browser
->selection
!= NULL
&&
1570 bi
->from
.sym
!= NULL
&&
1571 !bi
->from
.map
->dso
->annotate_warned
&&
1572 asprintf(&options
[nr_options
], "Annotate %s",
1573 bi
->from
.sym
->name
) > 0)
1574 annotate_f
= nr_options
++;
1576 if (browser
->selection
!= NULL
&&
1578 bi
->to
.sym
!= NULL
&&
1579 !bi
->to
.map
->dso
->annotate_warned
&&
1580 (bi
->to
.sym
!= bi
->from
.sym
||
1581 bi
->to
.map
->dso
!= bi
->from
.map
->dso
) &&
1582 asprintf(&options
[nr_options
], "Annotate %s",
1583 bi
->to
.sym
->name
) > 0)
1584 annotate_t
= nr_options
++;
1587 if (browser
->selection
!= NULL
&&
1588 browser
->selection
->sym
!= NULL
&&
1589 !browser
->selection
->map
->dso
->annotate_warned
&&
1590 asprintf(&options
[nr_options
], "Annotate %s",
1591 browser
->selection
->sym
->name
) > 0)
1592 annotate
= nr_options
++;
1595 if (thread
!= NULL
&&
1596 asprintf(&options
[nr_options
], "Zoom %s %s(%d) thread",
1597 (browser
->hists
->thread_filter
? "out of" : "into"),
1598 (thread
->comm_set
? thread__comm_str(thread
) : ""),
1600 zoom_thread
= nr_options
++;
1603 asprintf(&options
[nr_options
], "Zoom %s %s DSO",
1604 (browser
->hists
->dso_filter
? "out of" : "into"),
1605 (dso
->kernel
? "the Kernel" : dso
->short_name
)) > 0)
1606 zoom_dso
= nr_options
++;
1608 if (browser
->selection
!= NULL
&&
1609 browser
->selection
->map
!= NULL
&&
1610 asprintf(&options
[nr_options
], "Browse map details") > 0)
1611 browse_map
= nr_options
++;
1613 /* perf script support */
1614 if (browser
->he_selection
) {
1617 if (asprintf(&options
[nr_options
], "Run scripts for samples of thread [%s]",
1618 thread__comm_str(browser
->he_selection
->thread
)) > 0)
1619 scripts_comm
= nr_options
++;
1621 sym
= browser
->he_selection
->ms
.sym
;
1622 if (sym
&& sym
->namelen
&&
1623 asprintf(&options
[nr_options
], "Run scripts for samples of symbol [%s]",
1625 scripts_symbol
= nr_options
++;
1628 if (asprintf(&options
[nr_options
], "Run scripts for all samples") > 0)
1629 scripts_all
= nr_options
++;
1631 if (is_report_browser(hbt
) && asprintf(&options
[nr_options
],
1632 "Switch to another data file in PWD") > 0)
1633 switch_data
= nr_options
++;
1635 options
[nr_options
++] = (char *)"Exit";
1637 choice
= ui__popup_menu(nr_options
, options
);
1639 if (choice
== nr_options
- 1)
1643 free_popup_options(options
, nr_options
- 1);
1647 if (choice
== annotate
|| choice
== annotate_t
|| choice
== annotate_f
) {
1648 struct hist_entry
*he
;
1651 if (!objdump_path
&& perf_session_env__lookup_objdump(env
))
1654 he
= hist_browser__selected_entry(browser
);
1659 * we stash the branch_info symbol + map into the
1660 * the ms so we don't have to rewrite all the annotation
1661 * code to use branch_info.
1662 * in branch mode, the ms struct is not used
1664 if (choice
== annotate_f
) {
1665 he
->ms
.sym
= he
->branch_info
->from
.sym
;
1666 he
->ms
.map
= he
->branch_info
->from
.map
;
1667 } else if (choice
== annotate_t
) {
1668 he
->ms
.sym
= he
->branch_info
->to
.sym
;
1669 he
->ms
.map
= he
->branch_info
->to
.map
;
1673 * Don't let this be freed, say, by hists__decay_entry.
1676 err
= hist_entry__tui_annotate(he
, evsel
, hbt
);
1679 * offer option to annotate the other branch source or target
1680 * (if they exists) when returning from annotate
1682 if ((err
== 'q' || err
== CTRL('c'))
1683 && annotate_t
!= -2 && annotate_f
!= -2)
1684 goto retry_popup_menu
;
1686 ui_browser__update_nr_entries(&browser
->b
, browser
->hists
->nr_entries
);
1688 ui_browser__handle_resize(&browser
->b
);
1690 } else if (choice
== browse_map
)
1691 map__browse(browser
->selection
->map
);
1692 else if (choice
== zoom_dso
) {
1694 if (browser
->hists
->dso_filter
) {
1695 pstack__remove(fstack
, &browser
->hists
->dso_filter
);
1698 browser
->hists
->dso_filter
= NULL
;
1699 sort_dso
.elide
= false;
1703 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1704 dso
->kernel
? "the Kernel" : dso
->short_name
);
1705 browser
->hists
->dso_filter
= dso
;
1706 sort_dso
.elide
= true;
1707 pstack__push(fstack
, &browser
->hists
->dso_filter
);
1709 hists__filter_by_dso(hists
);
1710 hist_browser__reset(browser
);
1711 } else if (choice
== zoom_thread
) {
1713 if (browser
->hists
->thread_filter
) {
1714 pstack__remove(fstack
, &browser
->hists
->thread_filter
);
1717 browser
->hists
->thread_filter
= NULL
;
1718 sort_thread
.elide
= false;
1720 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1721 thread
->comm_set
? thread__comm_str(thread
) : "",
1723 browser
->hists
->thread_filter
= thread
;
1724 sort_thread
.elide
= true;
1725 pstack__push(fstack
, &browser
->hists
->thread_filter
);
1727 hists__filter_by_thread(hists
);
1728 hist_browser__reset(browser
);
1730 /* perf scripts support */
1731 else if (choice
== scripts_all
|| choice
== scripts_comm
||
1732 choice
== scripts_symbol
) {
1734 memset(script_opt
, 0, 64);
1736 if (choice
== scripts_comm
)
1737 sprintf(script_opt
, " -c %s ", thread__comm_str(browser
->he_selection
->thread
));
1739 if (choice
== scripts_symbol
)
1740 sprintf(script_opt
, " -S %s ", browser
->he_selection
->ms
.sym
->name
);
1742 script_browse(script_opt
);
1744 /* Switch to another data file */
1745 else if (choice
== switch_data
) {
1747 if (!switch_data_file()) {
1748 key
= K_SWITCH_INPUT_DATA
;
1751 ui__warning("Won't switch the data files due to\n"
1752 "no valid data file get selected!\n");
1756 pstack__delete(fstack
);
1758 hist_browser__delete(browser
);
1759 free_popup_options(options
, nr_options
- 1);
1763 struct perf_evsel_menu
{
1764 struct ui_browser b
;
1765 struct perf_evsel
*selection
;
1766 bool lost_events
, lost_events_warned
;
1768 struct perf_session_env
*env
;
1771 static void perf_evsel_menu__write(struct ui_browser
*browser
,
1772 void *entry
, int row
)
1774 struct perf_evsel_menu
*menu
= container_of(browser
,
1775 struct perf_evsel_menu
, b
);
1776 struct perf_evsel
*evsel
= list_entry(entry
, struct perf_evsel
, node
);
1777 bool current_entry
= ui_browser__is_current_entry(browser
, row
);
1778 unsigned long nr_events
= evsel
->hists
.stats
.nr_events
[PERF_RECORD_SAMPLE
];
1779 const char *ev_name
= perf_evsel__name(evsel
);
1781 const char *warn
= " ";
1784 ui_browser__set_color(browser
, current_entry
? HE_COLORSET_SELECTED
:
1785 HE_COLORSET_NORMAL
);
1787 if (perf_evsel__is_group_event(evsel
)) {
1788 struct perf_evsel
*pos
;
1790 ev_name
= perf_evsel__group_name(evsel
);
1792 for_each_group_member(pos
, evsel
) {
1793 nr_events
+= pos
->hists
.stats
.nr_events
[PERF_RECORD_SAMPLE
];
1797 nr_events
= convert_unit(nr_events
, &unit
);
1798 printed
= scnprintf(bf
, sizeof(bf
), "%lu%c%s%s", nr_events
,
1799 unit
, unit
== ' ' ? "" : " ", ev_name
);
1800 slsmg_printf("%s", bf
);
1802 nr_events
= evsel
->hists
.stats
.nr_events
[PERF_RECORD_LOST
];
1803 if (nr_events
!= 0) {
1804 menu
->lost_events
= true;
1806 ui_browser__set_color(browser
, HE_COLORSET_TOP
);
1807 nr_events
= convert_unit(nr_events
, &unit
);
1808 printed
+= scnprintf(bf
, sizeof(bf
), ": %ld%c%schunks LOST!",
1809 nr_events
, unit
, unit
== ' ' ? "" : " ");
1813 slsmg_write_nstring(warn
, browser
->width
- printed
);
1816 menu
->selection
= evsel
;
1819 static int perf_evsel_menu__run(struct perf_evsel_menu
*menu
,
1820 int nr_events
, const char *help
,
1821 struct hist_browser_timer
*hbt
)
1823 struct perf_evlist
*evlist
= menu
->b
.priv
;
1824 struct perf_evsel
*pos
;
1825 const char *ev_name
, *title
= "Available samples";
1826 int delay_secs
= hbt
? hbt
->refresh
: 0;
1829 if (ui_browser__show(&menu
->b
, title
,
1830 "ESC: exit, ENTER|->: Browse histograms") < 0)
1834 key
= ui_browser__run(&menu
->b
, delay_secs
);
1838 hbt
->timer(hbt
->arg
);
1840 if (!menu
->lost_events_warned
&& menu
->lost_events
) {
1841 ui_browser__warn_lost_events(&menu
->b
);
1842 menu
->lost_events_warned
= true;
1847 if (!menu
->selection
)
1849 pos
= menu
->selection
;
1851 perf_evlist__set_selected(evlist
, pos
);
1853 * Give the calling tool a chance to populate the non
1854 * default evsel resorted hists tree.
1857 hbt
->timer(hbt
->arg
);
1858 ev_name
= perf_evsel__name(pos
);
1859 key
= perf_evsel__hists_browse(pos
, nr_events
, help
,
1863 ui_browser__show_title(&menu
->b
, title
);
1866 if (pos
->node
.next
== &evlist
->entries
)
1867 pos
= perf_evlist__first(evlist
);
1869 pos
= perf_evsel__next(pos
);
1872 if (pos
->node
.prev
== &evlist
->entries
)
1873 pos
= perf_evlist__last(evlist
);
1875 pos
= perf_evsel__prev(pos
);
1878 if (!ui_browser__dialog_yesno(&menu
->b
,
1879 "Do you really want to exit?"))
1882 case K_SWITCH_INPUT_DATA
:
1892 if (!ui_browser__dialog_yesno(&menu
->b
,
1893 "Do you really want to exit?"))
1905 ui_browser__hide(&menu
->b
);
1909 static bool filter_group_entries(struct ui_browser
*browser __maybe_unused
,
1912 struct perf_evsel
*evsel
= list_entry(entry
, struct perf_evsel
, node
);
1914 if (symbol_conf
.event_group
&& !perf_evsel__is_group_leader(evsel
))
1920 static int __perf_evlist__tui_browse_hists(struct perf_evlist
*evlist
,
1921 int nr_entries
, const char *help
,
1922 struct hist_browser_timer
*hbt
,
1924 struct perf_session_env
*env
)
1926 struct perf_evsel
*pos
;
1927 struct perf_evsel_menu menu
= {
1929 .entries
= &evlist
->entries
,
1930 .refresh
= ui_browser__list_head_refresh
,
1931 .seek
= ui_browser__list_head_seek
,
1932 .write
= perf_evsel_menu__write
,
1933 .filter
= filter_group_entries
,
1934 .nr_entries
= nr_entries
,
1937 .min_pcnt
= min_pcnt
,
1941 ui_helpline__push("Press ESC to exit");
1943 evlist__for_each(evlist
, pos
) {
1944 const char *ev_name
= perf_evsel__name(pos
);
1945 size_t line_len
= strlen(ev_name
) + 7;
1947 if (menu
.b
.width
< line_len
)
1948 menu
.b
.width
= line_len
;
1951 return perf_evsel_menu__run(&menu
, nr_entries
, help
, hbt
);
1954 int perf_evlist__tui_browse_hists(struct perf_evlist
*evlist
, const char *help
,
1955 struct hist_browser_timer
*hbt
,
1957 struct perf_session_env
*env
)
1959 int nr_entries
= evlist
->nr_entries
;
1962 if (nr_entries
== 1) {
1963 struct perf_evsel
*first
= perf_evlist__first(evlist
);
1964 const char *ev_name
= perf_evsel__name(first
);
1966 return perf_evsel__hists_browse(first
, nr_entries
, help
,
1967 ev_name
, false, hbt
, min_pcnt
,
1971 if (symbol_conf
.event_group
) {
1972 struct perf_evsel
*pos
;
1975 evlist__for_each(evlist
, pos
) {
1976 if (perf_evsel__is_group_leader(pos
))
1980 if (nr_entries
== 1)
1984 return __perf_evlist__tui_browse_hists(evlist
, nr_entries
, help
,
1985 hbt
, min_pcnt
, env
);