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
|| symbol_conf
.has_filter
;
51 static int hist_browser__get_folding(struct hist_browser
*browser
)
54 struct hists
*hists
= browser
->hists
;
55 int unfolded_rows
= 0;
57 for (nd
= rb_first(&hists
->entries
);
58 (nd
= hists__filter_entries(nd
, browser
->min_pcnt
)) != NULL
;
60 struct hist_entry
*he
=
61 rb_entry(nd
, struct hist_entry
, rb_node
);
64 unfolded_rows
+= he
->nr_rows
;
69 static u32
hist_browser__nr_entries(struct hist_browser
*hb
)
73 if (hist_browser__has_filter(hb
))
74 nr_entries
= hb
->nr_non_filtered_entries
;
76 nr_entries
= hb
->hists
->nr_entries
;
78 hb
->nr_callchain_rows
= hist_browser__get_folding(hb
);
79 return nr_entries
+ hb
->nr_callchain_rows
;
82 static void hist_browser__update_rows(struct hist_browser
*hb
)
84 struct ui_browser
*browser
= &hb
->b
;
85 u16 header_offset
= hb
->show_headers
? 1 : 0, index_row
;
87 browser
->rows
= browser
->height
- header_offset
;
89 * Verify if we were at the last line and that line isn't
90 * visibe because we now show the header line(s).
92 index_row
= browser
->index
- browser
->top_idx
;
93 if (index_row
>= browser
->rows
)
94 browser
->index
-= index_row
- browser
->rows
+ 1;
97 static void hist_browser__refresh_dimensions(struct ui_browser
*browser
)
99 struct hist_browser
*hb
= container_of(browser
, struct hist_browser
, b
);
101 /* 3 == +/- toggle symbol before actual hist_entry rendering */
102 browser
->width
= 3 + (hists__sort_list_width(hb
->hists
) + sizeof("[k]"));
104 * FIXME: Just keeping existing behaviour, but this really should be
105 * before updating browser->width, as it will invalidate the
106 * calculation above. Fix this and the fallout in another
109 ui_browser__refresh_dimensions(browser
);
110 hist_browser__update_rows(hb
);
113 static void hist_browser__gotorc(struct hist_browser
*browser
, int row
, int column
)
115 u16 header_offset
= browser
->show_headers
? 1 : 0;
117 ui_browser__gotorc(&browser
->b
, row
+ header_offset
, column
);
120 static void hist_browser__reset(struct hist_browser
*browser
)
123 * The hists__remove_entry_filter() already folds non-filtered
124 * entries so we can assume it has 0 callchain rows.
126 browser
->nr_callchain_rows
= 0;
128 hist_browser__update_nr_entries(browser
);
129 browser
->b
.nr_entries
= hist_browser__nr_entries(browser
);
130 hist_browser__refresh_dimensions(&browser
->b
);
131 ui_browser__reset_index(&browser
->b
);
134 static char tree__folded_sign(bool unfolded
)
136 return unfolded
? '-' : '+';
139 static char map_symbol__folded(const struct map_symbol
*ms
)
141 return ms
->has_children
? tree__folded_sign(ms
->unfolded
) : ' ';
144 static char hist_entry__folded(const struct hist_entry
*he
)
146 return map_symbol__folded(&he
->ms
);
149 static char callchain_list__folded(const struct callchain_list
*cl
)
151 return map_symbol__folded(&cl
->ms
);
154 static void map_symbol__set_folding(struct map_symbol
*ms
, bool unfold
)
156 ms
->unfolded
= unfold
? ms
->has_children
: false;
159 static int callchain_node__count_rows_rb_tree(struct callchain_node
*node
)
164 for (nd
= rb_first(&node
->rb_root
); nd
; nd
= rb_next(nd
)) {
165 struct callchain_node
*child
= rb_entry(nd
, struct callchain_node
, rb_node
);
166 struct callchain_list
*chain
;
167 char folded_sign
= ' '; /* No children */
169 list_for_each_entry(chain
, &child
->val
, list
) {
171 /* We need this because we may not have children */
172 folded_sign
= callchain_list__folded(chain
);
173 if (folded_sign
== '+')
177 if (folded_sign
== '-') /* Have children and they're unfolded */
178 n
+= callchain_node__count_rows_rb_tree(child
);
184 static int callchain_node__count_rows(struct callchain_node
*node
)
186 struct callchain_list
*chain
;
187 bool unfolded
= false;
190 list_for_each_entry(chain
, &node
->val
, list
) {
192 unfolded
= chain
->ms
.unfolded
;
196 n
+= callchain_node__count_rows_rb_tree(node
);
201 static int callchain__count_rows(struct rb_root
*chain
)
206 for (nd
= rb_first(chain
); nd
; nd
= rb_next(nd
)) {
207 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
208 n
+= callchain_node__count_rows(node
);
214 static bool map_symbol__toggle_fold(struct map_symbol
*ms
)
219 if (!ms
->has_children
)
222 ms
->unfolded
= !ms
->unfolded
;
226 static void callchain_node__init_have_children_rb_tree(struct callchain_node
*node
)
228 struct rb_node
*nd
= rb_first(&node
->rb_root
);
230 for (nd
= rb_first(&node
->rb_root
); nd
; nd
= rb_next(nd
)) {
231 struct callchain_node
*child
= rb_entry(nd
, struct callchain_node
, rb_node
);
232 struct callchain_list
*chain
;
235 list_for_each_entry(chain
, &child
->val
, list
) {
238 chain
->ms
.has_children
= chain
->list
.next
!= &child
->val
||
239 !RB_EMPTY_ROOT(&child
->rb_root
);
241 chain
->ms
.has_children
= chain
->list
.next
== &child
->val
&&
242 !RB_EMPTY_ROOT(&child
->rb_root
);
245 callchain_node__init_have_children_rb_tree(child
);
249 static void callchain_node__init_have_children(struct callchain_node
*node
,
252 struct callchain_list
*chain
;
254 chain
= list_entry(node
->val
.next
, struct callchain_list
, list
);
255 chain
->ms
.has_children
= has_sibling
;
257 if (!list_empty(&node
->val
)) {
258 chain
= list_entry(node
->val
.prev
, struct callchain_list
, list
);
259 chain
->ms
.has_children
= !RB_EMPTY_ROOT(&node
->rb_root
);
262 callchain_node__init_have_children_rb_tree(node
);
265 static void callchain__init_have_children(struct rb_root
*root
)
267 struct rb_node
*nd
= rb_first(root
);
268 bool has_sibling
= nd
&& rb_next(nd
);
270 for (nd
= rb_first(root
); nd
; nd
= rb_next(nd
)) {
271 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
272 callchain_node__init_have_children(node
, has_sibling
);
276 static void hist_entry__init_have_children(struct hist_entry
*he
)
278 if (!he
->init_have_children
) {
279 he
->ms
.has_children
= !RB_EMPTY_ROOT(&he
->sorted_chain
);
280 callchain__init_have_children(&he
->sorted_chain
);
281 he
->init_have_children
= true;
285 static bool hist_browser__toggle_fold(struct hist_browser
*browser
)
287 if (map_symbol__toggle_fold(browser
->selection
)) {
288 struct hist_entry
*he
= browser
->he_selection
;
290 hist_entry__init_have_children(he
);
291 browser
->b
.nr_entries
-= he
->nr_rows
;
292 browser
->nr_callchain_rows
-= he
->nr_rows
;
295 he
->nr_rows
= callchain__count_rows(&he
->sorted_chain
);
299 browser
->b
.nr_entries
+= he
->nr_rows
;
300 browser
->nr_callchain_rows
+= he
->nr_rows
;
305 /* If it doesn't have children, no toggling performed */
309 static int callchain_node__set_folding_rb_tree(struct callchain_node
*node
, bool unfold
)
314 for (nd
= rb_first(&node
->rb_root
); nd
; nd
= rb_next(nd
)) {
315 struct callchain_node
*child
= rb_entry(nd
, struct callchain_node
, rb_node
);
316 struct callchain_list
*chain
;
317 bool has_children
= false;
319 list_for_each_entry(chain
, &child
->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(child
, unfold
);
332 static int callchain_node__set_folding(struct callchain_node
*node
, bool unfold
)
334 struct callchain_list
*chain
;
335 bool has_children
= false;
338 list_for_each_entry(chain
, &node
->val
, list
) {
340 map_symbol__set_folding(&chain
->ms
, unfold
);
341 has_children
= chain
->ms
.has_children
;
345 n
+= callchain_node__set_folding_rb_tree(node
, unfold
);
350 static int callchain__set_folding(struct rb_root
*chain
, bool unfold
)
355 for (nd
= rb_first(chain
); nd
; nd
= rb_next(nd
)) {
356 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
357 n
+= callchain_node__set_folding(node
, unfold
);
363 static void hist_entry__set_folding(struct hist_entry
*he
, bool unfold
)
365 hist_entry__init_have_children(he
);
366 map_symbol__set_folding(&he
->ms
, unfold
);
368 if (he
->ms
.has_children
) {
369 int n
= callchain__set_folding(&he
->sorted_chain
, unfold
);
370 he
->nr_rows
= unfold
? n
: 0;
376 __hist_browser__set_folding(struct hist_browser
*browser
, bool unfold
)
379 struct hists
*hists
= browser
->hists
;
381 for (nd
= rb_first(&hists
->entries
);
382 (nd
= hists__filter_entries(nd
, browser
->min_pcnt
)) != NULL
;
384 struct hist_entry
*he
= rb_entry(nd
, struct hist_entry
, rb_node
);
385 hist_entry__set_folding(he
, unfold
);
386 browser
->nr_callchain_rows
+= he
->nr_rows
;
390 static void hist_browser__set_folding(struct hist_browser
*browser
, bool unfold
)
392 browser
->nr_callchain_rows
= 0;
393 __hist_browser__set_folding(browser
, unfold
);
395 browser
->b
.nr_entries
= hist_browser__nr_entries(browser
);
396 /* Go to the start, we may be way after valid entries after a collapse */
397 ui_browser__reset_index(&browser
->b
);
400 static void ui_browser__warn_lost_events(struct ui_browser
*browser
)
402 ui_browser__warning(browser
, 4,
403 "Events are being lost, check IO/CPU overload!\n\n"
404 "You may want to run 'perf' using a RT scheduler policy:\n\n"
405 " perf top -r 80\n\n"
406 "Or reduce the sampling frequency.");
409 static int hist_browser__run(struct hist_browser
*browser
,
410 struct hist_browser_timer
*hbt
)
414 int delay_secs
= hbt
? hbt
->refresh
: 0;
416 browser
->b
.entries
= &browser
->hists
->entries
;
417 browser
->b
.nr_entries
= hist_browser__nr_entries(browser
);
419 hists__browser_title(browser
->hists
, hbt
, title
, sizeof(title
));
421 if (ui_browser__show(&browser
->b
, title
,
422 "Press '?' for help on key bindings") < 0)
426 key
= ui_browser__run(&browser
->b
, delay_secs
);
431 hbt
->timer(hbt
->arg
);
433 if (hist_browser__has_filter(browser
))
434 hist_browser__update_nr_entries(browser
);
436 nr_entries
= hist_browser__nr_entries(browser
);
437 ui_browser__update_nr_entries(&browser
->b
, nr_entries
);
439 if (browser
->hists
->stats
.nr_lost_warned
!=
440 browser
->hists
->stats
.nr_events
[PERF_RECORD_LOST
]) {
441 browser
->hists
->stats
.nr_lost_warned
=
442 browser
->hists
->stats
.nr_events
[PERF_RECORD_LOST
];
443 ui_browser__warn_lost_events(&browser
->b
);
446 hists__browser_title(browser
->hists
,
447 hbt
, title
, sizeof(title
));
448 ui_browser__show_title(&browser
->b
, title
);
451 case 'D': { /* Debug */
453 struct hist_entry
*h
= rb_entry(browser
->b
.top
,
454 struct hist_entry
, rb_node
);
456 ui_helpline__fpush("%d: nr_ent=(%d,%d), rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
457 seq
++, browser
->b
.nr_entries
,
458 browser
->hists
->nr_entries
,
462 h
->row_offset
, h
->nr_rows
);
466 /* Collapse the whole world. */
467 hist_browser__set_folding(browser
, false);
470 /* Expand the whole world. */
471 hist_browser__set_folding(browser
, true);
474 browser
->show_headers
= !browser
->show_headers
;
475 hist_browser__update_rows(browser
);
478 if (hist_browser__toggle_fold(browser
))
486 ui_browser__hide(&browser
->b
);
490 struct callchain_print_arg
{
491 /* for hists browser */
493 bool is_current_entry
;
500 typedef void (*print_callchain_entry_fn
)(struct hist_browser
*browser
,
501 struct callchain_list
*chain
,
502 const char *str
, int offset
,
504 struct callchain_print_arg
*arg
);
506 static void hist_browser__show_callchain_entry(struct hist_browser
*browser
,
507 struct callchain_list
*chain
,
508 const char *str
, int offset
,
510 struct callchain_print_arg
*arg
)
513 char folded_sign
= callchain_list__folded(chain
);
514 bool show_annotated
= browser
->show_dso
&& chain
->ms
.sym
&& symbol__annotation(chain
->ms
.sym
)->src
;
516 color
= HE_COLORSET_NORMAL
;
517 width
= browser
->b
.width
- (offset
+ 2);
518 if (ui_browser__is_current_entry(&browser
->b
, row
)) {
519 browser
->selection
= &chain
->ms
;
520 color
= HE_COLORSET_SELECTED
;
521 arg
->is_current_entry
= true;
524 ui_browser__set_color(&browser
->b
, color
);
525 hist_browser__gotorc(browser
, row
, 0);
526 slsmg_write_nstring(" ", offset
);
527 slsmg_printf("%c", folded_sign
);
528 ui_browser__write_graph(&browser
->b
, show_annotated
? SLSMG_RARROW_CHAR
: ' ');
529 slsmg_write_nstring(str
, width
);
532 static void hist_browser__fprintf_callchain_entry(struct hist_browser
*b __maybe_unused
,
533 struct callchain_list
*chain
,
534 const char *str
, int offset
,
535 unsigned short row __maybe_unused
,
536 struct callchain_print_arg
*arg
)
538 char folded_sign
= callchain_list__folded(chain
);
540 arg
->printed
+= fprintf(arg
->fp
, "%*s%c %s\n", offset
, " ",
544 typedef bool (*check_output_full_fn
)(struct hist_browser
*browser
,
547 static bool hist_browser__check_output_full(struct hist_browser
*browser
,
550 return browser
->b
.rows
== row
;
553 static bool hist_browser__check_dump_full(struct hist_browser
*browser __maybe_unused
,
554 unsigned short row __maybe_unused
)
559 #define LEVEL_OFFSET_STEP 3
561 static int hist_browser__show_callchain(struct hist_browser
*browser
,
562 struct rb_root
*root
, int level
,
563 unsigned short row
, u64 total
,
564 print_callchain_entry_fn print
,
565 struct callchain_print_arg
*arg
,
566 check_output_full_fn is_output_full
)
568 struct rb_node
*node
;
569 int first_row
= row
, offset
= level
* LEVEL_OFFSET_STEP
;
573 node
= rb_first(root
);
574 need_percent
= node
&& rb_next(node
);
577 struct callchain_node
*child
= rb_entry(node
, struct callchain_node
, rb_node
);
578 struct rb_node
*next
= rb_next(node
);
579 u64 cumul
= callchain_cumul_hits(child
);
580 struct callchain_list
*chain
;
581 char folded_sign
= ' ';
583 int extra_offset
= 0;
585 list_for_each_entry(chain
, &child
->val
, list
) {
586 char bf
[1024], *alloc_str
;
588 bool was_first
= first
;
592 else if (need_percent
)
593 extra_offset
= LEVEL_OFFSET_STEP
;
595 folded_sign
= callchain_list__folded(chain
);
596 if (arg
->row_offset
!= 0) {
602 str
= callchain_list__sym_name(chain
, bf
, sizeof(bf
),
605 if (was_first
&& need_percent
) {
606 double percent
= cumul
* 100.0 / total
;
608 if (asprintf(&alloc_str
, "%2.2f%% %s", percent
, str
) < 0)
609 str
= "Not enough memory!";
614 print(browser
, chain
, str
, offset
+ extra_offset
, row
, arg
);
618 if (is_output_full(browser
, ++row
))
621 if (folded_sign
== '+')
625 if (folded_sign
== '-') {
626 const int new_level
= level
+ (extra_offset
? 2 : 1);
628 if (callchain_param
.mode
== CHAIN_GRAPH_REL
)
629 new_total
= child
->children_hit
;
633 row
+= hist_browser__show_callchain(browser
, &child
->rb_root
,
634 new_level
, row
, new_total
,
635 print
, arg
, is_output_full
);
637 if (is_output_full(browser
, row
))
642 return row
- first_row
;
646 struct ui_browser
*b
;
651 static int __hpp__slsmg_color_printf(struct perf_hpp
*hpp
, const char *fmt
, ...)
653 struct hpp_arg
*arg
= hpp
->ptr
;
659 len
= va_arg(args
, int);
660 percent
= va_arg(args
, double);
663 ui_browser__set_percent_color(arg
->b
, percent
, arg
->current_entry
);
665 ret
= scnprintf(hpp
->buf
, hpp
->size
, fmt
, len
, percent
);
666 slsmg_printf("%s", hpp
->buf
);
668 advance_hpp(hpp
, ret
);
672 #define __HPP_COLOR_PERCENT_FN(_type, _field) \
673 static u64 __hpp_get_##_field(struct hist_entry *he) \
675 return he->stat._field; \
679 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
680 struct perf_hpp *hpp, \
681 struct hist_entry *he) \
683 return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%", \
684 __hpp__slsmg_color_printf, true); \
687 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
688 static u64 __hpp_get_acc_##_field(struct hist_entry *he) \
690 return he->stat_acc->_field; \
694 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
695 struct perf_hpp *hpp, \
696 struct hist_entry *he) \
698 if (!symbol_conf.cumulate_callchain) { \
699 int len = fmt->user_len ?: fmt->len; \
700 int ret = scnprintf(hpp->buf, hpp->size, \
701 "%*s", len, "N/A"); \
702 slsmg_printf("%s", hpp->buf); \
706 return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field, \
707 " %*.2f%%", __hpp__slsmg_color_printf, true); \
710 __HPP_COLOR_PERCENT_FN(overhead
, period
)
711 __HPP_COLOR_PERCENT_FN(overhead_sys
, period_sys
)
712 __HPP_COLOR_PERCENT_FN(overhead_us
, period_us
)
713 __HPP_COLOR_PERCENT_FN(overhead_guest_sys
, period_guest_sys
)
714 __HPP_COLOR_PERCENT_FN(overhead_guest_us
, period_guest_us
)
715 __HPP_COLOR_ACC_PERCENT_FN(overhead_acc
, period
)
717 #undef __HPP_COLOR_PERCENT_FN
718 #undef __HPP_COLOR_ACC_PERCENT_FN
720 void hist_browser__init_hpp(void)
722 perf_hpp__format
[PERF_HPP__OVERHEAD
].color
=
723 hist_browser__hpp_color_overhead
;
724 perf_hpp__format
[PERF_HPP__OVERHEAD_SYS
].color
=
725 hist_browser__hpp_color_overhead_sys
;
726 perf_hpp__format
[PERF_HPP__OVERHEAD_US
].color
=
727 hist_browser__hpp_color_overhead_us
;
728 perf_hpp__format
[PERF_HPP__OVERHEAD_GUEST_SYS
].color
=
729 hist_browser__hpp_color_overhead_guest_sys
;
730 perf_hpp__format
[PERF_HPP__OVERHEAD_GUEST_US
].color
=
731 hist_browser__hpp_color_overhead_guest_us
;
732 perf_hpp__format
[PERF_HPP__OVERHEAD_ACC
].color
=
733 hist_browser__hpp_color_overhead_acc
;
736 static int hist_browser__show_entry(struct hist_browser
*browser
,
737 struct hist_entry
*entry
,
742 int width
= browser
->b
.width
;
743 char folded_sign
= ' ';
744 bool current_entry
= ui_browser__is_current_entry(&browser
->b
, row
);
745 off_t row_offset
= entry
->row_offset
;
747 struct perf_hpp_fmt
*fmt
;
750 browser
->he_selection
= entry
;
751 browser
->selection
= &entry
->ms
;
754 if (symbol_conf
.use_callchain
) {
755 hist_entry__init_have_children(entry
);
756 folded_sign
= hist_entry__folded(entry
);
759 if (row_offset
== 0) {
760 struct hpp_arg arg
= {
762 .folded_sign
= folded_sign
,
763 .current_entry
= current_entry
,
765 struct perf_hpp hpp
= {
771 hist_browser__gotorc(browser
, row
, 0);
773 perf_hpp__for_each_format(fmt
) {
774 if (perf_hpp__should_skip(fmt
))
777 if (current_entry
&& browser
->b
.navkeypressed
) {
778 ui_browser__set_color(&browser
->b
,
779 HE_COLORSET_SELECTED
);
781 ui_browser__set_color(&browser
->b
,
786 if (symbol_conf
.use_callchain
) {
787 slsmg_printf("%c ", folded_sign
);
797 width
-= fmt
->color(fmt
, &hpp
, entry
);
799 width
-= fmt
->entry(fmt
, &hpp
, entry
);
800 slsmg_printf("%s", s
);
804 /* The scroll bar isn't being used */
805 if (!browser
->b
.navkeypressed
)
808 slsmg_write_nstring("", width
);
815 if (folded_sign
== '-' && row
!= browser
->b
.rows
) {
816 u64 total
= hists__total_period(entry
->hists
);
817 struct callchain_print_arg arg
= {
818 .row_offset
= row_offset
,
819 .is_current_entry
= current_entry
,
822 if (callchain_param
.mode
== CHAIN_GRAPH_REL
) {
823 if (symbol_conf
.cumulate_callchain
)
824 total
= entry
->stat_acc
->period
;
826 total
= entry
->stat
.period
;
829 printed
+= hist_browser__show_callchain(browser
,
830 &entry
->sorted_chain
, 1, row
, total
,
831 hist_browser__show_callchain_entry
, &arg
,
832 hist_browser__check_output_full
);
834 if (arg
.is_current_entry
)
835 browser
->he_selection
= entry
;
841 static int advance_hpp_check(struct perf_hpp
*hpp
, int inc
)
843 advance_hpp(hpp
, inc
);
844 return hpp
->size
<= 0;
847 static int hists__scnprintf_headers(char *buf
, size_t size
, struct hists
*hists
)
849 struct perf_hpp dummy_hpp
= {
853 struct perf_hpp_fmt
*fmt
;
856 if (symbol_conf
.use_callchain
) {
857 ret
= scnprintf(buf
, size
, " ");
858 if (advance_hpp_check(&dummy_hpp
, ret
))
862 perf_hpp__for_each_format(fmt
) {
863 if (perf_hpp__should_skip(fmt
))
866 ret
= fmt
->header(fmt
, &dummy_hpp
, hists_to_evsel(hists
));
867 if (advance_hpp_check(&dummy_hpp
, ret
))
870 ret
= scnprintf(dummy_hpp
.buf
, dummy_hpp
.size
, " ");
871 if (advance_hpp_check(&dummy_hpp
, ret
))
878 static void hist_browser__show_headers(struct hist_browser
*browser
)
882 hists__scnprintf_headers(headers
, sizeof(headers
), browser
->hists
);
883 ui_browser__gotorc(&browser
->b
, 0, 0);
884 ui_browser__set_color(&browser
->b
, HE_COLORSET_ROOT
);
885 slsmg_write_nstring(headers
, browser
->b
.width
+ 1);
888 static void ui_browser__hists_init_top(struct ui_browser
*browser
)
890 if (browser
->top
== NULL
) {
891 struct hist_browser
*hb
;
893 hb
= container_of(browser
, struct hist_browser
, b
);
894 browser
->top
= rb_first(&hb
->hists
->entries
);
898 static unsigned int hist_browser__refresh(struct ui_browser
*browser
)
901 u16 header_offset
= 0;
903 struct hist_browser
*hb
= container_of(browser
, struct hist_browser
, b
);
905 if (hb
->show_headers
) {
906 hist_browser__show_headers(hb
);
910 ui_browser__hists_init_top(browser
);
912 for (nd
= browser
->top
; nd
; nd
= rb_next(nd
)) {
913 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
919 percent
= hist_entry__get_percent_limit(h
);
920 if (percent
< hb
->min_pcnt
)
923 row
+= hist_browser__show_entry(hb
, h
, row
);
924 if (row
== browser
->rows
)
928 return row
+ header_offset
;
931 static struct rb_node
*hists__filter_entries(struct rb_node
*nd
,
935 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
936 float percent
= hist_entry__get_percent_limit(h
);
938 if (!h
->filtered
&& percent
>= min_pcnt
)
947 static struct rb_node
*hists__filter_prev_entries(struct rb_node
*nd
,
951 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
952 float percent
= hist_entry__get_percent_limit(h
);
954 if (!h
->filtered
&& percent
>= min_pcnt
)
963 static void ui_browser__hists_seek(struct ui_browser
*browser
,
964 off_t offset
, int whence
)
966 struct hist_entry
*h
;
969 struct hist_browser
*hb
;
971 hb
= container_of(browser
, struct hist_browser
, b
);
973 if (browser
->nr_entries
== 0)
976 ui_browser__hists_init_top(browser
);
980 nd
= hists__filter_entries(rb_first(browser
->entries
),
987 nd
= hists__filter_prev_entries(rb_last(browser
->entries
),
996 * Moves not relative to the first visible entry invalidates its
999 h
= rb_entry(browser
->top
, struct hist_entry
, rb_node
);
1003 * Here we have to check if nd is expanded (+), if it is we can't go
1004 * the next top level hist_entry, instead we must compute an offset of
1005 * what _not_ to show and not change the first visible entry.
1007 * This offset increments when we are going from top to bottom and
1008 * decreases when we're going from bottom to top.
1010 * As we don't have backpointers to the top level in the callchains
1011 * structure, we need to always print the whole hist_entry callchain,
1012 * skipping the first ones that are before the first visible entry
1013 * and stop when we printed enough lines to fill the screen.
1018 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1019 if (h
->ms
.unfolded
) {
1020 u16 remaining
= h
->nr_rows
- h
->row_offset
;
1021 if (offset
> remaining
) {
1022 offset
-= remaining
;
1025 h
->row_offset
+= offset
;
1031 nd
= hists__filter_entries(rb_next(nd
), hb
->min_pcnt
);
1036 } while (offset
!= 0);
1037 } else if (offset
< 0) {
1039 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1040 if (h
->ms
.unfolded
) {
1042 if (-offset
> h
->row_offset
) {
1043 offset
+= h
->row_offset
;
1046 h
->row_offset
+= offset
;
1052 if (-offset
> h
->nr_rows
) {
1053 offset
+= h
->nr_rows
;
1056 h
->row_offset
= h
->nr_rows
+ offset
;
1064 nd
= hists__filter_prev_entries(rb_prev(nd
),
1072 * Last unfiltered hist_entry, check if it is
1073 * unfolded, if it is then we should have
1074 * row_offset at its last entry.
1076 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1078 h
->row_offset
= h
->nr_rows
;
1085 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1090 static int hist_browser__fprintf_callchain(struct hist_browser
*browser
,
1091 struct hist_entry
*he
, FILE *fp
)
1093 u64 total
= hists__total_period(he
->hists
);
1094 struct callchain_print_arg arg
= {
1098 if (symbol_conf
.cumulate_callchain
)
1099 total
= he
->stat_acc
->period
;
1101 hist_browser__show_callchain(browser
, &he
->sorted_chain
, 1, 0, total
,
1102 hist_browser__fprintf_callchain_entry
, &arg
,
1103 hist_browser__check_dump_full
);
1107 static int hist_browser__fprintf_entry(struct hist_browser
*browser
,
1108 struct hist_entry
*he
, FILE *fp
)
1112 char folded_sign
= ' ';
1113 struct perf_hpp hpp
= {
1117 struct perf_hpp_fmt
*fmt
;
1121 if (symbol_conf
.use_callchain
)
1122 folded_sign
= hist_entry__folded(he
);
1124 if (symbol_conf
.use_callchain
)
1125 printed
+= fprintf(fp
, "%c ", folded_sign
);
1127 perf_hpp__for_each_format(fmt
) {
1128 if (perf_hpp__should_skip(fmt
))
1132 ret
= scnprintf(hpp
.buf
, hpp
.size
, " ");
1133 advance_hpp(&hpp
, ret
);
1137 ret
= fmt
->entry(fmt
, &hpp
, he
);
1138 advance_hpp(&hpp
, ret
);
1140 printed
+= fprintf(fp
, "%s\n", rtrim(s
));
1142 if (folded_sign
== '-')
1143 printed
+= hist_browser__fprintf_callchain(browser
, he
, fp
);
1148 static int hist_browser__fprintf(struct hist_browser
*browser
, FILE *fp
)
1150 struct rb_node
*nd
= hists__filter_entries(rb_first(browser
->b
.entries
),
1155 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
1157 printed
+= hist_browser__fprintf_entry(browser
, h
, fp
);
1158 nd
= hists__filter_entries(rb_next(nd
), browser
->min_pcnt
);
1164 static int hist_browser__dump(struct hist_browser
*browser
)
1170 scnprintf(filename
, sizeof(filename
), "perf.hist.%d", browser
->print_seq
);
1171 if (access(filename
, F_OK
))
1174 * XXX: Just an arbitrary lazy upper limit
1176 if (++browser
->print_seq
== 8192) {
1177 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1182 fp
= fopen(filename
, "w");
1185 const char *err
= strerror_r(errno
, bf
, sizeof(bf
));
1186 ui_helpline__fpush("Couldn't write to %s: %s", filename
, err
);
1190 ++browser
->print_seq
;
1191 hist_browser__fprintf(browser
, fp
);
1193 ui_helpline__fpush("%s written!", filename
);
1198 static struct hist_browser
*hist_browser__new(struct hists
*hists
)
1200 struct hist_browser
*browser
= zalloc(sizeof(*browser
));
1203 browser
->hists
= hists
;
1204 browser
->b
.refresh
= hist_browser__refresh
;
1205 browser
->b
.refresh_dimensions
= hist_browser__refresh_dimensions
;
1206 browser
->b
.seek
= ui_browser__hists_seek
;
1207 browser
->b
.use_navkeypressed
= true;
1208 browser
->show_headers
= symbol_conf
.show_hist_headers
;
1214 static void hist_browser__delete(struct hist_browser
*browser
)
1219 static struct hist_entry
*hist_browser__selected_entry(struct hist_browser
*browser
)
1221 return browser
->he_selection
;
1224 static struct thread
*hist_browser__selected_thread(struct hist_browser
*browser
)
1226 return browser
->he_selection
->thread
;
1229 /* Check whether the browser is for 'top' or 'report' */
1230 static inline bool is_report_browser(void *timer
)
1232 return timer
== NULL
;
1235 static int hists__browser_title(struct hists
*hists
,
1236 struct hist_browser_timer
*hbt
,
1237 char *bf
, size_t size
)
1241 const struct dso
*dso
= hists
->dso_filter
;
1242 const struct thread
*thread
= hists
->thread_filter
;
1243 unsigned long nr_samples
= hists
->stats
.nr_events
[PERF_RECORD_SAMPLE
];
1244 u64 nr_events
= hists
->stats
.total_period
;
1245 struct perf_evsel
*evsel
= hists_to_evsel(hists
);
1246 const char *ev_name
= perf_evsel__name(evsel
);
1248 size_t buflen
= sizeof(buf
);
1250 if (symbol_conf
.filter_relative
) {
1251 nr_samples
= hists
->stats
.nr_non_filtered_samples
;
1252 nr_events
= hists
->stats
.total_non_filtered_period
;
1255 if (perf_evsel__is_group_event(evsel
)) {
1256 struct perf_evsel
*pos
;
1258 perf_evsel__group_desc(evsel
, buf
, buflen
);
1261 for_each_group_member(pos
, evsel
) {
1262 struct hists
*pos_hists
= evsel__hists(pos
);
1264 if (symbol_conf
.filter_relative
) {
1265 nr_samples
+= pos_hists
->stats
.nr_non_filtered_samples
;
1266 nr_events
+= pos_hists
->stats
.total_non_filtered_period
;
1268 nr_samples
+= pos_hists
->stats
.nr_events
[PERF_RECORD_SAMPLE
];
1269 nr_events
+= pos_hists
->stats
.total_period
;
1274 nr_samples
= convert_unit(nr_samples
, &unit
);
1275 printed
= scnprintf(bf
, size
,
1276 "Samples: %lu%c of event '%s', Event count (approx.): %" PRIu64
,
1277 nr_samples
, unit
, ev_name
, nr_events
);
1280 if (hists
->uid_filter_str
)
1281 printed
+= snprintf(bf
+ printed
, size
- printed
,
1282 ", UID: %s", hists
->uid_filter_str
);
1284 printed
+= scnprintf(bf
+ printed
, size
- printed
,
1286 (thread
->comm_set
? thread__comm_str(thread
) : ""),
1289 printed
+= scnprintf(bf
+ printed
, size
- printed
,
1290 ", DSO: %s", dso
->short_name
);
1291 if (!is_report_browser(hbt
)) {
1292 struct perf_top
*top
= hbt
->arg
;
1295 printed
+= scnprintf(bf
+ printed
, size
- printed
, " [z]");
1301 static inline void free_popup_options(char **options
, int n
)
1305 for (i
= 0; i
< n
; ++i
)
1310 * Only runtime switching of perf data file will make "input_name" point
1311 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1312 * whether we need to call free() for current "input_name" during the switch.
1314 static bool is_input_name_malloced
= false;
1316 static int switch_data_file(void)
1318 char *pwd
, *options
[32], *abs_path
[32], *tmp
;
1320 int nr_options
= 0, choice
= -1, ret
= -1;
1321 struct dirent
*dent
;
1323 pwd
= getenv("PWD");
1327 pwd_dir
= opendir(pwd
);
1331 memset(options
, 0, sizeof(options
));
1332 memset(options
, 0, sizeof(abs_path
));
1334 while ((dent
= readdir(pwd_dir
))) {
1335 char path
[PATH_MAX
];
1337 char *name
= dent
->d_name
;
1340 if (!(dent
->d_type
== DT_REG
))
1343 snprintf(path
, sizeof(path
), "%s/%s", pwd
, name
);
1345 file
= fopen(path
, "r");
1349 if (fread(&magic
, 1, 8, file
) < 8)
1350 goto close_file_and_continue
;
1352 if (is_perf_magic(magic
)) {
1353 options
[nr_options
] = strdup(name
);
1354 if (!options
[nr_options
])
1355 goto close_file_and_continue
;
1357 abs_path
[nr_options
] = strdup(path
);
1358 if (!abs_path
[nr_options
]) {
1359 zfree(&options
[nr_options
]);
1360 ui__warning("Can't search all data files due to memory shortage.\n");
1368 close_file_and_continue
:
1370 if (nr_options
>= 32) {
1371 ui__warning("Too many perf data files in PWD!\n"
1372 "Only the first 32 files will be listed.\n");
1379 choice
= ui__popup_menu(nr_options
, options
);
1380 if (choice
< nr_options
&& choice
>= 0) {
1381 tmp
= strdup(abs_path
[choice
]);
1383 if (is_input_name_malloced
)
1384 free((void *)input_name
);
1386 is_input_name_malloced
= true;
1389 ui__warning("Data switch failed due to memory shortage!\n");
1393 free_popup_options(options
, nr_options
);
1394 free_popup_options(abs_path
, nr_options
);
1398 static void hist_browser__update_nr_entries(struct hist_browser
*hb
)
1401 struct rb_node
*nd
= rb_first(&hb
->hists
->entries
);
1403 if (hb
->min_pcnt
== 0) {
1404 hb
->nr_non_filtered_entries
= hb
->hists
->nr_non_filtered_entries
;
1408 while ((nd
= hists__filter_entries(nd
, hb
->min_pcnt
)) != NULL
) {
1413 hb
->nr_non_filtered_entries
= nr_entries
;
1416 static int perf_evsel__hists_browse(struct perf_evsel
*evsel
, int nr_events
,
1417 const char *helpline
,
1419 struct hist_browser_timer
*hbt
,
1421 struct perf_session_env
*env
)
1423 struct hists
*hists
= evsel__hists(evsel
);
1424 struct hist_browser
*browser
= hist_browser__new(hists
);
1425 struct branch_info
*bi
;
1426 struct pstack
*fstack
;
1431 char script_opt
[64];
1432 int delay_secs
= hbt
? hbt
->refresh
: 0;
1433 struct perf_hpp_fmt
*fmt
;
1435 #define HIST_BROWSER_HELP_COMMON \
1436 "h/?/F1 Show this window\n" \
1438 "PGDN/SPACE Navigate\n" \
1439 "q/ESC/CTRL+C Exit browser\n\n" \
1440 "For multiple event sessions:\n\n" \
1441 "TAB/UNTAB Switch events\n\n" \
1442 "For symbolic views (--sort has sym):\n\n" \
1443 "-> Zoom into DSO/Threads & Annotate current symbol\n" \
1445 "a Annotate current symbol\n" \
1446 "C Collapse all callchains\n" \
1447 "d Zoom into current DSO\n" \
1448 "E Expand all callchains\n" \
1449 "F Toggle percentage of filtered entries\n" \
1450 "H Display column headers\n" \
1452 /* help messages are sorted by lexical order of the hotkey */
1453 const char report_help
[] = HIST_BROWSER_HELP_COMMON
1454 "i Show header information\n"
1455 "P Print histograms to perf.hist.N\n"
1456 "r Run available scripts\n"
1457 "s Switch to another data file in PWD\n"
1458 "t Zoom into current Thread\n"
1459 "V Verbose (DSO names in callchains, etc)\n"
1460 "/ Filter symbol by name";
1461 const char top_help
[] = HIST_BROWSER_HELP_COMMON
1462 "P Print histograms to perf.hist.N\n"
1463 "t Zoom into current Thread\n"
1464 "V Verbose (DSO names in callchains, etc)\n"
1465 "z Toggle zeroing of samples\n"
1466 "/ Filter symbol by name";
1468 if (browser
== NULL
)
1472 browser
->min_pcnt
= min_pcnt
;
1473 hist_browser__update_nr_entries(browser
);
1476 fstack
= pstack__new(2);
1480 ui_helpline__push(helpline
);
1482 memset(options
, 0, sizeof(options
));
1484 perf_hpp__for_each_format(fmt
)
1485 perf_hpp__reset_width(fmt
, hists
);
1487 if (symbol_conf
.col_width_list_str
)
1488 perf_hpp__set_user_width(symbol_conf
.col_width_list_str
);
1491 struct thread
*thread
= NULL
;
1492 const struct dso
*dso
= NULL
;
1494 annotate
= -2, zoom_dso
= -2, zoom_thread
= -2,
1495 annotate_f
= -2, annotate_t
= -2, browse_map
= -2;
1496 int scripts_comm
= -2, scripts_symbol
= -2,
1497 scripts_all
= -2, switch_data
= -2;
1501 key
= hist_browser__run(browser
, hbt
);
1503 if (browser
->he_selection
!= NULL
) {
1504 thread
= hist_browser__selected_thread(browser
);
1505 dso
= browser
->selection
->map
? browser
->selection
->map
->dso
: NULL
;
1513 * Exit the browser, let hists__browser_tree
1514 * go to the next or previous
1516 goto out_free_stack
;
1518 if (!sort__has_sym
) {
1519 ui_browser__warning(&browser
->b
, delay_secs
* 2,
1520 "Annotation is only available for symbolic views, "
1521 "include \"sym*\" in --sort to use it.");
1525 if (browser
->selection
== NULL
||
1526 browser
->selection
->sym
== NULL
||
1527 browser
->selection
->map
->dso
->annotate_warned
)
1531 hist_browser__dump(browser
);
1536 browser
->show_dso
= !browser
->show_dso
;
1541 if (ui_browser__input_window("Symbol to show",
1542 "Please enter the name of symbol you want to see",
1543 buf
, "ENTER: OK, ESC: Cancel",
1544 delay_secs
* 2) == K_ENTER
) {
1545 hists
->symbol_filter_str
= *buf
? buf
: NULL
;
1546 hists__filter_by_symbol(hists
);
1547 hist_browser__reset(browser
);
1551 if (is_report_browser(hbt
))
1555 if (is_report_browser(hbt
))
1556 goto do_data_switch
;
1559 /* env->arch is NULL for live-mode (i.e. perf top) */
1561 tui__header_window(env
);
1564 symbol_conf
.filter_relative
^= 1;
1567 if (!is_report_browser(hbt
)) {
1568 struct perf_top
*top
= hbt
->arg
;
1570 top
->zero
= !top
->zero
;
1576 ui_browser__help_window(&browser
->b
,
1577 is_report_browser(hbt
) ? report_help
: top_help
);
1586 if (pstack__empty(fstack
)) {
1588 * Go back to the perf_evsel_menu__run or other user
1591 goto out_free_stack
;
1594 top
= pstack__pop(fstack
);
1595 if (top
== &browser
->hists
->dso_filter
)
1597 if (top
== &browser
->hists
->thread_filter
)
1598 goto zoom_out_thread
;
1603 !ui_browser__dialog_yesno(&browser
->b
,
1604 "Do you really want to exit?"))
1609 goto out_free_stack
;
1615 goto add_exit_option
;
1617 if (browser
->selection
== NULL
)
1618 goto skip_annotation
;
1620 if (sort__mode
== SORT_MODE__BRANCH
) {
1621 bi
= browser
->he_selection
->branch_info
;
1624 goto skip_annotation
;
1626 if (bi
->from
.sym
!= NULL
&&
1627 !bi
->from
.map
->dso
->annotate_warned
&&
1628 asprintf(&options
[nr_options
], "Annotate %s", bi
->from
.sym
->name
) > 0) {
1629 annotate_f
= nr_options
++;
1632 if (bi
->to
.sym
!= NULL
&&
1633 !bi
->to
.map
->dso
->annotate_warned
&&
1634 (bi
->to
.sym
!= bi
->from
.sym
||
1635 bi
->to
.map
->dso
!= bi
->from
.map
->dso
) &&
1636 asprintf(&options
[nr_options
], "Annotate %s", bi
->to
.sym
->name
) > 0) {
1637 annotate_t
= nr_options
++;
1640 if (browser
->selection
->sym
!= NULL
&&
1641 !browser
->selection
->map
->dso
->annotate_warned
) {
1642 struct annotation
*notes
;
1644 notes
= symbol__annotation(browser
->selection
->sym
);
1647 asprintf(&options
[nr_options
], "Annotate %s",
1648 browser
->selection
->sym
->name
) > 0) {
1649 annotate
= nr_options
++;
1654 if (thread
!= NULL
&&
1655 asprintf(&options
[nr_options
], "Zoom %s %s(%d) thread",
1656 (browser
->hists
->thread_filter
? "out of" : "into"),
1657 (thread
->comm_set
? thread__comm_str(thread
) : ""),
1659 zoom_thread
= nr_options
++;
1662 asprintf(&options
[nr_options
], "Zoom %s %s DSO",
1663 (browser
->hists
->dso_filter
? "out of" : "into"),
1664 (dso
->kernel
? "the Kernel" : dso
->short_name
)) > 0)
1665 zoom_dso
= nr_options
++;
1667 if (browser
->selection
!= NULL
&&
1668 browser
->selection
->map
!= NULL
&&
1669 asprintf(&options
[nr_options
], "Browse map details") > 0)
1670 browse_map
= nr_options
++;
1672 /* perf script support */
1673 if (browser
->he_selection
) {
1676 if (asprintf(&options
[nr_options
], "Run scripts for samples of thread [%s]",
1677 thread__comm_str(browser
->he_selection
->thread
)) > 0)
1678 scripts_comm
= nr_options
++;
1680 sym
= browser
->he_selection
->ms
.sym
;
1681 if (sym
&& sym
->namelen
&&
1682 asprintf(&options
[nr_options
], "Run scripts for samples of symbol [%s]",
1684 scripts_symbol
= nr_options
++;
1687 if (asprintf(&options
[nr_options
], "Run scripts for all samples") > 0)
1688 scripts_all
= nr_options
++;
1690 if (is_report_browser(hbt
) && asprintf(&options
[nr_options
],
1691 "Switch to another data file in PWD") > 0)
1692 switch_data
= nr_options
++;
1694 options
[nr_options
++] = (char *)"Exit";
1696 choice
= ui__popup_menu(nr_options
, options
);
1698 if (choice
== nr_options
- 1)
1702 free_popup_options(options
, nr_options
- 1);
1706 if (choice
== annotate
|| choice
== annotate_t
|| choice
== annotate_f
) {
1707 struct hist_entry
*he
;
1708 struct annotation
*notes
;
1709 struct map_symbol ms
;
1712 if (!objdump_path
&& perf_session_env__lookup_objdump(env
))
1715 he
= hist_browser__selected_entry(browser
);
1719 if (choice
== annotate_f
) {
1720 ms
.map
= he
->branch_info
->from
.map
;
1721 ms
.sym
= he
->branch_info
->from
.sym
;
1722 } else if (choice
== annotate_t
) {
1723 ms
.map
= he
->branch_info
->to
.map
;
1724 ms
.sym
= he
->branch_info
->to
.sym
;
1726 ms
= *browser
->selection
;
1729 notes
= symbol__annotation(ms
.sym
);
1733 err
= map_symbol__tui_annotate(&ms
, evsel
, hbt
);
1735 * offer option to annotate the other branch source or target
1736 * (if they exists) when returning from annotate
1738 if ((err
== 'q' || err
== CTRL('c'))
1739 && annotate_t
!= -2 && annotate_f
!= -2)
1740 goto retry_popup_menu
;
1742 ui_browser__update_nr_entries(&browser
->b
, browser
->hists
->nr_entries
);
1744 ui_browser__handle_resize(&browser
->b
);
1746 } else if (choice
== browse_map
)
1747 map__browse(browser
->selection
->map
);
1748 else if (choice
== zoom_dso
) {
1750 if (browser
->hists
->dso_filter
) {
1751 pstack__remove(fstack
, &browser
->hists
->dso_filter
);
1754 browser
->hists
->dso_filter
= NULL
;
1755 perf_hpp__set_elide(HISTC_DSO
, false);
1759 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1760 dso
->kernel
? "the Kernel" : dso
->short_name
);
1761 browser
->hists
->dso_filter
= dso
;
1762 perf_hpp__set_elide(HISTC_DSO
, true);
1763 pstack__push(fstack
, &browser
->hists
->dso_filter
);
1765 hists__filter_by_dso(hists
);
1766 hist_browser__reset(browser
);
1767 } else if (choice
== zoom_thread
) {
1769 if (browser
->hists
->thread_filter
) {
1770 pstack__remove(fstack
, &browser
->hists
->thread_filter
);
1773 thread__zput(browser
->hists
->thread_filter
);
1774 perf_hpp__set_elide(HISTC_THREAD
, false);
1776 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1777 thread
->comm_set
? thread__comm_str(thread
) : "",
1779 browser
->hists
->thread_filter
= thread__get(thread
);
1780 perf_hpp__set_elide(HISTC_THREAD
, false);
1781 pstack__push(fstack
, &browser
->hists
->thread_filter
);
1783 hists__filter_by_thread(hists
);
1784 hist_browser__reset(browser
);
1786 /* perf scripts support */
1787 else if (choice
== scripts_all
|| choice
== scripts_comm
||
1788 choice
== scripts_symbol
) {
1790 memset(script_opt
, 0, 64);
1792 if (choice
== scripts_comm
)
1793 sprintf(script_opt
, " -c %s ", thread__comm_str(browser
->he_selection
->thread
));
1795 if (choice
== scripts_symbol
)
1796 sprintf(script_opt
, " -S %s ", browser
->he_selection
->ms
.sym
->name
);
1798 script_browse(script_opt
);
1800 /* Switch to another data file */
1801 else if (choice
== switch_data
) {
1803 if (!switch_data_file()) {
1804 key
= K_SWITCH_INPUT_DATA
;
1807 ui__warning("Won't switch the data files due to\n"
1808 "no valid data file get selected!\n");
1812 pstack__delete(fstack
);
1814 hist_browser__delete(browser
);
1815 free_popup_options(options
, nr_options
- 1);
1819 struct perf_evsel_menu
{
1820 struct ui_browser b
;
1821 struct perf_evsel
*selection
;
1822 bool lost_events
, lost_events_warned
;
1824 struct perf_session_env
*env
;
1827 static void perf_evsel_menu__write(struct ui_browser
*browser
,
1828 void *entry
, int row
)
1830 struct perf_evsel_menu
*menu
= container_of(browser
,
1831 struct perf_evsel_menu
, b
);
1832 struct perf_evsel
*evsel
= list_entry(entry
, struct perf_evsel
, node
);
1833 struct hists
*hists
= evsel__hists(evsel
);
1834 bool current_entry
= ui_browser__is_current_entry(browser
, row
);
1835 unsigned long nr_events
= hists
->stats
.nr_events
[PERF_RECORD_SAMPLE
];
1836 const char *ev_name
= perf_evsel__name(evsel
);
1838 const char *warn
= " ";
1841 ui_browser__set_color(browser
, current_entry
? HE_COLORSET_SELECTED
:
1842 HE_COLORSET_NORMAL
);
1844 if (perf_evsel__is_group_event(evsel
)) {
1845 struct perf_evsel
*pos
;
1847 ev_name
= perf_evsel__group_name(evsel
);
1849 for_each_group_member(pos
, evsel
) {
1850 struct hists
*pos_hists
= evsel__hists(pos
);
1851 nr_events
+= pos_hists
->stats
.nr_events
[PERF_RECORD_SAMPLE
];
1855 nr_events
= convert_unit(nr_events
, &unit
);
1856 printed
= scnprintf(bf
, sizeof(bf
), "%lu%c%s%s", nr_events
,
1857 unit
, unit
== ' ' ? "" : " ", ev_name
);
1858 slsmg_printf("%s", bf
);
1860 nr_events
= hists
->stats
.nr_events
[PERF_RECORD_LOST
];
1861 if (nr_events
!= 0) {
1862 menu
->lost_events
= true;
1864 ui_browser__set_color(browser
, HE_COLORSET_TOP
);
1865 nr_events
= convert_unit(nr_events
, &unit
);
1866 printed
+= scnprintf(bf
, sizeof(bf
), ": %ld%c%schunks LOST!",
1867 nr_events
, unit
, unit
== ' ' ? "" : " ");
1871 slsmg_write_nstring(warn
, browser
->width
- printed
);
1874 menu
->selection
= evsel
;
1877 static int perf_evsel_menu__run(struct perf_evsel_menu
*menu
,
1878 int nr_events
, const char *help
,
1879 struct hist_browser_timer
*hbt
)
1881 struct perf_evlist
*evlist
= menu
->b
.priv
;
1882 struct perf_evsel
*pos
;
1883 const char *title
= "Available samples";
1884 int delay_secs
= hbt
? hbt
->refresh
: 0;
1887 if (ui_browser__show(&menu
->b
, title
,
1888 "ESC: exit, ENTER|->: Browse histograms") < 0)
1892 key
= ui_browser__run(&menu
->b
, delay_secs
);
1896 hbt
->timer(hbt
->arg
);
1898 if (!menu
->lost_events_warned
&& menu
->lost_events
) {
1899 ui_browser__warn_lost_events(&menu
->b
);
1900 menu
->lost_events_warned
= true;
1905 if (!menu
->selection
)
1907 pos
= menu
->selection
;
1909 perf_evlist__set_selected(evlist
, pos
);
1911 * Give the calling tool a chance to populate the non
1912 * default evsel resorted hists tree.
1915 hbt
->timer(hbt
->arg
);
1916 key
= perf_evsel__hists_browse(pos
, nr_events
, help
,
1920 ui_browser__show_title(&menu
->b
, title
);
1923 if (pos
->node
.next
== &evlist
->entries
)
1924 pos
= perf_evlist__first(evlist
);
1926 pos
= perf_evsel__next(pos
);
1929 if (pos
->node
.prev
== &evlist
->entries
)
1930 pos
= perf_evlist__last(evlist
);
1932 pos
= perf_evsel__prev(pos
);
1935 if (!ui_browser__dialog_yesno(&menu
->b
,
1936 "Do you really want to exit?"))
1939 case K_SWITCH_INPUT_DATA
:
1949 if (!ui_browser__dialog_yesno(&menu
->b
,
1950 "Do you really want to exit?"))
1962 ui_browser__hide(&menu
->b
);
1966 static bool filter_group_entries(struct ui_browser
*browser __maybe_unused
,
1969 struct perf_evsel
*evsel
= list_entry(entry
, struct perf_evsel
, node
);
1971 if (symbol_conf
.event_group
&& !perf_evsel__is_group_leader(evsel
))
1977 static int __perf_evlist__tui_browse_hists(struct perf_evlist
*evlist
,
1978 int nr_entries
, const char *help
,
1979 struct hist_browser_timer
*hbt
,
1981 struct perf_session_env
*env
)
1983 struct perf_evsel
*pos
;
1984 struct perf_evsel_menu menu
= {
1986 .entries
= &evlist
->entries
,
1987 .refresh
= ui_browser__list_head_refresh
,
1988 .seek
= ui_browser__list_head_seek
,
1989 .write
= perf_evsel_menu__write
,
1990 .filter
= filter_group_entries
,
1991 .nr_entries
= nr_entries
,
1994 .min_pcnt
= min_pcnt
,
1998 ui_helpline__push("Press ESC to exit");
2000 evlist__for_each(evlist
, pos
) {
2001 const char *ev_name
= perf_evsel__name(pos
);
2002 size_t line_len
= strlen(ev_name
) + 7;
2004 if (menu
.b
.width
< line_len
)
2005 menu
.b
.width
= line_len
;
2008 return perf_evsel_menu__run(&menu
, nr_entries
, help
, hbt
);
2011 int perf_evlist__tui_browse_hists(struct perf_evlist
*evlist
, const char *help
,
2012 struct hist_browser_timer
*hbt
,
2014 struct perf_session_env
*env
)
2016 int nr_entries
= evlist
->nr_entries
;
2019 if (nr_entries
== 1) {
2020 struct perf_evsel
*first
= perf_evlist__first(evlist
);
2022 return perf_evsel__hists_browse(first
, nr_entries
, help
,
2023 false, hbt
, min_pcnt
,
2027 if (symbol_conf
.event_group
) {
2028 struct perf_evsel
*pos
;
2031 evlist__for_each(evlist
, pos
) {
2032 if (perf_evsel__is_group_leader(pos
))
2036 if (nr_entries
== 1)
2040 return __perf_evlist__tui_browse_hists(evlist
, nr_entries
, help
,
2041 hbt
, min_pcnt
, env
);