4 #include "../libslang.h"
8 #include <linux/rbtree.h>
10 #include "../../evsel.h"
11 #include "../../evlist.h"
12 #include "../../hist.h"
13 #include "../../pstack.h"
14 #include "../../sort.h"
15 #include "../../util.h"
17 #include "../browser.h"
18 #include "../helpline.h"
25 struct hist_entry
*he_selection
;
26 struct map_symbol
*selection
;
30 static int hists__browser_title(struct hists
*self
, char *bf
, size_t size
,
33 static void hist_browser__refresh_dimensions(struct hist_browser
*self
)
35 /* 3 == +/- toggle symbol before actual hist_entry rendering */
36 self
->b
.width
= 3 + (hists__sort_list_width(self
->hists
) +
40 static void hist_browser__reset(struct hist_browser
*self
)
42 self
->b
.nr_entries
= self
->hists
->nr_entries
;
43 hist_browser__refresh_dimensions(self
);
44 ui_browser__reset_index(&self
->b
);
47 static char tree__folded_sign(bool unfolded
)
49 return unfolded
? '-' : '+';
52 static char map_symbol__folded(const struct map_symbol
*self
)
54 return self
->has_children
? tree__folded_sign(self
->unfolded
) : ' ';
57 static char hist_entry__folded(const struct hist_entry
*self
)
59 return map_symbol__folded(&self
->ms
);
62 static char callchain_list__folded(const struct callchain_list
*self
)
64 return map_symbol__folded(&self
->ms
);
67 static void map_symbol__set_folding(struct map_symbol
*self
, bool unfold
)
69 self
->unfolded
= unfold
? self
->has_children
: false;
72 static int callchain_node__count_rows_rb_tree(struct callchain_node
*self
)
77 for (nd
= rb_first(&self
->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
*self
)
129 if (!self
->has_children
)
132 self
->unfolded
= !self
->unfolded
;
136 static void callchain_node__init_have_children_rb_tree(struct callchain_node
*self
)
138 struct rb_node
*nd
= rb_first(&self
->rb_root
);
140 for (nd
= rb_first(&self
->rb_root
); nd
; nd
= rb_next(nd
)) {
141 struct callchain_node
*child
= rb_entry(nd
, struct callchain_node
, rb_node
);
142 struct callchain_list
*chain
;
145 list_for_each_entry(chain
, &child
->val
, list
) {
148 chain
->ms
.has_children
= chain
->list
.next
!= &child
->val
||
149 !RB_EMPTY_ROOT(&child
->rb_root
);
151 chain
->ms
.has_children
= chain
->list
.next
== &child
->val
&&
152 !RB_EMPTY_ROOT(&child
->rb_root
);
155 callchain_node__init_have_children_rb_tree(child
);
159 static void callchain_node__init_have_children(struct callchain_node
*self
)
161 struct callchain_list
*chain
;
163 list_for_each_entry(chain
, &self
->val
, list
)
164 chain
->ms
.has_children
= !RB_EMPTY_ROOT(&self
->rb_root
);
166 callchain_node__init_have_children_rb_tree(self
);
169 static void callchain__init_have_children(struct rb_root
*self
)
173 for (nd
= rb_first(self
); nd
; nd
= rb_next(nd
)) {
174 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
175 callchain_node__init_have_children(node
);
179 static void hist_entry__init_have_children(struct hist_entry
*self
)
181 if (!self
->init_have_children
) {
182 self
->ms
.has_children
= !RB_EMPTY_ROOT(&self
->sorted_chain
);
183 callchain__init_have_children(&self
->sorted_chain
);
184 self
->init_have_children
= true;
188 static bool hist_browser__toggle_fold(struct hist_browser
*self
)
190 if (map_symbol__toggle_fold(self
->selection
)) {
191 struct hist_entry
*he
= self
->he_selection
;
193 hist_entry__init_have_children(he
);
194 self
->hists
->nr_entries
-= he
->nr_rows
;
197 he
->nr_rows
= callchain__count_rows(&he
->sorted_chain
);
200 self
->hists
->nr_entries
+= he
->nr_rows
;
201 self
->b
.nr_entries
= self
->hists
->nr_entries
;
206 /* If it doesn't have children, no toggling performed */
210 static int callchain_node__set_folding_rb_tree(struct callchain_node
*self
, bool unfold
)
215 for (nd
= rb_first(&self
->rb_root
); nd
; nd
= rb_next(nd
)) {
216 struct callchain_node
*child
= rb_entry(nd
, struct callchain_node
, rb_node
);
217 struct callchain_list
*chain
;
218 bool has_children
= false;
220 list_for_each_entry(chain
, &child
->val
, list
) {
222 map_symbol__set_folding(&chain
->ms
, unfold
);
223 has_children
= chain
->ms
.has_children
;
227 n
+= callchain_node__set_folding_rb_tree(child
, unfold
);
233 static int callchain_node__set_folding(struct callchain_node
*node
, bool unfold
)
235 struct callchain_list
*chain
;
236 bool has_children
= false;
239 list_for_each_entry(chain
, &node
->val
, list
) {
241 map_symbol__set_folding(&chain
->ms
, unfold
);
242 has_children
= chain
->ms
.has_children
;
246 n
+= callchain_node__set_folding_rb_tree(node
, unfold
);
251 static int callchain__set_folding(struct rb_root
*chain
, bool unfold
)
256 for (nd
= rb_first(chain
); nd
; nd
= rb_next(nd
)) {
257 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
258 n
+= callchain_node__set_folding(node
, unfold
);
264 static void hist_entry__set_folding(struct hist_entry
*self
, bool unfold
)
266 hist_entry__init_have_children(self
);
267 map_symbol__set_folding(&self
->ms
, unfold
);
269 if (self
->ms
.has_children
) {
270 int n
= callchain__set_folding(&self
->sorted_chain
, unfold
);
271 self
->nr_rows
= unfold
? n
: 0;
276 static void hists__set_folding(struct hists
*self
, bool unfold
)
280 self
->nr_entries
= 0;
282 for (nd
= rb_first(&self
->entries
); nd
; nd
= rb_next(nd
)) {
283 struct hist_entry
*he
= rb_entry(nd
, struct hist_entry
, rb_node
);
284 hist_entry__set_folding(he
, unfold
);
285 self
->nr_entries
+= 1 + he
->nr_rows
;
289 static void hist_browser__set_folding(struct hist_browser
*self
, bool unfold
)
291 hists__set_folding(self
->hists
, unfold
);
292 self
->b
.nr_entries
= self
->hists
->nr_entries
;
293 /* Go to the start, we may be way after valid entries after a collapse */
294 ui_browser__reset_index(&self
->b
);
297 static int hist_browser__run(struct hist_browser
*self
, const char *ev_name
,
298 void(*timer
)(void *arg
), void *arg
, int delay_secs
)
303 self
->b
.entries
= &self
->hists
->entries
;
304 self
->b
.nr_entries
= self
->hists
->nr_entries
;
306 hist_browser__refresh_dimensions(self
);
307 hists__browser_title(self
->hists
, title
, sizeof(title
), ev_name
);
309 if (ui_browser__show(&self
->b
, title
,
310 "Press '?' for help on key bindings") < 0)
314 key
= ui_browser__run(&self
->b
, delay_secs
);
318 /* FIXME we need to check if it was es.reason == NEWT_EXIT_TIMER */
320 ui_browser__update_nr_entries(&self
->b
, self
->hists
->nr_entries
);
321 hists__browser_title(self
->hists
, title
, sizeof(title
),
323 ui_browser__show_title(&self
->b
, title
);
325 case 'D': { /* Debug */
327 struct hist_entry
*h
= rb_entry(self
->b
.top
,
328 struct hist_entry
, rb_node
);
330 ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
331 seq
++, self
->b
.nr_entries
,
332 self
->hists
->nr_entries
,
336 h
->row_offset
, h
->nr_rows
);
340 /* Collapse the whole world. */
341 hist_browser__set_folding(self
, false);
344 /* Expand the whole world. */
345 hist_browser__set_folding(self
, true);
348 if (hist_browser__toggle_fold(self
))
356 ui_browser__hide(&self
->b
);
360 static char *callchain_list__sym_name(struct callchain_list
*self
,
361 char *bf
, size_t bfsize
)
364 return self
->ms
.sym
->name
;
366 snprintf(bf
, bfsize
, "%#" PRIx64
, self
->ip
);
370 #define LEVEL_OFFSET_STEP 3
372 static int hist_browser__show_callchain_node_rb_tree(struct hist_browser
*self
,
373 struct callchain_node
*chain_node
,
374 u64 total
, int level
,
377 bool *is_current_entry
)
379 struct rb_node
*node
;
380 int first_row
= row
, width
, offset
= level
* LEVEL_OFFSET_STEP
;
381 u64 new_total
, remaining
;
383 if (callchain_param
.mode
== CHAIN_GRAPH_REL
)
384 new_total
= chain_node
->children_hit
;
388 remaining
= new_total
;
389 node
= rb_first(&chain_node
->rb_root
);
391 struct callchain_node
*child
= rb_entry(node
, struct callchain_node
, rb_node
);
392 struct rb_node
*next
= rb_next(node
);
393 u64 cumul
= callchain_cumul_hits(child
);
394 struct callchain_list
*chain
;
395 char folded_sign
= ' ';
397 int extra_offset
= 0;
401 list_for_each_entry(chain
, &child
->val
, list
) {
402 char ipstr
[BITS_PER_LONG
/ 4 + 1], *alloc_str
;
405 bool was_first
= first
;
410 extra_offset
= LEVEL_OFFSET_STEP
;
412 folded_sign
= callchain_list__folded(chain
);
413 if (*row_offset
!= 0) {
419 str
= callchain_list__sym_name(chain
, ipstr
, sizeof(ipstr
));
421 double percent
= cumul
* 100.0 / new_total
;
423 if (asprintf(&alloc_str
, "%2.2f%% %s", percent
, str
) < 0)
424 str
= "Not enough memory!";
429 color
= HE_COLORSET_NORMAL
;
430 width
= self
->b
.width
- (offset
+ extra_offset
+ 2);
431 if (ui_browser__is_current_entry(&self
->b
, row
)) {
432 self
->selection
= &chain
->ms
;
433 color
= HE_COLORSET_SELECTED
;
434 *is_current_entry
= true;
437 ui_browser__set_color(&self
->b
, color
);
438 ui_browser__gotorc(&self
->b
, row
, 0);
439 slsmg_write_nstring(" ", offset
+ extra_offset
);
440 slsmg_printf("%c ", folded_sign
);
441 slsmg_write_nstring(str
, width
);
444 if (++row
== self
->b
.height
)
447 if (folded_sign
== '+')
451 if (folded_sign
== '-') {
452 const int new_level
= level
+ (extra_offset
? 2 : 1);
453 row
+= hist_browser__show_callchain_node_rb_tree(self
, child
, new_total
,
454 new_level
, row
, row_offset
,
457 if (row
== self
->b
.height
)
462 return row
- first_row
;
465 static int hist_browser__show_callchain_node(struct hist_browser
*self
,
466 struct callchain_node
*node
,
467 int level
, unsigned short row
,
469 bool *is_current_entry
)
471 struct callchain_list
*chain
;
473 offset
= level
* LEVEL_OFFSET_STEP
,
474 width
= self
->b
.width
- offset
;
475 char folded_sign
= ' ';
477 list_for_each_entry(chain
, &node
->val
, list
) {
478 char ipstr
[BITS_PER_LONG
/ 4 + 1], *s
;
481 folded_sign
= callchain_list__folded(chain
);
483 if (*row_offset
!= 0) {
488 color
= HE_COLORSET_NORMAL
;
489 if (ui_browser__is_current_entry(&self
->b
, row
)) {
490 self
->selection
= &chain
->ms
;
491 color
= HE_COLORSET_SELECTED
;
492 *is_current_entry
= true;
495 s
= callchain_list__sym_name(chain
, ipstr
, sizeof(ipstr
));
496 ui_browser__gotorc(&self
->b
, row
, 0);
497 ui_browser__set_color(&self
->b
, color
);
498 slsmg_write_nstring(" ", offset
);
499 slsmg_printf("%c ", folded_sign
);
500 slsmg_write_nstring(s
, width
- 2);
502 if (++row
== self
->b
.height
)
506 if (folded_sign
== '-')
507 row
+= hist_browser__show_callchain_node_rb_tree(self
, node
,
508 self
->hists
->stats
.total_period
,
513 return row
- first_row
;
516 static int hist_browser__show_callchain(struct hist_browser
*self
,
517 struct rb_root
*chain
,
518 int level
, unsigned short row
,
520 bool *is_current_entry
)
525 for (nd
= rb_first(chain
); nd
; nd
= rb_next(nd
)) {
526 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
528 row
+= hist_browser__show_callchain_node(self
, node
, level
,
531 if (row
== self
->b
.height
)
535 return row
- first_row
;
538 static int hist_browser__show_entry(struct hist_browser
*self
,
539 struct hist_entry
*entry
,
545 int width
= self
->b
.width
- 6; /* The percentage */
546 char folded_sign
= ' ';
547 bool current_entry
= ui_browser__is_current_entry(&self
->b
, row
);
548 off_t row_offset
= entry
->row_offset
;
551 self
->he_selection
= entry
;
552 self
->selection
= &entry
->ms
;
555 if (symbol_conf
.use_callchain
) {
556 hist_entry__init_have_children(entry
);
557 folded_sign
= hist_entry__folded(entry
);
560 if (row_offset
== 0) {
561 hist_entry__snprintf(entry
, s
, sizeof(s
), self
->hists
);
562 percent
= (entry
->period
* 100.0) / self
->hists
->stats
.total_period
;
564 ui_browser__set_percent_color(&self
->b
, percent
, current_entry
);
565 ui_browser__gotorc(&self
->b
, row
, 0);
566 if (symbol_conf
.use_callchain
) {
567 slsmg_printf("%c ", folded_sign
);
571 slsmg_printf(" %5.2f%%", percent
);
573 /* The scroll bar isn't being used */
574 if (!self
->b
.navkeypressed
)
577 if (!current_entry
|| !self
->b
.navkeypressed
)
578 ui_browser__set_color(&self
->b
, HE_COLORSET_NORMAL
);
580 if (symbol_conf
.show_nr_samples
) {
581 slsmg_printf(" %11u", entry
->nr_events
);
585 if (symbol_conf
.show_total_period
) {
586 slsmg_printf(" %12" PRIu64
, entry
->period
);
590 slsmg_write_nstring(s
, width
);
596 if (folded_sign
== '-' && row
!= self
->b
.height
) {
597 printed
+= hist_browser__show_callchain(self
, &entry
->sorted_chain
,
601 self
->he_selection
= entry
;
607 static void ui_browser__hists_init_top(struct ui_browser
*browser
)
609 if (browser
->top
== NULL
) {
610 struct hist_browser
*hb
;
612 hb
= container_of(browser
, struct hist_browser
, b
);
613 browser
->top
= rb_first(&hb
->hists
->entries
);
617 static unsigned int hist_browser__refresh(struct ui_browser
*self
)
621 struct hist_browser
*hb
= container_of(self
, struct hist_browser
, b
);
623 ui_browser__hists_init_top(self
);
625 for (nd
= self
->top
; nd
; nd
= rb_next(nd
)) {
626 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
631 row
+= hist_browser__show_entry(hb
, h
, row
);
632 if (row
== self
->height
)
639 static struct rb_node
*hists__filter_entries(struct rb_node
*nd
)
642 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
652 static struct rb_node
*hists__filter_prev_entries(struct rb_node
*nd
)
655 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
665 static void ui_browser__hists_seek(struct ui_browser
*self
,
666 off_t offset
, int whence
)
668 struct hist_entry
*h
;
672 if (self
->nr_entries
== 0)
675 ui_browser__hists_init_top(self
);
679 nd
= hists__filter_entries(rb_first(self
->entries
));
685 nd
= hists__filter_prev_entries(rb_last(self
->entries
));
693 * Moves not relative to the first visible entry invalidates its
696 h
= rb_entry(self
->top
, struct hist_entry
, rb_node
);
700 * Here we have to check if nd is expanded (+), if it is we can't go
701 * the next top level hist_entry, instead we must compute an offset of
702 * what _not_ to show and not change the first visible entry.
704 * This offset increments when we are going from top to bottom and
705 * decreases when we're going from bottom to top.
707 * As we don't have backpointers to the top level in the callchains
708 * structure, we need to always print the whole hist_entry callchain,
709 * skipping the first ones that are before the first visible entry
710 * and stop when we printed enough lines to fill the screen.
715 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
716 if (h
->ms
.unfolded
) {
717 u16 remaining
= h
->nr_rows
- h
->row_offset
;
718 if (offset
> remaining
) {
722 h
->row_offset
+= offset
;
728 nd
= hists__filter_entries(rb_next(nd
));
733 } while (offset
!= 0);
734 } else if (offset
< 0) {
736 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
737 if (h
->ms
.unfolded
) {
739 if (-offset
> h
->row_offset
) {
740 offset
+= h
->row_offset
;
743 h
->row_offset
+= offset
;
749 if (-offset
> h
->nr_rows
) {
750 offset
+= h
->nr_rows
;
753 h
->row_offset
= h
->nr_rows
+ offset
;
761 nd
= hists__filter_prev_entries(rb_prev(nd
));
768 * Last unfiltered hist_entry, check if it is
769 * unfolded, if it is then we should have
770 * row_offset at its last entry.
772 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
774 h
->row_offset
= h
->nr_rows
;
781 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
786 static struct hist_browser
*hist_browser__new(struct hists
*hists
)
788 struct hist_browser
*self
= zalloc(sizeof(*self
));
792 self
->b
.refresh
= hist_browser__refresh
;
793 self
->b
.seek
= ui_browser__hists_seek
;
794 self
->b
.use_navkeypressed
= true,
795 self
->has_symbols
= sort_sym
.list
.next
!= NULL
;
801 static void hist_browser__delete(struct hist_browser
*self
)
806 static struct hist_entry
*hist_browser__selected_entry(struct hist_browser
*self
)
808 return self
->he_selection
;
811 static struct thread
*hist_browser__selected_thread(struct hist_browser
*self
)
813 return self
->he_selection
->thread
;
816 static int hists__browser_title(struct hists
*self
, char *bf
, size_t size
,
821 const struct dso
*dso
= self
->dso_filter
;
822 const struct thread
*thread
= self
->thread_filter
;
823 unsigned long nr_events
= self
->stats
.nr_events
[PERF_RECORD_SAMPLE
];
825 nr_events
= convert_unit(nr_events
, &unit
);
826 printed
= snprintf(bf
, size
, "Events: %lu%c %s", nr_events
, unit
, ev_name
);
829 printed
+= snprintf(bf
+ printed
, size
- printed
,
831 (thread
->comm_set
? thread
->comm
: ""),
834 printed
+= snprintf(bf
+ printed
, size
- printed
,
835 ", DSO: %s", dso
->short_name
);
839 static int perf_evsel__hists_browse(struct perf_evsel
*evsel
, int nr_events
,
840 const char *helpline
, const char *ev_name
,
842 void(*timer
)(void *arg
), void *arg
,
845 struct hists
*self
= &evsel
->hists
;
846 struct hist_browser
*browser
= hist_browser__new(self
);
847 struct pstack
*fstack
;
853 fstack
= pstack__new(2);
857 ui_helpline__push(helpline
);
860 const struct thread
*thread
= NULL
;
861 const struct dso
*dso
= NULL
;
863 int nr_options
= 0, choice
= 0, i
,
864 annotate
= -2, zoom_dso
= -2, zoom_thread
= -2,
867 key
= hist_browser__run(browser
, ev_name
, timer
, arg
, delay_secs
);
869 if (browser
->he_selection
!= NULL
) {
870 thread
= hist_browser__selected_thread(browser
);
871 dso
= browser
->selection
->map
? browser
->selection
->map
->dso
: NULL
;
880 * Exit the browser, let hists__browser_tree
881 * go to the next or previous
885 if (!browser
->has_symbols
) {
887 "Annotation is only available for symbolic views, "
888 "include \"sym\" in --sort to use it.");
892 if (browser
->selection
== NULL
||
893 browser
->selection
->sym
== NULL
||
894 browser
->selection
->map
->dso
->annotate_warned
)
904 ui__help_window("h/?/F1 Show this window\n"
906 "PGDN/SPACE Navigate\n"
907 "q/ESC/CTRL+C Exit browser\n\n"
908 "For multiple event sessions:\n\n"
909 "TAB/UNTAB Switch events\n\n"
910 "For symbolic views (--sort has sym):\n\n"
911 "-> Zoom into DSO/Threads & Annotate current symbol\n"
913 "a Annotate current symbol\n"
914 "C Collapse all callchains\n"
915 "E Expand all callchains\n"
916 "d Zoom into current DSO\n"
917 "t Zoom into current Thread\n");
926 if (pstack__empty(fstack
)) {
928 * Go back to the perf_evsel_menu__run or other user
934 top
= pstack__pop(fstack
);
935 if (top
== &browser
->hists
->dso_filter
)
937 if (top
== &browser
->hists
->thread_filter
)
938 goto zoom_out_thread
;
943 !ui__dialog_yesno("Do you really want to exit?"))
953 if (!browser
->has_symbols
)
954 goto add_exit_option
;
956 if (browser
->selection
!= NULL
&&
957 browser
->selection
->sym
!= NULL
&&
958 !browser
->selection
->map
->dso
->annotate_warned
&&
959 asprintf(&options
[nr_options
], "Annotate %s",
960 browser
->selection
->sym
->name
) > 0)
961 annotate
= nr_options
++;
963 if (thread
!= NULL
&&
964 asprintf(&options
[nr_options
], "Zoom %s %s(%d) thread",
965 (browser
->hists
->thread_filter
? "out of" : "into"),
966 (thread
->comm_set
? thread
->comm
: ""),
968 zoom_thread
= nr_options
++;
971 asprintf(&options
[nr_options
], "Zoom %s %s DSO",
972 (browser
->hists
->dso_filter
? "out of" : "into"),
973 (dso
->kernel
? "the Kernel" : dso
->short_name
)) > 0)
974 zoom_dso
= nr_options
++;
976 if (browser
->selection
!= NULL
&&
977 browser
->selection
->map
!= NULL
&&
978 asprintf(&options
[nr_options
], "Browse map details") > 0)
979 browse_map
= nr_options
++;
981 options
[nr_options
++] = (char *)"Exit";
983 choice
= ui__popup_menu(nr_options
, options
);
985 for (i
= 0; i
< nr_options
- 1; ++i
)
988 if (choice
== nr_options
- 1)
994 if (choice
== annotate
) {
995 struct hist_entry
*he
;
997 he
= hist_browser__selected_entry(browser
);
1001 * Don't let this be freed, say, by hists__decay_entry.
1004 hist_entry__tui_annotate(he
, evsel
->idx
, nr_events
,
1005 timer
, arg
, delay_secs
);
1007 ui_browser__update_nr_entries(&browser
->b
, browser
->hists
->nr_entries
);
1008 } else if (choice
== browse_map
)
1009 map__browse(browser
->selection
->map
);
1010 else if (choice
== zoom_dso
) {
1012 if (browser
->hists
->dso_filter
) {
1013 pstack__remove(fstack
, &browser
->hists
->dso_filter
);
1016 browser
->hists
->dso_filter
= NULL
;
1017 sort_dso
.elide
= false;
1021 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1022 dso
->kernel
? "the Kernel" : dso
->short_name
);
1023 browser
->hists
->dso_filter
= dso
;
1024 sort_dso
.elide
= true;
1025 pstack__push(fstack
, &browser
->hists
->dso_filter
);
1027 hists__filter_by_dso(self
);
1028 hist_browser__reset(browser
);
1029 } else if (choice
== zoom_thread
) {
1031 if (browser
->hists
->thread_filter
) {
1032 pstack__remove(fstack
, &browser
->hists
->thread_filter
);
1035 browser
->hists
->thread_filter
= NULL
;
1036 sort_thread
.elide
= false;
1038 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1039 thread
->comm_set
? thread
->comm
: "",
1041 browser
->hists
->thread_filter
= thread
;
1042 sort_thread
.elide
= true;
1043 pstack__push(fstack
, &browser
->hists
->thread_filter
);
1045 hists__filter_by_thread(self
);
1046 hist_browser__reset(browser
);
1050 pstack__delete(fstack
);
1052 hist_browser__delete(browser
);
1056 struct perf_evsel_menu
{
1057 struct ui_browser b
;
1058 struct perf_evsel
*selection
;
1061 static void perf_evsel_menu__write(struct ui_browser
*browser
,
1062 void *entry
, int row
)
1064 struct perf_evsel_menu
*menu
= container_of(browser
,
1065 struct perf_evsel_menu
, b
);
1066 struct perf_evsel
*evsel
= list_entry(entry
, struct perf_evsel
, node
);
1067 bool current_entry
= ui_browser__is_current_entry(browser
, row
);
1068 unsigned long nr_events
= evsel
->hists
.stats
.nr_events
[PERF_RECORD_SAMPLE
];
1069 const char *ev_name
= event_name(evsel
);
1072 ui_browser__set_color(browser
, current_entry
? HE_COLORSET_SELECTED
:
1073 HE_COLORSET_NORMAL
);
1075 nr_events
= convert_unit(nr_events
, &unit
);
1076 snprintf(bf
, sizeof(bf
), "%lu%c%s%s", nr_events
,
1077 unit
, unit
== ' ' ? "" : " ", ev_name
);
1078 slsmg_write_nstring(bf
, browser
->width
);
1081 menu
->selection
= evsel
;
1084 static int perf_evsel_menu__run(struct perf_evsel_menu
*menu
,
1085 int nr_events
, const char *help
,
1086 void(*timer
)(void *arg
), void *arg
, int delay_secs
)
1088 struct perf_evlist
*evlist
= menu
->b
.priv
;
1089 struct perf_evsel
*pos
;
1090 const char *ev_name
, *title
= "Available samples";
1093 if (ui_browser__show(&menu
->b
, title
,
1094 "ESC: exit, ENTER|->: Browse histograms") < 0)
1098 key
= ui_browser__run(&menu
->b
, delay_secs
);
1106 if (!menu
->selection
)
1108 pos
= menu
->selection
;
1110 perf_evlist__set_selected(evlist
, pos
);
1112 * Give the calling tool a chance to populate the non
1113 * default evsel resorted hists tree.
1117 ev_name
= event_name(pos
);
1118 key
= perf_evsel__hists_browse(pos
, nr_events
, help
,
1119 ev_name
, true, timer
,
1121 ui_browser__show_title(&menu
->b
, title
);
1124 if (pos
->node
.next
== &evlist
->entries
)
1125 pos
= list_entry(evlist
->entries
.next
, struct perf_evsel
, node
);
1127 pos
= list_entry(pos
->node
.next
, struct perf_evsel
, node
);
1130 if (pos
->node
.prev
== &evlist
->entries
)
1131 pos
= list_entry(evlist
->entries
.prev
, struct perf_evsel
, node
);
1133 pos
= list_entry(pos
->node
.prev
, struct perf_evsel
, node
);
1136 if (!ui__dialog_yesno("Do you really want to exit?"))
1148 if (!ui__dialog_yesno("Do you really want to exit?"))
1160 ui_browser__hide(&menu
->b
);
1164 static int __perf_evlist__tui_browse_hists(struct perf_evlist
*evlist
,
1166 void(*timer
)(void *arg
), void *arg
,
1169 struct perf_evsel
*pos
;
1170 struct perf_evsel_menu menu
= {
1172 .entries
= &evlist
->entries
,
1173 .refresh
= ui_browser__list_head_refresh
,
1174 .seek
= ui_browser__list_head_seek
,
1175 .write
= perf_evsel_menu__write
,
1176 .nr_entries
= evlist
->nr_entries
,
1181 ui_helpline__push("Press ESC to exit");
1183 list_for_each_entry(pos
, &evlist
->entries
, node
) {
1184 const char *ev_name
= event_name(pos
);
1185 size_t line_len
= strlen(ev_name
) + 7;
1187 if (menu
.b
.width
< line_len
)
1188 menu
.b
.width
= line_len
;
1190 * Cache the evsel name, tracepoints have a _high_ cost per
1191 * event_name() call.
1193 if (pos
->name
== NULL
)
1194 pos
->name
= strdup(ev_name
);
1197 return perf_evsel_menu__run(&menu
, evlist
->nr_entries
, help
, timer
,
1201 int perf_evlist__tui_browse_hists(struct perf_evlist
*evlist
, const char *help
,
1202 void(*timer
)(void *arg
), void *arg
,
1206 if (evlist
->nr_entries
== 1) {
1207 struct perf_evsel
*first
= list_entry(evlist
->entries
.next
,
1208 struct perf_evsel
, node
);
1209 const char *ev_name
= event_name(first
);
1210 return perf_evsel__hists_browse(first
, evlist
->nr_entries
, help
,
1211 ev_name
, false, timer
, arg
,
1215 return __perf_evlist__tui_browse_hists(evlist
, help
,
1216 timer
, arg
, delay_secs
);