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
;
30 static int hists__browser_title(struct hists
*hists
, char *bf
, size_t size
,
33 static void hist_browser__refresh_dimensions(struct hist_browser
*browser
)
35 /* 3 == +/- toggle symbol before actual hist_entry rendering */
36 browser
->b
.width
= 3 + (hists__sort_list_width(browser
->hists
) +
40 static void hist_browser__reset(struct hist_browser
*browser
)
42 browser
->b
.nr_entries
= browser
->hists
->nr_entries
;
43 hist_browser__refresh_dimensions(browser
);
44 ui_browser__reset_index(&browser
->b
);
47 static char tree__folded_sign(bool unfolded
)
49 return unfolded
? '-' : '+';
52 static char map_symbol__folded(const struct map_symbol
*ms
)
54 return ms
->has_children
? tree__folded_sign(ms
->unfolded
) : ' ';
57 static char hist_entry__folded(const struct hist_entry
*he
)
59 return map_symbol__folded(&he
->ms
);
62 static char callchain_list__folded(const struct callchain_list
*cl
)
64 return map_symbol__folded(&cl
->ms
);
67 static void map_symbol__set_folding(struct map_symbol
*ms
, bool unfold
)
69 ms
->unfolded
= unfold
? ms
->has_children
: false;
72 static int callchain_node__count_rows_rb_tree(struct callchain_node
*node
)
77 for (nd
= rb_first(&node
->rb_root
); nd
; nd
= rb_next(nd
)) {
78 struct callchain_node
*child
= rb_entry(nd
, struct callchain_node
, rb_node
);
79 struct callchain_list
*chain
;
80 char folded_sign
= ' '; /* No children */
82 list_for_each_entry(chain
, &child
->val
, list
) {
84 /* We need this because we may not have children */
85 folded_sign
= callchain_list__folded(chain
);
86 if (folded_sign
== '+')
90 if (folded_sign
== '-') /* Have children and they're unfolded */
91 n
+= callchain_node__count_rows_rb_tree(child
);
97 static int callchain_node__count_rows(struct callchain_node
*node
)
99 struct callchain_list
*chain
;
100 bool unfolded
= false;
103 list_for_each_entry(chain
, &node
->val
, list
) {
105 unfolded
= chain
->ms
.unfolded
;
109 n
+= callchain_node__count_rows_rb_tree(node
);
114 static int callchain__count_rows(struct rb_root
*chain
)
119 for (nd
= rb_first(chain
); nd
; nd
= rb_next(nd
)) {
120 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
121 n
+= callchain_node__count_rows(node
);
127 static bool map_symbol__toggle_fold(struct map_symbol
*ms
)
132 if (!ms
->has_children
)
135 ms
->unfolded
= !ms
->unfolded
;
139 static void callchain_node__init_have_children_rb_tree(struct callchain_node
*node
)
141 struct rb_node
*nd
= rb_first(&node
->rb_root
);
143 for (nd
= rb_first(&node
->rb_root
); nd
; nd
= rb_next(nd
)) {
144 struct callchain_node
*child
= rb_entry(nd
, struct callchain_node
, rb_node
);
145 struct callchain_list
*chain
;
148 list_for_each_entry(chain
, &child
->val
, list
) {
151 chain
->ms
.has_children
= chain
->list
.next
!= &child
->val
||
152 !RB_EMPTY_ROOT(&child
->rb_root
);
154 chain
->ms
.has_children
= chain
->list
.next
== &child
->val
&&
155 !RB_EMPTY_ROOT(&child
->rb_root
);
158 callchain_node__init_have_children_rb_tree(child
);
162 static void callchain_node__init_have_children(struct callchain_node
*node
)
164 struct callchain_list
*chain
;
166 list_for_each_entry(chain
, &node
->val
, list
)
167 chain
->ms
.has_children
= !RB_EMPTY_ROOT(&node
->rb_root
);
169 callchain_node__init_have_children_rb_tree(node
);
172 static void callchain__init_have_children(struct rb_root
*root
)
176 for (nd
= rb_first(root
); nd
; nd
= rb_next(nd
)) {
177 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
178 callchain_node__init_have_children(node
);
182 static void hist_entry__init_have_children(struct hist_entry
*he
)
184 if (!he
->init_have_children
) {
185 he
->ms
.has_children
= !RB_EMPTY_ROOT(&he
->sorted_chain
);
186 callchain__init_have_children(&he
->sorted_chain
);
187 he
->init_have_children
= true;
191 static bool hist_browser__toggle_fold(struct hist_browser
*browser
)
193 if (map_symbol__toggle_fold(browser
->selection
)) {
194 struct hist_entry
*he
= browser
->he_selection
;
196 hist_entry__init_have_children(he
);
197 browser
->hists
->nr_entries
-= he
->nr_rows
;
200 he
->nr_rows
= callchain__count_rows(&he
->sorted_chain
);
203 browser
->hists
->nr_entries
+= he
->nr_rows
;
204 browser
->b
.nr_entries
= browser
->hists
->nr_entries
;
209 /* If it doesn't have children, no toggling performed */
213 static int callchain_node__set_folding_rb_tree(struct callchain_node
*node
, bool unfold
)
218 for (nd
= rb_first(&node
->rb_root
); nd
; nd
= rb_next(nd
)) {
219 struct callchain_node
*child
= rb_entry(nd
, struct callchain_node
, rb_node
);
220 struct callchain_list
*chain
;
221 bool has_children
= false;
223 list_for_each_entry(chain
, &child
->val
, list
) {
225 map_symbol__set_folding(&chain
->ms
, unfold
);
226 has_children
= chain
->ms
.has_children
;
230 n
+= callchain_node__set_folding_rb_tree(child
, unfold
);
236 static int callchain_node__set_folding(struct callchain_node
*node
, bool unfold
)
238 struct callchain_list
*chain
;
239 bool has_children
= false;
242 list_for_each_entry(chain
, &node
->val
, list
) {
244 map_symbol__set_folding(&chain
->ms
, unfold
);
245 has_children
= chain
->ms
.has_children
;
249 n
+= callchain_node__set_folding_rb_tree(node
, unfold
);
254 static int callchain__set_folding(struct rb_root
*chain
, bool unfold
)
259 for (nd
= rb_first(chain
); nd
; nd
= rb_next(nd
)) {
260 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
261 n
+= callchain_node__set_folding(node
, unfold
);
267 static void hist_entry__set_folding(struct hist_entry
*he
, bool unfold
)
269 hist_entry__init_have_children(he
);
270 map_symbol__set_folding(&he
->ms
, unfold
);
272 if (he
->ms
.has_children
) {
273 int n
= callchain__set_folding(&he
->sorted_chain
, unfold
);
274 he
->nr_rows
= unfold
? n
: 0;
279 static void hists__set_folding(struct hists
*hists
, bool unfold
)
283 hists
->nr_entries
= 0;
285 for (nd
= rb_first(&hists
->entries
); nd
; nd
= rb_next(nd
)) {
286 struct hist_entry
*he
= rb_entry(nd
, struct hist_entry
, rb_node
);
287 hist_entry__set_folding(he
, unfold
);
288 hists
->nr_entries
+= 1 + he
->nr_rows
;
292 static void hist_browser__set_folding(struct hist_browser
*browser
, bool unfold
)
294 hists__set_folding(browser
->hists
, unfold
);
295 browser
->b
.nr_entries
= browser
->hists
->nr_entries
;
296 /* Go to the start, we may be way after valid entries after a collapse */
297 ui_browser__reset_index(&browser
->b
);
300 static void ui_browser__warn_lost_events(struct ui_browser
*browser
)
302 ui_browser__warning(browser
, 4,
303 "Events are being lost, check IO/CPU overload!\n\n"
304 "You may want to run 'perf' using a RT scheduler policy:\n\n"
305 " perf top -r 80\n\n"
306 "Or reduce the sampling frequency.");
309 static int hist_browser__run(struct hist_browser
*browser
, const char *ev_name
,
310 void(*timer
)(void *arg
), void *arg
, int delay_secs
)
315 browser
->b
.entries
= &browser
->hists
->entries
;
316 browser
->b
.nr_entries
= browser
->hists
->nr_entries
;
318 hist_browser__refresh_dimensions(browser
);
319 hists__browser_title(browser
->hists
, title
, sizeof(title
), ev_name
);
321 if (ui_browser__show(&browser
->b
, title
,
322 "Press '?' for help on key bindings") < 0)
326 key
= ui_browser__run(&browser
->b
, delay_secs
);
331 ui_browser__update_nr_entries(&browser
->b
, browser
->hists
->nr_entries
);
333 if (browser
->hists
->stats
.nr_lost_warned
!=
334 browser
->hists
->stats
.nr_events
[PERF_RECORD_LOST
]) {
335 browser
->hists
->stats
.nr_lost_warned
=
336 browser
->hists
->stats
.nr_events
[PERF_RECORD_LOST
];
337 ui_browser__warn_lost_events(&browser
->b
);
340 hists__browser_title(browser
->hists
, title
, sizeof(title
), ev_name
);
341 ui_browser__show_title(&browser
->b
, title
);
343 case 'D': { /* Debug */
345 struct hist_entry
*h
= rb_entry(browser
->b
.top
,
346 struct hist_entry
, rb_node
);
348 ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
349 seq
++, browser
->b
.nr_entries
,
350 browser
->hists
->nr_entries
,
354 h
->row_offset
, h
->nr_rows
);
358 /* Collapse the whole world. */
359 hist_browser__set_folding(browser
, false);
362 /* Expand the whole world. */
363 hist_browser__set_folding(browser
, true);
366 if (hist_browser__toggle_fold(browser
))
374 ui_browser__hide(&browser
->b
);
378 static char *callchain_list__sym_name(struct callchain_list
*cl
,
379 char *bf
, size_t bfsize
)
382 return cl
->ms
.sym
->name
;
384 snprintf(bf
, bfsize
, "%#" PRIx64
, cl
->ip
);
388 #define LEVEL_OFFSET_STEP 3
390 static int hist_browser__show_callchain_node_rb_tree(struct hist_browser
*browser
,
391 struct callchain_node
*chain_node
,
392 u64 total
, int level
,
395 bool *is_current_entry
)
397 struct rb_node
*node
;
398 int first_row
= row
, width
, offset
= level
* LEVEL_OFFSET_STEP
;
399 u64 new_total
, remaining
;
401 if (callchain_param
.mode
== CHAIN_GRAPH_REL
)
402 new_total
= chain_node
->children_hit
;
406 remaining
= new_total
;
407 node
= rb_first(&chain_node
->rb_root
);
409 struct callchain_node
*child
= rb_entry(node
, struct callchain_node
, rb_node
);
410 struct rb_node
*next
= rb_next(node
);
411 u64 cumul
= callchain_cumul_hits(child
);
412 struct callchain_list
*chain
;
413 char folded_sign
= ' ';
415 int extra_offset
= 0;
419 list_for_each_entry(chain
, &child
->val
, list
) {
420 char ipstr
[BITS_PER_LONG
/ 4 + 1], *alloc_str
;
423 bool was_first
= first
;
428 extra_offset
= LEVEL_OFFSET_STEP
;
430 folded_sign
= callchain_list__folded(chain
);
431 if (*row_offset
!= 0) {
437 str
= callchain_list__sym_name(chain
, ipstr
, sizeof(ipstr
));
439 double percent
= cumul
* 100.0 / new_total
;
441 if (asprintf(&alloc_str
, "%2.2f%% %s", percent
, str
) < 0)
442 str
= "Not enough memory!";
447 color
= HE_COLORSET_NORMAL
;
448 width
= browser
->b
.width
- (offset
+ extra_offset
+ 2);
449 if (ui_browser__is_current_entry(&browser
->b
, row
)) {
450 browser
->selection
= &chain
->ms
;
451 color
= HE_COLORSET_SELECTED
;
452 *is_current_entry
= true;
455 ui_browser__set_color(&browser
->b
, color
);
456 ui_browser__gotorc(&browser
->b
, row
, 0);
457 slsmg_write_nstring(" ", offset
+ extra_offset
);
458 slsmg_printf("%c ", folded_sign
);
459 slsmg_write_nstring(str
, width
);
462 if (++row
== browser
->b
.height
)
465 if (folded_sign
== '+')
469 if (folded_sign
== '-') {
470 const int new_level
= level
+ (extra_offset
? 2 : 1);
471 row
+= hist_browser__show_callchain_node_rb_tree(browser
, child
, new_total
,
472 new_level
, row
, row_offset
,
475 if (row
== browser
->b
.height
)
480 return row
- first_row
;
483 static int hist_browser__show_callchain_node(struct hist_browser
*browser
,
484 struct callchain_node
*node
,
485 int level
, unsigned short row
,
487 bool *is_current_entry
)
489 struct callchain_list
*chain
;
491 offset
= level
* LEVEL_OFFSET_STEP
,
492 width
= browser
->b
.width
- offset
;
493 char folded_sign
= ' ';
495 list_for_each_entry(chain
, &node
->val
, list
) {
496 char ipstr
[BITS_PER_LONG
/ 4 + 1], *s
;
499 folded_sign
= callchain_list__folded(chain
);
501 if (*row_offset
!= 0) {
506 color
= HE_COLORSET_NORMAL
;
507 if (ui_browser__is_current_entry(&browser
->b
, row
)) {
508 browser
->selection
= &chain
->ms
;
509 color
= HE_COLORSET_SELECTED
;
510 *is_current_entry
= true;
513 s
= callchain_list__sym_name(chain
, ipstr
, sizeof(ipstr
));
514 ui_browser__gotorc(&browser
->b
, row
, 0);
515 ui_browser__set_color(&browser
->b
, color
);
516 slsmg_write_nstring(" ", offset
);
517 slsmg_printf("%c ", folded_sign
);
518 slsmg_write_nstring(s
, width
- 2);
520 if (++row
== browser
->b
.height
)
524 if (folded_sign
== '-')
525 row
+= hist_browser__show_callchain_node_rb_tree(browser
, node
,
526 browser
->hists
->stats
.total_period
,
531 return row
- first_row
;
534 static int hist_browser__show_callchain(struct hist_browser
*browser
,
535 struct rb_root
*chain
,
536 int level
, unsigned short row
,
538 bool *is_current_entry
)
543 for (nd
= rb_first(chain
); nd
; nd
= rb_next(nd
)) {
544 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
546 row
+= hist_browser__show_callchain_node(browser
, node
, level
,
549 if (row
== browser
->b
.height
)
553 return row
- first_row
;
556 static int hist_browser__show_entry(struct hist_browser
*browser
,
557 struct hist_entry
*entry
,
563 int width
= browser
->b
.width
- 6; /* The percentage */
564 char folded_sign
= ' ';
565 bool current_entry
= ui_browser__is_current_entry(&browser
->b
, row
);
566 off_t row_offset
= entry
->row_offset
;
569 browser
->he_selection
= entry
;
570 browser
->selection
= &entry
->ms
;
573 if (symbol_conf
.use_callchain
) {
574 hist_entry__init_have_children(entry
);
575 folded_sign
= hist_entry__folded(entry
);
578 if (row_offset
== 0) {
579 hist_entry__snprintf(entry
, s
, sizeof(s
), browser
->hists
);
580 percent
= (entry
->period
* 100.0) / browser
->hists
->stats
.total_period
;
582 ui_browser__set_percent_color(&browser
->b
, percent
, current_entry
);
583 ui_browser__gotorc(&browser
->b
, row
, 0);
584 if (symbol_conf
.use_callchain
) {
585 slsmg_printf("%c ", folded_sign
);
589 slsmg_printf(" %5.2f%%", percent
);
591 /* The scroll bar isn't being used */
592 if (!browser
->b
.navkeypressed
)
595 if (!current_entry
|| !browser
->b
.navkeypressed
)
596 ui_browser__set_color(&browser
->b
, HE_COLORSET_NORMAL
);
598 if (symbol_conf
.show_nr_samples
) {
599 slsmg_printf(" %11u", entry
->nr_events
);
603 if (symbol_conf
.show_total_period
) {
604 slsmg_printf(" %12" PRIu64
, entry
->period
);
608 slsmg_write_nstring(s
, width
);
614 if (folded_sign
== '-' && row
!= browser
->b
.height
) {
615 printed
+= hist_browser__show_callchain(browser
, &entry
->sorted_chain
,
619 browser
->he_selection
= entry
;
625 static void ui_browser__hists_init_top(struct ui_browser
*browser
)
627 if (browser
->top
== NULL
) {
628 struct hist_browser
*hb
;
630 hb
= container_of(browser
, struct hist_browser
, b
);
631 browser
->top
= rb_first(&hb
->hists
->entries
);
635 static unsigned int hist_browser__refresh(struct ui_browser
*browser
)
639 struct hist_browser
*hb
= container_of(browser
, struct hist_browser
, b
);
641 ui_browser__hists_init_top(browser
);
643 for (nd
= browser
->top
; nd
; nd
= rb_next(nd
)) {
644 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
649 row
+= hist_browser__show_entry(hb
, h
, row
);
650 if (row
== browser
->height
)
657 static struct rb_node
*hists__filter_entries(struct rb_node
*nd
)
660 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
670 static struct rb_node
*hists__filter_prev_entries(struct rb_node
*nd
)
673 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
683 static void ui_browser__hists_seek(struct ui_browser
*browser
,
684 off_t offset
, int whence
)
686 struct hist_entry
*h
;
690 if (browser
->nr_entries
== 0)
693 ui_browser__hists_init_top(browser
);
697 nd
= hists__filter_entries(rb_first(browser
->entries
));
703 nd
= hists__filter_prev_entries(rb_last(browser
->entries
));
711 * Moves not relative to the first visible entry invalidates its
714 h
= rb_entry(browser
->top
, struct hist_entry
, rb_node
);
718 * Here we have to check if nd is expanded (+), if it is we can't go
719 * the next top level hist_entry, instead we must compute an offset of
720 * what _not_ to show and not change the first visible entry.
722 * This offset increments when we are going from top to bottom and
723 * decreases when we're going from bottom to top.
725 * As we don't have backpointers to the top level in the callchains
726 * structure, we need to always print the whole hist_entry callchain,
727 * skipping the first ones that are before the first visible entry
728 * and stop when we printed enough lines to fill the screen.
733 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
734 if (h
->ms
.unfolded
) {
735 u16 remaining
= h
->nr_rows
- h
->row_offset
;
736 if (offset
> remaining
) {
740 h
->row_offset
+= offset
;
746 nd
= hists__filter_entries(rb_next(nd
));
751 } while (offset
!= 0);
752 } else if (offset
< 0) {
754 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
755 if (h
->ms
.unfolded
) {
757 if (-offset
> h
->row_offset
) {
758 offset
+= h
->row_offset
;
761 h
->row_offset
+= offset
;
767 if (-offset
> h
->nr_rows
) {
768 offset
+= h
->nr_rows
;
771 h
->row_offset
= h
->nr_rows
+ offset
;
779 nd
= hists__filter_prev_entries(rb_prev(nd
));
786 * Last unfiltered hist_entry, check if it is
787 * unfolded, if it is then we should have
788 * row_offset at its last entry.
790 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
792 h
->row_offset
= h
->nr_rows
;
799 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
804 static int hist_browser__fprintf_callchain_node_rb_tree(struct hist_browser
*browser
,
805 struct callchain_node
*chain_node
,
806 u64 total
, int level
,
809 struct rb_node
*node
;
810 int offset
= level
* LEVEL_OFFSET_STEP
;
811 u64 new_total
, remaining
;
814 if (callchain_param
.mode
== CHAIN_GRAPH_REL
)
815 new_total
= chain_node
->children_hit
;
819 remaining
= new_total
;
820 node
= rb_first(&chain_node
->rb_root
);
822 struct callchain_node
*child
= rb_entry(node
, struct callchain_node
, rb_node
);
823 struct rb_node
*next
= rb_next(node
);
824 u64 cumul
= callchain_cumul_hits(child
);
825 struct callchain_list
*chain
;
826 char folded_sign
= ' ';
828 int extra_offset
= 0;
832 list_for_each_entry(chain
, &child
->val
, list
) {
833 char ipstr
[BITS_PER_LONG
/ 4 + 1], *alloc_str
;
835 bool was_first
= first
;
840 extra_offset
= LEVEL_OFFSET_STEP
;
842 folded_sign
= callchain_list__folded(chain
);
845 str
= callchain_list__sym_name(chain
, ipstr
, sizeof(ipstr
));
847 double percent
= cumul
* 100.0 / new_total
;
849 if (asprintf(&alloc_str
, "%2.2f%% %s", percent
, str
) < 0)
850 str
= "Not enough memory!";
855 printed
+= fprintf(fp
, "%*s%c %s\n", offset
+ extra_offset
, " ", folded_sign
, str
);
857 if (folded_sign
== '+')
861 if (folded_sign
== '-') {
862 const int new_level
= level
+ (extra_offset
? 2 : 1);
863 printed
+= hist_browser__fprintf_callchain_node_rb_tree(browser
, child
, new_total
,
873 static int hist_browser__fprintf_callchain_node(struct hist_browser
*browser
,
874 struct callchain_node
*node
,
877 struct callchain_list
*chain
;
878 int offset
= level
* LEVEL_OFFSET_STEP
;
879 char folded_sign
= ' ';
882 list_for_each_entry(chain
, &node
->val
, list
) {
883 char ipstr
[BITS_PER_LONG
/ 4 + 1], *s
;
885 folded_sign
= callchain_list__folded(chain
);
886 s
= callchain_list__sym_name(chain
, ipstr
, sizeof(ipstr
));
887 printed
+= fprintf(fp
, "%*s%c %s\n", offset
, " ", folded_sign
, s
);
890 if (folded_sign
== '-')
891 printed
+= hist_browser__fprintf_callchain_node_rb_tree(browser
, node
,
892 browser
->hists
->stats
.total_period
,
897 static int hist_browser__fprintf_callchain(struct hist_browser
*browser
,
898 struct rb_root
*chain
, int level
, FILE *fp
)
903 for (nd
= rb_first(chain
); nd
; nd
= rb_next(nd
)) {
904 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
906 printed
+= hist_browser__fprintf_callchain_node(browser
, node
, level
, fp
);
912 static int hist_browser__fprintf_entry(struct hist_browser
*browser
,
913 struct hist_entry
*he
, FILE *fp
)
918 char folded_sign
= ' ';
920 if (symbol_conf
.use_callchain
)
921 folded_sign
= hist_entry__folded(he
);
923 hist_entry__snprintf(he
, s
, sizeof(s
), browser
->hists
);
924 percent
= (he
->period
* 100.0) / browser
->hists
->stats
.total_period
;
926 if (symbol_conf
.use_callchain
)
927 printed
+= fprintf(fp
, "%c ", folded_sign
);
929 printed
+= fprintf(fp
, " %5.2f%%", percent
);
931 if (symbol_conf
.show_nr_samples
)
932 printed
+= fprintf(fp
, " %11u", he
->nr_events
);
934 if (symbol_conf
.show_total_period
)
935 printed
+= fprintf(fp
, " %12" PRIu64
, he
->period
);
937 printed
+= fprintf(fp
, "%s\n", rtrim(s
));
939 if (folded_sign
== '-')
940 printed
+= hist_browser__fprintf_callchain(browser
, &he
->sorted_chain
, 1, fp
);
945 static int hist_browser__fprintf(struct hist_browser
*browser
, FILE *fp
)
947 struct rb_node
*nd
= hists__filter_entries(rb_first(browser
->b
.entries
));
951 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
953 printed
+= hist_browser__fprintf_entry(browser
, h
, fp
);
954 nd
= hists__filter_entries(rb_next(nd
));
960 static int hist_browser__dump(struct hist_browser
*browser
)
966 scnprintf(filename
, sizeof(filename
), "perf.hist.%d", browser
->print_seq
);
967 if (access(filename
, F_OK
))
970 * XXX: Just an arbitrary lazy upper limit
972 if (++browser
->print_seq
== 8192) {
973 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
978 fp
= fopen(filename
, "w");
981 strerror_r(errno
, bf
, sizeof(bf
));
982 ui_helpline__fpush("Couldn't write to %s: %s", filename
, bf
);
986 ++browser
->print_seq
;
987 hist_browser__fprintf(browser
, fp
);
989 ui_helpline__fpush("%s written!", filename
);
994 static struct hist_browser
*hist_browser__new(struct hists
*hists
)
996 struct hist_browser
*browser
= zalloc(sizeof(*browser
));
999 browser
->hists
= hists
;
1000 browser
->b
.refresh
= hist_browser__refresh
;
1001 browser
->b
.seek
= ui_browser__hists_seek
;
1002 browser
->b
.use_navkeypressed
= true;
1003 if (sort__branch_mode
== 1)
1004 browser
->has_symbols
= sort_sym_from
.list
.next
!= NULL
;
1006 browser
->has_symbols
= sort_sym
.list
.next
!= NULL
;
1012 static void hist_browser__delete(struct hist_browser
*browser
)
1017 static struct hist_entry
*hist_browser__selected_entry(struct hist_browser
*browser
)
1019 return browser
->he_selection
;
1022 static struct thread
*hist_browser__selected_thread(struct hist_browser
*browser
)
1024 return browser
->he_selection
->thread
;
1027 static int hists__browser_title(struct hists
*hists
, char *bf
, size_t size
,
1028 const char *ev_name
)
1032 const struct dso
*dso
= hists
->dso_filter
;
1033 const struct thread
*thread
= hists
->thread_filter
;
1034 unsigned long nr_samples
= hists
->stats
.nr_events
[PERF_RECORD_SAMPLE
];
1035 u64 nr_events
= hists
->stats
.total_period
;
1037 nr_samples
= convert_unit(nr_samples
, &unit
);
1038 printed
= scnprintf(bf
, size
,
1039 "Samples: %lu%c of event '%s', Event count (approx.): %lu",
1040 nr_samples
, unit
, ev_name
, nr_events
);
1043 if (hists
->uid_filter_str
)
1044 printed
+= snprintf(bf
+ printed
, size
- printed
,
1045 ", UID: %s", hists
->uid_filter_str
);
1047 printed
+= scnprintf(bf
+ printed
, size
- printed
,
1049 (thread
->comm_set
? thread
->comm
: ""),
1052 printed
+= scnprintf(bf
+ printed
, size
- printed
,
1053 ", DSO: %s", dso
->short_name
);
1057 static inline void free_popup_options(char **options
, int n
)
1061 for (i
= 0; i
< n
; ++i
) {
1067 static int perf_evsel__hists_browse(struct perf_evsel
*evsel
, int nr_events
,
1068 const char *helpline
, const char *ev_name
,
1070 void(*timer
)(void *arg
), void *arg
,
1073 struct hists
*hists
= &evsel
->hists
;
1074 struct hist_browser
*browser
= hist_browser__new(hists
);
1075 struct branch_info
*bi
;
1076 struct pstack
*fstack
;
1082 if (browser
== NULL
)
1085 fstack
= pstack__new(2);
1089 ui_helpline__push(helpline
);
1091 memset(options
, 0, sizeof(options
));
1094 const struct thread
*thread
= NULL
;
1095 const struct dso
*dso
= NULL
;
1097 annotate
= -2, zoom_dso
= -2, zoom_thread
= -2,
1098 annotate_f
= -2, annotate_t
= -2, browse_map
= -2;
1102 key
= hist_browser__run(browser
, ev_name
, timer
, arg
, delay_secs
);
1104 if (browser
->he_selection
!= NULL
) {
1105 thread
= hist_browser__selected_thread(browser
);
1106 dso
= browser
->selection
->map
? browser
->selection
->map
->dso
: NULL
;
1114 * Exit the browser, let hists__browser_tree
1115 * go to the next or previous
1117 goto out_free_stack
;
1119 if (!browser
->has_symbols
) {
1120 ui_browser__warning(&browser
->b
, delay_secs
* 2,
1121 "Annotation is only available for symbolic views, "
1122 "include \"sym*\" in --sort to use it.");
1126 if (browser
->selection
== NULL
||
1127 browser
->selection
->sym
== NULL
||
1128 browser
->selection
->map
->dso
->annotate_warned
)
1132 hist_browser__dump(browser
);
1139 if (ui_browser__input_window("Symbol to show",
1140 "Please enter the name of symbol you want to see",
1141 buf
, "ENTER: OK, ESC: Cancel",
1142 delay_secs
* 2) == K_ENTER
) {
1143 hists
->symbol_filter_str
= *buf
? buf
: NULL
;
1144 hists__filter_by_symbol(hists
);
1145 hist_browser__reset(browser
);
1151 ui_browser__help_window(&browser
->b
,
1152 "h/?/F1 Show this window\n"
1154 "PGDN/SPACE Navigate\n"
1155 "q/ESC/CTRL+C Exit browser\n\n"
1156 "For multiple event sessions:\n\n"
1157 "TAB/UNTAB Switch events\n\n"
1158 "For symbolic views (--sort has sym):\n\n"
1159 "-> Zoom into DSO/Threads & Annotate current symbol\n"
1161 "a Annotate current symbol\n"
1162 "C Collapse all callchains\n"
1163 "E Expand all callchains\n"
1164 "d Zoom into current DSO\n"
1165 "t Zoom into current Thread\n"
1166 "P Print histograms to perf.hist.N\n"
1167 "/ Filter symbol by name");
1176 if (pstack__empty(fstack
)) {
1178 * Go back to the perf_evsel_menu__run or other user
1181 goto out_free_stack
;
1184 top
= pstack__pop(fstack
);
1185 if (top
== &browser
->hists
->dso_filter
)
1187 if (top
== &browser
->hists
->thread_filter
)
1188 goto zoom_out_thread
;
1193 !ui_browser__dialog_yesno(&browser
->b
,
1194 "Do you really want to exit?"))
1199 goto out_free_stack
;
1204 if (!browser
->has_symbols
)
1205 goto add_exit_option
;
1207 if (sort__branch_mode
== 1) {
1208 bi
= browser
->he_selection
->branch_info
;
1209 if (browser
->selection
!= NULL
&&
1211 bi
->from
.sym
!= NULL
&&
1212 !bi
->from
.map
->dso
->annotate_warned
&&
1213 asprintf(&options
[nr_options
], "Annotate %s",
1214 bi
->from
.sym
->name
) > 0)
1215 annotate_f
= nr_options
++;
1217 if (browser
->selection
!= NULL
&&
1219 bi
->to
.sym
!= NULL
&&
1220 !bi
->to
.map
->dso
->annotate_warned
&&
1221 (bi
->to
.sym
!= bi
->from
.sym
||
1222 bi
->to
.map
->dso
!= bi
->from
.map
->dso
) &&
1223 asprintf(&options
[nr_options
], "Annotate %s",
1224 bi
->to
.sym
->name
) > 0)
1225 annotate_t
= nr_options
++;
1228 if (browser
->selection
!= NULL
&&
1229 browser
->selection
->sym
!= NULL
&&
1230 !browser
->selection
->map
->dso
->annotate_warned
&&
1231 asprintf(&options
[nr_options
], "Annotate %s",
1232 browser
->selection
->sym
->name
) > 0)
1233 annotate
= nr_options
++;
1236 if (thread
!= NULL
&&
1237 asprintf(&options
[nr_options
], "Zoom %s %s(%d) thread",
1238 (browser
->hists
->thread_filter
? "out of" : "into"),
1239 (thread
->comm_set
? thread
->comm
: ""),
1241 zoom_thread
= nr_options
++;
1244 asprintf(&options
[nr_options
], "Zoom %s %s DSO",
1245 (browser
->hists
->dso_filter
? "out of" : "into"),
1246 (dso
->kernel
? "the Kernel" : dso
->short_name
)) > 0)
1247 zoom_dso
= nr_options
++;
1249 if (browser
->selection
!= NULL
&&
1250 browser
->selection
->map
!= NULL
&&
1251 asprintf(&options
[nr_options
], "Browse map details") > 0)
1252 browse_map
= nr_options
++;
1254 options
[nr_options
++] = (char *)"Exit";
1256 choice
= ui__popup_menu(nr_options
, options
);
1258 if (choice
== nr_options
- 1)
1262 free_popup_options(options
, nr_options
- 1);
1266 if (choice
== annotate
|| choice
== annotate_t
|| choice
== annotate_f
) {
1267 struct hist_entry
*he
;
1270 he
= hist_browser__selected_entry(browser
);
1275 * we stash the branch_info symbol + map into the
1276 * the ms so we don't have to rewrite all the annotation
1277 * code to use branch_info.
1278 * in branch mode, the ms struct is not used
1280 if (choice
== annotate_f
) {
1281 he
->ms
.sym
= he
->branch_info
->from
.sym
;
1282 he
->ms
.map
= he
->branch_info
->from
.map
;
1283 } else if (choice
== annotate_t
) {
1284 he
->ms
.sym
= he
->branch_info
->to
.sym
;
1285 he
->ms
.map
= he
->branch_info
->to
.map
;
1289 * Don't let this be freed, say, by hists__decay_entry.
1292 err
= hist_entry__tui_annotate(he
, evsel
->idx
,
1293 timer
, arg
, delay_secs
);
1296 * offer option to annotate the other branch source or target
1297 * (if they exists) when returning from annotate
1299 if ((err
== 'q' || err
== CTRL('c'))
1300 && annotate_t
!= -2 && annotate_f
!= -2)
1301 goto retry_popup_menu
;
1303 ui_browser__update_nr_entries(&browser
->b
, browser
->hists
->nr_entries
);
1305 ui_browser__handle_resize(&browser
->b
);
1307 } else if (choice
== browse_map
)
1308 map__browse(browser
->selection
->map
);
1309 else if (choice
== zoom_dso
) {
1311 if (browser
->hists
->dso_filter
) {
1312 pstack__remove(fstack
, &browser
->hists
->dso_filter
);
1315 browser
->hists
->dso_filter
= NULL
;
1316 sort_dso
.elide
= false;
1320 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1321 dso
->kernel
? "the Kernel" : dso
->short_name
);
1322 browser
->hists
->dso_filter
= dso
;
1323 sort_dso
.elide
= true;
1324 pstack__push(fstack
, &browser
->hists
->dso_filter
);
1326 hists__filter_by_dso(hists
);
1327 hist_browser__reset(browser
);
1328 } else if (choice
== zoom_thread
) {
1330 if (browser
->hists
->thread_filter
) {
1331 pstack__remove(fstack
, &browser
->hists
->thread_filter
);
1334 browser
->hists
->thread_filter
= NULL
;
1335 sort_thread
.elide
= false;
1337 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1338 thread
->comm_set
? thread
->comm
: "",
1340 browser
->hists
->thread_filter
= thread
;
1341 sort_thread
.elide
= true;
1342 pstack__push(fstack
, &browser
->hists
->thread_filter
);
1344 hists__filter_by_thread(hists
);
1345 hist_browser__reset(browser
);
1349 pstack__delete(fstack
);
1351 hist_browser__delete(browser
);
1352 free_popup_options(options
, nr_options
- 1);
1356 struct perf_evsel_menu
{
1357 struct ui_browser b
;
1358 struct perf_evsel
*selection
;
1359 bool lost_events
, lost_events_warned
;
1362 static void perf_evsel_menu__write(struct ui_browser
*browser
,
1363 void *entry
, int row
)
1365 struct perf_evsel_menu
*menu
= container_of(browser
,
1366 struct perf_evsel_menu
, b
);
1367 struct perf_evsel
*evsel
= list_entry(entry
, struct perf_evsel
, node
);
1368 bool current_entry
= ui_browser__is_current_entry(browser
, row
);
1369 unsigned long nr_events
= evsel
->hists
.stats
.nr_events
[PERF_RECORD_SAMPLE
];
1370 const char *ev_name
= perf_evsel__name(evsel
);
1372 const char *warn
= " ";
1375 ui_browser__set_color(browser
, current_entry
? HE_COLORSET_SELECTED
:
1376 HE_COLORSET_NORMAL
);
1378 nr_events
= convert_unit(nr_events
, &unit
);
1379 printed
= scnprintf(bf
, sizeof(bf
), "%lu%c%s%s", nr_events
,
1380 unit
, unit
== ' ' ? "" : " ", ev_name
);
1381 slsmg_printf("%s", bf
);
1383 nr_events
= evsel
->hists
.stats
.nr_events
[PERF_RECORD_LOST
];
1384 if (nr_events
!= 0) {
1385 menu
->lost_events
= true;
1387 ui_browser__set_color(browser
, HE_COLORSET_TOP
);
1388 nr_events
= convert_unit(nr_events
, &unit
);
1389 printed
+= scnprintf(bf
, sizeof(bf
), ": %ld%c%schunks LOST!",
1390 nr_events
, unit
, unit
== ' ' ? "" : " ");
1394 slsmg_write_nstring(warn
, browser
->width
- printed
);
1397 menu
->selection
= evsel
;
1400 static int perf_evsel_menu__run(struct perf_evsel_menu
*menu
,
1401 int nr_events
, const char *help
,
1402 void(*timer
)(void *arg
), void *arg
, int delay_secs
)
1404 struct perf_evlist
*evlist
= menu
->b
.priv
;
1405 struct perf_evsel
*pos
;
1406 const char *ev_name
, *title
= "Available samples";
1409 if (ui_browser__show(&menu
->b
, title
,
1410 "ESC: exit, ENTER|->: Browse histograms") < 0)
1414 key
= ui_browser__run(&menu
->b
, delay_secs
);
1420 if (!menu
->lost_events_warned
&& menu
->lost_events
) {
1421 ui_browser__warn_lost_events(&menu
->b
);
1422 menu
->lost_events_warned
= true;
1427 if (!menu
->selection
)
1429 pos
= menu
->selection
;
1431 perf_evlist__set_selected(evlist
, pos
);
1433 * Give the calling tool a chance to populate the non
1434 * default evsel resorted hists tree.
1438 ev_name
= perf_evsel__name(pos
);
1439 key
= perf_evsel__hists_browse(pos
, nr_events
, help
,
1440 ev_name
, true, timer
,
1442 ui_browser__show_title(&menu
->b
, title
);
1445 if (pos
->node
.next
== &evlist
->entries
)
1446 pos
= list_entry(evlist
->entries
.next
, struct perf_evsel
, node
);
1448 pos
= list_entry(pos
->node
.next
, struct perf_evsel
, node
);
1451 if (pos
->node
.prev
== &evlist
->entries
)
1452 pos
= list_entry(evlist
->entries
.prev
, struct perf_evsel
, node
);
1454 pos
= list_entry(pos
->node
.prev
, struct perf_evsel
, node
);
1457 if (!ui_browser__dialog_yesno(&menu
->b
,
1458 "Do you really want to exit?"))
1470 if (!ui_browser__dialog_yesno(&menu
->b
,
1471 "Do you really want to exit?"))
1483 ui_browser__hide(&menu
->b
);
1487 static int __perf_evlist__tui_browse_hists(struct perf_evlist
*evlist
,
1489 void(*timer
)(void *arg
), void *arg
,
1492 struct perf_evsel
*pos
;
1493 struct perf_evsel_menu menu
= {
1495 .entries
= &evlist
->entries
,
1496 .refresh
= ui_browser__list_head_refresh
,
1497 .seek
= ui_browser__list_head_seek
,
1498 .write
= perf_evsel_menu__write
,
1499 .nr_entries
= evlist
->nr_entries
,
1504 ui_helpline__push("Press ESC to exit");
1506 list_for_each_entry(pos
, &evlist
->entries
, node
) {
1507 const char *ev_name
= perf_evsel__name(pos
);
1508 size_t line_len
= strlen(ev_name
) + 7;
1510 if (menu
.b
.width
< line_len
)
1511 menu
.b
.width
= line_len
;
1514 return perf_evsel_menu__run(&menu
, evlist
->nr_entries
, help
, timer
,
1518 int perf_evlist__tui_browse_hists(struct perf_evlist
*evlist
, const char *help
,
1519 void(*timer
)(void *arg
), void *arg
,
1522 if (evlist
->nr_entries
== 1) {
1523 struct perf_evsel
*first
= list_entry(evlist
->entries
.next
,
1524 struct perf_evsel
, node
);
1525 const char *ev_name
= perf_evsel__name(first
);
1526 return perf_evsel__hists_browse(first
, evlist
->nr_entries
, help
,
1527 ev_name
, false, timer
, arg
,
1531 return __perf_evlist__tui_browse_hists(evlist
, help
,
1532 timer
, arg
, delay_secs
);