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"
25 struct hist_entry
*he_selection
;
26 struct map_symbol
*selection
;
31 u64 nr_non_filtered_entries
;
32 u64 nr_callchain_rows
;
35 extern void hist_browser__init_hpp(void);
37 static int hists__browser_title(struct hists
*hists
, char *bf
, size_t size
);
38 static void hist_browser__update_nr_entries(struct hist_browser
*hb
);
40 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__update_rows(struct hist_browser
*hb
)
62 struct ui_browser
*browser
= &hb
->b
;
63 u16 header_offset
= hb
->show_headers
? 1 : 0, index_row
;
65 browser
->rows
= browser
->height
- header_offset
;
67 * Verify if we were at the last line and that line isn't
68 * visibe because we now show the header line(s).
70 index_row
= browser
->index
- browser
->top_idx
;
71 if (index_row
>= browser
->rows
)
72 browser
->index
-= index_row
- browser
->rows
+ 1;
75 static void hist_browser__refresh_dimensions(struct ui_browser
*browser
)
77 struct hist_browser
*hb
= container_of(browser
, struct hist_browser
, b
);
79 /* 3 == +/- toggle symbol before actual hist_entry rendering */
80 browser
->width
= 3 + (hists__sort_list_width(hb
->hists
) + sizeof("[k]"));
82 * FIXME: Just keeping existing behaviour, but this really should be
83 * before updating browser->width, as it will invalidate the
84 * calculation above. Fix this and the fallout in another
87 ui_browser__refresh_dimensions(browser
);
88 hist_browser__update_rows(hb
);
91 static void hist_browser__gotorc(struct hist_browser
*browser
, int row
, int column
)
93 u16 header_offset
= browser
->show_headers
? 1 : 0;
95 ui_browser__gotorc(&browser
->b
, row
+ header_offset
, column
);
98 static void hist_browser__reset(struct hist_browser
*browser
)
101 * The hists__remove_entry_filter() already folds non-filtered
102 * entries so we can assume it has 0 callchain rows.
104 browser
->nr_callchain_rows
= 0;
106 hist_browser__update_nr_entries(browser
);
107 browser
->b
.nr_entries
= hist_browser__nr_entries(browser
);
108 hist_browser__refresh_dimensions(&browser
->b
);
109 ui_browser__reset_index(&browser
->b
);
112 static char tree__folded_sign(bool unfolded
)
114 return unfolded
? '-' : '+';
117 static char map_symbol__folded(const struct map_symbol
*ms
)
119 return ms
->has_children
? tree__folded_sign(ms
->unfolded
) : ' ';
122 static char hist_entry__folded(const struct hist_entry
*he
)
124 return map_symbol__folded(&he
->ms
);
127 static char callchain_list__folded(const struct callchain_list
*cl
)
129 return map_symbol__folded(&cl
->ms
);
132 static void map_symbol__set_folding(struct map_symbol
*ms
, bool unfold
)
134 ms
->unfolded
= unfold
? ms
->has_children
: false;
137 static int callchain_node__count_rows_rb_tree(struct callchain_node
*node
)
142 for (nd
= rb_first(&node
->rb_root
); nd
; nd
= rb_next(nd
)) {
143 struct callchain_node
*child
= rb_entry(nd
, struct callchain_node
, rb_node
);
144 struct callchain_list
*chain
;
145 char folded_sign
= ' '; /* No children */
147 list_for_each_entry(chain
, &child
->val
, list
) {
149 /* We need this because we may not have children */
150 folded_sign
= callchain_list__folded(chain
);
151 if (folded_sign
== '+')
155 if (folded_sign
== '-') /* Have children and they're unfolded */
156 n
+= callchain_node__count_rows_rb_tree(child
);
162 static int callchain_node__count_rows(struct callchain_node
*node
)
164 struct callchain_list
*chain
;
165 bool unfolded
= false;
168 list_for_each_entry(chain
, &node
->val
, list
) {
170 unfolded
= chain
->ms
.unfolded
;
174 n
+= callchain_node__count_rows_rb_tree(node
);
179 static int callchain__count_rows(struct rb_root
*chain
)
184 for (nd
= rb_first(chain
); nd
; nd
= rb_next(nd
)) {
185 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
186 n
+= callchain_node__count_rows(node
);
192 static bool map_symbol__toggle_fold(struct map_symbol
*ms
)
197 if (!ms
->has_children
)
200 ms
->unfolded
= !ms
->unfolded
;
204 static void callchain_node__init_have_children_rb_tree(struct callchain_node
*node
)
206 struct rb_node
*nd
= rb_first(&node
->rb_root
);
208 for (nd
= rb_first(&node
->rb_root
); nd
; nd
= rb_next(nd
)) {
209 struct callchain_node
*child
= rb_entry(nd
, struct callchain_node
, rb_node
);
210 struct callchain_list
*chain
;
213 list_for_each_entry(chain
, &child
->val
, list
) {
216 chain
->ms
.has_children
= chain
->list
.next
!= &child
->val
||
217 !RB_EMPTY_ROOT(&child
->rb_root
);
219 chain
->ms
.has_children
= chain
->list
.next
== &child
->val
&&
220 !RB_EMPTY_ROOT(&child
->rb_root
);
223 callchain_node__init_have_children_rb_tree(child
);
227 static void callchain_node__init_have_children(struct callchain_node
*node
)
229 struct callchain_list
*chain
;
231 list_for_each_entry(chain
, &node
->val
, list
)
232 chain
->ms
.has_children
= !RB_EMPTY_ROOT(&node
->rb_root
);
234 callchain_node__init_have_children_rb_tree(node
);
237 static void callchain__init_have_children(struct rb_root
*root
)
241 for (nd
= rb_first(root
); nd
; nd
= rb_next(nd
)) {
242 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
243 callchain_node__init_have_children(node
);
247 static void hist_entry__init_have_children(struct hist_entry
*he
)
249 if (!he
->init_have_children
) {
250 he
->ms
.has_children
= !RB_EMPTY_ROOT(&he
->sorted_chain
);
251 callchain__init_have_children(&he
->sorted_chain
);
252 he
->init_have_children
= true;
256 static bool hist_browser__toggle_fold(struct hist_browser
*browser
)
258 if (map_symbol__toggle_fold(browser
->selection
)) {
259 struct hist_entry
*he
= browser
->he_selection
;
261 hist_entry__init_have_children(he
);
262 browser
->b
.nr_entries
-= he
->nr_rows
;
263 browser
->nr_callchain_rows
-= he
->nr_rows
;
266 he
->nr_rows
= callchain__count_rows(&he
->sorted_chain
);
270 browser
->b
.nr_entries
+= he
->nr_rows
;
271 browser
->nr_callchain_rows
+= he
->nr_rows
;
276 /* If it doesn't have children, no toggling performed */
280 static int callchain_node__set_folding_rb_tree(struct callchain_node
*node
, bool unfold
)
285 for (nd
= rb_first(&node
->rb_root
); nd
; nd
= rb_next(nd
)) {
286 struct callchain_node
*child
= rb_entry(nd
, struct callchain_node
, rb_node
);
287 struct callchain_list
*chain
;
288 bool has_children
= false;
290 list_for_each_entry(chain
, &child
->val
, list
) {
292 map_symbol__set_folding(&chain
->ms
, unfold
);
293 has_children
= chain
->ms
.has_children
;
297 n
+= callchain_node__set_folding_rb_tree(child
, unfold
);
303 static int callchain_node__set_folding(struct callchain_node
*node
, bool unfold
)
305 struct callchain_list
*chain
;
306 bool has_children
= false;
309 list_for_each_entry(chain
, &node
->val
, list
) {
311 map_symbol__set_folding(&chain
->ms
, unfold
);
312 has_children
= chain
->ms
.has_children
;
316 n
+= callchain_node__set_folding_rb_tree(node
, unfold
);
321 static int callchain__set_folding(struct rb_root
*chain
, bool unfold
)
326 for (nd
= rb_first(chain
); nd
; nd
= rb_next(nd
)) {
327 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
328 n
+= callchain_node__set_folding(node
, unfold
);
334 static void hist_entry__set_folding(struct hist_entry
*he
, bool unfold
)
336 hist_entry__init_have_children(he
);
337 map_symbol__set_folding(&he
->ms
, unfold
);
339 if (he
->ms
.has_children
) {
340 int n
= callchain__set_folding(&he
->sorted_chain
, unfold
);
341 he
->nr_rows
= unfold
? n
: 0;
347 __hist_browser__set_folding(struct hist_browser
*browser
, bool unfold
)
350 struct hists
*hists
= browser
->hists
;
352 for (nd
= rb_first(&hists
->entries
);
353 (nd
= hists__filter_entries(nd
, browser
->min_pcnt
)) != NULL
;
355 struct hist_entry
*he
= rb_entry(nd
, struct hist_entry
, rb_node
);
356 hist_entry__set_folding(he
, unfold
);
357 browser
->nr_callchain_rows
+= he
->nr_rows
;
361 static void hist_browser__set_folding(struct hist_browser
*browser
, bool unfold
)
363 browser
->nr_callchain_rows
= 0;
364 __hist_browser__set_folding(browser
, unfold
);
366 browser
->b
.nr_entries
= hist_browser__nr_entries(browser
);
367 /* Go to the start, we may be way after valid entries after a collapse */
368 ui_browser__reset_index(&browser
->b
);
371 static void ui_browser__warn_lost_events(struct ui_browser
*browser
)
373 ui_browser__warning(browser
, 4,
374 "Events are being lost, check IO/CPU overload!\n\n"
375 "You may want to run 'perf' using a RT scheduler policy:\n\n"
376 " perf top -r 80\n\n"
377 "Or reduce the sampling frequency.");
380 static int hist_browser__run(struct hist_browser
*browser
,
381 struct hist_browser_timer
*hbt
)
385 int delay_secs
= hbt
? hbt
->refresh
: 0;
387 browser
->b
.entries
= &browser
->hists
->entries
;
388 browser
->b
.nr_entries
= hist_browser__nr_entries(browser
);
390 hists__browser_title(browser
->hists
, title
, sizeof(title
));
392 if (ui_browser__show(&browser
->b
, title
,
393 "Press '?' for help on key bindings") < 0)
397 key
= ui_browser__run(&browser
->b
, delay_secs
);
402 hbt
->timer(hbt
->arg
);
404 if (hist_browser__has_filter(browser
))
405 hist_browser__update_nr_entries(browser
);
407 nr_entries
= hist_browser__nr_entries(browser
);
408 ui_browser__update_nr_entries(&browser
->b
, nr_entries
);
410 if (browser
->hists
->stats
.nr_lost_warned
!=
411 browser
->hists
->stats
.nr_events
[PERF_RECORD_LOST
]) {
412 browser
->hists
->stats
.nr_lost_warned
=
413 browser
->hists
->stats
.nr_events
[PERF_RECORD_LOST
];
414 ui_browser__warn_lost_events(&browser
->b
);
417 hists__browser_title(browser
->hists
, title
, sizeof(title
));
418 ui_browser__show_title(&browser
->b
, title
);
421 case 'D': { /* Debug */
423 struct hist_entry
*h
= rb_entry(browser
->b
.top
,
424 struct hist_entry
, rb_node
);
426 ui_helpline__fpush("%d: nr_ent=(%d,%d), rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
427 seq
++, browser
->b
.nr_entries
,
428 browser
->hists
->nr_entries
,
432 h
->row_offset
, h
->nr_rows
);
436 /* Collapse the whole world. */
437 hist_browser__set_folding(browser
, false);
440 /* Expand the whole world. */
441 hist_browser__set_folding(browser
, true);
444 browser
->show_headers
= !browser
->show_headers
;
445 hist_browser__update_rows(browser
);
448 if (hist_browser__toggle_fold(browser
))
456 ui_browser__hide(&browser
->b
);
460 static char *callchain_list__sym_name(struct callchain_list
*cl
,
461 char *bf
, size_t bfsize
, bool show_dso
)
466 printed
= scnprintf(bf
, bfsize
, "%s", cl
->ms
.sym
->name
);
468 printed
= scnprintf(bf
, bfsize
, "%#" PRIx64
, cl
->ip
);
471 scnprintf(bf
+ printed
, bfsize
- printed
, " %s",
472 cl
->ms
.map
? cl
->ms
.map
->dso
->short_name
: "unknown");
477 #define LEVEL_OFFSET_STEP 3
479 static int hist_browser__show_callchain_node_rb_tree(struct hist_browser
*browser
,
480 struct callchain_node
*chain_node
,
481 u64 total
, int level
,
484 bool *is_current_entry
)
486 struct rb_node
*node
;
487 int first_row
= row
, width
, offset
= level
* LEVEL_OFFSET_STEP
;
488 u64 new_total
, remaining
;
490 if (callchain_param
.mode
== CHAIN_GRAPH_REL
)
491 new_total
= chain_node
->children_hit
;
495 remaining
= new_total
;
496 node
= rb_first(&chain_node
->rb_root
);
498 struct callchain_node
*child
= rb_entry(node
, struct callchain_node
, rb_node
);
499 struct rb_node
*next
= rb_next(node
);
500 u64 cumul
= callchain_cumul_hits(child
);
501 struct callchain_list
*chain
;
502 char folded_sign
= ' ';
504 int extra_offset
= 0;
508 list_for_each_entry(chain
, &child
->val
, list
) {
509 char bf
[1024], *alloc_str
;
512 bool was_first
= first
;
517 extra_offset
= LEVEL_OFFSET_STEP
;
519 folded_sign
= callchain_list__folded(chain
);
520 if (*row_offset
!= 0) {
526 str
= callchain_list__sym_name(chain
, bf
, sizeof(bf
),
529 double percent
= cumul
* 100.0 / new_total
;
531 if (asprintf(&alloc_str
, "%2.2f%% %s", percent
, str
) < 0)
532 str
= "Not enough memory!";
537 color
= HE_COLORSET_NORMAL
;
538 width
= browser
->b
.width
- (offset
+ extra_offset
+ 2);
539 if (ui_browser__is_current_entry(&browser
->b
, row
)) {
540 browser
->selection
= &chain
->ms
;
541 color
= HE_COLORSET_SELECTED
;
542 *is_current_entry
= true;
545 ui_browser__set_color(&browser
->b
, color
);
546 hist_browser__gotorc(browser
, row
, 0);
547 slsmg_write_nstring(" ", offset
+ extra_offset
);
548 slsmg_printf("%c ", folded_sign
);
549 slsmg_write_nstring(str
, width
);
552 if (++row
== browser
->b
.rows
)
555 if (folded_sign
== '+')
559 if (folded_sign
== '-') {
560 const int new_level
= level
+ (extra_offset
? 2 : 1);
561 row
+= hist_browser__show_callchain_node_rb_tree(browser
, child
, new_total
,
562 new_level
, row
, row_offset
,
565 if (row
== browser
->b
.rows
)
570 return row
- first_row
;
573 static int hist_browser__show_callchain_node(struct hist_browser
*browser
,
574 struct callchain_node
*node
,
575 int level
, unsigned short row
,
577 bool *is_current_entry
)
579 struct callchain_list
*chain
;
581 offset
= level
* LEVEL_OFFSET_STEP
,
582 width
= browser
->b
.width
- offset
;
583 char folded_sign
= ' ';
585 list_for_each_entry(chain
, &node
->val
, list
) {
589 folded_sign
= callchain_list__folded(chain
);
591 if (*row_offset
!= 0) {
596 color
= HE_COLORSET_NORMAL
;
597 if (ui_browser__is_current_entry(&browser
->b
, row
)) {
598 browser
->selection
= &chain
->ms
;
599 color
= HE_COLORSET_SELECTED
;
600 *is_current_entry
= true;
603 s
= callchain_list__sym_name(chain
, bf
, sizeof(bf
),
605 hist_browser__gotorc(browser
, row
, 0);
606 ui_browser__set_color(&browser
->b
, color
);
607 slsmg_write_nstring(" ", offset
);
608 slsmg_printf("%c ", folded_sign
);
609 slsmg_write_nstring(s
, width
- 2);
611 if (++row
== browser
->b
.rows
)
615 if (folded_sign
== '-')
616 row
+= hist_browser__show_callchain_node_rb_tree(browser
, node
,
617 browser
->hists
->stats
.total_period
,
622 return row
- first_row
;
625 static int hist_browser__show_callchain(struct hist_browser
*browser
,
626 struct rb_root
*chain
,
627 int level
, unsigned short row
,
629 bool *is_current_entry
)
634 for (nd
= rb_first(chain
); nd
; nd
= rb_next(nd
)) {
635 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
637 row
+= hist_browser__show_callchain_node(browser
, node
, level
,
640 if (row
== browser
->b
.rows
)
644 return row
- first_row
;
648 struct ui_browser
*b
;
653 static int __hpp__slsmg_color_printf(struct perf_hpp
*hpp
, const char *fmt
, ...)
655 struct hpp_arg
*arg
= hpp
->ptr
;
661 len
= va_arg(args
, int);
662 percent
= va_arg(args
, double);
665 ui_browser__set_percent_color(arg
->b
, percent
, arg
->current_entry
);
667 ret
= scnprintf(hpp
->buf
, hpp
->size
, fmt
, len
, percent
);
668 slsmg_printf("%s", hpp
->buf
);
670 advance_hpp(hpp
, ret
);
674 #define __HPP_COLOR_PERCENT_FN(_type, _field) \
675 static u64 __hpp_get_##_field(struct hist_entry *he) \
677 return he->stat._field; \
681 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,\
682 struct perf_hpp *hpp, \
683 struct hist_entry *he) \
685 return __hpp__fmt(hpp, he, __hpp_get_##_field, " %*.2f%%", 6, \
686 __hpp__slsmg_color_printf, true); \
689 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
690 static u64 __hpp_get_acc_##_field(struct hist_entry *he) \
692 return he->stat_acc->_field; \
696 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,\
697 struct perf_hpp *hpp, \
698 struct hist_entry *he) \
700 if (!symbol_conf.cumulate_callchain) { \
701 int ret = scnprintf(hpp->buf, hpp->size, \
703 slsmg_printf("%s", hpp->buf); \
707 return __hpp__fmt(hpp, he, __hpp_get_acc_##_field, " %*.2f%%", \
708 6, __hpp__slsmg_color_printf, true); \
711 __HPP_COLOR_PERCENT_FN(overhead
, period
)
712 __HPP_COLOR_PERCENT_FN(overhead_sys
, period_sys
)
713 __HPP_COLOR_PERCENT_FN(overhead_us
, period_us
)
714 __HPP_COLOR_PERCENT_FN(overhead_guest_sys
, period_guest_sys
)
715 __HPP_COLOR_PERCENT_FN(overhead_guest_us
, period_guest_us
)
716 __HPP_COLOR_ACC_PERCENT_FN(overhead_acc
, period
)
718 #undef __HPP_COLOR_PERCENT_FN
719 #undef __HPP_COLOR_ACC_PERCENT_FN
721 void hist_browser__init_hpp(void)
723 perf_hpp__format
[PERF_HPP__OVERHEAD
].color
=
724 hist_browser__hpp_color_overhead
;
725 perf_hpp__format
[PERF_HPP__OVERHEAD_SYS
].color
=
726 hist_browser__hpp_color_overhead_sys
;
727 perf_hpp__format
[PERF_HPP__OVERHEAD_US
].color
=
728 hist_browser__hpp_color_overhead_us
;
729 perf_hpp__format
[PERF_HPP__OVERHEAD_GUEST_SYS
].color
=
730 hist_browser__hpp_color_overhead_guest_sys
;
731 perf_hpp__format
[PERF_HPP__OVERHEAD_GUEST_US
].color
=
732 hist_browser__hpp_color_overhead_guest_us
;
733 perf_hpp__format
[PERF_HPP__OVERHEAD_ACC
].color
=
734 hist_browser__hpp_color_overhead_acc
;
737 static int hist_browser__show_entry(struct hist_browser
*browser
,
738 struct hist_entry
*entry
,
743 int width
= browser
->b
.width
;
744 char folded_sign
= ' ';
745 bool current_entry
= ui_browser__is_current_entry(&browser
->b
, row
);
746 off_t row_offset
= entry
->row_offset
;
748 struct perf_hpp_fmt
*fmt
;
751 browser
->he_selection
= entry
;
752 browser
->selection
= &entry
->ms
;
755 if (symbol_conf
.use_callchain
) {
756 hist_entry__init_have_children(entry
);
757 folded_sign
= hist_entry__folded(entry
);
760 if (row_offset
== 0) {
761 struct hpp_arg arg
= {
763 .folded_sign
= folded_sign
,
764 .current_entry
= current_entry
,
766 struct perf_hpp hpp
= {
772 hist_browser__gotorc(browser
, row
, 0);
774 perf_hpp__for_each_format(fmt
) {
775 if (perf_hpp__should_skip(fmt
))
778 if (current_entry
&& browser
->b
.navkeypressed
) {
779 ui_browser__set_color(&browser
->b
,
780 HE_COLORSET_SELECTED
);
782 ui_browser__set_color(&browser
->b
,
787 if (symbol_conf
.use_callchain
) {
788 slsmg_printf("%c ", folded_sign
);
798 width
-= fmt
->color(fmt
, &hpp
, entry
);
800 width
-= fmt
->entry(fmt
, &hpp
, entry
);
801 slsmg_printf("%s", s
);
805 /* The scroll bar isn't being used */
806 if (!browser
->b
.navkeypressed
)
809 slsmg_write_nstring("", width
);
816 if (folded_sign
== '-' && row
!= browser
->b
.rows
) {
817 printed
+= hist_browser__show_callchain(browser
, &entry
->sorted_chain
,
821 browser
->he_selection
= entry
;
827 static int advance_hpp_check(struct perf_hpp
*hpp
, int inc
)
829 advance_hpp(hpp
, inc
);
830 return hpp
->size
<= 0;
833 static int hists__scnprintf_headers(char *buf
, size_t size
, struct hists
*hists
)
835 struct perf_hpp dummy_hpp
= {
839 struct perf_hpp_fmt
*fmt
;
842 if (symbol_conf
.use_callchain
) {
843 ret
= scnprintf(buf
, size
, " ");
844 if (advance_hpp_check(&dummy_hpp
, ret
))
848 perf_hpp__for_each_format(fmt
) {
849 if (perf_hpp__should_skip(fmt
))
852 /* We need to ensure length of the columns header. */
853 perf_hpp__reset_width(fmt
, hists
);
855 ret
= fmt
->header(fmt
, &dummy_hpp
, hists_to_evsel(hists
));
856 if (advance_hpp_check(&dummy_hpp
, ret
))
859 ret
= scnprintf(dummy_hpp
.buf
, dummy_hpp
.size
, " ");
860 if (advance_hpp_check(&dummy_hpp
, ret
))
867 static void hist_browser__show_headers(struct hist_browser
*browser
)
871 hists__scnprintf_headers(headers
, sizeof(headers
), browser
->hists
);
872 ui_browser__gotorc(&browser
->b
, 0, 0);
873 ui_browser__set_color(&browser
->b
, HE_COLORSET_ROOT
);
874 slsmg_write_nstring(headers
, browser
->b
.width
+ 1);
877 static void ui_browser__hists_init_top(struct ui_browser
*browser
)
879 if (browser
->top
== NULL
) {
880 struct hist_browser
*hb
;
882 hb
= container_of(browser
, struct hist_browser
, b
);
883 browser
->top
= rb_first(&hb
->hists
->entries
);
887 static unsigned int hist_browser__refresh(struct ui_browser
*browser
)
890 u16 header_offset
= 0;
892 struct hist_browser
*hb
= container_of(browser
, struct hist_browser
, b
);
894 if (hb
->show_headers
) {
895 hist_browser__show_headers(hb
);
899 ui_browser__hists_init_top(browser
);
901 for (nd
= browser
->top
; nd
; nd
= rb_next(nd
)) {
902 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
908 percent
= hist_entry__get_percent_limit(h
);
909 if (percent
< hb
->min_pcnt
)
912 row
+= hist_browser__show_entry(hb
, h
, row
);
913 if (row
== browser
->rows
)
917 return row
+ header_offset
;
920 static struct rb_node
*hists__filter_entries(struct rb_node
*nd
,
924 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
925 float percent
= hist_entry__get_percent_limit(h
);
927 if (!h
->filtered
&& percent
>= min_pcnt
)
936 static struct rb_node
*hists__filter_prev_entries(struct rb_node
*nd
,
940 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
941 float percent
= hist_entry__get_percent_limit(h
);
943 if (!h
->filtered
&& percent
>= min_pcnt
)
952 static void ui_browser__hists_seek(struct ui_browser
*browser
,
953 off_t offset
, int whence
)
955 struct hist_entry
*h
;
958 struct hist_browser
*hb
;
960 hb
= container_of(browser
, struct hist_browser
, b
);
962 if (browser
->nr_entries
== 0)
965 ui_browser__hists_init_top(browser
);
969 nd
= hists__filter_entries(rb_first(browser
->entries
),
976 nd
= hists__filter_prev_entries(rb_last(browser
->entries
),
985 * Moves not relative to the first visible entry invalidates its
988 h
= rb_entry(browser
->top
, struct hist_entry
, rb_node
);
992 * Here we have to check if nd is expanded (+), if it is we can't go
993 * the next top level hist_entry, instead we must compute an offset of
994 * what _not_ to show and not change the first visible entry.
996 * This offset increments when we are going from top to bottom and
997 * decreases when we're going from bottom to top.
999 * As we don't have backpointers to the top level in the callchains
1000 * structure, we need to always print the whole hist_entry callchain,
1001 * skipping the first ones that are before the first visible entry
1002 * and stop when we printed enough lines to fill the screen.
1007 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1008 if (h
->ms
.unfolded
) {
1009 u16 remaining
= h
->nr_rows
- h
->row_offset
;
1010 if (offset
> remaining
) {
1011 offset
-= remaining
;
1014 h
->row_offset
+= offset
;
1020 nd
= hists__filter_entries(rb_next(nd
), hb
->min_pcnt
);
1025 } while (offset
!= 0);
1026 } else if (offset
< 0) {
1028 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1029 if (h
->ms
.unfolded
) {
1031 if (-offset
> h
->row_offset
) {
1032 offset
+= h
->row_offset
;
1035 h
->row_offset
+= offset
;
1041 if (-offset
> h
->nr_rows
) {
1042 offset
+= h
->nr_rows
;
1045 h
->row_offset
= h
->nr_rows
+ offset
;
1053 nd
= hists__filter_prev_entries(rb_prev(nd
),
1061 * Last unfiltered hist_entry, check if it is
1062 * unfolded, if it is then we should have
1063 * row_offset at its last entry.
1065 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1067 h
->row_offset
= h
->nr_rows
;
1074 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1079 static int hist_browser__fprintf_callchain_node_rb_tree(struct hist_browser
*browser
,
1080 struct callchain_node
*chain_node
,
1081 u64 total
, int level
,
1084 struct rb_node
*node
;
1085 int offset
= level
* LEVEL_OFFSET_STEP
;
1086 u64 new_total
, remaining
;
1089 if (callchain_param
.mode
== CHAIN_GRAPH_REL
)
1090 new_total
= chain_node
->children_hit
;
1094 remaining
= new_total
;
1095 node
= rb_first(&chain_node
->rb_root
);
1097 struct callchain_node
*child
= rb_entry(node
, struct callchain_node
, rb_node
);
1098 struct rb_node
*next
= rb_next(node
);
1099 u64 cumul
= callchain_cumul_hits(child
);
1100 struct callchain_list
*chain
;
1101 char folded_sign
= ' ';
1103 int extra_offset
= 0;
1107 list_for_each_entry(chain
, &child
->val
, list
) {
1108 char bf
[1024], *alloc_str
;
1110 bool was_first
= first
;
1115 extra_offset
= LEVEL_OFFSET_STEP
;
1117 folded_sign
= callchain_list__folded(chain
);
1120 str
= callchain_list__sym_name(chain
, bf
, sizeof(bf
),
1123 double percent
= cumul
* 100.0 / new_total
;
1125 if (asprintf(&alloc_str
, "%2.2f%% %s", percent
, str
) < 0)
1126 str
= "Not enough memory!";
1131 printed
+= fprintf(fp
, "%*s%c %s\n", offset
+ extra_offset
, " ", folded_sign
, str
);
1133 if (folded_sign
== '+')
1137 if (folded_sign
== '-') {
1138 const int new_level
= level
+ (extra_offset
? 2 : 1);
1139 printed
+= hist_browser__fprintf_callchain_node_rb_tree(browser
, child
, new_total
,
1149 static int hist_browser__fprintf_callchain_node(struct hist_browser
*browser
,
1150 struct callchain_node
*node
,
1151 int level
, FILE *fp
)
1153 struct callchain_list
*chain
;
1154 int offset
= level
* LEVEL_OFFSET_STEP
;
1155 char folded_sign
= ' ';
1158 list_for_each_entry(chain
, &node
->val
, list
) {
1161 folded_sign
= callchain_list__folded(chain
);
1162 s
= callchain_list__sym_name(chain
, bf
, sizeof(bf
), browser
->show_dso
);
1163 printed
+= fprintf(fp
, "%*s%c %s\n", offset
, " ", folded_sign
, s
);
1166 if (folded_sign
== '-')
1167 printed
+= hist_browser__fprintf_callchain_node_rb_tree(browser
, node
,
1168 browser
->hists
->stats
.total_period
,
1173 static int hist_browser__fprintf_callchain(struct hist_browser
*browser
,
1174 struct rb_root
*chain
, int level
, FILE *fp
)
1179 for (nd
= rb_first(chain
); nd
; nd
= rb_next(nd
)) {
1180 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
1182 printed
+= hist_browser__fprintf_callchain_node(browser
, node
, level
, fp
);
1188 static int hist_browser__fprintf_entry(struct hist_browser
*browser
,
1189 struct hist_entry
*he
, FILE *fp
)
1193 char folded_sign
= ' ';
1194 struct perf_hpp hpp
= {
1198 struct perf_hpp_fmt
*fmt
;
1202 if (symbol_conf
.use_callchain
)
1203 folded_sign
= hist_entry__folded(he
);
1205 if (symbol_conf
.use_callchain
)
1206 printed
+= fprintf(fp
, "%c ", folded_sign
);
1208 perf_hpp__for_each_format(fmt
) {
1209 if (perf_hpp__should_skip(fmt
))
1213 ret
= scnprintf(hpp
.buf
, hpp
.size
, " ");
1214 advance_hpp(&hpp
, ret
);
1218 ret
= fmt
->entry(fmt
, &hpp
, he
);
1219 advance_hpp(&hpp
, ret
);
1221 printed
+= fprintf(fp
, "%s\n", rtrim(s
));
1223 if (folded_sign
== '-')
1224 printed
+= hist_browser__fprintf_callchain(browser
, &he
->sorted_chain
, 1, fp
);
1229 static int hist_browser__fprintf(struct hist_browser
*browser
, FILE *fp
)
1231 struct rb_node
*nd
= hists__filter_entries(rb_first(browser
->b
.entries
),
1236 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1238 printed
+= hist_browser__fprintf_entry(browser
, h
, fp
);
1239 nd
= hists__filter_entries(rb_next(nd
), browser
->min_pcnt
);
1245 static int hist_browser__dump(struct hist_browser
*browser
)
1251 scnprintf(filename
, sizeof(filename
), "perf.hist.%d", browser
->print_seq
);
1252 if (access(filename
, F_OK
))
1255 * XXX: Just an arbitrary lazy upper limit
1257 if (++browser
->print_seq
== 8192) {
1258 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1263 fp
= fopen(filename
, "w");
1266 const char *err
= strerror_r(errno
, bf
, sizeof(bf
));
1267 ui_helpline__fpush("Couldn't write to %s: %s", filename
, err
);
1271 ++browser
->print_seq
;
1272 hist_browser__fprintf(browser
, fp
);
1274 ui_helpline__fpush("%s written!", filename
);
1279 static struct hist_browser
*hist_browser__new(struct hists
*hists
)
1281 struct hist_browser
*browser
= zalloc(sizeof(*browser
));
1284 browser
->hists
= hists
;
1285 browser
->b
.refresh
= hist_browser__refresh
;
1286 browser
->b
.refresh_dimensions
= hist_browser__refresh_dimensions
;
1287 browser
->b
.seek
= ui_browser__hists_seek
;
1288 browser
->b
.use_navkeypressed
= true;
1289 browser
->show_headers
= symbol_conf
.show_hist_headers
;
1295 static void hist_browser__delete(struct hist_browser
*browser
)
1300 static struct hist_entry
*hist_browser__selected_entry(struct hist_browser
*browser
)
1302 return browser
->he_selection
;
1305 static struct thread
*hist_browser__selected_thread(struct hist_browser
*browser
)
1307 return browser
->he_selection
->thread
;
1310 static int hists__browser_title(struct hists
*hists
, char *bf
, size_t size
)
1314 const struct dso
*dso
= hists
->dso_filter
;
1315 const struct thread
*thread
= hists
->thread_filter
;
1316 unsigned long nr_samples
= hists
->stats
.nr_events
[PERF_RECORD_SAMPLE
];
1317 u64 nr_events
= hists
->stats
.total_period
;
1318 struct perf_evsel
*evsel
= hists_to_evsel(hists
);
1319 const char *ev_name
= perf_evsel__name(evsel
);
1321 size_t buflen
= sizeof(buf
);
1323 if (symbol_conf
.filter_relative
) {
1324 nr_samples
= hists
->stats
.nr_non_filtered_samples
;
1325 nr_events
= hists
->stats
.total_non_filtered_period
;
1328 if (perf_evsel__is_group_event(evsel
)) {
1329 struct perf_evsel
*pos
;
1331 perf_evsel__group_desc(evsel
, buf
, buflen
);
1334 for_each_group_member(pos
, evsel
) {
1335 if (symbol_conf
.filter_relative
) {
1336 nr_samples
+= pos
->hists
.stats
.nr_non_filtered_samples
;
1337 nr_events
+= pos
->hists
.stats
.total_non_filtered_period
;
1339 nr_samples
+= pos
->hists
.stats
.nr_events
[PERF_RECORD_SAMPLE
];
1340 nr_events
+= pos
->hists
.stats
.total_period
;
1345 nr_samples
= convert_unit(nr_samples
, &unit
);
1346 printed
= scnprintf(bf
, size
,
1347 "Samples: %lu%c of event '%s', Event count (approx.): %lu",
1348 nr_samples
, unit
, ev_name
, nr_events
);
1351 if (hists
->uid_filter_str
)
1352 printed
+= snprintf(bf
+ printed
, size
- printed
,
1353 ", UID: %s", hists
->uid_filter_str
);
1355 printed
+= scnprintf(bf
+ printed
, size
- printed
,
1357 (thread
->comm_set
? thread__comm_str(thread
) : ""),
1360 printed
+= scnprintf(bf
+ printed
, size
- printed
,
1361 ", DSO: %s", dso
->short_name
);
1365 static inline void free_popup_options(char **options
, int n
)
1369 for (i
= 0; i
< n
; ++i
)
1373 /* Check whether the browser is for 'top' or 'report' */
1374 static inline bool is_report_browser(void *timer
)
1376 return timer
== NULL
;
1380 * Only runtime switching of perf data file will make "input_name" point
1381 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1382 * whether we need to call free() for current "input_name" during the switch.
1384 static bool is_input_name_malloced
= false;
1386 static int switch_data_file(void)
1388 char *pwd
, *options
[32], *abs_path
[32], *tmp
;
1390 int nr_options
= 0, choice
= -1, ret
= -1;
1391 struct dirent
*dent
;
1393 pwd
= getenv("PWD");
1397 pwd_dir
= opendir(pwd
);
1401 memset(options
, 0, sizeof(options
));
1402 memset(options
, 0, sizeof(abs_path
));
1404 while ((dent
= readdir(pwd_dir
))) {
1405 char path
[PATH_MAX
];
1407 char *name
= dent
->d_name
;
1410 if (!(dent
->d_type
== DT_REG
))
1413 snprintf(path
, sizeof(path
), "%s/%s", pwd
, name
);
1415 file
= fopen(path
, "r");
1419 if (fread(&magic
, 1, 8, file
) < 8)
1420 goto close_file_and_continue
;
1422 if (is_perf_magic(magic
)) {
1423 options
[nr_options
] = strdup(name
);
1424 if (!options
[nr_options
])
1425 goto close_file_and_continue
;
1427 abs_path
[nr_options
] = strdup(path
);
1428 if (!abs_path
[nr_options
]) {
1429 zfree(&options
[nr_options
]);
1430 ui__warning("Can't search all data files due to memory shortage.\n");
1438 close_file_and_continue
:
1440 if (nr_options
>= 32) {
1441 ui__warning("Too many perf data files in PWD!\n"
1442 "Only the first 32 files will be listed.\n");
1449 choice
= ui__popup_menu(nr_options
, options
);
1450 if (choice
< nr_options
&& choice
>= 0) {
1451 tmp
= strdup(abs_path
[choice
]);
1453 if (is_input_name_malloced
)
1454 free((void *)input_name
);
1456 is_input_name_malloced
= true;
1459 ui__warning("Data switch failed due to memory shortage!\n");
1463 free_popup_options(options
, nr_options
);
1464 free_popup_options(abs_path
, nr_options
);
1468 static void hist_browser__update_nr_entries(struct hist_browser
*hb
)
1471 struct rb_node
*nd
= rb_first(&hb
->hists
->entries
);
1473 if (hb
->min_pcnt
== 0) {
1474 hb
->nr_non_filtered_entries
= hb
->hists
->nr_non_filtered_entries
;
1478 while ((nd
= hists__filter_entries(nd
, hb
->min_pcnt
)) != NULL
) {
1483 hb
->nr_non_filtered_entries
= nr_entries
;
1486 static int perf_evsel__hists_browse(struct perf_evsel
*evsel
, int nr_events
,
1487 const char *helpline
,
1489 struct hist_browser_timer
*hbt
,
1491 struct perf_session_env
*env
)
1493 struct hists
*hists
= &evsel
->hists
;
1494 struct hist_browser
*browser
= hist_browser__new(hists
);
1495 struct branch_info
*bi
;
1496 struct pstack
*fstack
;
1501 char script_opt
[64];
1502 int delay_secs
= hbt
? hbt
->refresh
: 0;
1504 #define HIST_BROWSER_HELP_COMMON \
1505 "h/?/F1 Show this window\n" \
1507 "PGDN/SPACE Navigate\n" \
1508 "q/ESC/CTRL+C Exit browser\n\n" \
1509 "For multiple event sessions:\n\n" \
1510 "TAB/UNTAB Switch events\n\n" \
1511 "For symbolic views (--sort has sym):\n\n" \
1512 "-> Zoom into DSO/Threads & Annotate current symbol\n" \
1514 "a Annotate current symbol\n" \
1515 "C Collapse all callchains\n" \
1516 "d Zoom into current DSO\n" \
1517 "E Expand all callchains\n" \
1518 "F Toggle percentage of filtered entries\n" \
1519 "H Display column headers\n" \
1521 /* help messages are sorted by lexical order of the hotkey */
1522 const char report_help
[] = HIST_BROWSER_HELP_COMMON
1523 "i Show header information\n"
1524 "P Print histograms to perf.hist.N\n"
1525 "r Run available scripts\n"
1526 "s Switch to another data file in PWD\n"
1527 "t Zoom into current Thread\n"
1528 "V Verbose (DSO names in callchains, etc)\n"
1529 "/ Filter symbol by name";
1530 const char top_help
[] = HIST_BROWSER_HELP_COMMON
1531 "P Print histograms to perf.hist.N\n"
1532 "t Zoom into current Thread\n"
1533 "V Verbose (DSO names in callchains, etc)\n"
1534 "/ Filter symbol by name";
1536 if (browser
== NULL
)
1540 browser
->min_pcnt
= min_pcnt
;
1541 hist_browser__update_nr_entries(browser
);
1544 fstack
= pstack__new(2);
1548 ui_helpline__push(helpline
);
1550 memset(options
, 0, sizeof(options
));
1553 const struct thread
*thread
= NULL
;
1554 const struct dso
*dso
= NULL
;
1556 annotate
= -2, zoom_dso
= -2, zoom_thread
= -2,
1557 annotate_f
= -2, annotate_t
= -2, browse_map
= -2;
1558 int scripts_comm
= -2, scripts_symbol
= -2,
1559 scripts_all
= -2, switch_data
= -2;
1563 key
= hist_browser__run(browser
, hbt
);
1565 if (browser
->he_selection
!= NULL
) {
1566 thread
= hist_browser__selected_thread(browser
);
1567 dso
= browser
->selection
->map
? browser
->selection
->map
->dso
: NULL
;
1575 * Exit the browser, let hists__browser_tree
1576 * go to the next or previous
1578 goto out_free_stack
;
1580 if (!sort__has_sym
) {
1581 ui_browser__warning(&browser
->b
, delay_secs
* 2,
1582 "Annotation is only available for symbolic views, "
1583 "include \"sym*\" in --sort to use it.");
1587 if (browser
->selection
== NULL
||
1588 browser
->selection
->sym
== NULL
||
1589 browser
->selection
->map
->dso
->annotate_warned
)
1593 hist_browser__dump(browser
);
1598 browser
->show_dso
= !browser
->show_dso
;
1603 if (ui_browser__input_window("Symbol to show",
1604 "Please enter the name of symbol you want to see",
1605 buf
, "ENTER: OK, ESC: Cancel",
1606 delay_secs
* 2) == K_ENTER
) {
1607 hists
->symbol_filter_str
= *buf
? buf
: NULL
;
1608 hists__filter_by_symbol(hists
);
1609 hist_browser__reset(browser
);
1613 if (is_report_browser(hbt
))
1617 if (is_report_browser(hbt
))
1618 goto do_data_switch
;
1621 /* env->arch is NULL for live-mode (i.e. perf top) */
1623 tui__header_window(env
);
1626 symbol_conf
.filter_relative
^= 1;
1631 ui_browser__help_window(&browser
->b
,
1632 is_report_browser(hbt
) ? report_help
: top_help
);
1641 if (pstack__empty(fstack
)) {
1643 * Go back to the perf_evsel_menu__run or other user
1646 goto out_free_stack
;
1649 top
= pstack__pop(fstack
);
1650 if (top
== &browser
->hists
->dso_filter
)
1652 if (top
== &browser
->hists
->thread_filter
)
1653 goto zoom_out_thread
;
1658 !ui_browser__dialog_yesno(&browser
->b
,
1659 "Do you really want to exit?"))
1664 goto out_free_stack
;
1670 goto add_exit_option
;
1672 if (sort__mode
== SORT_MODE__BRANCH
) {
1673 bi
= browser
->he_selection
->branch_info
;
1674 if (browser
->selection
!= NULL
&&
1676 bi
->from
.sym
!= NULL
&&
1677 !bi
->from
.map
->dso
->annotate_warned
&&
1678 asprintf(&options
[nr_options
], "Annotate %s",
1679 bi
->from
.sym
->name
) > 0)
1680 annotate_f
= nr_options
++;
1682 if (browser
->selection
!= NULL
&&
1684 bi
->to
.sym
!= NULL
&&
1685 !bi
->to
.map
->dso
->annotate_warned
&&
1686 (bi
->to
.sym
!= bi
->from
.sym
||
1687 bi
->to
.map
->dso
!= bi
->from
.map
->dso
) &&
1688 asprintf(&options
[nr_options
], "Annotate %s",
1689 bi
->to
.sym
->name
) > 0)
1690 annotate_t
= nr_options
++;
1692 if (browser
->selection
!= NULL
&&
1693 browser
->selection
->sym
!= NULL
&&
1694 !browser
->selection
->map
->dso
->annotate_warned
) {
1695 struct annotation
*notes
;
1697 notes
= symbol__annotation(browser
->selection
->sym
);
1700 asprintf(&options
[nr_options
], "Annotate %s",
1701 browser
->selection
->sym
->name
) > 0)
1702 annotate
= nr_options
++;
1706 if (thread
!= NULL
&&
1707 asprintf(&options
[nr_options
], "Zoom %s %s(%d) thread",
1708 (browser
->hists
->thread_filter
? "out of" : "into"),
1709 (thread
->comm_set
? thread__comm_str(thread
) : ""),
1711 zoom_thread
= nr_options
++;
1714 asprintf(&options
[nr_options
], "Zoom %s %s DSO",
1715 (browser
->hists
->dso_filter
? "out of" : "into"),
1716 (dso
->kernel
? "the Kernel" : dso
->short_name
)) > 0)
1717 zoom_dso
= nr_options
++;
1719 if (browser
->selection
!= NULL
&&
1720 browser
->selection
->map
!= NULL
&&
1721 asprintf(&options
[nr_options
], "Browse map details") > 0)
1722 browse_map
= nr_options
++;
1724 /* perf script support */
1725 if (browser
->he_selection
) {
1728 if (asprintf(&options
[nr_options
], "Run scripts for samples of thread [%s]",
1729 thread__comm_str(browser
->he_selection
->thread
)) > 0)
1730 scripts_comm
= nr_options
++;
1732 sym
= browser
->he_selection
->ms
.sym
;
1733 if (sym
&& sym
->namelen
&&
1734 asprintf(&options
[nr_options
], "Run scripts for samples of symbol [%s]",
1736 scripts_symbol
= nr_options
++;
1739 if (asprintf(&options
[nr_options
], "Run scripts for all samples") > 0)
1740 scripts_all
= nr_options
++;
1742 if (is_report_browser(hbt
) && asprintf(&options
[nr_options
],
1743 "Switch to another data file in PWD") > 0)
1744 switch_data
= nr_options
++;
1746 options
[nr_options
++] = (char *)"Exit";
1748 choice
= ui__popup_menu(nr_options
, options
);
1750 if (choice
== nr_options
- 1)
1754 free_popup_options(options
, nr_options
- 1);
1758 if (choice
== annotate
|| choice
== annotate_t
|| choice
== annotate_f
) {
1759 struct hist_entry
*he
;
1760 struct annotation
*notes
;
1763 if (!objdump_path
&& perf_session_env__lookup_objdump(env
))
1766 he
= hist_browser__selected_entry(browser
);
1771 * we stash the branch_info symbol + map into the
1772 * the ms so we don't have to rewrite all the annotation
1773 * code to use branch_info.
1774 * in branch mode, the ms struct is not used
1776 if (choice
== annotate_f
) {
1777 he
->ms
.sym
= he
->branch_info
->from
.sym
;
1778 he
->ms
.map
= he
->branch_info
->from
.map
;
1779 } else if (choice
== annotate_t
) {
1780 he
->ms
.sym
= he
->branch_info
->to
.sym
;
1781 he
->ms
.map
= he
->branch_info
->to
.map
;
1784 notes
= symbol__annotation(he
->ms
.sym
);
1789 * Don't let this be freed, say, by hists__decay_entry.
1792 err
= hist_entry__tui_annotate(he
, evsel
, hbt
);
1795 * offer option to annotate the other branch source or target
1796 * (if they exists) when returning from annotate
1798 if ((err
== 'q' || err
== CTRL('c'))
1799 && annotate_t
!= -2 && annotate_f
!= -2)
1800 goto retry_popup_menu
;
1802 ui_browser__update_nr_entries(&browser
->b
, browser
->hists
->nr_entries
);
1804 ui_browser__handle_resize(&browser
->b
);
1806 } else if (choice
== browse_map
)
1807 map__browse(browser
->selection
->map
);
1808 else if (choice
== zoom_dso
) {
1810 if (browser
->hists
->dso_filter
) {
1811 pstack__remove(fstack
, &browser
->hists
->dso_filter
);
1814 browser
->hists
->dso_filter
= NULL
;
1815 perf_hpp__set_elide(HISTC_DSO
, false);
1819 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1820 dso
->kernel
? "the Kernel" : dso
->short_name
);
1821 browser
->hists
->dso_filter
= dso
;
1822 perf_hpp__set_elide(HISTC_DSO
, true);
1823 pstack__push(fstack
, &browser
->hists
->dso_filter
);
1825 hists__filter_by_dso(hists
);
1826 hist_browser__reset(browser
);
1827 } else if (choice
== zoom_thread
) {
1829 if (browser
->hists
->thread_filter
) {
1830 pstack__remove(fstack
, &browser
->hists
->thread_filter
);
1833 browser
->hists
->thread_filter
= NULL
;
1834 perf_hpp__set_elide(HISTC_THREAD
, false);
1836 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1837 thread
->comm_set
? thread__comm_str(thread
) : "",
1839 browser
->hists
->thread_filter
= thread
;
1840 perf_hpp__set_elide(HISTC_THREAD
, false);
1841 pstack__push(fstack
, &browser
->hists
->thread_filter
);
1843 hists__filter_by_thread(hists
);
1844 hist_browser__reset(browser
);
1846 /* perf scripts support */
1847 else if (choice
== scripts_all
|| choice
== scripts_comm
||
1848 choice
== scripts_symbol
) {
1850 memset(script_opt
, 0, 64);
1852 if (choice
== scripts_comm
)
1853 sprintf(script_opt
, " -c %s ", thread__comm_str(browser
->he_selection
->thread
));
1855 if (choice
== scripts_symbol
)
1856 sprintf(script_opt
, " -S %s ", browser
->he_selection
->ms
.sym
->name
);
1858 script_browse(script_opt
);
1860 /* Switch to another data file */
1861 else if (choice
== switch_data
) {
1863 if (!switch_data_file()) {
1864 key
= K_SWITCH_INPUT_DATA
;
1867 ui__warning("Won't switch the data files due to\n"
1868 "no valid data file get selected!\n");
1872 pstack__delete(fstack
);
1874 hist_browser__delete(browser
);
1875 free_popup_options(options
, nr_options
- 1);
1879 struct perf_evsel_menu
{
1880 struct ui_browser b
;
1881 struct perf_evsel
*selection
;
1882 bool lost_events
, lost_events_warned
;
1884 struct perf_session_env
*env
;
1887 static void perf_evsel_menu__write(struct ui_browser
*browser
,
1888 void *entry
, int row
)
1890 struct perf_evsel_menu
*menu
= container_of(browser
,
1891 struct perf_evsel_menu
, b
);
1892 struct perf_evsel
*evsel
= list_entry(entry
, struct perf_evsel
, node
);
1893 bool current_entry
= ui_browser__is_current_entry(browser
, row
);
1894 unsigned long nr_events
= evsel
->hists
.stats
.nr_events
[PERF_RECORD_SAMPLE
];
1895 const char *ev_name
= perf_evsel__name(evsel
);
1897 const char *warn
= " ";
1900 ui_browser__set_color(browser
, current_entry
? HE_COLORSET_SELECTED
:
1901 HE_COLORSET_NORMAL
);
1903 if (perf_evsel__is_group_event(evsel
)) {
1904 struct perf_evsel
*pos
;
1906 ev_name
= perf_evsel__group_name(evsel
);
1908 for_each_group_member(pos
, evsel
) {
1909 nr_events
+= pos
->hists
.stats
.nr_events
[PERF_RECORD_SAMPLE
];
1913 nr_events
= convert_unit(nr_events
, &unit
);
1914 printed
= scnprintf(bf
, sizeof(bf
), "%lu%c%s%s", nr_events
,
1915 unit
, unit
== ' ' ? "" : " ", ev_name
);
1916 slsmg_printf("%s", bf
);
1918 nr_events
= evsel
->hists
.stats
.nr_events
[PERF_RECORD_LOST
];
1919 if (nr_events
!= 0) {
1920 menu
->lost_events
= true;
1922 ui_browser__set_color(browser
, HE_COLORSET_TOP
);
1923 nr_events
= convert_unit(nr_events
, &unit
);
1924 printed
+= scnprintf(bf
, sizeof(bf
), ": %ld%c%schunks LOST!",
1925 nr_events
, unit
, unit
== ' ' ? "" : " ");
1929 slsmg_write_nstring(warn
, browser
->width
- printed
);
1932 menu
->selection
= evsel
;
1935 static int perf_evsel_menu__run(struct perf_evsel_menu
*menu
,
1936 int nr_events
, const char *help
,
1937 struct hist_browser_timer
*hbt
)
1939 struct perf_evlist
*evlist
= menu
->b
.priv
;
1940 struct perf_evsel
*pos
;
1941 const char *title
= "Available samples";
1942 int delay_secs
= hbt
? hbt
->refresh
: 0;
1945 if (ui_browser__show(&menu
->b
, title
,
1946 "ESC: exit, ENTER|->: Browse histograms") < 0)
1950 key
= ui_browser__run(&menu
->b
, delay_secs
);
1954 hbt
->timer(hbt
->arg
);
1956 if (!menu
->lost_events_warned
&& menu
->lost_events
) {
1957 ui_browser__warn_lost_events(&menu
->b
);
1958 menu
->lost_events_warned
= true;
1963 if (!menu
->selection
)
1965 pos
= menu
->selection
;
1967 perf_evlist__set_selected(evlist
, pos
);
1969 * Give the calling tool a chance to populate the non
1970 * default evsel resorted hists tree.
1973 hbt
->timer(hbt
->arg
);
1974 key
= perf_evsel__hists_browse(pos
, nr_events
, help
,
1978 ui_browser__show_title(&menu
->b
, title
);
1981 if (pos
->node
.next
== &evlist
->entries
)
1982 pos
= perf_evlist__first(evlist
);
1984 pos
= perf_evsel__next(pos
);
1987 if (pos
->node
.prev
== &evlist
->entries
)
1988 pos
= perf_evlist__last(evlist
);
1990 pos
= perf_evsel__prev(pos
);
1993 if (!ui_browser__dialog_yesno(&menu
->b
,
1994 "Do you really want to exit?"))
1997 case K_SWITCH_INPUT_DATA
:
2007 if (!ui_browser__dialog_yesno(&menu
->b
,
2008 "Do you really want to exit?"))
2020 ui_browser__hide(&menu
->b
);
2024 static bool filter_group_entries(struct ui_browser
*browser __maybe_unused
,
2027 struct perf_evsel
*evsel
= list_entry(entry
, struct perf_evsel
, node
);
2029 if (symbol_conf
.event_group
&& !perf_evsel__is_group_leader(evsel
))
2035 static int __perf_evlist__tui_browse_hists(struct perf_evlist
*evlist
,
2036 int nr_entries
, const char *help
,
2037 struct hist_browser_timer
*hbt
,
2039 struct perf_session_env
*env
)
2041 struct perf_evsel
*pos
;
2042 struct perf_evsel_menu menu
= {
2044 .entries
= &evlist
->entries
,
2045 .refresh
= ui_browser__list_head_refresh
,
2046 .seek
= ui_browser__list_head_seek
,
2047 .write
= perf_evsel_menu__write
,
2048 .filter
= filter_group_entries
,
2049 .nr_entries
= nr_entries
,
2052 .min_pcnt
= min_pcnt
,
2056 ui_helpline__push("Press ESC to exit");
2058 evlist__for_each(evlist
, pos
) {
2059 const char *ev_name
= perf_evsel__name(pos
);
2060 size_t line_len
= strlen(ev_name
) + 7;
2062 if (menu
.b
.width
< line_len
)
2063 menu
.b
.width
= line_len
;
2066 return perf_evsel_menu__run(&menu
, nr_entries
, help
, hbt
);
2069 int perf_evlist__tui_browse_hists(struct perf_evlist
*evlist
, const char *help
,
2070 struct hist_browser_timer
*hbt
,
2072 struct perf_session_env
*env
)
2074 int nr_entries
= evlist
->nr_entries
;
2077 if (nr_entries
== 1) {
2078 struct perf_evsel
*first
= perf_evlist__first(evlist
);
2080 return perf_evsel__hists_browse(first
, nr_entries
, help
,
2081 false, hbt
, min_pcnt
,
2085 if (symbol_conf
.event_group
) {
2086 struct perf_evsel
*pos
;
2089 evlist__for_each(evlist
, pos
) {
2090 if (perf_evsel__is_group_leader(pos
))
2094 if (nr_entries
== 1)
2098 return __perf_evlist__tui_browse_hists(evlist
, nr_entries
, help
,
2099 hbt
, min_pcnt
, env
);