2 #include "../libslang.h"
5 #include <linux/rbtree.h>
7 #include "../../util/evsel.h"
8 #include "../../util/evlist.h"
9 #include "../../util/hist.h"
10 #include "../../util/pstack.h"
11 #include "../../util/sort.h"
12 #include "../../util/util.h"
13 #include "../../util/top.h"
14 #include "../../arch/common.h"
16 #include "../browser.h"
17 #include "../helpline.h"
26 struct hist_entry
*he_selection
;
27 struct map_symbol
*selection
;
32 u64 nr_non_filtered_entries
;
33 u64 nr_callchain_rows
;
36 extern void hist_browser__init_hpp(void);
38 static int hists__browser_title(struct hists
*hists
,
39 struct hist_browser_timer
*hbt
,
40 char *bf
, size_t size
);
41 static void hist_browser__update_nr_entries(struct hist_browser
*hb
);
43 static struct rb_node
*hists__filter_entries(struct rb_node
*nd
,
46 static bool hist_browser__has_filter(struct hist_browser
*hb
)
48 return hists__has_filter(hb
->hists
) || hb
->min_pcnt
;
51 static u32
hist_browser__nr_entries(struct hist_browser
*hb
)
55 if (hist_browser__has_filter(hb
))
56 nr_entries
= hb
->nr_non_filtered_entries
;
58 nr_entries
= hb
->hists
->nr_entries
;
60 return nr_entries
+ hb
->nr_callchain_rows
;
63 static void hist_browser__update_rows(struct hist_browser
*hb
)
65 struct ui_browser
*browser
= &hb
->b
;
66 u16 header_offset
= hb
->show_headers
? 1 : 0, index_row
;
68 browser
->rows
= browser
->height
- header_offset
;
70 * Verify if we were at the last line and that line isn't
71 * visibe because we now show the header line(s).
73 index_row
= browser
->index
- browser
->top_idx
;
74 if (index_row
>= browser
->rows
)
75 browser
->index
-= index_row
- browser
->rows
+ 1;
78 static void hist_browser__refresh_dimensions(struct ui_browser
*browser
)
80 struct hist_browser
*hb
= container_of(browser
, struct hist_browser
, b
);
82 /* 3 == +/- toggle symbol before actual hist_entry rendering */
83 browser
->width
= 3 + (hists__sort_list_width(hb
->hists
) + sizeof("[k]"));
85 * FIXME: Just keeping existing behaviour, but this really should be
86 * before updating browser->width, as it will invalidate the
87 * calculation above. Fix this and the fallout in another
90 ui_browser__refresh_dimensions(browser
);
91 hist_browser__update_rows(hb
);
94 static void hist_browser__gotorc(struct hist_browser
*browser
, int row
, int column
)
96 u16 header_offset
= browser
->show_headers
? 1 : 0;
98 ui_browser__gotorc(&browser
->b
, row
+ header_offset
, column
);
101 static void hist_browser__reset(struct hist_browser
*browser
)
104 * The hists__remove_entry_filter() already folds non-filtered
105 * entries so we can assume it has 0 callchain rows.
107 browser
->nr_callchain_rows
= 0;
109 hist_browser__update_nr_entries(browser
);
110 browser
->b
.nr_entries
= hist_browser__nr_entries(browser
);
111 hist_browser__refresh_dimensions(&browser
->b
);
112 ui_browser__reset_index(&browser
->b
);
115 static char tree__folded_sign(bool unfolded
)
117 return unfolded
? '-' : '+';
120 static char map_symbol__folded(const struct map_symbol
*ms
)
122 return ms
->has_children
? tree__folded_sign(ms
->unfolded
) : ' ';
125 static char hist_entry__folded(const struct hist_entry
*he
)
127 return map_symbol__folded(&he
->ms
);
130 static char callchain_list__folded(const struct callchain_list
*cl
)
132 return map_symbol__folded(&cl
->ms
);
135 static void map_symbol__set_folding(struct map_symbol
*ms
, bool unfold
)
137 ms
->unfolded
= unfold
? ms
->has_children
: false;
140 static int callchain_node__count_rows_rb_tree(struct callchain_node
*node
)
145 for (nd
= rb_first(&node
->rb_root
); nd
; nd
= rb_next(nd
)) {
146 struct callchain_node
*child
= rb_entry(nd
, struct callchain_node
, rb_node
);
147 struct callchain_list
*chain
;
148 char folded_sign
= ' '; /* No children */
150 list_for_each_entry(chain
, &child
->val
, list
) {
152 /* We need this because we may not have children */
153 folded_sign
= callchain_list__folded(chain
);
154 if (folded_sign
== '+')
158 if (folded_sign
== '-') /* Have children and they're unfolded */
159 n
+= callchain_node__count_rows_rb_tree(child
);
165 static int callchain_node__count_rows(struct callchain_node
*node
)
167 struct callchain_list
*chain
;
168 bool unfolded
= false;
171 list_for_each_entry(chain
, &node
->val
, list
) {
173 unfolded
= chain
->ms
.unfolded
;
177 n
+= callchain_node__count_rows_rb_tree(node
);
182 static int callchain__count_rows(struct rb_root
*chain
)
187 for (nd
= rb_first(chain
); nd
; nd
= rb_next(nd
)) {
188 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
189 n
+= callchain_node__count_rows(node
);
195 static bool map_symbol__toggle_fold(struct map_symbol
*ms
)
200 if (!ms
->has_children
)
203 ms
->unfolded
= !ms
->unfolded
;
207 static void callchain_node__init_have_children_rb_tree(struct callchain_node
*node
)
209 struct rb_node
*nd
= rb_first(&node
->rb_root
);
211 for (nd
= rb_first(&node
->rb_root
); nd
; nd
= rb_next(nd
)) {
212 struct callchain_node
*child
= rb_entry(nd
, struct callchain_node
, rb_node
);
213 struct callchain_list
*chain
;
216 list_for_each_entry(chain
, &child
->val
, list
) {
219 chain
->ms
.has_children
= chain
->list
.next
!= &child
->val
||
220 !RB_EMPTY_ROOT(&child
->rb_root
);
222 chain
->ms
.has_children
= chain
->list
.next
== &child
->val
&&
223 !RB_EMPTY_ROOT(&child
->rb_root
);
226 callchain_node__init_have_children_rb_tree(child
);
230 static void callchain_node__init_have_children(struct callchain_node
*node
,
233 struct callchain_list
*chain
;
235 chain
= list_entry(node
->val
.next
, struct callchain_list
, list
);
236 chain
->ms
.has_children
= has_sibling
;
238 if (!list_empty(&node
->val
)) {
239 chain
= list_entry(node
->val
.prev
, struct callchain_list
, list
);
240 chain
->ms
.has_children
= !RB_EMPTY_ROOT(&node
->rb_root
);
243 callchain_node__init_have_children_rb_tree(node
);
246 static void callchain__init_have_children(struct rb_root
*root
)
248 struct rb_node
*nd
= rb_first(root
);
249 bool has_sibling
= nd
&& rb_next(nd
);
251 for (nd
= rb_first(root
); nd
; nd
= rb_next(nd
)) {
252 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
253 callchain_node__init_have_children(node
, has_sibling
);
257 static void hist_entry__init_have_children(struct hist_entry
*he
)
259 if (!he
->init_have_children
) {
260 he
->ms
.has_children
= !RB_EMPTY_ROOT(&he
->sorted_chain
);
261 callchain__init_have_children(&he
->sorted_chain
);
262 he
->init_have_children
= true;
266 static bool hist_browser__toggle_fold(struct hist_browser
*browser
)
268 if (map_symbol__toggle_fold(browser
->selection
)) {
269 struct hist_entry
*he
= browser
->he_selection
;
271 hist_entry__init_have_children(he
);
272 browser
->b
.nr_entries
-= he
->nr_rows
;
273 browser
->nr_callchain_rows
-= he
->nr_rows
;
276 he
->nr_rows
= callchain__count_rows(&he
->sorted_chain
);
280 browser
->b
.nr_entries
+= he
->nr_rows
;
281 browser
->nr_callchain_rows
+= he
->nr_rows
;
286 /* If it doesn't have children, no toggling performed */
290 static int callchain_node__set_folding_rb_tree(struct callchain_node
*node
, bool unfold
)
295 for (nd
= rb_first(&node
->rb_root
); nd
; nd
= rb_next(nd
)) {
296 struct callchain_node
*child
= rb_entry(nd
, struct callchain_node
, rb_node
);
297 struct callchain_list
*chain
;
298 bool has_children
= false;
300 list_for_each_entry(chain
, &child
->val
, list
) {
302 map_symbol__set_folding(&chain
->ms
, unfold
);
303 has_children
= chain
->ms
.has_children
;
307 n
+= callchain_node__set_folding_rb_tree(child
, unfold
);
313 static int callchain_node__set_folding(struct callchain_node
*node
, bool unfold
)
315 struct callchain_list
*chain
;
316 bool has_children
= false;
319 list_for_each_entry(chain
, &node
->val
, list
) {
321 map_symbol__set_folding(&chain
->ms
, unfold
);
322 has_children
= chain
->ms
.has_children
;
326 n
+= callchain_node__set_folding_rb_tree(node
, unfold
);
331 static int callchain__set_folding(struct rb_root
*chain
, bool unfold
)
336 for (nd
= rb_first(chain
); nd
; nd
= rb_next(nd
)) {
337 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
338 n
+= callchain_node__set_folding(node
, unfold
);
344 static void hist_entry__set_folding(struct hist_entry
*he
, bool unfold
)
346 hist_entry__init_have_children(he
);
347 map_symbol__set_folding(&he
->ms
, unfold
);
349 if (he
->ms
.has_children
) {
350 int n
= callchain__set_folding(&he
->sorted_chain
, unfold
);
351 he
->nr_rows
= unfold
? n
: 0;
357 __hist_browser__set_folding(struct hist_browser
*browser
, bool unfold
)
360 struct hists
*hists
= browser
->hists
;
362 for (nd
= rb_first(&hists
->entries
);
363 (nd
= hists__filter_entries(nd
, browser
->min_pcnt
)) != NULL
;
365 struct hist_entry
*he
= rb_entry(nd
, struct hist_entry
, rb_node
);
366 hist_entry__set_folding(he
, unfold
);
367 browser
->nr_callchain_rows
+= he
->nr_rows
;
371 static void hist_browser__set_folding(struct hist_browser
*browser
, bool unfold
)
373 browser
->nr_callchain_rows
= 0;
374 __hist_browser__set_folding(browser
, unfold
);
376 browser
->b
.nr_entries
= hist_browser__nr_entries(browser
);
377 /* Go to the start, we may be way after valid entries after a collapse */
378 ui_browser__reset_index(&browser
->b
);
381 static void ui_browser__warn_lost_events(struct ui_browser
*browser
)
383 ui_browser__warning(browser
, 4,
384 "Events are being lost, check IO/CPU overload!\n\n"
385 "You may want to run 'perf' using a RT scheduler policy:\n\n"
386 " perf top -r 80\n\n"
387 "Or reduce the sampling frequency.");
390 static int hist_browser__run(struct hist_browser
*browser
,
391 struct hist_browser_timer
*hbt
)
395 int delay_secs
= hbt
? hbt
->refresh
: 0;
397 browser
->b
.entries
= &browser
->hists
->entries
;
398 browser
->b
.nr_entries
= hist_browser__nr_entries(browser
);
400 hists__browser_title(browser
->hists
, hbt
, title
, sizeof(title
));
402 if (ui_browser__show(&browser
->b
, title
,
403 "Press '?' for help on key bindings") < 0)
407 key
= ui_browser__run(&browser
->b
, delay_secs
);
412 hbt
->timer(hbt
->arg
);
414 if (hist_browser__has_filter(browser
))
415 hist_browser__update_nr_entries(browser
);
417 nr_entries
= hist_browser__nr_entries(browser
);
418 ui_browser__update_nr_entries(&browser
->b
, nr_entries
);
420 if (browser
->hists
->stats
.nr_lost_warned
!=
421 browser
->hists
->stats
.nr_events
[PERF_RECORD_LOST
]) {
422 browser
->hists
->stats
.nr_lost_warned
=
423 browser
->hists
->stats
.nr_events
[PERF_RECORD_LOST
];
424 ui_browser__warn_lost_events(&browser
->b
);
427 hists__browser_title(browser
->hists
,
428 hbt
, title
, sizeof(title
));
429 ui_browser__show_title(&browser
->b
, title
);
432 case 'D': { /* Debug */
434 struct hist_entry
*h
= rb_entry(browser
->b
.top
,
435 struct hist_entry
, rb_node
);
437 ui_helpline__fpush("%d: nr_ent=(%d,%d), rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
438 seq
++, browser
->b
.nr_entries
,
439 browser
->hists
->nr_entries
,
443 h
->row_offset
, h
->nr_rows
);
447 /* Collapse the whole world. */
448 hist_browser__set_folding(browser
, false);
451 /* Expand the whole world. */
452 hist_browser__set_folding(browser
, true);
455 browser
->show_headers
= !browser
->show_headers
;
456 hist_browser__update_rows(browser
);
459 if (hist_browser__toggle_fold(browser
))
467 ui_browser__hide(&browser
->b
);
471 struct callchain_print_arg
{
472 /* for hists browser */
474 bool is_current_entry
;
481 typedef void (*print_callchain_entry_fn
)(struct hist_browser
*browser
,
482 struct callchain_list
*chain
,
483 const char *str
, int offset
,
485 struct callchain_print_arg
*arg
);
487 static void hist_browser__show_callchain_entry(struct hist_browser
*browser
,
488 struct callchain_list
*chain
,
489 const char *str
, int offset
,
491 struct callchain_print_arg
*arg
)
494 char folded_sign
= callchain_list__folded(chain
);
496 color
= HE_COLORSET_NORMAL
;
497 width
= browser
->b
.width
- (offset
+ 2);
498 if (ui_browser__is_current_entry(&browser
->b
, row
)) {
499 browser
->selection
= &chain
->ms
;
500 color
= HE_COLORSET_SELECTED
;
501 arg
->is_current_entry
= true;
504 ui_browser__set_color(&browser
->b
, color
);
505 hist_browser__gotorc(browser
, row
, 0);
506 slsmg_write_nstring(" ", offset
);
507 slsmg_printf("%c ", folded_sign
);
508 slsmg_write_nstring(str
, width
);
511 static void hist_browser__fprintf_callchain_entry(struct hist_browser
*b __maybe_unused
,
512 struct callchain_list
*chain
,
513 const char *str
, int offset
,
514 unsigned short row __maybe_unused
,
515 struct callchain_print_arg
*arg
)
517 char folded_sign
= callchain_list__folded(chain
);
519 arg
->printed
+= fprintf(arg
->fp
, "%*s%c %s\n", offset
, " ",
523 typedef bool (*check_output_full_fn
)(struct hist_browser
*browser
,
526 static bool hist_browser__check_output_full(struct hist_browser
*browser
,
529 return browser
->b
.rows
== row
;
532 static bool hist_browser__check_dump_full(struct hist_browser
*browser __maybe_unused
,
533 unsigned short row __maybe_unused
)
538 #define LEVEL_OFFSET_STEP 3
540 static int hist_browser__show_callchain(struct hist_browser
*browser
,
541 struct rb_root
*root
, int level
,
542 unsigned short row
, u64 total
,
543 print_callchain_entry_fn print
,
544 struct callchain_print_arg
*arg
,
545 check_output_full_fn is_output_full
)
547 struct rb_node
*node
;
548 int first_row
= row
, offset
= level
* LEVEL_OFFSET_STEP
;
552 node
= rb_first(root
);
553 need_percent
= node
&& rb_next(node
);
556 struct callchain_node
*child
= rb_entry(node
, struct callchain_node
, rb_node
);
557 struct rb_node
*next
= rb_next(node
);
558 u64 cumul
= callchain_cumul_hits(child
);
559 struct callchain_list
*chain
;
560 char folded_sign
= ' ';
562 int extra_offset
= 0;
564 list_for_each_entry(chain
, &child
->val
, list
) {
565 char bf
[1024], *alloc_str
;
567 bool was_first
= first
;
571 else if (need_percent
)
572 extra_offset
= LEVEL_OFFSET_STEP
;
574 folded_sign
= callchain_list__folded(chain
);
575 if (arg
->row_offset
!= 0) {
581 str
= callchain_list__sym_name(chain
, bf
, sizeof(bf
),
584 if (was_first
&& need_percent
) {
585 double percent
= cumul
* 100.0 / total
;
587 if (asprintf(&alloc_str
, "%2.2f%% %s", percent
, str
) < 0)
588 str
= "Not enough memory!";
593 print(browser
, chain
, str
, offset
+ extra_offset
, row
, arg
);
597 if (is_output_full(browser
, ++row
))
600 if (folded_sign
== '+')
604 if (folded_sign
== '-') {
605 const int new_level
= level
+ (extra_offset
? 2 : 1);
607 if (callchain_param
.mode
== CHAIN_GRAPH_REL
)
608 new_total
= child
->children_hit
;
612 row
+= hist_browser__show_callchain(browser
, &child
->rb_root
,
613 new_level
, row
, new_total
,
614 print
, arg
, is_output_full
);
616 if (is_output_full(browser
, row
))
621 return row
- first_row
;
625 struct ui_browser
*b
;
630 static int __hpp__slsmg_color_printf(struct perf_hpp
*hpp
, const char *fmt
, ...)
632 struct hpp_arg
*arg
= hpp
->ptr
;
638 len
= va_arg(args
, int);
639 percent
= va_arg(args
, double);
642 ui_browser__set_percent_color(arg
->b
, percent
, arg
->current_entry
);
644 ret
= scnprintf(hpp
->buf
, hpp
->size
, fmt
, len
, percent
);
645 slsmg_printf("%s", hpp
->buf
);
647 advance_hpp(hpp
, ret
);
651 #define __HPP_COLOR_PERCENT_FN(_type, _field) \
652 static u64 __hpp_get_##_field(struct hist_entry *he) \
654 return he->stat._field; \
658 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
659 struct perf_hpp *hpp, \
660 struct hist_entry *he) \
662 return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%", \
663 __hpp__slsmg_color_printf, true); \
666 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
667 static u64 __hpp_get_acc_##_field(struct hist_entry *he) \
669 return he->stat_acc->_field; \
673 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
674 struct perf_hpp *hpp, \
675 struct hist_entry *he) \
677 if (!symbol_conf.cumulate_callchain) { \
678 int len = fmt->user_len ?: fmt->len; \
679 int ret = scnprintf(hpp->buf, hpp->size, \
680 "%*s", len, "N/A"); \
681 slsmg_printf("%s", hpp->buf); \
685 return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field, \
686 " %*.2f%%", __hpp__slsmg_color_printf, true); \
689 __HPP_COLOR_PERCENT_FN(overhead
, period
)
690 __HPP_COLOR_PERCENT_FN(overhead_sys
, period_sys
)
691 __HPP_COLOR_PERCENT_FN(overhead_us
, period_us
)
692 __HPP_COLOR_PERCENT_FN(overhead_guest_sys
, period_guest_sys
)
693 __HPP_COLOR_PERCENT_FN(overhead_guest_us
, period_guest_us
)
694 __HPP_COLOR_ACC_PERCENT_FN(overhead_acc
, period
)
696 #undef __HPP_COLOR_PERCENT_FN
697 #undef __HPP_COLOR_ACC_PERCENT_FN
699 void hist_browser__init_hpp(void)
701 perf_hpp__format
[PERF_HPP__OVERHEAD
].color
=
702 hist_browser__hpp_color_overhead
;
703 perf_hpp__format
[PERF_HPP__OVERHEAD_SYS
].color
=
704 hist_browser__hpp_color_overhead_sys
;
705 perf_hpp__format
[PERF_HPP__OVERHEAD_US
].color
=
706 hist_browser__hpp_color_overhead_us
;
707 perf_hpp__format
[PERF_HPP__OVERHEAD_GUEST_SYS
].color
=
708 hist_browser__hpp_color_overhead_guest_sys
;
709 perf_hpp__format
[PERF_HPP__OVERHEAD_GUEST_US
].color
=
710 hist_browser__hpp_color_overhead_guest_us
;
711 perf_hpp__format
[PERF_HPP__OVERHEAD_ACC
].color
=
712 hist_browser__hpp_color_overhead_acc
;
715 static int hist_browser__show_entry(struct hist_browser
*browser
,
716 struct hist_entry
*entry
,
721 int width
= browser
->b
.width
;
722 char folded_sign
= ' ';
723 bool current_entry
= ui_browser__is_current_entry(&browser
->b
, row
);
724 off_t row_offset
= entry
->row_offset
;
726 struct perf_hpp_fmt
*fmt
;
729 browser
->he_selection
= entry
;
730 browser
->selection
= &entry
->ms
;
733 if (symbol_conf
.use_callchain
) {
734 hist_entry__init_have_children(entry
);
735 folded_sign
= hist_entry__folded(entry
);
738 if (row_offset
== 0) {
739 struct hpp_arg arg
= {
741 .folded_sign
= folded_sign
,
742 .current_entry
= current_entry
,
744 struct perf_hpp hpp
= {
750 hist_browser__gotorc(browser
, row
, 0);
752 perf_hpp__for_each_format(fmt
) {
753 if (perf_hpp__should_skip(fmt
))
756 if (current_entry
&& browser
->b
.navkeypressed
) {
757 ui_browser__set_color(&browser
->b
,
758 HE_COLORSET_SELECTED
);
760 ui_browser__set_color(&browser
->b
,
765 if (symbol_conf
.use_callchain
) {
766 slsmg_printf("%c ", folded_sign
);
776 width
-= fmt
->color(fmt
, &hpp
, entry
);
778 width
-= fmt
->entry(fmt
, &hpp
, entry
);
779 slsmg_printf("%s", s
);
783 /* The scroll bar isn't being used */
784 if (!browser
->b
.navkeypressed
)
787 slsmg_write_nstring("", width
);
794 if (folded_sign
== '-' && row
!= browser
->b
.rows
) {
795 u64 total
= hists__total_period(entry
->hists
);
796 struct callchain_print_arg arg
= {
797 .row_offset
= row_offset
,
798 .is_current_entry
= current_entry
,
801 if (callchain_param
.mode
== CHAIN_GRAPH_REL
) {
802 if (symbol_conf
.cumulate_callchain
)
803 total
= entry
->stat_acc
->period
;
805 total
= entry
->stat
.period
;
808 printed
+= hist_browser__show_callchain(browser
,
809 &entry
->sorted_chain
, 1, row
, total
,
810 hist_browser__show_callchain_entry
, &arg
,
811 hist_browser__check_output_full
);
813 if (arg
.is_current_entry
)
814 browser
->he_selection
= entry
;
820 static int advance_hpp_check(struct perf_hpp
*hpp
, int inc
)
822 advance_hpp(hpp
, inc
);
823 return hpp
->size
<= 0;
826 static int hists__scnprintf_headers(char *buf
, size_t size
, struct hists
*hists
)
828 struct perf_hpp dummy_hpp
= {
832 struct perf_hpp_fmt
*fmt
;
835 if (symbol_conf
.use_callchain
) {
836 ret
= scnprintf(buf
, size
, " ");
837 if (advance_hpp_check(&dummy_hpp
, ret
))
841 perf_hpp__for_each_format(fmt
) {
842 if (perf_hpp__should_skip(fmt
))
845 ret
= fmt
->header(fmt
, &dummy_hpp
, hists_to_evsel(hists
));
846 if (advance_hpp_check(&dummy_hpp
, ret
))
849 ret
= scnprintf(dummy_hpp
.buf
, dummy_hpp
.size
, " ");
850 if (advance_hpp_check(&dummy_hpp
, ret
))
857 static void hist_browser__show_headers(struct hist_browser
*browser
)
861 hists__scnprintf_headers(headers
, sizeof(headers
), browser
->hists
);
862 ui_browser__gotorc(&browser
->b
, 0, 0);
863 ui_browser__set_color(&browser
->b
, HE_COLORSET_ROOT
);
864 slsmg_write_nstring(headers
, browser
->b
.width
+ 1);
867 static void ui_browser__hists_init_top(struct ui_browser
*browser
)
869 if (browser
->top
== NULL
) {
870 struct hist_browser
*hb
;
872 hb
= container_of(browser
, struct hist_browser
, b
);
873 browser
->top
= rb_first(&hb
->hists
->entries
);
877 static unsigned int hist_browser__refresh(struct ui_browser
*browser
)
880 u16 header_offset
= 0;
882 struct hist_browser
*hb
= container_of(browser
, struct hist_browser
, b
);
884 if (hb
->show_headers
) {
885 hist_browser__show_headers(hb
);
889 ui_browser__hists_init_top(browser
);
891 for (nd
= browser
->top
; nd
; nd
= rb_next(nd
)) {
892 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
898 percent
= hist_entry__get_percent_limit(h
);
899 if (percent
< hb
->min_pcnt
)
902 row
+= hist_browser__show_entry(hb
, h
, row
);
903 if (row
== browser
->rows
)
907 return row
+ header_offset
;
910 static struct rb_node
*hists__filter_entries(struct rb_node
*nd
,
914 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
915 float percent
= hist_entry__get_percent_limit(h
);
917 if (!h
->filtered
&& percent
>= min_pcnt
)
926 static struct rb_node
*hists__filter_prev_entries(struct rb_node
*nd
,
930 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
931 float percent
= hist_entry__get_percent_limit(h
);
933 if (!h
->filtered
&& percent
>= min_pcnt
)
942 static void ui_browser__hists_seek(struct ui_browser
*browser
,
943 off_t offset
, int whence
)
945 struct hist_entry
*h
;
948 struct hist_browser
*hb
;
950 hb
= container_of(browser
, struct hist_browser
, b
);
952 if (browser
->nr_entries
== 0)
955 ui_browser__hists_init_top(browser
);
959 nd
= hists__filter_entries(rb_first(browser
->entries
),
966 nd
= hists__filter_prev_entries(rb_last(browser
->entries
),
975 * Moves not relative to the first visible entry invalidates its
978 h
= rb_entry(browser
->top
, struct hist_entry
, rb_node
);
982 * Here we have to check if nd is expanded (+), if it is we can't go
983 * the next top level hist_entry, instead we must compute an offset of
984 * what _not_ to show and not change the first visible entry.
986 * This offset increments when we are going from top to bottom and
987 * decreases when we're going from bottom to top.
989 * As we don't have backpointers to the top level in the callchains
990 * structure, we need to always print the whole hist_entry callchain,
991 * skipping the first ones that are before the first visible entry
992 * and stop when we printed enough lines to fill the screen.
997 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
998 if (h
->ms
.unfolded
) {
999 u16 remaining
= h
->nr_rows
- h
->row_offset
;
1000 if (offset
> remaining
) {
1001 offset
-= remaining
;
1004 h
->row_offset
+= offset
;
1010 nd
= hists__filter_entries(rb_next(nd
), hb
->min_pcnt
);
1015 } while (offset
!= 0);
1016 } else if (offset
< 0) {
1018 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1019 if (h
->ms
.unfolded
) {
1021 if (-offset
> h
->row_offset
) {
1022 offset
+= h
->row_offset
;
1025 h
->row_offset
+= offset
;
1031 if (-offset
> h
->nr_rows
) {
1032 offset
+= h
->nr_rows
;
1035 h
->row_offset
= h
->nr_rows
+ offset
;
1043 nd
= hists__filter_prev_entries(rb_prev(nd
),
1051 * Last unfiltered hist_entry, check if it is
1052 * unfolded, if it is then we should have
1053 * row_offset at its last entry.
1055 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1057 h
->row_offset
= h
->nr_rows
;
1064 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1069 static int hist_browser__fprintf_callchain(struct hist_browser
*browser
,
1070 struct hist_entry
*he
, FILE *fp
)
1072 u64 total
= hists__total_period(he
->hists
);
1073 struct callchain_print_arg arg
= {
1077 if (symbol_conf
.cumulate_callchain
)
1078 total
= he
->stat_acc
->period
;
1080 hist_browser__show_callchain(browser
, &he
->sorted_chain
, 1, 0, total
,
1081 hist_browser__fprintf_callchain_entry
, &arg
,
1082 hist_browser__check_dump_full
);
1086 static int hist_browser__fprintf_entry(struct hist_browser
*browser
,
1087 struct hist_entry
*he
, FILE *fp
)
1091 char folded_sign
= ' ';
1092 struct perf_hpp hpp
= {
1096 struct perf_hpp_fmt
*fmt
;
1100 if (symbol_conf
.use_callchain
)
1101 folded_sign
= hist_entry__folded(he
);
1103 if (symbol_conf
.use_callchain
)
1104 printed
+= fprintf(fp
, "%c ", folded_sign
);
1106 perf_hpp__for_each_format(fmt
) {
1107 if (perf_hpp__should_skip(fmt
))
1111 ret
= scnprintf(hpp
.buf
, hpp
.size
, " ");
1112 advance_hpp(&hpp
, ret
);
1116 ret
= fmt
->entry(fmt
, &hpp
, he
);
1117 advance_hpp(&hpp
, ret
);
1119 printed
+= fprintf(fp
, "%s\n", rtrim(s
));
1121 if (folded_sign
== '-')
1122 printed
+= hist_browser__fprintf_callchain(browser
, he
, fp
);
1127 static int hist_browser__fprintf(struct hist_browser
*browser
, FILE *fp
)
1129 struct rb_node
*nd
= hists__filter_entries(rb_first(browser
->b
.entries
),
1134 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1136 printed
+= hist_browser__fprintf_entry(browser
, h
, fp
);
1137 nd
= hists__filter_entries(rb_next(nd
), browser
->min_pcnt
);
1143 static int hist_browser__dump(struct hist_browser
*browser
)
1149 scnprintf(filename
, sizeof(filename
), "perf.hist.%d", browser
->print_seq
);
1150 if (access(filename
, F_OK
))
1153 * XXX: Just an arbitrary lazy upper limit
1155 if (++browser
->print_seq
== 8192) {
1156 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1161 fp
= fopen(filename
, "w");
1164 const char *err
= strerror_r(errno
, bf
, sizeof(bf
));
1165 ui_helpline__fpush("Couldn't write to %s: %s", filename
, err
);
1169 ++browser
->print_seq
;
1170 hist_browser__fprintf(browser
, fp
);
1172 ui_helpline__fpush("%s written!", filename
);
1177 static struct hist_browser
*hist_browser__new(struct hists
*hists
)
1179 struct hist_browser
*browser
= zalloc(sizeof(*browser
));
1182 browser
->hists
= hists
;
1183 browser
->b
.refresh
= hist_browser__refresh
;
1184 browser
->b
.refresh_dimensions
= hist_browser__refresh_dimensions
;
1185 browser
->b
.seek
= ui_browser__hists_seek
;
1186 browser
->b
.use_navkeypressed
= true;
1187 browser
->show_headers
= symbol_conf
.show_hist_headers
;
1193 static void hist_browser__delete(struct hist_browser
*browser
)
1198 static struct hist_entry
*hist_browser__selected_entry(struct hist_browser
*browser
)
1200 return browser
->he_selection
;
1203 static struct thread
*hist_browser__selected_thread(struct hist_browser
*browser
)
1205 return browser
->he_selection
->thread
;
1208 /* Check whether the browser is for 'top' or 'report' */
1209 static inline bool is_report_browser(void *timer
)
1211 return timer
== NULL
;
1214 static int hists__browser_title(struct hists
*hists
,
1215 struct hist_browser_timer
*hbt
,
1216 char *bf
, size_t size
)
1220 const struct dso
*dso
= hists
->dso_filter
;
1221 const struct thread
*thread
= hists
->thread_filter
;
1222 unsigned long nr_samples
= hists
->stats
.nr_events
[PERF_RECORD_SAMPLE
];
1223 u64 nr_events
= hists
->stats
.total_period
;
1224 struct perf_evsel
*evsel
= hists_to_evsel(hists
);
1225 const char *ev_name
= perf_evsel__name(evsel
);
1227 size_t buflen
= sizeof(buf
);
1229 if (symbol_conf
.filter_relative
) {
1230 nr_samples
= hists
->stats
.nr_non_filtered_samples
;
1231 nr_events
= hists
->stats
.total_non_filtered_period
;
1234 if (perf_evsel__is_group_event(evsel
)) {
1235 struct perf_evsel
*pos
;
1237 perf_evsel__group_desc(evsel
, buf
, buflen
);
1240 for_each_group_member(pos
, evsel
) {
1241 struct hists
*pos_hists
= evsel__hists(pos
);
1243 if (symbol_conf
.filter_relative
) {
1244 nr_samples
+= pos_hists
->stats
.nr_non_filtered_samples
;
1245 nr_events
+= pos_hists
->stats
.total_non_filtered_period
;
1247 nr_samples
+= pos_hists
->stats
.nr_events
[PERF_RECORD_SAMPLE
];
1248 nr_events
+= pos_hists
->stats
.total_period
;
1253 nr_samples
= convert_unit(nr_samples
, &unit
);
1254 printed
= scnprintf(bf
, size
,
1255 "Samples: %lu%c of event '%s', Event count (approx.): %" PRIu64
,
1256 nr_samples
, unit
, ev_name
, nr_events
);
1259 if (hists
->uid_filter_str
)
1260 printed
+= snprintf(bf
+ printed
, size
- printed
,
1261 ", UID: %s", hists
->uid_filter_str
);
1263 printed
+= scnprintf(bf
+ printed
, size
- printed
,
1265 (thread
->comm_set
? thread__comm_str(thread
) : ""),
1268 printed
+= scnprintf(bf
+ printed
, size
- printed
,
1269 ", DSO: %s", dso
->short_name
);
1270 if (!is_report_browser(hbt
)) {
1271 struct perf_top
*top
= hbt
->arg
;
1274 printed
+= scnprintf(bf
+ printed
, size
- printed
, " [z]");
1280 static inline void free_popup_options(char **options
, int n
)
1284 for (i
= 0; i
< n
; ++i
)
1289 * Only runtime switching of perf data file will make "input_name" point
1290 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1291 * whether we need to call free() for current "input_name" during the switch.
1293 static bool is_input_name_malloced
= false;
1295 static int switch_data_file(void)
1297 char *pwd
, *options
[32], *abs_path
[32], *tmp
;
1299 int nr_options
= 0, choice
= -1, ret
= -1;
1300 struct dirent
*dent
;
1302 pwd
= getenv("PWD");
1306 pwd_dir
= opendir(pwd
);
1310 memset(options
, 0, sizeof(options
));
1311 memset(options
, 0, sizeof(abs_path
));
1313 while ((dent
= readdir(pwd_dir
))) {
1314 char path
[PATH_MAX
];
1316 char *name
= dent
->d_name
;
1319 if (!(dent
->d_type
== DT_REG
))
1322 snprintf(path
, sizeof(path
), "%s/%s", pwd
, name
);
1324 file
= fopen(path
, "r");
1328 if (fread(&magic
, 1, 8, file
) < 8)
1329 goto close_file_and_continue
;
1331 if (is_perf_magic(magic
)) {
1332 options
[nr_options
] = strdup(name
);
1333 if (!options
[nr_options
])
1334 goto close_file_and_continue
;
1336 abs_path
[nr_options
] = strdup(path
);
1337 if (!abs_path
[nr_options
]) {
1338 zfree(&options
[nr_options
]);
1339 ui__warning("Can't search all data files due to memory shortage.\n");
1347 close_file_and_continue
:
1349 if (nr_options
>= 32) {
1350 ui__warning("Too many perf data files in PWD!\n"
1351 "Only the first 32 files will be listed.\n");
1358 choice
= ui__popup_menu(nr_options
, options
);
1359 if (choice
< nr_options
&& choice
>= 0) {
1360 tmp
= strdup(abs_path
[choice
]);
1362 if (is_input_name_malloced
)
1363 free((void *)input_name
);
1365 is_input_name_malloced
= true;
1368 ui__warning("Data switch failed due to memory shortage!\n");
1372 free_popup_options(options
, nr_options
);
1373 free_popup_options(abs_path
, nr_options
);
1377 static void hist_browser__update_nr_entries(struct hist_browser
*hb
)
1380 struct rb_node
*nd
= rb_first(&hb
->hists
->entries
);
1382 if (hb
->min_pcnt
== 0) {
1383 hb
->nr_non_filtered_entries
= hb
->hists
->nr_non_filtered_entries
;
1387 while ((nd
= hists__filter_entries(nd
, hb
->min_pcnt
)) != NULL
) {
1392 hb
->nr_non_filtered_entries
= nr_entries
;
1395 static int perf_evsel__hists_browse(struct perf_evsel
*evsel
, int nr_events
,
1396 const char *helpline
,
1398 struct hist_browser_timer
*hbt
,
1400 struct perf_session_env
*env
)
1402 struct hists
*hists
= evsel__hists(evsel
);
1403 struct hist_browser
*browser
= hist_browser__new(hists
);
1404 struct branch_info
*bi
;
1405 struct pstack
*fstack
;
1410 char script_opt
[64];
1411 int delay_secs
= hbt
? hbt
->refresh
: 0;
1412 struct perf_hpp_fmt
*fmt
;
1414 #define HIST_BROWSER_HELP_COMMON \
1415 "h/?/F1 Show this window\n" \
1417 "PGDN/SPACE Navigate\n" \
1418 "q/ESC/CTRL+C Exit browser\n\n" \
1419 "For multiple event sessions:\n\n" \
1420 "TAB/UNTAB Switch events\n\n" \
1421 "For symbolic views (--sort has sym):\n\n" \
1422 "-> Zoom into DSO/Threads & Annotate current symbol\n" \
1424 "a Annotate current symbol\n" \
1425 "C Collapse all callchains\n" \
1426 "d Zoom into current DSO\n" \
1427 "E Expand all callchains\n" \
1428 "F Toggle percentage of filtered entries\n" \
1429 "H Display column headers\n" \
1431 /* help messages are sorted by lexical order of the hotkey */
1432 const char report_help
[] = HIST_BROWSER_HELP_COMMON
1433 "i Show header information\n"
1434 "P Print histograms to perf.hist.N\n"
1435 "r Run available scripts\n"
1436 "s Switch to another data file in PWD\n"
1437 "t Zoom into current Thread\n"
1438 "V Verbose (DSO names in callchains, etc)\n"
1439 "/ Filter symbol by name";
1440 const char top_help
[] = HIST_BROWSER_HELP_COMMON
1441 "P Print histograms to perf.hist.N\n"
1442 "t Zoom into current Thread\n"
1443 "V Verbose (DSO names in callchains, etc)\n"
1444 "z Toggle zeroing of samples\n"
1445 "/ Filter symbol by name";
1447 if (browser
== NULL
)
1451 browser
->min_pcnt
= min_pcnt
;
1452 hist_browser__update_nr_entries(browser
);
1455 fstack
= pstack__new(2);
1459 ui_helpline__push(helpline
);
1461 memset(options
, 0, sizeof(options
));
1463 perf_hpp__for_each_format(fmt
)
1464 perf_hpp__reset_width(fmt
, hists
);
1466 if (symbol_conf
.col_width_list_str
)
1467 perf_hpp__set_user_width(symbol_conf
.col_width_list_str
);
1470 const struct thread
*thread
= NULL
;
1471 const struct dso
*dso
= NULL
;
1473 annotate
= -2, zoom_dso
= -2, zoom_thread
= -2,
1474 annotate_f
= -2, annotate_t
= -2, browse_map
= -2;
1475 int scripts_comm
= -2, scripts_symbol
= -2,
1476 scripts_all
= -2, switch_data
= -2;
1480 key
= hist_browser__run(browser
, hbt
);
1482 if (browser
->he_selection
!= NULL
) {
1483 thread
= hist_browser__selected_thread(browser
);
1484 dso
= browser
->selection
->map
? browser
->selection
->map
->dso
: NULL
;
1492 * Exit the browser, let hists__browser_tree
1493 * go to the next or previous
1495 goto out_free_stack
;
1497 if (!sort__has_sym
) {
1498 ui_browser__warning(&browser
->b
, delay_secs
* 2,
1499 "Annotation is only available for symbolic views, "
1500 "include \"sym*\" in --sort to use it.");
1504 if (browser
->selection
== NULL
||
1505 browser
->selection
->sym
== NULL
||
1506 browser
->selection
->map
->dso
->annotate_warned
)
1510 hist_browser__dump(browser
);
1515 browser
->show_dso
= !browser
->show_dso
;
1520 if (ui_browser__input_window("Symbol to show",
1521 "Please enter the name of symbol you want to see",
1522 buf
, "ENTER: OK, ESC: Cancel",
1523 delay_secs
* 2) == K_ENTER
) {
1524 hists
->symbol_filter_str
= *buf
? buf
: NULL
;
1525 hists__filter_by_symbol(hists
);
1526 hist_browser__reset(browser
);
1530 if (is_report_browser(hbt
))
1534 if (is_report_browser(hbt
))
1535 goto do_data_switch
;
1538 /* env->arch is NULL for live-mode (i.e. perf top) */
1540 tui__header_window(env
);
1543 symbol_conf
.filter_relative
^= 1;
1546 if (!is_report_browser(hbt
)) {
1547 struct perf_top
*top
= hbt
->arg
;
1549 top
->zero
= !top
->zero
;
1555 ui_browser__help_window(&browser
->b
,
1556 is_report_browser(hbt
) ? report_help
: top_help
);
1565 if (pstack__empty(fstack
)) {
1567 * Go back to the perf_evsel_menu__run or other user
1570 goto out_free_stack
;
1573 top
= pstack__pop(fstack
);
1574 if (top
== &browser
->hists
->dso_filter
)
1576 if (top
== &browser
->hists
->thread_filter
)
1577 goto zoom_out_thread
;
1582 !ui_browser__dialog_yesno(&browser
->b
,
1583 "Do you really want to exit?"))
1588 goto out_free_stack
;
1594 goto add_exit_option
;
1596 if (sort__mode
== SORT_MODE__BRANCH
) {
1597 bi
= browser
->he_selection
->branch_info
;
1598 if (browser
->selection
!= NULL
&&
1600 bi
->from
.sym
!= NULL
&&
1601 !bi
->from
.map
->dso
->annotate_warned
&&
1602 asprintf(&options
[nr_options
], "Annotate %s",
1603 bi
->from
.sym
->name
) > 0)
1604 annotate_f
= nr_options
++;
1606 if (browser
->selection
!= NULL
&&
1608 bi
->to
.sym
!= NULL
&&
1609 !bi
->to
.map
->dso
->annotate_warned
&&
1610 (bi
->to
.sym
!= bi
->from
.sym
||
1611 bi
->to
.map
->dso
!= bi
->from
.map
->dso
) &&
1612 asprintf(&options
[nr_options
], "Annotate %s",
1613 bi
->to
.sym
->name
) > 0)
1614 annotate_t
= nr_options
++;
1616 if (browser
->selection
!= NULL
&&
1617 browser
->selection
->sym
!= NULL
&&
1618 !browser
->selection
->map
->dso
->annotate_warned
) {
1619 struct annotation
*notes
;
1621 notes
= symbol__annotation(browser
->selection
->sym
);
1624 asprintf(&options
[nr_options
], "Annotate %s",
1625 browser
->selection
->sym
->name
) > 0)
1626 annotate
= nr_options
++;
1630 if (thread
!= NULL
&&
1631 asprintf(&options
[nr_options
], "Zoom %s %s(%d) thread",
1632 (browser
->hists
->thread_filter
? "out of" : "into"),
1633 (thread
->comm_set
? thread__comm_str(thread
) : ""),
1635 zoom_thread
= nr_options
++;
1638 asprintf(&options
[nr_options
], "Zoom %s %s DSO",
1639 (browser
->hists
->dso_filter
? "out of" : "into"),
1640 (dso
->kernel
? "the Kernel" : dso
->short_name
)) > 0)
1641 zoom_dso
= nr_options
++;
1643 if (browser
->selection
!= NULL
&&
1644 browser
->selection
->map
!= NULL
&&
1645 asprintf(&options
[nr_options
], "Browse map details") > 0)
1646 browse_map
= nr_options
++;
1648 /* perf script support */
1649 if (browser
->he_selection
) {
1652 if (asprintf(&options
[nr_options
], "Run scripts for samples of thread [%s]",
1653 thread__comm_str(browser
->he_selection
->thread
)) > 0)
1654 scripts_comm
= nr_options
++;
1656 sym
= browser
->he_selection
->ms
.sym
;
1657 if (sym
&& sym
->namelen
&&
1658 asprintf(&options
[nr_options
], "Run scripts for samples of symbol [%s]",
1660 scripts_symbol
= nr_options
++;
1663 if (asprintf(&options
[nr_options
], "Run scripts for all samples") > 0)
1664 scripts_all
= nr_options
++;
1666 if (is_report_browser(hbt
) && asprintf(&options
[nr_options
],
1667 "Switch to another data file in PWD") > 0)
1668 switch_data
= nr_options
++;
1670 options
[nr_options
++] = (char *)"Exit";
1672 choice
= ui__popup_menu(nr_options
, options
);
1674 if (choice
== nr_options
- 1)
1678 free_popup_options(options
, nr_options
- 1);
1682 if (choice
== annotate
|| choice
== annotate_t
|| choice
== annotate_f
) {
1683 struct hist_entry
*he
;
1684 struct annotation
*notes
;
1687 if (!objdump_path
&& perf_session_env__lookup_objdump(env
))
1690 he
= hist_browser__selected_entry(browser
);
1695 * we stash the branch_info symbol + map into the
1696 * the ms so we don't have to rewrite all the annotation
1697 * code to use branch_info.
1698 * in branch mode, the ms struct is not used
1700 if (choice
== annotate_f
) {
1701 he
->ms
.sym
= he
->branch_info
->from
.sym
;
1702 he
->ms
.map
= he
->branch_info
->from
.map
;
1703 } else if (choice
== annotate_t
) {
1704 he
->ms
.sym
= he
->branch_info
->to
.sym
;
1705 he
->ms
.map
= he
->branch_info
->to
.map
;
1708 notes
= symbol__annotation(he
->ms
.sym
);
1713 * Don't let this be freed, say, by hists__decay_entry.
1716 err
= hist_entry__tui_annotate(he
, evsel
, hbt
);
1719 * offer option to annotate the other branch source or target
1720 * (if they exists) when returning from annotate
1722 if ((err
== 'q' || err
== CTRL('c'))
1723 && annotate_t
!= -2 && annotate_f
!= -2)
1724 goto retry_popup_menu
;
1726 ui_browser__update_nr_entries(&browser
->b
, browser
->hists
->nr_entries
);
1728 ui_browser__handle_resize(&browser
->b
);
1730 } else if (choice
== browse_map
)
1731 map__browse(browser
->selection
->map
);
1732 else if (choice
== zoom_dso
) {
1734 if (browser
->hists
->dso_filter
) {
1735 pstack__remove(fstack
, &browser
->hists
->dso_filter
);
1738 browser
->hists
->dso_filter
= NULL
;
1739 perf_hpp__set_elide(HISTC_DSO
, false);
1743 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1744 dso
->kernel
? "the Kernel" : dso
->short_name
);
1745 browser
->hists
->dso_filter
= dso
;
1746 perf_hpp__set_elide(HISTC_DSO
, true);
1747 pstack__push(fstack
, &browser
->hists
->dso_filter
);
1749 hists__filter_by_dso(hists
);
1750 hist_browser__reset(browser
);
1751 } else if (choice
== zoom_thread
) {
1753 if (browser
->hists
->thread_filter
) {
1754 pstack__remove(fstack
, &browser
->hists
->thread_filter
);
1757 browser
->hists
->thread_filter
= NULL
;
1758 perf_hpp__set_elide(HISTC_THREAD
, false);
1760 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1761 thread
->comm_set
? thread__comm_str(thread
) : "",
1763 browser
->hists
->thread_filter
= thread
;
1764 perf_hpp__set_elide(HISTC_THREAD
, false);
1765 pstack__push(fstack
, &browser
->hists
->thread_filter
);
1767 hists__filter_by_thread(hists
);
1768 hist_browser__reset(browser
);
1770 /* perf scripts support */
1771 else if (choice
== scripts_all
|| choice
== scripts_comm
||
1772 choice
== scripts_symbol
) {
1774 memset(script_opt
, 0, 64);
1776 if (choice
== scripts_comm
)
1777 sprintf(script_opt
, " -c %s ", thread__comm_str(browser
->he_selection
->thread
));
1779 if (choice
== scripts_symbol
)
1780 sprintf(script_opt
, " -S %s ", browser
->he_selection
->ms
.sym
->name
);
1782 script_browse(script_opt
);
1784 /* Switch to another data file */
1785 else if (choice
== switch_data
) {
1787 if (!switch_data_file()) {
1788 key
= K_SWITCH_INPUT_DATA
;
1791 ui__warning("Won't switch the data files due to\n"
1792 "no valid data file get selected!\n");
1796 pstack__delete(fstack
);
1798 hist_browser__delete(browser
);
1799 free_popup_options(options
, nr_options
- 1);
1803 struct perf_evsel_menu
{
1804 struct ui_browser b
;
1805 struct perf_evsel
*selection
;
1806 bool lost_events
, lost_events_warned
;
1808 struct perf_session_env
*env
;
1811 static void perf_evsel_menu__write(struct ui_browser
*browser
,
1812 void *entry
, int row
)
1814 struct perf_evsel_menu
*menu
= container_of(browser
,
1815 struct perf_evsel_menu
, b
);
1816 struct perf_evsel
*evsel
= list_entry(entry
, struct perf_evsel
, node
);
1817 struct hists
*hists
= evsel__hists(evsel
);
1818 bool current_entry
= ui_browser__is_current_entry(browser
, row
);
1819 unsigned long nr_events
= hists
->stats
.nr_events
[PERF_RECORD_SAMPLE
];
1820 const char *ev_name
= perf_evsel__name(evsel
);
1822 const char *warn
= " ";
1825 ui_browser__set_color(browser
, current_entry
? HE_COLORSET_SELECTED
:
1826 HE_COLORSET_NORMAL
);
1828 if (perf_evsel__is_group_event(evsel
)) {
1829 struct perf_evsel
*pos
;
1831 ev_name
= perf_evsel__group_name(evsel
);
1833 for_each_group_member(pos
, evsel
) {
1834 struct hists
*pos_hists
= evsel__hists(pos
);
1835 nr_events
+= pos_hists
->stats
.nr_events
[PERF_RECORD_SAMPLE
];
1839 nr_events
= convert_unit(nr_events
, &unit
);
1840 printed
= scnprintf(bf
, sizeof(bf
), "%lu%c%s%s", nr_events
,
1841 unit
, unit
== ' ' ? "" : " ", ev_name
);
1842 slsmg_printf("%s", bf
);
1844 nr_events
= hists
->stats
.nr_events
[PERF_RECORD_LOST
];
1845 if (nr_events
!= 0) {
1846 menu
->lost_events
= true;
1848 ui_browser__set_color(browser
, HE_COLORSET_TOP
);
1849 nr_events
= convert_unit(nr_events
, &unit
);
1850 printed
+= scnprintf(bf
, sizeof(bf
), ": %ld%c%schunks LOST!",
1851 nr_events
, unit
, unit
== ' ' ? "" : " ");
1855 slsmg_write_nstring(warn
, browser
->width
- printed
);
1858 menu
->selection
= evsel
;
1861 static int perf_evsel_menu__run(struct perf_evsel_menu
*menu
,
1862 int nr_events
, const char *help
,
1863 struct hist_browser_timer
*hbt
)
1865 struct perf_evlist
*evlist
= menu
->b
.priv
;
1866 struct perf_evsel
*pos
;
1867 const char *title
= "Available samples";
1868 int delay_secs
= hbt
? hbt
->refresh
: 0;
1871 if (ui_browser__show(&menu
->b
, title
,
1872 "ESC: exit, ENTER|->: Browse histograms") < 0)
1876 key
= ui_browser__run(&menu
->b
, delay_secs
);
1880 hbt
->timer(hbt
->arg
);
1882 if (!menu
->lost_events_warned
&& menu
->lost_events
) {
1883 ui_browser__warn_lost_events(&menu
->b
);
1884 menu
->lost_events_warned
= true;
1889 if (!menu
->selection
)
1891 pos
= menu
->selection
;
1893 perf_evlist__set_selected(evlist
, pos
);
1895 * Give the calling tool a chance to populate the non
1896 * default evsel resorted hists tree.
1899 hbt
->timer(hbt
->arg
);
1900 key
= perf_evsel__hists_browse(pos
, nr_events
, help
,
1904 ui_browser__show_title(&menu
->b
, title
);
1907 if (pos
->node
.next
== &evlist
->entries
)
1908 pos
= perf_evlist__first(evlist
);
1910 pos
= perf_evsel__next(pos
);
1913 if (pos
->node
.prev
== &evlist
->entries
)
1914 pos
= perf_evlist__last(evlist
);
1916 pos
= perf_evsel__prev(pos
);
1919 if (!ui_browser__dialog_yesno(&menu
->b
,
1920 "Do you really want to exit?"))
1923 case K_SWITCH_INPUT_DATA
:
1933 if (!ui_browser__dialog_yesno(&menu
->b
,
1934 "Do you really want to exit?"))
1946 ui_browser__hide(&menu
->b
);
1950 static bool filter_group_entries(struct ui_browser
*browser __maybe_unused
,
1953 struct perf_evsel
*evsel
= list_entry(entry
, struct perf_evsel
, node
);
1955 if (symbol_conf
.event_group
&& !perf_evsel__is_group_leader(evsel
))
1961 static int __perf_evlist__tui_browse_hists(struct perf_evlist
*evlist
,
1962 int nr_entries
, const char *help
,
1963 struct hist_browser_timer
*hbt
,
1965 struct perf_session_env
*env
)
1967 struct perf_evsel
*pos
;
1968 struct perf_evsel_menu menu
= {
1970 .entries
= &evlist
->entries
,
1971 .refresh
= ui_browser__list_head_refresh
,
1972 .seek
= ui_browser__list_head_seek
,
1973 .write
= perf_evsel_menu__write
,
1974 .filter
= filter_group_entries
,
1975 .nr_entries
= nr_entries
,
1978 .min_pcnt
= min_pcnt
,
1982 ui_helpline__push("Press ESC to exit");
1984 evlist__for_each(evlist
, pos
) {
1985 const char *ev_name
= perf_evsel__name(pos
);
1986 size_t line_len
= strlen(ev_name
) + 7;
1988 if (menu
.b
.width
< line_len
)
1989 menu
.b
.width
= line_len
;
1992 return perf_evsel_menu__run(&menu
, nr_entries
, help
, hbt
);
1995 int perf_evlist__tui_browse_hists(struct perf_evlist
*evlist
, const char *help
,
1996 struct hist_browser_timer
*hbt
,
1998 struct perf_session_env
*env
)
2000 int nr_entries
= evlist
->nr_entries
;
2003 if (nr_entries
== 1) {
2004 struct perf_evsel
*first
= perf_evlist__first(evlist
);
2006 return perf_evsel__hists_browse(first
, nr_entries
, help
,
2007 false, hbt
, min_pcnt
,
2011 if (symbol_conf
.event_group
) {
2012 struct perf_evsel
*pos
;
2015 evlist__for_each(evlist
, pos
) {
2016 if (perf_evsel__is_group_leader(pos
))
2020 if (nr_entries
== 1)
2024 return __perf_evlist__tui_browse_hists(evlist
, nr_entries
, help
,
2025 hbt
, min_pcnt
, env
);