2 #include "../libslang.h"
6 #include <linux/rbtree.h>
8 #include "../../util/evsel.h"
9 #include "../../util/evlist.h"
10 #include "../../util/hist.h"
11 #include "../../util/pstack.h"
12 #include "../../util/sort.h"
13 #include "../../util/util.h"
15 #include "../browser.h"
16 #include "../helpline.h"
24 struct hist_entry
*he_selection
;
25 struct map_symbol
*selection
;
31 static int hists__browser_title(struct hists
*hists
, char *bf
, size_t size
,
34 static void hist_browser__refresh_dimensions(struct hist_browser
*browser
)
36 /* 3 == +/- toggle symbol before actual hist_entry rendering */
37 browser
->b
.width
= 3 + (hists__sort_list_width(browser
->hists
) +
41 static void hist_browser__reset(struct hist_browser
*browser
)
43 browser
->b
.nr_entries
= browser
->hists
->nr_entries
;
44 hist_browser__refresh_dimensions(browser
);
45 ui_browser__reset_index(&browser
->b
);
48 static char tree__folded_sign(bool unfolded
)
50 return unfolded
? '-' : '+';
53 static char map_symbol__folded(const struct map_symbol
*ms
)
55 return ms
->has_children
? tree__folded_sign(ms
->unfolded
) : ' ';
58 static char hist_entry__folded(const struct hist_entry
*he
)
60 return map_symbol__folded(&he
->ms
);
63 static char callchain_list__folded(const struct callchain_list
*cl
)
65 return map_symbol__folded(&cl
->ms
);
68 static void map_symbol__set_folding(struct map_symbol
*ms
, bool unfold
)
70 ms
->unfolded
= unfold
? ms
->has_children
: false;
73 static int callchain_node__count_rows_rb_tree(struct callchain_node
*node
)
78 for (nd
= rb_first(&node
->rb_root
); nd
; nd
= rb_next(nd
)) {
79 struct callchain_node
*child
= rb_entry(nd
, struct callchain_node
, rb_node
);
80 struct callchain_list
*chain
;
81 char folded_sign
= ' '; /* No children */
83 list_for_each_entry(chain
, &child
->val
, list
) {
85 /* We need this because we may not have children */
86 folded_sign
= callchain_list__folded(chain
);
87 if (folded_sign
== '+')
91 if (folded_sign
== '-') /* Have children and they're unfolded */
92 n
+= callchain_node__count_rows_rb_tree(child
);
98 static int callchain_node__count_rows(struct callchain_node
*node
)
100 struct callchain_list
*chain
;
101 bool unfolded
= false;
104 list_for_each_entry(chain
, &node
->val
, list
) {
106 unfolded
= chain
->ms
.unfolded
;
110 n
+= callchain_node__count_rows_rb_tree(node
);
115 static int callchain__count_rows(struct rb_root
*chain
)
120 for (nd
= rb_first(chain
); nd
; nd
= rb_next(nd
)) {
121 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
122 n
+= callchain_node__count_rows(node
);
128 static bool map_symbol__toggle_fold(struct map_symbol
*ms
)
133 if (!ms
->has_children
)
136 ms
->unfolded
= !ms
->unfolded
;
140 static void callchain_node__init_have_children_rb_tree(struct callchain_node
*node
)
142 struct rb_node
*nd
= rb_first(&node
->rb_root
);
144 for (nd
= rb_first(&node
->rb_root
); nd
; nd
= rb_next(nd
)) {
145 struct callchain_node
*child
= rb_entry(nd
, struct callchain_node
, rb_node
);
146 struct callchain_list
*chain
;
149 list_for_each_entry(chain
, &child
->val
, list
) {
152 chain
->ms
.has_children
= chain
->list
.next
!= &child
->val
||
153 !RB_EMPTY_ROOT(&child
->rb_root
);
155 chain
->ms
.has_children
= chain
->list
.next
== &child
->val
&&
156 !RB_EMPTY_ROOT(&child
->rb_root
);
159 callchain_node__init_have_children_rb_tree(child
);
163 static void callchain_node__init_have_children(struct callchain_node
*node
)
165 struct callchain_list
*chain
;
167 list_for_each_entry(chain
, &node
->val
, list
)
168 chain
->ms
.has_children
= !RB_EMPTY_ROOT(&node
->rb_root
);
170 callchain_node__init_have_children_rb_tree(node
);
173 static void callchain__init_have_children(struct rb_root
*root
)
177 for (nd
= rb_first(root
); nd
; nd
= rb_next(nd
)) {
178 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
179 callchain_node__init_have_children(node
);
183 static void hist_entry__init_have_children(struct hist_entry
*he
)
185 if (!he
->init_have_children
) {
186 he
->ms
.has_children
= !RB_EMPTY_ROOT(&he
->sorted_chain
);
187 callchain__init_have_children(&he
->sorted_chain
);
188 he
->init_have_children
= true;
192 static bool hist_browser__toggle_fold(struct hist_browser
*browser
)
194 if (map_symbol__toggle_fold(browser
->selection
)) {
195 struct hist_entry
*he
= browser
->he_selection
;
197 hist_entry__init_have_children(he
);
198 browser
->hists
->nr_entries
-= he
->nr_rows
;
201 he
->nr_rows
= callchain__count_rows(&he
->sorted_chain
);
204 browser
->hists
->nr_entries
+= he
->nr_rows
;
205 browser
->b
.nr_entries
= browser
->hists
->nr_entries
;
210 /* If it doesn't have children, no toggling performed */
214 static int callchain_node__set_folding_rb_tree(struct callchain_node
*node
, bool unfold
)
219 for (nd
= rb_first(&node
->rb_root
); nd
; nd
= rb_next(nd
)) {
220 struct callchain_node
*child
= rb_entry(nd
, struct callchain_node
, rb_node
);
221 struct callchain_list
*chain
;
222 bool has_children
= false;
224 list_for_each_entry(chain
, &child
->val
, list
) {
226 map_symbol__set_folding(&chain
->ms
, unfold
);
227 has_children
= chain
->ms
.has_children
;
231 n
+= callchain_node__set_folding_rb_tree(child
, unfold
);
237 static int callchain_node__set_folding(struct callchain_node
*node
, bool unfold
)
239 struct callchain_list
*chain
;
240 bool has_children
= false;
243 list_for_each_entry(chain
, &node
->val
, list
) {
245 map_symbol__set_folding(&chain
->ms
, unfold
);
246 has_children
= chain
->ms
.has_children
;
250 n
+= callchain_node__set_folding_rb_tree(node
, unfold
);
255 static int callchain__set_folding(struct rb_root
*chain
, bool unfold
)
260 for (nd
= rb_first(chain
); nd
; nd
= rb_next(nd
)) {
261 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
262 n
+= callchain_node__set_folding(node
, unfold
);
268 static void hist_entry__set_folding(struct hist_entry
*he
, bool unfold
)
270 hist_entry__init_have_children(he
);
271 map_symbol__set_folding(&he
->ms
, unfold
);
273 if (he
->ms
.has_children
) {
274 int n
= callchain__set_folding(&he
->sorted_chain
, unfold
);
275 he
->nr_rows
= unfold
? n
: 0;
280 static void hists__set_folding(struct hists
*hists
, bool unfold
)
284 hists
->nr_entries
= 0;
286 for (nd
= rb_first(&hists
->entries
); nd
; nd
= rb_next(nd
)) {
287 struct hist_entry
*he
= rb_entry(nd
, struct hist_entry
, rb_node
);
288 hist_entry__set_folding(he
, unfold
);
289 hists
->nr_entries
+= 1 + he
->nr_rows
;
293 static void hist_browser__set_folding(struct hist_browser
*browser
, bool unfold
)
295 hists__set_folding(browser
->hists
, unfold
);
296 browser
->b
.nr_entries
= browser
->hists
->nr_entries
;
297 /* Go to the start, we may be way after valid entries after a collapse */
298 ui_browser__reset_index(&browser
->b
);
301 static void ui_browser__warn_lost_events(struct ui_browser
*browser
)
303 ui_browser__warning(browser
, 4,
304 "Events are being lost, check IO/CPU overload!\n\n"
305 "You may want to run 'perf' using a RT scheduler policy:\n\n"
306 " perf top -r 80\n\n"
307 "Or reduce the sampling frequency.");
310 static int hist_browser__run(struct hist_browser
*browser
, const char *ev_name
,
311 void(*timer
)(void *arg
), void *arg
, int delay_secs
)
316 browser
->b
.entries
= &browser
->hists
->entries
;
317 browser
->b
.nr_entries
= browser
->hists
->nr_entries
;
319 hist_browser__refresh_dimensions(browser
);
320 hists__browser_title(browser
->hists
, title
, sizeof(title
), ev_name
);
322 if (ui_browser__show(&browser
->b
, title
,
323 "Press '?' for help on key bindings") < 0)
327 key
= ui_browser__run(&browser
->b
, delay_secs
);
332 ui_browser__update_nr_entries(&browser
->b
, browser
->hists
->nr_entries
);
334 if (browser
->hists
->stats
.nr_lost_warned
!=
335 browser
->hists
->stats
.nr_events
[PERF_RECORD_LOST
]) {
336 browser
->hists
->stats
.nr_lost_warned
=
337 browser
->hists
->stats
.nr_events
[PERF_RECORD_LOST
];
338 ui_browser__warn_lost_events(&browser
->b
);
341 hists__browser_title(browser
->hists
, title
, sizeof(title
), ev_name
);
342 ui_browser__show_title(&browser
->b
, title
);
344 case 'D': { /* Debug */
346 struct hist_entry
*h
= rb_entry(browser
->b
.top
,
347 struct hist_entry
, rb_node
);
349 ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
350 seq
++, browser
->b
.nr_entries
,
351 browser
->hists
->nr_entries
,
355 h
->row_offset
, h
->nr_rows
);
359 /* Collapse the whole world. */
360 hist_browser__set_folding(browser
, false);
363 /* Expand the whole world. */
364 hist_browser__set_folding(browser
, true);
367 if (hist_browser__toggle_fold(browser
))
375 ui_browser__hide(&browser
->b
);
379 static char *callchain_list__sym_name(struct callchain_list
*cl
,
380 char *bf
, size_t bfsize
, bool show_dso
)
385 printed
= scnprintf(bf
, bfsize
, "%s", cl
->ms
.sym
->name
);
387 printed
= scnprintf(bf
, bfsize
, "%#" PRIx64
, cl
->ip
);
390 scnprintf(bf
+ printed
, bfsize
- printed
, " %s",
391 cl
->ms
.map
? cl
->ms
.map
->dso
->short_name
: "unknown");
396 #define LEVEL_OFFSET_STEP 3
398 static int hist_browser__show_callchain_node_rb_tree(struct hist_browser
*browser
,
399 struct callchain_node
*chain_node
,
400 u64 total
, int level
,
403 bool *is_current_entry
)
405 struct rb_node
*node
;
406 int first_row
= row
, width
, offset
= level
* LEVEL_OFFSET_STEP
;
407 u64 new_total
, remaining
;
409 if (callchain_param
.mode
== CHAIN_GRAPH_REL
)
410 new_total
= chain_node
->children_hit
;
414 remaining
= new_total
;
415 node
= rb_first(&chain_node
->rb_root
);
417 struct callchain_node
*child
= rb_entry(node
, struct callchain_node
, rb_node
);
418 struct rb_node
*next
= rb_next(node
);
419 u64 cumul
= callchain_cumul_hits(child
);
420 struct callchain_list
*chain
;
421 char folded_sign
= ' ';
423 int extra_offset
= 0;
427 list_for_each_entry(chain
, &child
->val
, list
) {
428 char bf
[1024], *alloc_str
;
431 bool was_first
= first
;
436 extra_offset
= LEVEL_OFFSET_STEP
;
438 folded_sign
= callchain_list__folded(chain
);
439 if (*row_offset
!= 0) {
445 str
= callchain_list__sym_name(chain
, bf
, sizeof(bf
),
448 double percent
= cumul
* 100.0 / new_total
;
450 if (asprintf(&alloc_str
, "%2.2f%% %s", percent
, str
) < 0)
451 str
= "Not enough memory!";
456 color
= HE_COLORSET_NORMAL
;
457 width
= browser
->b
.width
- (offset
+ extra_offset
+ 2);
458 if (ui_browser__is_current_entry(&browser
->b
, row
)) {
459 browser
->selection
= &chain
->ms
;
460 color
= HE_COLORSET_SELECTED
;
461 *is_current_entry
= true;
464 ui_browser__set_color(&browser
->b
, color
);
465 ui_browser__gotorc(&browser
->b
, row
, 0);
466 slsmg_write_nstring(" ", offset
+ extra_offset
);
467 slsmg_printf("%c ", folded_sign
);
468 slsmg_write_nstring(str
, width
);
471 if (++row
== browser
->b
.height
)
474 if (folded_sign
== '+')
478 if (folded_sign
== '-') {
479 const int new_level
= level
+ (extra_offset
? 2 : 1);
480 row
+= hist_browser__show_callchain_node_rb_tree(browser
, child
, new_total
,
481 new_level
, row
, row_offset
,
484 if (row
== browser
->b
.height
)
489 return row
- first_row
;
492 static int hist_browser__show_callchain_node(struct hist_browser
*browser
,
493 struct callchain_node
*node
,
494 int level
, unsigned short row
,
496 bool *is_current_entry
)
498 struct callchain_list
*chain
;
500 offset
= level
* LEVEL_OFFSET_STEP
,
501 width
= browser
->b
.width
- offset
;
502 char folded_sign
= ' ';
504 list_for_each_entry(chain
, &node
->val
, list
) {
508 folded_sign
= callchain_list__folded(chain
);
510 if (*row_offset
!= 0) {
515 color
= HE_COLORSET_NORMAL
;
516 if (ui_browser__is_current_entry(&browser
->b
, row
)) {
517 browser
->selection
= &chain
->ms
;
518 color
= HE_COLORSET_SELECTED
;
519 *is_current_entry
= true;
522 s
= callchain_list__sym_name(chain
, bf
, sizeof(bf
),
524 ui_browser__gotorc(&browser
->b
, row
, 0);
525 ui_browser__set_color(&browser
->b
, color
);
526 slsmg_write_nstring(" ", offset
);
527 slsmg_printf("%c ", folded_sign
);
528 slsmg_write_nstring(s
, width
- 2);
530 if (++row
== browser
->b
.height
)
534 if (folded_sign
== '-')
535 row
+= hist_browser__show_callchain_node_rb_tree(browser
, node
,
536 browser
->hists
->stats
.total_period
,
541 return row
- first_row
;
544 static int hist_browser__show_callchain(struct hist_browser
*browser
,
545 struct rb_root
*chain
,
546 int level
, unsigned short row
,
548 bool *is_current_entry
)
553 for (nd
= rb_first(chain
); nd
; nd
= rb_next(nd
)) {
554 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
556 row
+= hist_browser__show_callchain_node(browser
, node
, level
,
559 if (row
== browser
->b
.height
)
563 return row
- first_row
;
566 static int hist_browser__show_entry(struct hist_browser
*browser
,
567 struct hist_entry
*entry
,
573 int width
= browser
->b
.width
- 6; /* The percentage */
574 char folded_sign
= ' ';
575 bool current_entry
= ui_browser__is_current_entry(&browser
->b
, row
);
576 off_t row_offset
= entry
->row_offset
;
579 browser
->he_selection
= entry
;
580 browser
->selection
= &entry
->ms
;
583 if (symbol_conf
.use_callchain
) {
584 hist_entry__init_have_children(entry
);
585 folded_sign
= hist_entry__folded(entry
);
588 if (row_offset
== 0) {
589 hist_entry__sort_snprintf(entry
, s
, sizeof(s
), browser
->hists
);
590 percent
= (entry
->period
* 100.0) / browser
->hists
->stats
.total_period
;
592 ui_browser__set_percent_color(&browser
->b
, percent
, current_entry
);
593 ui_browser__gotorc(&browser
->b
, row
, 0);
594 if (symbol_conf
.use_callchain
) {
595 slsmg_printf("%c ", folded_sign
);
599 slsmg_printf(" %5.2f%%", percent
);
601 /* The scroll bar isn't being used */
602 if (!browser
->b
.navkeypressed
)
605 if (!current_entry
|| !browser
->b
.navkeypressed
)
606 ui_browser__set_color(&browser
->b
, HE_COLORSET_NORMAL
);
608 if (symbol_conf
.show_nr_samples
) {
609 slsmg_printf(" %11u", entry
->nr_events
);
613 if (symbol_conf
.show_total_period
) {
614 slsmg_printf(" %12" PRIu64
, entry
->period
);
618 slsmg_write_nstring(s
, width
);
624 if (folded_sign
== '-' && row
!= browser
->b
.height
) {
625 printed
+= hist_browser__show_callchain(browser
, &entry
->sorted_chain
,
629 browser
->he_selection
= entry
;
635 static void ui_browser__hists_init_top(struct ui_browser
*browser
)
637 if (browser
->top
== NULL
) {
638 struct hist_browser
*hb
;
640 hb
= container_of(browser
, struct hist_browser
, b
);
641 browser
->top
= rb_first(&hb
->hists
->entries
);
645 static unsigned int hist_browser__refresh(struct ui_browser
*browser
)
649 struct hist_browser
*hb
= container_of(browser
, struct hist_browser
, b
);
651 ui_browser__hists_init_top(browser
);
653 for (nd
= browser
->top
; nd
; nd
= rb_next(nd
)) {
654 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
659 row
+= hist_browser__show_entry(hb
, h
, row
);
660 if (row
== browser
->height
)
667 static struct rb_node
*hists__filter_entries(struct rb_node
*nd
)
670 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
680 static struct rb_node
*hists__filter_prev_entries(struct rb_node
*nd
)
683 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
693 static void ui_browser__hists_seek(struct ui_browser
*browser
,
694 off_t offset
, int whence
)
696 struct hist_entry
*h
;
700 if (browser
->nr_entries
== 0)
703 ui_browser__hists_init_top(browser
);
707 nd
= hists__filter_entries(rb_first(browser
->entries
));
713 nd
= hists__filter_prev_entries(rb_last(browser
->entries
));
721 * Moves not relative to the first visible entry invalidates its
724 h
= rb_entry(browser
->top
, struct hist_entry
, rb_node
);
728 * Here we have to check if nd is expanded (+), if it is we can't go
729 * the next top level hist_entry, instead we must compute an offset of
730 * what _not_ to show and not change the first visible entry.
732 * This offset increments when we are going from top to bottom and
733 * decreases when we're going from bottom to top.
735 * As we don't have backpointers to the top level in the callchains
736 * structure, we need to always print the whole hist_entry callchain,
737 * skipping the first ones that are before the first visible entry
738 * and stop when we printed enough lines to fill the screen.
743 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
744 if (h
->ms
.unfolded
) {
745 u16 remaining
= h
->nr_rows
- h
->row_offset
;
746 if (offset
> remaining
) {
750 h
->row_offset
+= offset
;
756 nd
= hists__filter_entries(rb_next(nd
));
761 } while (offset
!= 0);
762 } else if (offset
< 0) {
764 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
765 if (h
->ms
.unfolded
) {
767 if (-offset
> h
->row_offset
) {
768 offset
+= h
->row_offset
;
771 h
->row_offset
+= offset
;
777 if (-offset
> h
->nr_rows
) {
778 offset
+= h
->nr_rows
;
781 h
->row_offset
= h
->nr_rows
+ offset
;
789 nd
= hists__filter_prev_entries(rb_prev(nd
));
796 * Last unfiltered hist_entry, check if it is
797 * unfolded, if it is then we should have
798 * row_offset at its last entry.
800 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
802 h
->row_offset
= h
->nr_rows
;
809 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
814 static int hist_browser__fprintf_callchain_node_rb_tree(struct hist_browser
*browser
,
815 struct callchain_node
*chain_node
,
816 u64 total
, int level
,
819 struct rb_node
*node
;
820 int offset
= level
* LEVEL_OFFSET_STEP
;
821 u64 new_total
, remaining
;
824 if (callchain_param
.mode
== CHAIN_GRAPH_REL
)
825 new_total
= chain_node
->children_hit
;
829 remaining
= new_total
;
830 node
= rb_first(&chain_node
->rb_root
);
832 struct callchain_node
*child
= rb_entry(node
, struct callchain_node
, rb_node
);
833 struct rb_node
*next
= rb_next(node
);
834 u64 cumul
= callchain_cumul_hits(child
);
835 struct callchain_list
*chain
;
836 char folded_sign
= ' ';
838 int extra_offset
= 0;
842 list_for_each_entry(chain
, &child
->val
, list
) {
843 char bf
[1024], *alloc_str
;
845 bool was_first
= first
;
850 extra_offset
= LEVEL_OFFSET_STEP
;
852 folded_sign
= callchain_list__folded(chain
);
855 str
= callchain_list__sym_name(chain
, bf
, sizeof(bf
),
858 double percent
= cumul
* 100.0 / new_total
;
860 if (asprintf(&alloc_str
, "%2.2f%% %s", percent
, str
) < 0)
861 str
= "Not enough memory!";
866 printed
+= fprintf(fp
, "%*s%c %s\n", offset
+ extra_offset
, " ", folded_sign
, str
);
868 if (folded_sign
== '+')
872 if (folded_sign
== '-') {
873 const int new_level
= level
+ (extra_offset
? 2 : 1);
874 printed
+= hist_browser__fprintf_callchain_node_rb_tree(browser
, child
, new_total
,
884 static int hist_browser__fprintf_callchain_node(struct hist_browser
*browser
,
885 struct callchain_node
*node
,
888 struct callchain_list
*chain
;
889 int offset
= level
* LEVEL_OFFSET_STEP
;
890 char folded_sign
= ' ';
893 list_for_each_entry(chain
, &node
->val
, list
) {
896 folded_sign
= callchain_list__folded(chain
);
897 s
= callchain_list__sym_name(chain
, bf
, sizeof(bf
), browser
->show_dso
);
898 printed
+= fprintf(fp
, "%*s%c %s\n", offset
, " ", folded_sign
, s
);
901 if (folded_sign
== '-')
902 printed
+= hist_browser__fprintf_callchain_node_rb_tree(browser
, node
,
903 browser
->hists
->stats
.total_period
,
908 static int hist_browser__fprintf_callchain(struct hist_browser
*browser
,
909 struct rb_root
*chain
, int level
, FILE *fp
)
914 for (nd
= rb_first(chain
); nd
; nd
= rb_next(nd
)) {
915 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
917 printed
+= hist_browser__fprintf_callchain_node(browser
, node
, level
, fp
);
923 static int hist_browser__fprintf_entry(struct hist_browser
*browser
,
924 struct hist_entry
*he
, FILE *fp
)
929 char folded_sign
= ' ';
931 if (symbol_conf
.use_callchain
)
932 folded_sign
= hist_entry__folded(he
);
934 hist_entry__sort_snprintf(he
, s
, sizeof(s
), browser
->hists
);
935 percent
= (he
->period
* 100.0) / browser
->hists
->stats
.total_period
;
937 if (symbol_conf
.use_callchain
)
938 printed
+= fprintf(fp
, "%c ", folded_sign
);
940 printed
+= fprintf(fp
, " %5.2f%%", percent
);
942 if (symbol_conf
.show_nr_samples
)
943 printed
+= fprintf(fp
, " %11u", he
->nr_events
);
945 if (symbol_conf
.show_total_period
)
946 printed
+= fprintf(fp
, " %12" PRIu64
, he
->period
);
948 printed
+= fprintf(fp
, "%s\n", rtrim(s
));
950 if (folded_sign
== '-')
951 printed
+= hist_browser__fprintf_callchain(browser
, &he
->sorted_chain
, 1, fp
);
956 static int hist_browser__fprintf(struct hist_browser
*browser
, FILE *fp
)
958 struct rb_node
*nd
= hists__filter_entries(rb_first(browser
->b
.entries
));
962 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
964 printed
+= hist_browser__fprintf_entry(browser
, h
, fp
);
965 nd
= hists__filter_entries(rb_next(nd
));
971 static int hist_browser__dump(struct hist_browser
*browser
)
977 scnprintf(filename
, sizeof(filename
), "perf.hist.%d", browser
->print_seq
);
978 if (access(filename
, F_OK
))
981 * XXX: Just an arbitrary lazy upper limit
983 if (++browser
->print_seq
== 8192) {
984 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
989 fp
= fopen(filename
, "w");
992 const char *err
= strerror_r(errno
, bf
, sizeof(bf
));
993 ui_helpline__fpush("Couldn't write to %s: %s", filename
, err
);
997 ++browser
->print_seq
;
998 hist_browser__fprintf(browser
, fp
);
1000 ui_helpline__fpush("%s written!", filename
);
1005 static struct hist_browser
*hist_browser__new(struct hists
*hists
)
1007 struct hist_browser
*browser
= zalloc(sizeof(*browser
));
1010 browser
->hists
= hists
;
1011 browser
->b
.refresh
= hist_browser__refresh
;
1012 browser
->b
.seek
= ui_browser__hists_seek
;
1013 browser
->b
.use_navkeypressed
= true;
1014 if (sort__branch_mode
== 1)
1015 browser
->has_symbols
= sort_sym_from
.list
.next
!= NULL
;
1017 browser
->has_symbols
= sort_sym
.list
.next
!= NULL
;
1023 static void hist_browser__delete(struct hist_browser
*browser
)
1028 static struct hist_entry
*hist_browser__selected_entry(struct hist_browser
*browser
)
1030 return browser
->he_selection
;
1033 static struct thread
*hist_browser__selected_thread(struct hist_browser
*browser
)
1035 return browser
->he_selection
->thread
;
1038 static int hists__browser_title(struct hists
*hists
, char *bf
, size_t size
,
1039 const char *ev_name
)
1043 const struct dso
*dso
= hists
->dso_filter
;
1044 const struct thread
*thread
= hists
->thread_filter
;
1045 unsigned long nr_samples
= hists
->stats
.nr_events
[PERF_RECORD_SAMPLE
];
1046 u64 nr_events
= hists
->stats
.total_period
;
1048 nr_samples
= convert_unit(nr_samples
, &unit
);
1049 printed
= scnprintf(bf
, size
,
1050 "Samples: %lu%c of event '%s', Event count (approx.): %lu",
1051 nr_samples
, unit
, ev_name
, nr_events
);
1054 if (hists
->uid_filter_str
)
1055 printed
+= snprintf(bf
+ printed
, size
- printed
,
1056 ", UID: %s", hists
->uid_filter_str
);
1058 printed
+= scnprintf(bf
+ printed
, size
- printed
,
1060 (thread
->comm_set
? thread
->comm
: ""),
1063 printed
+= scnprintf(bf
+ printed
, size
- printed
,
1064 ", DSO: %s", dso
->short_name
);
1068 static inline void free_popup_options(char **options
, int n
)
1072 for (i
= 0; i
< n
; ++i
) {
1078 static int perf_evsel__hists_browse(struct perf_evsel
*evsel
, int nr_events
,
1079 const char *helpline
, const char *ev_name
,
1081 void(*timer
)(void *arg
), void *arg
,
1084 struct hists
*hists
= &evsel
->hists
;
1085 struct hist_browser
*browser
= hist_browser__new(hists
);
1086 struct branch_info
*bi
;
1087 struct pstack
*fstack
;
1093 if (browser
== NULL
)
1096 fstack
= pstack__new(2);
1100 ui_helpline__push(helpline
);
1102 memset(options
, 0, sizeof(options
));
1105 const struct thread
*thread
= NULL
;
1106 const struct dso
*dso
= NULL
;
1108 annotate
= -2, zoom_dso
= -2, zoom_thread
= -2,
1109 annotate_f
= -2, annotate_t
= -2, browse_map
= -2;
1113 key
= hist_browser__run(browser
, ev_name
, timer
, arg
, delay_secs
);
1115 if (browser
->he_selection
!= NULL
) {
1116 thread
= hist_browser__selected_thread(browser
);
1117 dso
= browser
->selection
->map
? browser
->selection
->map
->dso
: NULL
;
1125 * Exit the browser, let hists__browser_tree
1126 * go to the next or previous
1128 goto out_free_stack
;
1130 if (!browser
->has_symbols
) {
1131 ui_browser__warning(&browser
->b
, delay_secs
* 2,
1132 "Annotation is only available for symbolic views, "
1133 "include \"sym*\" in --sort to use it.");
1137 if (browser
->selection
== NULL
||
1138 browser
->selection
->sym
== NULL
||
1139 browser
->selection
->map
->dso
->annotate_warned
)
1143 hist_browser__dump(browser
);
1148 browser
->show_dso
= !browser
->show_dso
;
1153 if (ui_browser__input_window("Symbol to show",
1154 "Please enter the name of symbol you want to see",
1155 buf
, "ENTER: OK, ESC: Cancel",
1156 delay_secs
* 2) == K_ENTER
) {
1157 hists
->symbol_filter_str
= *buf
? buf
: NULL
;
1158 hists__filter_by_symbol(hists
);
1159 hist_browser__reset(browser
);
1165 ui_browser__help_window(&browser
->b
,
1166 "h/?/F1 Show this window\n"
1168 "PGDN/SPACE Navigate\n"
1169 "q/ESC/CTRL+C Exit browser\n\n"
1170 "For multiple event sessions:\n\n"
1171 "TAB/UNTAB Switch events\n\n"
1172 "For symbolic views (--sort has sym):\n\n"
1173 "-> Zoom into DSO/Threads & Annotate current symbol\n"
1175 "a Annotate current symbol\n"
1176 "C Collapse all callchains\n"
1177 "E Expand all callchains\n"
1178 "d Zoom into current DSO\n"
1179 "t Zoom into current Thread\n"
1180 "P Print histograms to perf.hist.N\n"
1181 "V Verbose (DSO names in callchains, etc)\n"
1182 "/ Filter symbol by name");
1191 if (pstack__empty(fstack
)) {
1193 * Go back to the perf_evsel_menu__run or other user
1196 goto out_free_stack
;
1199 top
= pstack__pop(fstack
);
1200 if (top
== &browser
->hists
->dso_filter
)
1202 if (top
== &browser
->hists
->thread_filter
)
1203 goto zoom_out_thread
;
1208 !ui_browser__dialog_yesno(&browser
->b
,
1209 "Do you really want to exit?"))
1214 goto out_free_stack
;
1219 if (!browser
->has_symbols
)
1220 goto add_exit_option
;
1222 if (sort__branch_mode
== 1) {
1223 bi
= browser
->he_selection
->branch_info
;
1224 if (browser
->selection
!= NULL
&&
1226 bi
->from
.sym
!= NULL
&&
1227 !bi
->from
.map
->dso
->annotate_warned
&&
1228 asprintf(&options
[nr_options
], "Annotate %s",
1229 bi
->from
.sym
->name
) > 0)
1230 annotate_f
= nr_options
++;
1232 if (browser
->selection
!= NULL
&&
1234 bi
->to
.sym
!= NULL
&&
1235 !bi
->to
.map
->dso
->annotate_warned
&&
1236 (bi
->to
.sym
!= bi
->from
.sym
||
1237 bi
->to
.map
->dso
!= bi
->from
.map
->dso
) &&
1238 asprintf(&options
[nr_options
], "Annotate %s",
1239 bi
->to
.sym
->name
) > 0)
1240 annotate_t
= nr_options
++;
1243 if (browser
->selection
!= NULL
&&
1244 browser
->selection
->sym
!= NULL
&&
1245 !browser
->selection
->map
->dso
->annotate_warned
&&
1246 asprintf(&options
[nr_options
], "Annotate %s",
1247 browser
->selection
->sym
->name
) > 0)
1248 annotate
= nr_options
++;
1251 if (thread
!= NULL
&&
1252 asprintf(&options
[nr_options
], "Zoom %s %s(%d) thread",
1253 (browser
->hists
->thread_filter
? "out of" : "into"),
1254 (thread
->comm_set
? thread
->comm
: ""),
1256 zoom_thread
= nr_options
++;
1259 asprintf(&options
[nr_options
], "Zoom %s %s DSO",
1260 (browser
->hists
->dso_filter
? "out of" : "into"),
1261 (dso
->kernel
? "the Kernel" : dso
->short_name
)) > 0)
1262 zoom_dso
= nr_options
++;
1264 if (browser
->selection
!= NULL
&&
1265 browser
->selection
->map
!= NULL
&&
1266 asprintf(&options
[nr_options
], "Browse map details") > 0)
1267 browse_map
= nr_options
++;
1269 options
[nr_options
++] = (char *)"Exit";
1271 choice
= ui__popup_menu(nr_options
, options
);
1273 if (choice
== nr_options
- 1)
1277 free_popup_options(options
, nr_options
- 1);
1281 if (choice
== annotate
|| choice
== annotate_t
|| choice
== annotate_f
) {
1282 struct hist_entry
*he
;
1285 he
= hist_browser__selected_entry(browser
);
1290 * we stash the branch_info symbol + map into the
1291 * the ms so we don't have to rewrite all the annotation
1292 * code to use branch_info.
1293 * in branch mode, the ms struct is not used
1295 if (choice
== annotate_f
) {
1296 he
->ms
.sym
= he
->branch_info
->from
.sym
;
1297 he
->ms
.map
= he
->branch_info
->from
.map
;
1298 } else if (choice
== annotate_t
) {
1299 he
->ms
.sym
= he
->branch_info
->to
.sym
;
1300 he
->ms
.map
= he
->branch_info
->to
.map
;
1304 * Don't let this be freed, say, by hists__decay_entry.
1307 err
= hist_entry__tui_annotate(he
, evsel
->idx
,
1308 timer
, arg
, delay_secs
);
1311 * offer option to annotate the other branch source or target
1312 * (if they exists) when returning from annotate
1314 if ((err
== 'q' || err
== CTRL('c'))
1315 && annotate_t
!= -2 && annotate_f
!= -2)
1316 goto retry_popup_menu
;
1318 ui_browser__update_nr_entries(&browser
->b
, browser
->hists
->nr_entries
);
1320 ui_browser__handle_resize(&browser
->b
);
1322 } else if (choice
== browse_map
)
1323 map__browse(browser
->selection
->map
);
1324 else if (choice
== zoom_dso
) {
1326 if (browser
->hists
->dso_filter
) {
1327 pstack__remove(fstack
, &browser
->hists
->dso_filter
);
1330 browser
->hists
->dso_filter
= NULL
;
1331 sort_dso
.elide
= false;
1335 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1336 dso
->kernel
? "the Kernel" : dso
->short_name
);
1337 browser
->hists
->dso_filter
= dso
;
1338 sort_dso
.elide
= true;
1339 pstack__push(fstack
, &browser
->hists
->dso_filter
);
1341 hists__filter_by_dso(hists
);
1342 hist_browser__reset(browser
);
1343 } else if (choice
== zoom_thread
) {
1345 if (browser
->hists
->thread_filter
) {
1346 pstack__remove(fstack
, &browser
->hists
->thread_filter
);
1349 browser
->hists
->thread_filter
= NULL
;
1350 sort_thread
.elide
= false;
1352 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1353 thread
->comm_set
? thread
->comm
: "",
1355 browser
->hists
->thread_filter
= thread
;
1356 sort_thread
.elide
= true;
1357 pstack__push(fstack
, &browser
->hists
->thread_filter
);
1359 hists__filter_by_thread(hists
);
1360 hist_browser__reset(browser
);
1364 pstack__delete(fstack
);
1366 hist_browser__delete(browser
);
1367 free_popup_options(options
, nr_options
- 1);
1371 struct perf_evsel_menu
{
1372 struct ui_browser b
;
1373 struct perf_evsel
*selection
;
1374 bool lost_events
, lost_events_warned
;
1377 static void perf_evsel_menu__write(struct ui_browser
*browser
,
1378 void *entry
, int row
)
1380 struct perf_evsel_menu
*menu
= container_of(browser
,
1381 struct perf_evsel_menu
, b
);
1382 struct perf_evsel
*evsel
= list_entry(entry
, struct perf_evsel
, node
);
1383 bool current_entry
= ui_browser__is_current_entry(browser
, row
);
1384 unsigned long nr_events
= evsel
->hists
.stats
.nr_events
[PERF_RECORD_SAMPLE
];
1385 const char *ev_name
= perf_evsel__name(evsel
);
1387 const char *warn
= " ";
1390 ui_browser__set_color(browser
, current_entry
? HE_COLORSET_SELECTED
:
1391 HE_COLORSET_NORMAL
);
1393 nr_events
= convert_unit(nr_events
, &unit
);
1394 printed
= scnprintf(bf
, sizeof(bf
), "%lu%c%s%s", nr_events
,
1395 unit
, unit
== ' ' ? "" : " ", ev_name
);
1396 slsmg_printf("%s", bf
);
1398 nr_events
= evsel
->hists
.stats
.nr_events
[PERF_RECORD_LOST
];
1399 if (nr_events
!= 0) {
1400 menu
->lost_events
= true;
1402 ui_browser__set_color(browser
, HE_COLORSET_TOP
);
1403 nr_events
= convert_unit(nr_events
, &unit
);
1404 printed
+= scnprintf(bf
, sizeof(bf
), ": %ld%c%schunks LOST!",
1405 nr_events
, unit
, unit
== ' ' ? "" : " ");
1409 slsmg_write_nstring(warn
, browser
->width
- printed
);
1412 menu
->selection
= evsel
;
1415 static int perf_evsel_menu__run(struct perf_evsel_menu
*menu
,
1416 int nr_events
, const char *help
,
1417 void(*timer
)(void *arg
), void *arg
, int delay_secs
)
1419 struct perf_evlist
*evlist
= menu
->b
.priv
;
1420 struct perf_evsel
*pos
;
1421 const char *ev_name
, *title
= "Available samples";
1424 if (ui_browser__show(&menu
->b
, title
,
1425 "ESC: exit, ENTER|->: Browse histograms") < 0)
1429 key
= ui_browser__run(&menu
->b
, delay_secs
);
1435 if (!menu
->lost_events_warned
&& menu
->lost_events
) {
1436 ui_browser__warn_lost_events(&menu
->b
);
1437 menu
->lost_events_warned
= true;
1442 if (!menu
->selection
)
1444 pos
= menu
->selection
;
1446 perf_evlist__set_selected(evlist
, pos
);
1448 * Give the calling tool a chance to populate the non
1449 * default evsel resorted hists tree.
1453 ev_name
= perf_evsel__name(pos
);
1454 key
= perf_evsel__hists_browse(pos
, nr_events
, help
,
1455 ev_name
, true, timer
,
1457 ui_browser__show_title(&menu
->b
, title
);
1460 if (pos
->node
.next
== &evlist
->entries
)
1461 pos
= list_entry(evlist
->entries
.next
, struct perf_evsel
, node
);
1463 pos
= list_entry(pos
->node
.next
, struct perf_evsel
, node
);
1466 if (pos
->node
.prev
== &evlist
->entries
)
1467 pos
= list_entry(evlist
->entries
.prev
, struct perf_evsel
, node
);
1469 pos
= list_entry(pos
->node
.prev
, struct perf_evsel
, node
);
1472 if (!ui_browser__dialog_yesno(&menu
->b
,
1473 "Do you really want to exit?"))
1485 if (!ui_browser__dialog_yesno(&menu
->b
,
1486 "Do you really want to exit?"))
1498 ui_browser__hide(&menu
->b
);
1502 static int __perf_evlist__tui_browse_hists(struct perf_evlist
*evlist
,
1504 void(*timer
)(void *arg
), void *arg
,
1507 struct perf_evsel
*pos
;
1508 struct perf_evsel_menu menu
= {
1510 .entries
= &evlist
->entries
,
1511 .refresh
= ui_browser__list_head_refresh
,
1512 .seek
= ui_browser__list_head_seek
,
1513 .write
= perf_evsel_menu__write
,
1514 .nr_entries
= evlist
->nr_entries
,
1519 ui_helpline__push("Press ESC to exit");
1521 list_for_each_entry(pos
, &evlist
->entries
, node
) {
1522 const char *ev_name
= perf_evsel__name(pos
);
1523 size_t line_len
= strlen(ev_name
) + 7;
1525 if (menu
.b
.width
< line_len
)
1526 menu
.b
.width
= line_len
;
1529 return perf_evsel_menu__run(&menu
, evlist
->nr_entries
, help
, timer
,
1533 int perf_evlist__tui_browse_hists(struct perf_evlist
*evlist
, const char *help
,
1534 void(*timer
)(void *arg
), void *arg
,
1537 if (evlist
->nr_entries
== 1) {
1538 struct perf_evsel
*first
= list_entry(evlist
->entries
.next
,
1539 struct perf_evsel
, node
);
1540 const char *ev_name
= perf_evsel__name(first
);
1541 return perf_evsel__hists_browse(first
, evlist
->nr_entries
, help
,
1542 ev_name
, false, timer
, arg
,
1546 return __perf_evlist__tui_browse_hists(evlist
, help
,
1547 timer
, arg
, delay_secs
);