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
;
27 const struct thread
*thread_filter
;
28 const struct dso
*dso_filter
;
31 static int hists__browser_title(struct hists
*self
, char *bf
, size_t size
,
32 const char *ev_name
, const struct dso
*dso
,
33 const struct thread
*thread
);
35 static void hist_browser__refresh_dimensions(struct hist_browser
*self
)
37 /* 3 == +/- toggle symbol before actual hist_entry rendering */
38 self
->b
.width
= 3 + (hists__sort_list_width(self
->hists
) +
42 static void hist_browser__reset(struct hist_browser
*self
)
44 self
->b
.nr_entries
= self
->hists
->nr_entries
;
45 hist_browser__refresh_dimensions(self
);
46 ui_browser__reset_index(&self
->b
);
49 static char tree__folded_sign(bool unfolded
)
51 return unfolded
? '-' : '+';
54 static char map_symbol__folded(const struct map_symbol
*self
)
56 return self
->has_children
? tree__folded_sign(self
->unfolded
) : ' ';
59 static char hist_entry__folded(const struct hist_entry
*self
)
61 return map_symbol__folded(&self
->ms
);
64 static char callchain_list__folded(const struct callchain_list
*self
)
66 return map_symbol__folded(&self
->ms
);
69 static void map_symbol__set_folding(struct map_symbol
*self
, bool unfold
)
71 self
->unfolded
= unfold
? self
->has_children
: false;
74 static int callchain_node__count_rows_rb_tree(struct callchain_node
*self
)
79 for (nd
= rb_first(&self
->rb_root
); nd
; nd
= rb_next(nd
)) {
80 struct callchain_node
*child
= rb_entry(nd
, struct callchain_node
, rb_node
);
81 struct callchain_list
*chain
;
82 char folded_sign
= ' '; /* No children */
84 list_for_each_entry(chain
, &child
->val
, list
) {
86 /* We need this because we may not have children */
87 folded_sign
= callchain_list__folded(chain
);
88 if (folded_sign
== '+')
92 if (folded_sign
== '-') /* Have children and they're unfolded */
93 n
+= callchain_node__count_rows_rb_tree(child
);
99 static int callchain_node__count_rows(struct callchain_node
*node
)
101 struct callchain_list
*chain
;
102 bool unfolded
= false;
105 list_for_each_entry(chain
, &node
->val
, list
) {
107 unfolded
= chain
->ms
.unfolded
;
111 n
+= callchain_node__count_rows_rb_tree(node
);
116 static int callchain__count_rows(struct rb_root
*chain
)
121 for (nd
= rb_first(chain
); nd
; nd
= rb_next(nd
)) {
122 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
123 n
+= callchain_node__count_rows(node
);
129 static bool map_symbol__toggle_fold(struct map_symbol
*self
)
131 if (!self
->has_children
)
134 self
->unfolded
= !self
->unfolded
;
138 static void callchain_node__init_have_children_rb_tree(struct callchain_node
*self
)
140 struct rb_node
*nd
= rb_first(&self
->rb_root
);
142 for (nd
= rb_first(&self
->rb_root
); nd
; nd
= rb_next(nd
)) {
143 struct callchain_node
*child
= rb_entry(nd
, struct callchain_node
, rb_node
);
144 struct callchain_list
*chain
;
147 list_for_each_entry(chain
, &child
->val
, list
) {
150 chain
->ms
.has_children
= chain
->list
.next
!= &child
->val
||
151 !RB_EMPTY_ROOT(&child
->rb_root
);
153 chain
->ms
.has_children
= chain
->list
.next
== &child
->val
&&
154 !RB_EMPTY_ROOT(&child
->rb_root
);
157 callchain_node__init_have_children_rb_tree(child
);
161 static void callchain_node__init_have_children(struct callchain_node
*self
)
163 struct callchain_list
*chain
;
165 list_for_each_entry(chain
, &self
->val
, list
)
166 chain
->ms
.has_children
= !RB_EMPTY_ROOT(&self
->rb_root
);
168 callchain_node__init_have_children_rb_tree(self
);
171 static void callchain__init_have_children(struct rb_root
*self
)
175 for (nd
= rb_first(self
); nd
; nd
= rb_next(nd
)) {
176 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
177 callchain_node__init_have_children(node
);
181 static void hist_entry__init_have_children(struct hist_entry
*self
)
183 if (!self
->init_have_children
) {
184 self
->ms
.has_children
= !RB_EMPTY_ROOT(&self
->sorted_chain
);
185 callchain__init_have_children(&self
->sorted_chain
);
186 self
->init_have_children
= true;
190 static bool hist_browser__toggle_fold(struct hist_browser
*self
)
192 if (map_symbol__toggle_fold(self
->selection
)) {
193 struct hist_entry
*he
= self
->he_selection
;
195 hist_entry__init_have_children(he
);
196 self
->hists
->nr_entries
-= he
->nr_rows
;
199 he
->nr_rows
= callchain__count_rows(&he
->sorted_chain
);
202 self
->hists
->nr_entries
+= he
->nr_rows
;
203 self
->b
.nr_entries
= self
->hists
->nr_entries
;
208 /* If it doesn't have children, no toggling performed */
212 static int callchain_node__set_folding_rb_tree(struct callchain_node
*self
, bool unfold
)
217 for (nd
= rb_first(&self
->rb_root
); nd
; nd
= rb_next(nd
)) {
218 struct callchain_node
*child
= rb_entry(nd
, struct callchain_node
, rb_node
);
219 struct callchain_list
*chain
;
220 bool has_children
= false;
222 list_for_each_entry(chain
, &child
->val
, list
) {
224 map_symbol__set_folding(&chain
->ms
, unfold
);
225 has_children
= chain
->ms
.has_children
;
229 n
+= callchain_node__set_folding_rb_tree(child
, unfold
);
235 static int callchain_node__set_folding(struct callchain_node
*node
, bool unfold
)
237 struct callchain_list
*chain
;
238 bool has_children
= false;
241 list_for_each_entry(chain
, &node
->val
, list
) {
243 map_symbol__set_folding(&chain
->ms
, unfold
);
244 has_children
= chain
->ms
.has_children
;
248 n
+= callchain_node__set_folding_rb_tree(node
, unfold
);
253 static int callchain__set_folding(struct rb_root
*chain
, bool unfold
)
258 for (nd
= rb_first(chain
); nd
; nd
= rb_next(nd
)) {
259 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
260 n
+= callchain_node__set_folding(node
, unfold
);
266 static void hist_entry__set_folding(struct hist_entry
*self
, bool unfold
)
268 hist_entry__init_have_children(self
);
269 map_symbol__set_folding(&self
->ms
, unfold
);
271 if (self
->ms
.has_children
) {
272 int n
= callchain__set_folding(&self
->sorted_chain
, unfold
);
273 self
->nr_rows
= unfold
? n
: 0;
278 static void hists__set_folding(struct hists
*self
, bool unfold
)
282 self
->nr_entries
= 0;
284 for (nd
= rb_first(&self
->entries
); nd
; nd
= rb_next(nd
)) {
285 struct hist_entry
*he
= rb_entry(nd
, struct hist_entry
, rb_node
);
286 hist_entry__set_folding(he
, unfold
);
287 self
->nr_entries
+= 1 + he
->nr_rows
;
291 static void hist_browser__set_folding(struct hist_browser
*self
, bool unfold
)
293 hists__set_folding(self
->hists
, unfold
);
294 self
->b
.nr_entries
= self
->hists
->nr_entries
;
295 /* Go to the start, we may be way after valid entries after a collapse */
296 ui_browser__reset_index(&self
->b
);
299 static int hist_browser__run(struct hist_browser
*self
, const char *ev_name
,
300 void(*timer
)(void *arg
), void *arg
, int delay_secs
)
303 int delay_msecs
= delay_secs
* 1000;
305 int exit_keys
[] = { 'a', '?', 'h', 'C', 'd', 'D', 'E', 't',
306 NEWT_KEY_ENTER
, NEWT_KEY_RIGHT
, NEWT_KEY_LEFT
,
307 NEWT_KEY_TAB
, NEWT_KEY_UNTAB
, 0, };
309 self
->b
.entries
= &self
->hists
->entries
;
310 self
->b
.nr_entries
= self
->hists
->nr_entries
;
312 hist_browser__refresh_dimensions(self
);
313 hists__browser_title(self
->hists
, title
, sizeof(title
), ev_name
,
314 self
->dso_filter
, self
->thread_filter
);
316 if (ui_browser__show(&self
->b
, title
,
317 "Press '?' for help on key bindings") < 0)
321 newtFormSetTimer(self
->b
.form
, delay_msecs
);
323 ui_browser__add_exit_keys(&self
->b
, exit_keys
);
326 key
= ui_browser__run(&self
->b
);
330 /* FIXME we need to check if it was es.reason == NEWT_EXIT_TIMER */
332 hists__browser_title(self
->hists
, title
, sizeof(title
),
333 ev_name
, self
->dso_filter
,
334 self
->thread_filter
);
335 ui_browser__show_title(&self
->b
, title
);
337 case 'D': { /* Debug */
339 struct hist_entry
*h
= rb_entry(self
->b
.top
,
340 struct hist_entry
, rb_node
);
342 ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
343 seq
++, self
->b
.nr_entries
,
344 self
->hists
->nr_entries
,
348 h
->row_offset
, h
->nr_rows
);
352 /* Collapse the whole world. */
353 hist_browser__set_folding(self
, false);
356 /* Expand the whole world. */
357 hist_browser__set_folding(self
, true);
360 if (hist_browser__toggle_fold(self
))
368 ui_browser__hide(&self
->b
);
372 static char *callchain_list__sym_name(struct callchain_list
*self
,
373 char *bf
, size_t bfsize
)
376 return self
->ms
.sym
->name
;
378 snprintf(bf
, bfsize
, "%#" PRIx64
, self
->ip
);
382 #define LEVEL_OFFSET_STEP 3
384 static int hist_browser__show_callchain_node_rb_tree(struct hist_browser
*self
,
385 struct callchain_node
*chain_node
,
386 u64 total
, int level
,
389 bool *is_current_entry
)
391 struct rb_node
*node
;
392 int first_row
= row
, width
, offset
= level
* LEVEL_OFFSET_STEP
;
393 u64 new_total
, remaining
;
395 if (callchain_param
.mode
== CHAIN_GRAPH_REL
)
396 new_total
= chain_node
->children_hit
;
400 remaining
= new_total
;
401 node
= rb_first(&chain_node
->rb_root
);
403 struct callchain_node
*child
= rb_entry(node
, struct callchain_node
, rb_node
);
404 struct rb_node
*next
= rb_next(node
);
405 u64 cumul
= callchain_cumul_hits(child
);
406 struct callchain_list
*chain
;
407 char folded_sign
= ' ';
409 int extra_offset
= 0;
413 list_for_each_entry(chain
, &child
->val
, list
) {
414 char ipstr
[BITS_PER_LONG
/ 4 + 1], *alloc_str
;
417 bool was_first
= first
;
422 extra_offset
= LEVEL_OFFSET_STEP
;
424 folded_sign
= callchain_list__folded(chain
);
425 if (*row_offset
!= 0) {
431 str
= callchain_list__sym_name(chain
, ipstr
, sizeof(ipstr
));
433 double percent
= cumul
* 100.0 / new_total
;
435 if (asprintf(&alloc_str
, "%2.2f%% %s", percent
, str
) < 0)
436 str
= "Not enough memory!";
441 color
= HE_COLORSET_NORMAL
;
442 width
= self
->b
.width
- (offset
+ extra_offset
+ 2);
443 if (ui_browser__is_current_entry(&self
->b
, row
)) {
444 self
->selection
= &chain
->ms
;
445 color
= HE_COLORSET_SELECTED
;
446 *is_current_entry
= true;
449 ui_browser__set_color(&self
->b
, color
);
450 ui_browser__gotorc(&self
->b
, row
, 0);
451 slsmg_write_nstring(" ", offset
+ extra_offset
);
452 slsmg_printf("%c ", folded_sign
);
453 slsmg_write_nstring(str
, width
);
456 if (++row
== self
->b
.height
)
459 if (folded_sign
== '+')
463 if (folded_sign
== '-') {
464 const int new_level
= level
+ (extra_offset
? 2 : 1);
465 row
+= hist_browser__show_callchain_node_rb_tree(self
, child
, new_total
,
466 new_level
, row
, row_offset
,
469 if (row
== self
->b
.height
)
474 return row
- first_row
;
477 static int hist_browser__show_callchain_node(struct hist_browser
*self
,
478 struct callchain_node
*node
,
479 int level
, unsigned short row
,
481 bool *is_current_entry
)
483 struct callchain_list
*chain
;
485 offset
= level
* LEVEL_OFFSET_STEP
,
486 width
= self
->b
.width
- offset
;
487 char folded_sign
= ' ';
489 list_for_each_entry(chain
, &node
->val
, list
) {
490 char ipstr
[BITS_PER_LONG
/ 4 + 1], *s
;
493 folded_sign
= callchain_list__folded(chain
);
495 if (*row_offset
!= 0) {
500 color
= HE_COLORSET_NORMAL
;
501 if (ui_browser__is_current_entry(&self
->b
, row
)) {
502 self
->selection
= &chain
->ms
;
503 color
= HE_COLORSET_SELECTED
;
504 *is_current_entry
= true;
507 s
= callchain_list__sym_name(chain
, ipstr
, sizeof(ipstr
));
508 ui_browser__gotorc(&self
->b
, row
, 0);
509 ui_browser__set_color(&self
->b
, color
);
510 slsmg_write_nstring(" ", offset
);
511 slsmg_printf("%c ", folded_sign
);
512 slsmg_write_nstring(s
, width
- 2);
514 if (++row
== self
->b
.height
)
518 if (folded_sign
== '-')
519 row
+= hist_browser__show_callchain_node_rb_tree(self
, node
,
520 self
->hists
->stats
.total_period
,
525 return row
- first_row
;
528 static int hist_browser__show_callchain(struct hist_browser
*self
,
529 struct rb_root
*chain
,
530 int level
, unsigned short row
,
532 bool *is_current_entry
)
537 for (nd
= rb_first(chain
); nd
; nd
= rb_next(nd
)) {
538 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
540 row
+= hist_browser__show_callchain_node(self
, node
, level
,
543 if (row
== self
->b
.height
)
547 return row
- first_row
;
550 static int hist_browser__show_entry(struct hist_browser
*self
,
551 struct hist_entry
*entry
,
557 int color
, width
= self
->b
.width
;
558 char folded_sign
= ' ';
559 bool current_entry
= ui_browser__is_current_entry(&self
->b
, row
);
560 off_t row_offset
= entry
->row_offset
;
563 self
->he_selection
= entry
;
564 self
->selection
= &entry
->ms
;
567 if (symbol_conf
.use_callchain
) {
568 hist_entry__init_have_children(entry
);
569 folded_sign
= hist_entry__folded(entry
);
572 if (row_offset
== 0) {
573 hist_entry__snprintf(entry
, s
, sizeof(s
), self
->hists
, NULL
, false,
574 0, false, self
->hists
->stats
.total_period
);
575 percent
= (entry
->period
* 100.0) / self
->hists
->stats
.total_period
;
577 color
= HE_COLORSET_SELECTED
;
578 if (!current_entry
) {
579 if (percent
>= MIN_RED
)
580 color
= HE_COLORSET_TOP
;
581 else if (percent
>= MIN_GREEN
)
582 color
= HE_COLORSET_MEDIUM
;
584 color
= HE_COLORSET_NORMAL
;
587 ui_browser__set_color(&self
->b
, color
);
588 ui_browser__gotorc(&self
->b
, row
, 0);
589 if (symbol_conf
.use_callchain
) {
590 slsmg_printf("%c ", folded_sign
);
593 slsmg_write_nstring(s
, width
);
599 if (folded_sign
== '-' && row
!= self
->b
.height
) {
600 printed
+= hist_browser__show_callchain(self
, &entry
->sorted_chain
,
604 self
->he_selection
= entry
;
610 static unsigned int hist_browser__refresh(struct ui_browser
*self
)
614 struct hist_browser
*hb
= container_of(self
, struct hist_browser
, b
);
616 if (self
->top
== NULL
)
617 self
->top
= rb_first(&hb
->hists
->entries
);
619 for (nd
= self
->top
; nd
; nd
= rb_next(nd
)) {
620 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
625 row
+= hist_browser__show_entry(hb
, h
, row
);
626 if (row
== self
->height
)
633 static struct rb_node
*hists__filter_entries(struct rb_node
*nd
)
636 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
646 static struct rb_node
*hists__filter_prev_entries(struct rb_node
*nd
)
649 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
659 static void ui_browser__hists_seek(struct ui_browser
*self
,
660 off_t offset
, int whence
)
662 struct hist_entry
*h
;
666 if (self
->nr_entries
== 0)
671 nd
= hists__filter_entries(rb_first(self
->entries
));
677 nd
= hists__filter_prev_entries(rb_last(self
->entries
));
685 * Moves not relative to the first visible entry invalidates its
688 h
= rb_entry(self
->top
, struct hist_entry
, rb_node
);
692 * Here we have to check if nd is expanded (+), if it is we can't go
693 * the next top level hist_entry, instead we must compute an offset of
694 * what _not_ to show and not change the first visible entry.
696 * This offset increments when we are going from top to bottom and
697 * decreases when we're going from bottom to top.
699 * As we don't have backpointers to the top level in the callchains
700 * structure, we need to always print the whole hist_entry callchain,
701 * skipping the first ones that are before the first visible entry
702 * and stop when we printed enough lines to fill the screen.
707 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
708 if (h
->ms
.unfolded
) {
709 u16 remaining
= h
->nr_rows
- h
->row_offset
;
710 if (offset
> remaining
) {
714 h
->row_offset
+= offset
;
720 nd
= hists__filter_entries(rb_next(nd
));
725 } while (offset
!= 0);
726 } else if (offset
< 0) {
728 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
729 if (h
->ms
.unfolded
) {
731 if (-offset
> h
->row_offset
) {
732 offset
+= h
->row_offset
;
735 h
->row_offset
+= offset
;
741 if (-offset
> h
->nr_rows
) {
742 offset
+= h
->nr_rows
;
745 h
->row_offset
= h
->nr_rows
+ offset
;
753 nd
= hists__filter_prev_entries(rb_prev(nd
));
760 * Last unfiltered hist_entry, check if it is
761 * unfolded, if it is then we should have
762 * row_offset at its last entry.
764 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
766 h
->row_offset
= h
->nr_rows
;
773 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
778 static struct hist_browser
*hist_browser__new(struct hists
*hists
)
780 struct hist_browser
*self
= zalloc(sizeof(*self
));
784 self
->b
.refresh
= hist_browser__refresh
;
785 self
->b
.seek
= ui_browser__hists_seek
;
791 static void hist_browser__delete(struct hist_browser
*self
)
796 static struct hist_entry
*hist_browser__selected_entry(struct hist_browser
*self
)
798 return self
->he_selection
;
801 static struct thread
*hist_browser__selected_thread(struct hist_browser
*self
)
803 return self
->he_selection
->thread
;
806 static int hists__browser_title(struct hists
*self
, char *bf
, size_t size
,
807 const char *ev_name
, const struct dso
*dso
,
808 const struct thread
*thread
)
812 unsigned long nr_events
= self
->stats
.nr_events
[PERF_RECORD_SAMPLE
];
814 nr_events
= convert_unit(nr_events
, &unit
);
815 printed
= snprintf(bf
, size
, "Events: %lu%c %s", nr_events
, unit
, ev_name
);
818 printed
+= snprintf(bf
+ printed
, size
- printed
,
820 (thread
->comm_set
? thread
->comm
: ""),
823 printed
+= snprintf(bf
+ printed
, size
- printed
,
824 ", DSO: %s", dso
->short_name
);
828 static int perf_evsel__hists_browse(struct perf_evsel
*evsel
,
829 const char *helpline
, const char *ev_name
,
831 void(*timer
)(void *arg
), void *arg
,
834 struct hists
*self
= &evsel
->hists
;
835 struct hist_browser
*browser
= hist_browser__new(self
);
836 struct pstack
*fstack
;
842 fstack
= pstack__new(2);
846 ui_helpline__push(helpline
);
849 const struct thread
*thread
= NULL
;
850 const struct dso
*dso
= NULL
;
852 int nr_options
= 0, choice
= 0, i
,
853 annotate
= -2, zoom_dso
= -2, zoom_thread
= -2,
856 key
= hist_browser__run(browser
, ev_name
, timer
, arg
, delay_secs
);
858 if (browser
->he_selection
!= NULL
) {
859 thread
= hist_browser__selected_thread(browser
);
860 dso
= browser
->selection
->map
? browser
->selection
->map
->dso
: NULL
;
867 * Exit the browser, let hists__browser_tree
868 * go to the next or previous
872 if (browser
->selection
== NULL
||
873 browser
->selection
->sym
== NULL
||
874 browser
->selection
->map
->dso
->annotate_warned
)
884 ui__help_window("-> Zoom into DSO/Threads & Annotate current symbol\n"
886 "a Annotate current symbol\n"
887 "h/?/F1 Show this window\n"
888 "C Collapse all callchains\n"
889 "E Expand all callchains\n"
890 "d Zoom into current DSO\n"
891 "t Zoom into current Thread\n"
892 "TAB/UNTAB Switch events\n"
893 "q/CTRL+C Exit browser");
899 case NEWT_KEY_LEFT
: {
902 if (pstack__empty(fstack
)) {
904 * Go back to the perf_evsel_menu__run or other user
910 top
= pstack__pop(fstack
);
911 if (top
== &browser
->dso_filter
)
913 if (top
== &browser
->thread_filter
)
914 goto zoom_out_thread
;
917 case NEWT_KEY_ESCAPE
:
919 !ui__dialog_yesno("Do you really want to exit?"))
926 if (browser
->selection
!= NULL
&&
927 browser
->selection
->sym
!= NULL
&&
928 !browser
->selection
->map
->dso
->annotate_warned
&&
929 asprintf(&options
[nr_options
], "Annotate %s",
930 browser
->selection
->sym
->name
) > 0)
931 annotate
= nr_options
++;
933 if (thread
!= NULL
&&
934 asprintf(&options
[nr_options
], "Zoom %s %s(%d) thread",
935 (browser
->thread_filter
? "out of" : "into"),
936 (thread
->comm_set
? thread
->comm
: ""),
938 zoom_thread
= nr_options
++;
941 asprintf(&options
[nr_options
], "Zoom %s %s DSO",
942 (browser
->dso_filter
? "out of" : "into"),
943 (dso
->kernel
? "the Kernel" : dso
->short_name
)) > 0)
944 zoom_dso
= nr_options
++;
946 if (browser
->selection
!= NULL
&&
947 browser
->selection
->map
!= NULL
&&
948 asprintf(&options
[nr_options
], "Browse map details") > 0)
949 browse_map
= nr_options
++;
951 options
[nr_options
++] = (char *)"Exit";
953 choice
= ui__popup_menu(nr_options
, options
);
955 for (i
= 0; i
< nr_options
- 1; ++i
)
958 if (choice
== nr_options
- 1)
964 if (choice
== annotate
) {
965 struct hist_entry
*he
;
967 he
= hist_browser__selected_entry(browser
);
971 hist_entry__tui_annotate(he
, evsel
->idx
,
972 timer
, arg
, delay_secs
);
973 } else if (choice
== browse_map
)
974 map__browse(browser
->selection
->map
);
975 else if (choice
== zoom_dso
) {
977 if (browser
->dso_filter
) {
978 pstack__remove(fstack
, &browser
->dso_filter
);
981 browser
->dso_filter
= NULL
;
985 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
986 dso
->kernel
? "the Kernel" : dso
->short_name
);
987 browser
->dso_filter
= dso
;
988 pstack__push(fstack
, &browser
->dso_filter
);
990 hists__filter_by_dso(self
, browser
->dso_filter
);
991 hist_browser__reset(browser
);
992 } else if (choice
== zoom_thread
) {
994 if (browser
->thread_filter
) {
995 pstack__remove(fstack
, &browser
->thread_filter
);
998 browser
->thread_filter
= NULL
;
1000 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1001 thread
->comm_set
? thread
->comm
: "",
1003 browser
->thread_filter
= thread
;
1004 pstack__push(fstack
, &browser
->thread_filter
);
1006 hists__filter_by_thread(self
, browser
->thread_filter
);
1007 hist_browser__reset(browser
);
1011 pstack__delete(fstack
);
1013 hist_browser__delete(browser
);
1017 struct perf_evsel_menu
{
1018 struct ui_browser b
;
1019 struct perf_evsel
*selection
;
1022 static void perf_evsel_menu__write(struct ui_browser
*browser
,
1023 void *entry
, int row
)
1025 struct perf_evsel_menu
*menu
= container_of(browser
,
1026 struct perf_evsel_menu
, b
);
1027 struct perf_evsel
*evsel
= list_entry(entry
, struct perf_evsel
, node
);
1028 bool current_entry
= ui_browser__is_current_entry(browser
, row
);
1029 unsigned long nr_events
= evsel
->hists
.stats
.nr_events
[PERF_RECORD_SAMPLE
];
1030 const char *ev_name
= event_name(evsel
);
1033 ui_browser__set_color(browser
, current_entry
? HE_COLORSET_SELECTED
:
1034 HE_COLORSET_NORMAL
);
1036 nr_events
= convert_unit(nr_events
, &unit
);
1037 snprintf(bf
, sizeof(bf
), "%lu%c%s%s", nr_events
,
1038 unit
, unit
== ' ' ? "" : " ", ev_name
);
1039 slsmg_write_nstring(bf
, browser
->width
);
1042 menu
->selection
= evsel
;
1045 static int perf_evsel_menu__run(struct perf_evsel_menu
*menu
, const char *help
,
1046 void(*timer
)(void *arg
), void *arg
, int delay_secs
)
1048 int exit_keys
[] = { NEWT_KEY_ENTER
, NEWT_KEY_RIGHT
, 0, };
1049 int delay_msecs
= delay_secs
* 1000;
1050 struct perf_evlist
*evlist
= menu
->b
.priv
;
1051 struct perf_evsel
*pos
;
1052 const char *ev_name
, *title
= "Available samples";
1055 if (ui_browser__show(&menu
->b
, title
,
1056 "ESC: exit, ENTER|->: Browse histograms") < 0)
1060 newtFormSetTimer(menu
->b
.form
, delay_msecs
);
1062 ui_browser__add_exit_keys(&menu
->b
, exit_keys
);
1065 key
= ui_browser__run(&menu
->b
);
1069 /* FIXME we need to check if it was es.reason == NEWT_EXIT_TIMER */
1072 case NEWT_KEY_RIGHT
:
1073 case NEWT_KEY_ENTER
:
1074 if (!menu
->selection
)
1076 pos
= menu
->selection
;
1077 perf_evlist__set_selected(evlist
, pos
);
1079 ev_name
= event_name(pos
);
1080 key
= perf_evsel__hists_browse(pos
, help
, ev_name
, true,
1081 timer
, arg
, delay_secs
);
1082 ui_browser__show_title(&menu
->b
, title
);
1086 case NEWT_KEY_ESCAPE
:
1087 if (!ui__dialog_yesno("Do you really want to exit?"))
1096 if (pos
->node
.next
== &evlist
->entries
)
1097 pos
= list_entry(evlist
->entries
.next
, struct perf_evsel
, node
);
1099 pos
= list_entry(pos
->node
.next
, struct perf_evsel
, node
);
1101 case NEWT_KEY_UNTAB
:
1102 if (pos
->node
.prev
== &evlist
->entries
)
1103 pos
= list_entry(evlist
->entries
.prev
, struct perf_evsel
, node
);
1105 pos
= list_entry(pos
->node
.prev
, struct perf_evsel
, node
);
1116 ui_browser__hide(&menu
->b
);
1120 static int __perf_evlist__tui_browse_hists(struct perf_evlist
*evlist
,
1122 void(*timer
)(void *arg
), void *arg
,
1125 struct perf_evsel
*pos
;
1126 struct perf_evsel_menu menu
= {
1128 .entries
= &evlist
->entries
,
1129 .refresh
= ui_browser__list_head_refresh
,
1130 .seek
= ui_browser__list_head_seek
,
1131 .write
= perf_evsel_menu__write
,
1132 .nr_entries
= evlist
->nr_entries
,
1137 ui_helpline__push("Press ESC to exit");
1139 list_for_each_entry(pos
, &evlist
->entries
, node
) {
1140 const char *ev_name
= event_name(pos
);
1141 size_t line_len
= strlen(ev_name
) + 7;
1143 if (menu
.b
.width
< line_len
)
1144 menu
.b
.width
= line_len
;
1146 * Cache the evsel name, tracepoints have a _high_ cost per
1147 * event_name() call.
1149 if (pos
->name
== NULL
)
1150 pos
->name
= strdup(ev_name
);
1153 return perf_evsel_menu__run(&menu
, help
, timer
, arg
, delay_secs
);
1156 int perf_evlist__tui_browse_hists(struct perf_evlist
*evlist
, const char *help
,
1157 void(*timer
)(void *arg
), void *arg
,
1161 if (evlist
->nr_entries
== 1) {
1162 struct perf_evsel
*first
= list_entry(evlist
->entries
.next
,
1163 struct perf_evsel
, node
);
1164 const char *ev_name
= event_name(first
);
1165 return perf_evsel__hists_browse(first
, help
, ev_name
, false,
1166 timer
, arg
, delay_secs
);
1169 return __perf_evlist__tui_browse_hists(evlist
, help
,
1170 timer
, arg
, delay_secs
);