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
;
31 extern void hist_browser__init_hpp(void);
33 static int hists__browser_title(struct hists
*hists
, char *bf
, size_t size
,
36 static void hist_browser__refresh_dimensions(struct hist_browser
*browser
)
38 /* 3 == +/- toggle symbol before actual hist_entry rendering */
39 browser
->b
.width
= 3 + (hists__sort_list_width(browser
->hists
) +
43 static void hist_browser__reset(struct hist_browser
*browser
)
45 browser
->b
.nr_entries
= browser
->hists
->nr_entries
;
46 hist_browser__refresh_dimensions(browser
);
47 ui_browser__reset_index(&browser
->b
);
50 static char tree__folded_sign(bool unfolded
)
52 return unfolded
? '-' : '+';
55 static char map_symbol__folded(const struct map_symbol
*ms
)
57 return ms
->has_children
? tree__folded_sign(ms
->unfolded
) : ' ';
60 static char hist_entry__folded(const struct hist_entry
*he
)
62 return map_symbol__folded(&he
->ms
);
65 static char callchain_list__folded(const struct callchain_list
*cl
)
67 return map_symbol__folded(&cl
->ms
);
70 static void map_symbol__set_folding(struct map_symbol
*ms
, bool unfold
)
72 ms
->unfolded
= unfold
? ms
->has_children
: false;
75 static int callchain_node__count_rows_rb_tree(struct callchain_node
*node
)
80 for (nd
= rb_first(&node
->rb_root
); nd
; nd
= rb_next(nd
)) {
81 struct callchain_node
*child
= rb_entry(nd
, struct callchain_node
, rb_node
);
82 struct callchain_list
*chain
;
83 char folded_sign
= ' '; /* No children */
85 list_for_each_entry(chain
, &child
->val
, list
) {
87 /* We need this because we may not have children */
88 folded_sign
= callchain_list__folded(chain
);
89 if (folded_sign
== '+')
93 if (folded_sign
== '-') /* Have children and they're unfolded */
94 n
+= callchain_node__count_rows_rb_tree(child
);
100 static int callchain_node__count_rows(struct callchain_node
*node
)
102 struct callchain_list
*chain
;
103 bool unfolded
= false;
106 list_for_each_entry(chain
, &node
->val
, list
) {
108 unfolded
= chain
->ms
.unfolded
;
112 n
+= callchain_node__count_rows_rb_tree(node
);
117 static int callchain__count_rows(struct rb_root
*chain
)
122 for (nd
= rb_first(chain
); nd
; nd
= rb_next(nd
)) {
123 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
124 n
+= callchain_node__count_rows(node
);
130 static bool map_symbol__toggle_fold(struct map_symbol
*ms
)
135 if (!ms
->has_children
)
138 ms
->unfolded
= !ms
->unfolded
;
142 static void callchain_node__init_have_children_rb_tree(struct callchain_node
*node
)
144 struct rb_node
*nd
= rb_first(&node
->rb_root
);
146 for (nd
= rb_first(&node
->rb_root
); nd
; nd
= rb_next(nd
)) {
147 struct callchain_node
*child
= rb_entry(nd
, struct callchain_node
, rb_node
);
148 struct callchain_list
*chain
;
151 list_for_each_entry(chain
, &child
->val
, list
) {
154 chain
->ms
.has_children
= chain
->list
.next
!= &child
->val
||
155 !RB_EMPTY_ROOT(&child
->rb_root
);
157 chain
->ms
.has_children
= chain
->list
.next
== &child
->val
&&
158 !RB_EMPTY_ROOT(&child
->rb_root
);
161 callchain_node__init_have_children_rb_tree(child
);
165 static void callchain_node__init_have_children(struct callchain_node
*node
)
167 struct callchain_list
*chain
;
169 list_for_each_entry(chain
, &node
->val
, list
)
170 chain
->ms
.has_children
= !RB_EMPTY_ROOT(&node
->rb_root
);
172 callchain_node__init_have_children_rb_tree(node
);
175 static void callchain__init_have_children(struct rb_root
*root
)
179 for (nd
= rb_first(root
); nd
; nd
= rb_next(nd
)) {
180 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
181 callchain_node__init_have_children(node
);
185 static void hist_entry__init_have_children(struct hist_entry
*he
)
187 if (!he
->init_have_children
) {
188 he
->ms
.has_children
= !RB_EMPTY_ROOT(&he
->sorted_chain
);
189 callchain__init_have_children(&he
->sorted_chain
);
190 he
->init_have_children
= true;
194 static bool hist_browser__toggle_fold(struct hist_browser
*browser
)
196 if (map_symbol__toggle_fold(browser
->selection
)) {
197 struct hist_entry
*he
= browser
->he_selection
;
199 hist_entry__init_have_children(he
);
200 browser
->hists
->nr_entries
-= he
->nr_rows
;
203 he
->nr_rows
= callchain__count_rows(&he
->sorted_chain
);
206 browser
->hists
->nr_entries
+= he
->nr_rows
;
207 browser
->b
.nr_entries
= browser
->hists
->nr_entries
;
212 /* If it doesn't have children, no toggling performed */
216 static int callchain_node__set_folding_rb_tree(struct callchain_node
*node
, bool unfold
)
221 for (nd
= rb_first(&node
->rb_root
); nd
; nd
= rb_next(nd
)) {
222 struct callchain_node
*child
= rb_entry(nd
, struct callchain_node
, rb_node
);
223 struct callchain_list
*chain
;
224 bool has_children
= false;
226 list_for_each_entry(chain
, &child
->val
, list
) {
228 map_symbol__set_folding(&chain
->ms
, unfold
);
229 has_children
= chain
->ms
.has_children
;
233 n
+= callchain_node__set_folding_rb_tree(child
, unfold
);
239 static int callchain_node__set_folding(struct callchain_node
*node
, bool unfold
)
241 struct callchain_list
*chain
;
242 bool has_children
= false;
245 list_for_each_entry(chain
, &node
->val
, list
) {
247 map_symbol__set_folding(&chain
->ms
, unfold
);
248 has_children
= chain
->ms
.has_children
;
252 n
+= callchain_node__set_folding_rb_tree(node
, unfold
);
257 static int callchain__set_folding(struct rb_root
*chain
, bool unfold
)
262 for (nd
= rb_first(chain
); nd
; nd
= rb_next(nd
)) {
263 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
264 n
+= callchain_node__set_folding(node
, unfold
);
270 static void hist_entry__set_folding(struct hist_entry
*he
, bool unfold
)
272 hist_entry__init_have_children(he
);
273 map_symbol__set_folding(&he
->ms
, unfold
);
275 if (he
->ms
.has_children
) {
276 int n
= callchain__set_folding(&he
->sorted_chain
, unfold
);
277 he
->nr_rows
= unfold
? n
: 0;
282 static void hists__set_folding(struct hists
*hists
, bool unfold
)
286 hists
->nr_entries
= 0;
288 for (nd
= rb_first(&hists
->entries
); nd
; nd
= rb_next(nd
)) {
289 struct hist_entry
*he
= rb_entry(nd
, struct hist_entry
, rb_node
);
290 hist_entry__set_folding(he
, unfold
);
291 hists
->nr_entries
+= 1 + he
->nr_rows
;
295 static void hist_browser__set_folding(struct hist_browser
*browser
, bool unfold
)
297 hists__set_folding(browser
->hists
, unfold
);
298 browser
->b
.nr_entries
= browser
->hists
->nr_entries
;
299 /* Go to the start, we may be way after valid entries after a collapse */
300 ui_browser__reset_index(&browser
->b
);
303 static void ui_browser__warn_lost_events(struct ui_browser
*browser
)
305 ui_browser__warning(browser
, 4,
306 "Events are being lost, check IO/CPU overload!\n\n"
307 "You may want to run 'perf' using a RT scheduler policy:\n\n"
308 " perf top -r 80\n\n"
309 "Or reduce the sampling frequency.");
312 static int hist_browser__run(struct hist_browser
*browser
, const char *ev_name
,
313 struct hist_browser_timer
*hbt
)
317 int delay_secs
= hbt
? hbt
->refresh
: 0;
319 browser
->b
.entries
= &browser
->hists
->entries
;
320 browser
->b
.nr_entries
= browser
->hists
->nr_entries
;
322 hist_browser__refresh_dimensions(browser
);
323 hists__browser_title(browser
->hists
, title
, sizeof(title
), ev_name
);
325 if (ui_browser__show(&browser
->b
, title
,
326 "Press '?' for help on key bindings") < 0)
330 key
= ui_browser__run(&browser
->b
, delay_secs
);
334 hbt
->timer(hbt
->arg
);
335 ui_browser__update_nr_entries(&browser
->b
, browser
->hists
->nr_entries
);
337 if (browser
->hists
->stats
.nr_lost_warned
!=
338 browser
->hists
->stats
.nr_events
[PERF_RECORD_LOST
]) {
339 browser
->hists
->stats
.nr_lost_warned
=
340 browser
->hists
->stats
.nr_events
[PERF_RECORD_LOST
];
341 ui_browser__warn_lost_events(&browser
->b
);
344 hists__browser_title(browser
->hists
, title
, sizeof(title
), ev_name
);
345 ui_browser__show_title(&browser
->b
, title
);
347 case 'D': { /* Debug */
349 struct hist_entry
*h
= rb_entry(browser
->b
.top
,
350 struct hist_entry
, rb_node
);
352 ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
353 seq
++, browser
->b
.nr_entries
,
354 browser
->hists
->nr_entries
,
358 h
->row_offset
, h
->nr_rows
);
362 /* Collapse the whole world. */
363 hist_browser__set_folding(browser
, false);
366 /* Expand the whole world. */
367 hist_browser__set_folding(browser
, true);
370 if (hist_browser__toggle_fold(browser
))
378 ui_browser__hide(&browser
->b
);
382 static char *callchain_list__sym_name(struct callchain_list
*cl
,
383 char *bf
, size_t bfsize
, bool show_dso
)
388 printed
= scnprintf(bf
, bfsize
, "%s", cl
->ms
.sym
->name
);
390 printed
= scnprintf(bf
, bfsize
, "%#" PRIx64
, cl
->ip
);
393 scnprintf(bf
+ printed
, bfsize
- printed
, " %s",
394 cl
->ms
.map
? cl
->ms
.map
->dso
->short_name
: "unknown");
399 #define LEVEL_OFFSET_STEP 3
401 static int hist_browser__show_callchain_node_rb_tree(struct hist_browser
*browser
,
402 struct callchain_node
*chain_node
,
403 u64 total
, int level
,
406 bool *is_current_entry
)
408 struct rb_node
*node
;
409 int first_row
= row
, width
, offset
= level
* LEVEL_OFFSET_STEP
;
410 u64 new_total
, remaining
;
412 if (callchain_param
.mode
== CHAIN_GRAPH_REL
)
413 new_total
= chain_node
->children_hit
;
417 remaining
= new_total
;
418 node
= rb_first(&chain_node
->rb_root
);
420 struct callchain_node
*child
= rb_entry(node
, struct callchain_node
, rb_node
);
421 struct rb_node
*next
= rb_next(node
);
422 u64 cumul
= callchain_cumul_hits(child
);
423 struct callchain_list
*chain
;
424 char folded_sign
= ' ';
426 int extra_offset
= 0;
430 list_for_each_entry(chain
, &child
->val
, list
) {
431 char bf
[1024], *alloc_str
;
434 bool was_first
= first
;
439 extra_offset
= LEVEL_OFFSET_STEP
;
441 folded_sign
= callchain_list__folded(chain
);
442 if (*row_offset
!= 0) {
448 str
= callchain_list__sym_name(chain
, bf
, sizeof(bf
),
451 double percent
= cumul
* 100.0 / new_total
;
453 if (asprintf(&alloc_str
, "%2.2f%% %s", percent
, str
) < 0)
454 str
= "Not enough memory!";
459 color
= HE_COLORSET_NORMAL
;
460 width
= browser
->b
.width
- (offset
+ extra_offset
+ 2);
461 if (ui_browser__is_current_entry(&browser
->b
, row
)) {
462 browser
->selection
= &chain
->ms
;
463 color
= HE_COLORSET_SELECTED
;
464 *is_current_entry
= true;
467 ui_browser__set_color(&browser
->b
, color
);
468 ui_browser__gotorc(&browser
->b
, row
, 0);
469 slsmg_write_nstring(" ", offset
+ extra_offset
);
470 slsmg_printf("%c ", folded_sign
);
471 slsmg_write_nstring(str
, width
);
474 if (++row
== browser
->b
.height
)
477 if (folded_sign
== '+')
481 if (folded_sign
== '-') {
482 const int new_level
= level
+ (extra_offset
? 2 : 1);
483 row
+= hist_browser__show_callchain_node_rb_tree(browser
, child
, new_total
,
484 new_level
, row
, row_offset
,
487 if (row
== browser
->b
.height
)
492 return row
- first_row
;
495 static int hist_browser__show_callchain_node(struct hist_browser
*browser
,
496 struct callchain_node
*node
,
497 int level
, unsigned short row
,
499 bool *is_current_entry
)
501 struct callchain_list
*chain
;
503 offset
= level
* LEVEL_OFFSET_STEP
,
504 width
= browser
->b
.width
- offset
;
505 char folded_sign
= ' ';
507 list_for_each_entry(chain
, &node
->val
, list
) {
511 folded_sign
= callchain_list__folded(chain
);
513 if (*row_offset
!= 0) {
518 color
= HE_COLORSET_NORMAL
;
519 if (ui_browser__is_current_entry(&browser
->b
, row
)) {
520 browser
->selection
= &chain
->ms
;
521 color
= HE_COLORSET_SELECTED
;
522 *is_current_entry
= true;
525 s
= callchain_list__sym_name(chain
, bf
, sizeof(bf
),
527 ui_browser__gotorc(&browser
->b
, row
, 0);
528 ui_browser__set_color(&browser
->b
, color
);
529 slsmg_write_nstring(" ", offset
);
530 slsmg_printf("%c ", folded_sign
);
531 slsmg_write_nstring(s
, width
- 2);
533 if (++row
== browser
->b
.height
)
537 if (folded_sign
== '-')
538 row
+= hist_browser__show_callchain_node_rb_tree(browser
, node
,
539 browser
->hists
->stats
.total_period
,
544 return row
- first_row
;
547 static int hist_browser__show_callchain(struct hist_browser
*browser
,
548 struct rb_root
*chain
,
549 int level
, unsigned short row
,
551 bool *is_current_entry
)
556 for (nd
= rb_first(chain
); nd
; nd
= rb_next(nd
)) {
557 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
559 row
+= hist_browser__show_callchain_node(browser
, node
, level
,
562 if (row
== browser
->b
.height
)
566 return row
- first_row
;
570 struct ui_browser
*b
;
575 static int __hpp__color_callchain(struct hpp_arg
*arg
)
577 if (!symbol_conf
.use_callchain
)
580 slsmg_printf("%c ", arg
->folded_sign
);
584 static int __hpp__color_fmt(struct perf_hpp
*hpp
, struct hist_entry
*he
,
585 u64 (*get_field
)(struct hist_entry
*),
586 int (*callchain_cb
)(struct hpp_arg
*))
589 double percent
= 0.0;
590 struct hists
*hists
= he
->hists
;
591 struct hpp_arg
*arg
= hpp
->ptr
;
593 if (hists
->stats
.total_period
)
594 percent
= 100.0 * get_field(he
) / hists
->stats
.total_period
;
596 ui_browser__set_percent_color(arg
->b
, percent
, arg
->current_entry
);
599 ret
+= callchain_cb(arg
);
601 ret
+= scnprintf(hpp
->buf
, hpp
->size
, "%6.2f%%", percent
);
602 slsmg_printf("%s", hpp
->buf
);
604 if (symbol_conf
.event_group
) {
605 int prev_idx
, idx_delta
;
606 struct perf_evsel
*evsel
= hists_to_evsel(hists
);
607 struct hist_entry
*pair
;
608 int nr_members
= evsel
->nr_members
;
613 prev_idx
= perf_evsel__group_idx(evsel
);
615 list_for_each_entry(pair
, &he
->pairs
.head
, pairs
.node
) {
616 u64 period
= get_field(pair
);
617 u64 total
= pair
->hists
->stats
.total_period
;
622 evsel
= hists_to_evsel(pair
->hists
);
623 idx_delta
= perf_evsel__group_idx(evsel
) - prev_idx
- 1;
625 while (idx_delta
--) {
627 * zero-fill group members in the middle which
630 ui_browser__set_percent_color(arg
->b
, 0.0,
632 ret
+= scnprintf(hpp
->buf
, hpp
->size
,
634 slsmg_printf("%s", hpp
->buf
);
637 percent
= 100.0 * period
/ total
;
638 ui_browser__set_percent_color(arg
->b
, percent
,
640 ret
+= scnprintf(hpp
->buf
, hpp
->size
,
641 " %6.2f%%", percent
);
642 slsmg_printf("%s", hpp
->buf
);
644 prev_idx
= perf_evsel__group_idx(evsel
);
647 idx_delta
= nr_members
- prev_idx
- 1;
649 while (idx_delta
--) {
651 * zero-fill group members at last which have no sample
653 ui_browser__set_percent_color(arg
->b
, 0.0,
655 ret
+= scnprintf(hpp
->buf
, hpp
->size
,
657 slsmg_printf("%s", hpp
->buf
);
661 if (!arg
->current_entry
|| !arg
->b
->navkeypressed
)
662 ui_browser__set_color(arg
->b
, HE_COLORSET_NORMAL
);
667 #define __HPP_COLOR_PERCENT_FN(_type, _field, _cb) \
668 static u64 __hpp_get_##_field(struct hist_entry *he) \
670 return he->stat._field; \
673 static int hist_browser__hpp_color_##_type(struct perf_hpp *hpp, \
674 struct hist_entry *he) \
676 return __hpp__color_fmt(hpp, he, __hpp_get_##_field, _cb); \
679 __HPP_COLOR_PERCENT_FN(overhead
, period
, __hpp__color_callchain
)
680 __HPP_COLOR_PERCENT_FN(overhead_sys
, period_sys
, NULL
)
681 __HPP_COLOR_PERCENT_FN(overhead_us
, period_us
, NULL
)
682 __HPP_COLOR_PERCENT_FN(overhead_guest_sys
, period_guest_sys
, NULL
)
683 __HPP_COLOR_PERCENT_FN(overhead_guest_us
, period_guest_us
, NULL
)
685 #undef __HPP_COLOR_PERCENT_FN
687 void hist_browser__init_hpp(void)
689 perf_hpp__column_enable(PERF_HPP__OVERHEAD
);
693 perf_hpp__format
[PERF_HPP__OVERHEAD
].color
=
694 hist_browser__hpp_color_overhead
;
695 perf_hpp__format
[PERF_HPP__OVERHEAD_SYS
].color
=
696 hist_browser__hpp_color_overhead_sys
;
697 perf_hpp__format
[PERF_HPP__OVERHEAD_US
].color
=
698 hist_browser__hpp_color_overhead_us
;
699 perf_hpp__format
[PERF_HPP__OVERHEAD_GUEST_SYS
].color
=
700 hist_browser__hpp_color_overhead_guest_sys
;
701 perf_hpp__format
[PERF_HPP__OVERHEAD_GUEST_US
].color
=
702 hist_browser__hpp_color_overhead_guest_us
;
705 static int hist_browser__show_entry(struct hist_browser
*browser
,
706 struct hist_entry
*entry
,
711 int width
= browser
->b
.width
;
712 char folded_sign
= ' ';
713 bool current_entry
= ui_browser__is_current_entry(&browser
->b
, row
);
714 off_t row_offset
= entry
->row_offset
;
716 struct perf_hpp_fmt
*fmt
;
719 browser
->he_selection
= entry
;
720 browser
->selection
= &entry
->ms
;
723 if (symbol_conf
.use_callchain
) {
724 hist_entry__init_have_children(entry
);
725 folded_sign
= hist_entry__folded(entry
);
728 if (row_offset
== 0) {
729 struct hpp_arg arg
= {
731 .folded_sign
= folded_sign
,
732 .current_entry
= current_entry
,
734 struct perf_hpp hpp
= {
740 ui_browser__gotorc(&browser
->b
, row
, 0);
742 perf_hpp__for_each_format(fmt
) {
750 width
-= fmt
->color(&hpp
, entry
);
752 width
-= fmt
->entry(&hpp
, entry
);
753 slsmg_printf("%s", s
);
757 /* The scroll bar isn't being used */
758 if (!browser
->b
.navkeypressed
)
761 hist_entry__sort_snprintf(entry
, s
, sizeof(s
), browser
->hists
);
762 slsmg_write_nstring(s
, width
);
768 if (folded_sign
== '-' && row
!= browser
->b
.height
) {
769 printed
+= hist_browser__show_callchain(browser
, &entry
->sorted_chain
,
773 browser
->he_selection
= entry
;
779 static void ui_browser__hists_init_top(struct ui_browser
*browser
)
781 if (browser
->top
== NULL
) {
782 struct hist_browser
*hb
;
784 hb
= container_of(browser
, struct hist_browser
, b
);
785 browser
->top
= rb_first(&hb
->hists
->entries
);
789 static unsigned int hist_browser__refresh(struct ui_browser
*browser
)
793 struct hist_browser
*hb
= container_of(browser
, struct hist_browser
, b
);
795 ui_browser__hists_init_top(browser
);
797 for (nd
= browser
->top
; nd
; nd
= rb_next(nd
)) {
798 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
803 row
+= hist_browser__show_entry(hb
, h
, row
);
804 if (row
== browser
->height
)
811 static struct rb_node
*hists__filter_entries(struct rb_node
*nd
)
814 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
824 static struct rb_node
*hists__filter_prev_entries(struct rb_node
*nd
)
827 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
837 static void ui_browser__hists_seek(struct ui_browser
*browser
,
838 off_t offset
, int whence
)
840 struct hist_entry
*h
;
844 if (browser
->nr_entries
== 0)
847 ui_browser__hists_init_top(browser
);
851 nd
= hists__filter_entries(rb_first(browser
->entries
));
857 nd
= hists__filter_prev_entries(rb_last(browser
->entries
));
865 * Moves not relative to the first visible entry invalidates its
868 h
= rb_entry(browser
->top
, struct hist_entry
, rb_node
);
872 * Here we have to check if nd is expanded (+), if it is we can't go
873 * the next top level hist_entry, instead we must compute an offset of
874 * what _not_ to show and not change the first visible entry.
876 * This offset increments when we are going from top to bottom and
877 * decreases when we're going from bottom to top.
879 * As we don't have backpointers to the top level in the callchains
880 * structure, we need to always print the whole hist_entry callchain,
881 * skipping the first ones that are before the first visible entry
882 * and stop when we printed enough lines to fill the screen.
887 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
888 if (h
->ms
.unfolded
) {
889 u16 remaining
= h
->nr_rows
- h
->row_offset
;
890 if (offset
> remaining
) {
894 h
->row_offset
+= offset
;
900 nd
= hists__filter_entries(rb_next(nd
));
905 } while (offset
!= 0);
906 } else if (offset
< 0) {
908 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
909 if (h
->ms
.unfolded
) {
911 if (-offset
> h
->row_offset
) {
912 offset
+= h
->row_offset
;
915 h
->row_offset
+= offset
;
921 if (-offset
> h
->nr_rows
) {
922 offset
+= h
->nr_rows
;
925 h
->row_offset
= h
->nr_rows
+ offset
;
933 nd
= hists__filter_prev_entries(rb_prev(nd
));
940 * Last unfiltered hist_entry, check if it is
941 * unfolded, if it is then we should have
942 * row_offset at its last entry.
944 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
946 h
->row_offset
= h
->nr_rows
;
953 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
958 static int hist_browser__fprintf_callchain_node_rb_tree(struct hist_browser
*browser
,
959 struct callchain_node
*chain_node
,
960 u64 total
, int level
,
963 struct rb_node
*node
;
964 int offset
= level
* LEVEL_OFFSET_STEP
;
965 u64 new_total
, remaining
;
968 if (callchain_param
.mode
== CHAIN_GRAPH_REL
)
969 new_total
= chain_node
->children_hit
;
973 remaining
= new_total
;
974 node
= rb_first(&chain_node
->rb_root
);
976 struct callchain_node
*child
= rb_entry(node
, struct callchain_node
, rb_node
);
977 struct rb_node
*next
= rb_next(node
);
978 u64 cumul
= callchain_cumul_hits(child
);
979 struct callchain_list
*chain
;
980 char folded_sign
= ' ';
982 int extra_offset
= 0;
986 list_for_each_entry(chain
, &child
->val
, list
) {
987 char bf
[1024], *alloc_str
;
989 bool was_first
= first
;
994 extra_offset
= LEVEL_OFFSET_STEP
;
996 folded_sign
= callchain_list__folded(chain
);
999 str
= callchain_list__sym_name(chain
, bf
, sizeof(bf
),
1002 double percent
= cumul
* 100.0 / new_total
;
1004 if (asprintf(&alloc_str
, "%2.2f%% %s", percent
, str
) < 0)
1005 str
= "Not enough memory!";
1010 printed
+= fprintf(fp
, "%*s%c %s\n", offset
+ extra_offset
, " ", folded_sign
, str
);
1012 if (folded_sign
== '+')
1016 if (folded_sign
== '-') {
1017 const int new_level
= level
+ (extra_offset
? 2 : 1);
1018 printed
+= hist_browser__fprintf_callchain_node_rb_tree(browser
, child
, new_total
,
1028 static int hist_browser__fprintf_callchain_node(struct hist_browser
*browser
,
1029 struct callchain_node
*node
,
1030 int level
, FILE *fp
)
1032 struct callchain_list
*chain
;
1033 int offset
= level
* LEVEL_OFFSET_STEP
;
1034 char folded_sign
= ' ';
1037 list_for_each_entry(chain
, &node
->val
, list
) {
1040 folded_sign
= callchain_list__folded(chain
);
1041 s
= callchain_list__sym_name(chain
, bf
, sizeof(bf
), browser
->show_dso
);
1042 printed
+= fprintf(fp
, "%*s%c %s\n", offset
, " ", folded_sign
, s
);
1045 if (folded_sign
== '-')
1046 printed
+= hist_browser__fprintf_callchain_node_rb_tree(browser
, node
,
1047 browser
->hists
->stats
.total_period
,
1052 static int hist_browser__fprintf_callchain(struct hist_browser
*browser
,
1053 struct rb_root
*chain
, int level
, FILE *fp
)
1058 for (nd
= rb_first(chain
); nd
; nd
= rb_next(nd
)) {
1059 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
1061 printed
+= hist_browser__fprintf_callchain_node(browser
, node
, level
, fp
);
1067 static int hist_browser__fprintf_entry(struct hist_browser
*browser
,
1068 struct hist_entry
*he
, FILE *fp
)
1073 char folded_sign
= ' ';
1075 if (symbol_conf
.use_callchain
)
1076 folded_sign
= hist_entry__folded(he
);
1078 hist_entry__sort_snprintf(he
, s
, sizeof(s
), browser
->hists
);
1079 percent
= (he
->stat
.period
* 100.0) / browser
->hists
->stats
.total_period
;
1081 if (symbol_conf
.use_callchain
)
1082 printed
+= fprintf(fp
, "%c ", folded_sign
);
1084 printed
+= fprintf(fp
, " %5.2f%%", percent
);
1086 if (symbol_conf
.show_nr_samples
)
1087 printed
+= fprintf(fp
, " %11u", he
->stat
.nr_events
);
1089 if (symbol_conf
.show_total_period
)
1090 printed
+= fprintf(fp
, " %12" PRIu64
, he
->stat
.period
);
1092 printed
+= fprintf(fp
, "%s\n", rtrim(s
));
1094 if (folded_sign
== '-')
1095 printed
+= hist_browser__fprintf_callchain(browser
, &he
->sorted_chain
, 1, fp
);
1100 static int hist_browser__fprintf(struct hist_browser
*browser
, FILE *fp
)
1102 struct rb_node
*nd
= hists__filter_entries(rb_first(browser
->b
.entries
));
1106 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1108 printed
+= hist_browser__fprintf_entry(browser
, h
, fp
);
1109 nd
= hists__filter_entries(rb_next(nd
));
1115 static int hist_browser__dump(struct hist_browser
*browser
)
1121 scnprintf(filename
, sizeof(filename
), "perf.hist.%d", browser
->print_seq
);
1122 if (access(filename
, F_OK
))
1125 * XXX: Just an arbitrary lazy upper limit
1127 if (++browser
->print_seq
== 8192) {
1128 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1133 fp
= fopen(filename
, "w");
1136 const char *err
= strerror_r(errno
, bf
, sizeof(bf
));
1137 ui_helpline__fpush("Couldn't write to %s: %s", filename
, err
);
1141 ++browser
->print_seq
;
1142 hist_browser__fprintf(browser
, fp
);
1144 ui_helpline__fpush("%s written!", filename
);
1149 static struct hist_browser
*hist_browser__new(struct hists
*hists
)
1151 struct hist_browser
*browser
= zalloc(sizeof(*browser
));
1154 browser
->hists
= hists
;
1155 browser
->b
.refresh
= hist_browser__refresh
;
1156 browser
->b
.seek
= ui_browser__hists_seek
;
1157 browser
->b
.use_navkeypressed
= true;
1158 if (sort__branch_mode
== 1)
1159 browser
->has_symbols
= sort_sym_from
.list
.next
!= NULL
;
1161 browser
->has_symbols
= sort_sym
.list
.next
!= NULL
;
1167 static void hist_browser__delete(struct hist_browser
*browser
)
1172 static struct hist_entry
*hist_browser__selected_entry(struct hist_browser
*browser
)
1174 return browser
->he_selection
;
1177 static struct thread
*hist_browser__selected_thread(struct hist_browser
*browser
)
1179 return browser
->he_selection
->thread
;
1182 static int hists__browser_title(struct hists
*hists
, char *bf
, size_t size
,
1183 const char *ev_name
)
1187 const struct dso
*dso
= hists
->dso_filter
;
1188 const struct thread
*thread
= hists
->thread_filter
;
1189 unsigned long nr_samples
= hists
->stats
.nr_events
[PERF_RECORD_SAMPLE
];
1190 u64 nr_events
= hists
->stats
.total_period
;
1191 struct perf_evsel
*evsel
= hists_to_evsel(hists
);
1193 size_t buflen
= sizeof(buf
);
1195 if (perf_evsel__is_group_event(evsel
)) {
1196 struct perf_evsel
*pos
;
1198 perf_evsel__group_desc(evsel
, buf
, buflen
);
1201 for_each_group_member(pos
, evsel
) {
1202 nr_samples
+= pos
->hists
.stats
.nr_events
[PERF_RECORD_SAMPLE
];
1203 nr_events
+= pos
->hists
.stats
.total_period
;
1207 nr_samples
= convert_unit(nr_samples
, &unit
);
1208 printed
= scnprintf(bf
, size
,
1209 "Samples: %lu%c of event '%s', Event count (approx.): %lu",
1210 nr_samples
, unit
, ev_name
, nr_events
);
1213 if (hists
->uid_filter_str
)
1214 printed
+= snprintf(bf
+ printed
, size
- printed
,
1215 ", UID: %s", hists
->uid_filter_str
);
1217 printed
+= scnprintf(bf
+ printed
, size
- printed
,
1219 (thread
->comm_set
? thread
->comm
: ""),
1222 printed
+= scnprintf(bf
+ printed
, size
- printed
,
1223 ", DSO: %s", dso
->short_name
);
1227 static inline void free_popup_options(char **options
, int n
)
1231 for (i
= 0; i
< n
; ++i
) {
1237 /* Check whether the browser is for 'top' or 'report' */
1238 static inline bool is_report_browser(void *timer
)
1240 return timer
== NULL
;
1244 * Only runtime switching of perf data file will make "input_name" point
1245 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1246 * whether we need to call free() for current "input_name" during the switch.
1248 static bool is_input_name_malloced
= false;
1250 static int switch_data_file(void)
1252 char *pwd
, *options
[32], *abs_path
[32], *tmp
;
1254 int nr_options
= 0, choice
= -1, ret
= -1;
1255 struct dirent
*dent
;
1257 pwd
= getenv("PWD");
1261 pwd_dir
= opendir(pwd
);
1265 memset(options
, 0, sizeof(options
));
1266 memset(options
, 0, sizeof(abs_path
));
1268 while ((dent
= readdir(pwd_dir
))) {
1269 char path
[PATH_MAX
];
1271 char *name
= dent
->d_name
;
1274 if (!(dent
->d_type
== DT_REG
))
1277 snprintf(path
, sizeof(path
), "%s/%s", pwd
, name
);
1279 file
= fopen(path
, "r");
1283 if (fread(&magic
, 1, 8, file
) < 8)
1284 goto close_file_and_continue
;
1286 if (is_perf_magic(magic
)) {
1287 options
[nr_options
] = strdup(name
);
1288 if (!options
[nr_options
])
1289 goto close_file_and_continue
;
1291 abs_path
[nr_options
] = strdup(path
);
1292 if (!abs_path
[nr_options
]) {
1293 free(options
[nr_options
]);
1294 ui__warning("Can't search all data files due to memory shortage.\n");
1302 close_file_and_continue
:
1304 if (nr_options
>= 32) {
1305 ui__warning("Too many perf data files in PWD!\n"
1306 "Only the first 32 files will be listed.\n");
1313 choice
= ui__popup_menu(nr_options
, options
);
1314 if (choice
< nr_options
&& choice
>= 0) {
1315 tmp
= strdup(abs_path
[choice
]);
1317 if (is_input_name_malloced
)
1318 free((void *)input_name
);
1320 is_input_name_malloced
= true;
1323 ui__warning("Data switch failed due to memory shortage!\n");
1327 free_popup_options(options
, nr_options
);
1328 free_popup_options(abs_path
, nr_options
);
1333 static int perf_evsel__hists_browse(struct perf_evsel
*evsel
, int nr_events
,
1334 const char *helpline
, const char *ev_name
,
1336 struct hist_browser_timer
*hbt
,
1337 struct perf_session_env
*env
)
1339 struct hists
*hists
= &evsel
->hists
;
1340 struct hist_browser
*browser
= hist_browser__new(hists
);
1341 struct branch_info
*bi
;
1342 struct pstack
*fstack
;
1347 char script_opt
[64];
1348 int delay_secs
= hbt
? hbt
->refresh
: 0;
1350 if (browser
== NULL
)
1353 fstack
= pstack__new(2);
1357 ui_helpline__push(helpline
);
1359 memset(options
, 0, sizeof(options
));
1362 const struct thread
*thread
= NULL
;
1363 const struct dso
*dso
= NULL
;
1365 annotate
= -2, zoom_dso
= -2, zoom_thread
= -2,
1366 annotate_f
= -2, annotate_t
= -2, browse_map
= -2;
1367 int scripts_comm
= -2, scripts_symbol
= -2,
1368 scripts_all
= -2, switch_data
= -2;
1372 key
= hist_browser__run(browser
, ev_name
, hbt
);
1374 if (browser
->he_selection
!= NULL
) {
1375 thread
= hist_browser__selected_thread(browser
);
1376 dso
= browser
->selection
->map
? browser
->selection
->map
->dso
: NULL
;
1384 * Exit the browser, let hists__browser_tree
1385 * go to the next or previous
1387 goto out_free_stack
;
1389 if (!browser
->has_symbols
) {
1390 ui_browser__warning(&browser
->b
, delay_secs
* 2,
1391 "Annotation is only available for symbolic views, "
1392 "include \"sym*\" in --sort to use it.");
1396 if (browser
->selection
== NULL
||
1397 browser
->selection
->sym
== NULL
||
1398 browser
->selection
->map
->dso
->annotate_warned
)
1402 hist_browser__dump(browser
);
1407 browser
->show_dso
= !browser
->show_dso
;
1412 if (ui_browser__input_window("Symbol to show",
1413 "Please enter the name of symbol you want to see",
1414 buf
, "ENTER: OK, ESC: Cancel",
1415 delay_secs
* 2) == K_ENTER
) {
1416 hists
->symbol_filter_str
= *buf
? buf
: NULL
;
1417 hists__filter_by_symbol(hists
);
1418 hist_browser__reset(browser
);
1422 if (is_report_browser(hbt
))
1426 if (is_report_browser(hbt
))
1427 goto do_data_switch
;
1432 ui_browser__help_window(&browser
->b
,
1433 "h/?/F1 Show this window\n"
1435 "PGDN/SPACE Navigate\n"
1436 "q/ESC/CTRL+C Exit browser\n\n"
1437 "For multiple event sessions:\n\n"
1438 "TAB/UNTAB Switch events\n\n"
1439 "For symbolic views (--sort has sym):\n\n"
1440 "-> Zoom into DSO/Threads & Annotate current symbol\n"
1442 "a Annotate current symbol\n"
1443 "C Collapse all callchains\n"
1444 "E Expand all callchains\n"
1445 "d Zoom into current DSO\n"
1446 "t Zoom into current Thread\n"
1447 "r Run available scripts('perf report' only)\n"
1448 "s Switch to another data file in PWD ('perf report' only)\n"
1449 "P Print histograms to perf.hist.N\n"
1450 "V Verbose (DSO names in callchains, etc)\n"
1451 "/ Filter symbol by name");
1460 if (pstack__empty(fstack
)) {
1462 * Go back to the perf_evsel_menu__run or other user
1465 goto out_free_stack
;
1468 top
= pstack__pop(fstack
);
1469 if (top
== &browser
->hists
->dso_filter
)
1471 if (top
== &browser
->hists
->thread_filter
)
1472 goto zoom_out_thread
;
1477 !ui_browser__dialog_yesno(&browser
->b
,
1478 "Do you really want to exit?"))
1483 goto out_free_stack
;
1488 if (!browser
->has_symbols
)
1489 goto add_exit_option
;
1491 if (sort__branch_mode
== 1) {
1492 bi
= browser
->he_selection
->branch_info
;
1493 if (browser
->selection
!= NULL
&&
1495 bi
->from
.sym
!= NULL
&&
1496 !bi
->from
.map
->dso
->annotate_warned
&&
1497 asprintf(&options
[nr_options
], "Annotate %s",
1498 bi
->from
.sym
->name
) > 0)
1499 annotate_f
= nr_options
++;
1501 if (browser
->selection
!= NULL
&&
1503 bi
->to
.sym
!= NULL
&&
1504 !bi
->to
.map
->dso
->annotate_warned
&&
1505 (bi
->to
.sym
!= bi
->from
.sym
||
1506 bi
->to
.map
->dso
!= bi
->from
.map
->dso
) &&
1507 asprintf(&options
[nr_options
], "Annotate %s",
1508 bi
->to
.sym
->name
) > 0)
1509 annotate_t
= nr_options
++;
1512 if (browser
->selection
!= NULL
&&
1513 browser
->selection
->sym
!= NULL
&&
1514 !browser
->selection
->map
->dso
->annotate_warned
&&
1515 asprintf(&options
[nr_options
], "Annotate %s",
1516 browser
->selection
->sym
->name
) > 0)
1517 annotate
= nr_options
++;
1520 if (thread
!= NULL
&&
1521 asprintf(&options
[nr_options
], "Zoom %s %s(%d) thread",
1522 (browser
->hists
->thread_filter
? "out of" : "into"),
1523 (thread
->comm_set
? thread
->comm
: ""),
1525 zoom_thread
= nr_options
++;
1528 asprintf(&options
[nr_options
], "Zoom %s %s DSO",
1529 (browser
->hists
->dso_filter
? "out of" : "into"),
1530 (dso
->kernel
? "the Kernel" : dso
->short_name
)) > 0)
1531 zoom_dso
= nr_options
++;
1533 if (browser
->selection
!= NULL
&&
1534 browser
->selection
->map
!= NULL
&&
1535 asprintf(&options
[nr_options
], "Browse map details") > 0)
1536 browse_map
= nr_options
++;
1538 /* perf script support */
1539 if (browser
->he_selection
) {
1542 if (asprintf(&options
[nr_options
], "Run scripts for samples of thread [%s]",
1543 browser
->he_selection
->thread
->comm
) > 0)
1544 scripts_comm
= nr_options
++;
1546 sym
= browser
->he_selection
->ms
.sym
;
1547 if (sym
&& sym
->namelen
&&
1548 asprintf(&options
[nr_options
], "Run scripts for samples of symbol [%s]",
1550 scripts_symbol
= nr_options
++;
1553 if (asprintf(&options
[nr_options
], "Run scripts for all samples") > 0)
1554 scripts_all
= nr_options
++;
1556 if (is_report_browser(hbt
) && asprintf(&options
[nr_options
],
1557 "Switch to another data file in PWD") > 0)
1558 switch_data
= nr_options
++;
1560 options
[nr_options
++] = (char *)"Exit";
1562 choice
= ui__popup_menu(nr_options
, options
);
1564 if (choice
== nr_options
- 1)
1568 free_popup_options(options
, nr_options
- 1);
1572 if (choice
== annotate
|| choice
== annotate_t
|| choice
== annotate_f
) {
1573 struct hist_entry
*he
;
1576 if (!objdump_path
&& perf_session_env__lookup_objdump(env
))
1579 he
= hist_browser__selected_entry(browser
);
1584 * we stash the branch_info symbol + map into the
1585 * the ms so we don't have to rewrite all the annotation
1586 * code to use branch_info.
1587 * in branch mode, the ms struct is not used
1589 if (choice
== annotate_f
) {
1590 he
->ms
.sym
= he
->branch_info
->from
.sym
;
1591 he
->ms
.map
= he
->branch_info
->from
.map
;
1592 } else if (choice
== annotate_t
) {
1593 he
->ms
.sym
= he
->branch_info
->to
.sym
;
1594 he
->ms
.map
= he
->branch_info
->to
.map
;
1598 * Don't let this be freed, say, by hists__decay_entry.
1601 err
= hist_entry__tui_annotate(he
, evsel
, hbt
);
1604 * offer option to annotate the other branch source or target
1605 * (if they exists) when returning from annotate
1607 if ((err
== 'q' || err
== CTRL('c'))
1608 && annotate_t
!= -2 && annotate_f
!= -2)
1609 goto retry_popup_menu
;
1611 ui_browser__update_nr_entries(&browser
->b
, browser
->hists
->nr_entries
);
1613 ui_browser__handle_resize(&browser
->b
);
1615 } else if (choice
== browse_map
)
1616 map__browse(browser
->selection
->map
);
1617 else if (choice
== zoom_dso
) {
1619 if (browser
->hists
->dso_filter
) {
1620 pstack__remove(fstack
, &browser
->hists
->dso_filter
);
1623 browser
->hists
->dso_filter
= NULL
;
1624 sort_dso
.elide
= false;
1628 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1629 dso
->kernel
? "the Kernel" : dso
->short_name
);
1630 browser
->hists
->dso_filter
= dso
;
1631 sort_dso
.elide
= true;
1632 pstack__push(fstack
, &browser
->hists
->dso_filter
);
1634 hists__filter_by_dso(hists
);
1635 hist_browser__reset(browser
);
1636 } else if (choice
== zoom_thread
) {
1638 if (browser
->hists
->thread_filter
) {
1639 pstack__remove(fstack
, &browser
->hists
->thread_filter
);
1642 browser
->hists
->thread_filter
= NULL
;
1643 sort_thread
.elide
= false;
1645 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1646 thread
->comm_set
? thread
->comm
: "",
1648 browser
->hists
->thread_filter
= thread
;
1649 sort_thread
.elide
= true;
1650 pstack__push(fstack
, &browser
->hists
->thread_filter
);
1652 hists__filter_by_thread(hists
);
1653 hist_browser__reset(browser
);
1655 /* perf scripts support */
1656 else if (choice
== scripts_all
|| choice
== scripts_comm
||
1657 choice
== scripts_symbol
) {
1659 memset(script_opt
, 0, 64);
1661 if (choice
== scripts_comm
)
1662 sprintf(script_opt
, " -c %s ", browser
->he_selection
->thread
->comm
);
1664 if (choice
== scripts_symbol
)
1665 sprintf(script_opt
, " -S %s ", browser
->he_selection
->ms
.sym
->name
);
1667 script_browse(script_opt
);
1669 /* Switch to another data file */
1670 else if (choice
== switch_data
) {
1672 if (!switch_data_file()) {
1673 key
= K_SWITCH_INPUT_DATA
;
1676 ui__warning("Won't switch the data files due to\n"
1677 "no valid data file get selected!\n");
1681 pstack__delete(fstack
);
1683 hist_browser__delete(browser
);
1684 free_popup_options(options
, nr_options
- 1);
1688 struct perf_evsel_menu
{
1689 struct ui_browser b
;
1690 struct perf_evsel
*selection
;
1691 bool lost_events
, lost_events_warned
;
1692 struct perf_session_env
*env
;
1695 static void perf_evsel_menu__write(struct ui_browser
*browser
,
1696 void *entry
, int row
)
1698 struct perf_evsel_menu
*menu
= container_of(browser
,
1699 struct perf_evsel_menu
, b
);
1700 struct perf_evsel
*evsel
= list_entry(entry
, struct perf_evsel
, node
);
1701 bool current_entry
= ui_browser__is_current_entry(browser
, row
);
1702 unsigned long nr_events
= evsel
->hists
.stats
.nr_events
[PERF_RECORD_SAMPLE
];
1703 const char *ev_name
= perf_evsel__name(evsel
);
1705 const char *warn
= " ";
1708 ui_browser__set_color(browser
, current_entry
? HE_COLORSET_SELECTED
:
1709 HE_COLORSET_NORMAL
);
1711 if (perf_evsel__is_group_event(evsel
)) {
1712 struct perf_evsel
*pos
;
1714 ev_name
= perf_evsel__group_name(evsel
);
1716 for_each_group_member(pos
, evsel
) {
1717 nr_events
+= pos
->hists
.stats
.nr_events
[PERF_RECORD_SAMPLE
];
1721 nr_events
= convert_unit(nr_events
, &unit
);
1722 printed
= scnprintf(bf
, sizeof(bf
), "%lu%c%s%s", nr_events
,
1723 unit
, unit
== ' ' ? "" : " ", ev_name
);
1724 slsmg_printf("%s", bf
);
1726 nr_events
= evsel
->hists
.stats
.nr_events
[PERF_RECORD_LOST
];
1727 if (nr_events
!= 0) {
1728 menu
->lost_events
= true;
1730 ui_browser__set_color(browser
, HE_COLORSET_TOP
);
1731 nr_events
= convert_unit(nr_events
, &unit
);
1732 printed
+= scnprintf(bf
, sizeof(bf
), ": %ld%c%schunks LOST!",
1733 nr_events
, unit
, unit
== ' ' ? "" : " ");
1737 slsmg_write_nstring(warn
, browser
->width
- printed
);
1740 menu
->selection
= evsel
;
1743 static int perf_evsel_menu__run(struct perf_evsel_menu
*menu
,
1744 int nr_events
, const char *help
,
1745 struct hist_browser_timer
*hbt
)
1747 struct perf_evlist
*evlist
= menu
->b
.priv
;
1748 struct perf_evsel
*pos
;
1749 const char *ev_name
, *title
= "Available samples";
1750 int delay_secs
= hbt
? hbt
->refresh
: 0;
1753 if (ui_browser__show(&menu
->b
, title
,
1754 "ESC: exit, ENTER|->: Browse histograms") < 0)
1758 key
= ui_browser__run(&menu
->b
, delay_secs
);
1762 hbt
->timer(hbt
->arg
);
1764 if (!menu
->lost_events_warned
&& menu
->lost_events
) {
1765 ui_browser__warn_lost_events(&menu
->b
);
1766 menu
->lost_events_warned
= true;
1771 if (!menu
->selection
)
1773 pos
= menu
->selection
;
1775 perf_evlist__set_selected(evlist
, pos
);
1777 * Give the calling tool a chance to populate the non
1778 * default evsel resorted hists tree.
1781 hbt
->timer(hbt
->arg
);
1782 ev_name
= perf_evsel__name(pos
);
1783 key
= perf_evsel__hists_browse(pos
, nr_events
, help
,
1786 ui_browser__show_title(&menu
->b
, title
);
1789 if (pos
->node
.next
== &evlist
->entries
)
1790 pos
= list_entry(evlist
->entries
.next
, struct perf_evsel
, node
);
1792 pos
= list_entry(pos
->node
.next
, struct perf_evsel
, node
);
1795 if (pos
->node
.prev
== &evlist
->entries
)
1796 pos
= list_entry(evlist
->entries
.prev
, struct perf_evsel
, node
);
1798 pos
= list_entry(pos
->node
.prev
, struct perf_evsel
, node
);
1801 if (!ui_browser__dialog_yesno(&menu
->b
,
1802 "Do you really want to exit?"))
1805 case K_SWITCH_INPUT_DATA
:
1815 if (!ui_browser__dialog_yesno(&menu
->b
,
1816 "Do you really want to exit?"))
1828 ui_browser__hide(&menu
->b
);
1832 static bool filter_group_entries(struct ui_browser
*self __maybe_unused
,
1835 struct perf_evsel
*evsel
= list_entry(entry
, struct perf_evsel
, node
);
1837 if (symbol_conf
.event_group
&& !perf_evsel__is_group_leader(evsel
))
1843 static int __perf_evlist__tui_browse_hists(struct perf_evlist
*evlist
,
1844 int nr_entries
, const char *help
,
1845 struct hist_browser_timer
*hbt
,
1846 struct perf_session_env
*env
)
1848 struct perf_evsel
*pos
;
1849 struct perf_evsel_menu menu
= {
1851 .entries
= &evlist
->entries
,
1852 .refresh
= ui_browser__list_head_refresh
,
1853 .seek
= ui_browser__list_head_seek
,
1854 .write
= perf_evsel_menu__write
,
1855 .filter
= filter_group_entries
,
1856 .nr_entries
= nr_entries
,
1862 ui_helpline__push("Press ESC to exit");
1864 list_for_each_entry(pos
, &evlist
->entries
, node
) {
1865 const char *ev_name
= perf_evsel__name(pos
);
1866 size_t line_len
= strlen(ev_name
) + 7;
1868 if (menu
.b
.width
< line_len
)
1869 menu
.b
.width
= line_len
;
1872 return perf_evsel_menu__run(&menu
, nr_entries
, help
, hbt
);
1875 int perf_evlist__tui_browse_hists(struct perf_evlist
*evlist
, const char *help
,
1876 struct hist_browser_timer
*hbt
,
1877 struct perf_session_env
*env
)
1879 int nr_entries
= evlist
->nr_entries
;
1882 if (nr_entries
== 1) {
1883 struct perf_evsel
*first
= list_entry(evlist
->entries
.next
,
1884 struct perf_evsel
, node
);
1885 const char *ev_name
= perf_evsel__name(first
);
1887 return perf_evsel__hists_browse(first
, nr_entries
, help
,
1888 ev_name
, false, hbt
, env
);
1891 if (symbol_conf
.event_group
) {
1892 struct perf_evsel
*pos
;
1895 list_for_each_entry(pos
, &evlist
->entries
, node
)
1896 if (perf_evsel__is_group_leader(pos
))
1899 if (nr_entries
== 1)
1903 return __perf_evlist__tui_browse_hists(evlist
, nr_entries
, help
,