2 #include "../libslang.h"
6 #include <linux/rbtree.h>
8 #include "../../evsel.h"
9 #include "../../evlist.h"
10 #include "../../hist.h"
11 #include "../../pstack.h"
12 #include "../../sort.h"
13 #include "../../util.h"
15 #include "../browser.h"
16 #include "../helpline.h"
24 struct hist_entry
*he_selection
;
25 struct map_symbol
*selection
;
29 static int hists__browser_title(struct hists
*self
, char *bf
, size_t size
,
32 static void hist_browser__refresh_dimensions(struct hist_browser
*self
)
34 /* 3 == +/- toggle symbol before actual hist_entry rendering */
35 self
->b
.width
= 3 + (hists__sort_list_width(self
->hists
) +
39 static void hist_browser__reset(struct hist_browser
*self
)
41 self
->b
.nr_entries
= self
->hists
->nr_entries
;
42 hist_browser__refresh_dimensions(self
);
43 ui_browser__reset_index(&self
->b
);
46 static char tree__folded_sign(bool unfolded
)
48 return unfolded
? '-' : '+';
51 static char map_symbol__folded(const struct map_symbol
*self
)
53 return self
->has_children
? tree__folded_sign(self
->unfolded
) : ' ';
56 static char hist_entry__folded(const struct hist_entry
*self
)
58 return map_symbol__folded(&self
->ms
);
61 static char callchain_list__folded(const struct callchain_list
*self
)
63 return map_symbol__folded(&self
->ms
);
66 static void map_symbol__set_folding(struct map_symbol
*self
, bool unfold
)
68 self
->unfolded
= unfold
? self
->has_children
: false;
71 static int callchain_node__count_rows_rb_tree(struct callchain_node
*self
)
76 for (nd
= rb_first(&self
->rb_root
); nd
; nd
= rb_next(nd
)) {
77 struct callchain_node
*child
= rb_entry(nd
, struct callchain_node
, rb_node
);
78 struct callchain_list
*chain
;
79 char folded_sign
= ' '; /* No children */
81 list_for_each_entry(chain
, &child
->val
, list
) {
83 /* We need this because we may not have children */
84 folded_sign
= callchain_list__folded(chain
);
85 if (folded_sign
== '+')
89 if (folded_sign
== '-') /* Have children and they're unfolded */
90 n
+= callchain_node__count_rows_rb_tree(child
);
96 static int callchain_node__count_rows(struct callchain_node
*node
)
98 struct callchain_list
*chain
;
99 bool unfolded
= false;
102 list_for_each_entry(chain
, &node
->val
, list
) {
104 unfolded
= chain
->ms
.unfolded
;
108 n
+= callchain_node__count_rows_rb_tree(node
);
113 static int callchain__count_rows(struct rb_root
*chain
)
118 for (nd
= rb_first(chain
); nd
; nd
= rb_next(nd
)) {
119 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
120 n
+= callchain_node__count_rows(node
);
126 static bool map_symbol__toggle_fold(struct map_symbol
*self
)
128 if (!self
->has_children
)
131 self
->unfolded
= !self
->unfolded
;
135 static void callchain_node__init_have_children_rb_tree(struct callchain_node
*self
)
137 struct rb_node
*nd
= rb_first(&self
->rb_root
);
139 for (nd
= rb_first(&self
->rb_root
); nd
; nd
= rb_next(nd
)) {
140 struct callchain_node
*child
= rb_entry(nd
, struct callchain_node
, rb_node
);
141 struct callchain_list
*chain
;
144 list_for_each_entry(chain
, &child
->val
, list
) {
147 chain
->ms
.has_children
= chain
->list
.next
!= &child
->val
||
148 !RB_EMPTY_ROOT(&child
->rb_root
);
150 chain
->ms
.has_children
= chain
->list
.next
== &child
->val
&&
151 !RB_EMPTY_ROOT(&child
->rb_root
);
154 callchain_node__init_have_children_rb_tree(child
);
158 static void callchain_node__init_have_children(struct callchain_node
*self
)
160 struct callchain_list
*chain
;
162 list_for_each_entry(chain
, &self
->val
, list
)
163 chain
->ms
.has_children
= !RB_EMPTY_ROOT(&self
->rb_root
);
165 callchain_node__init_have_children_rb_tree(self
);
168 static void callchain__init_have_children(struct rb_root
*self
)
172 for (nd
= rb_first(self
); nd
; nd
= rb_next(nd
)) {
173 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
174 callchain_node__init_have_children(node
);
178 static void hist_entry__init_have_children(struct hist_entry
*self
)
180 if (!self
->init_have_children
) {
181 self
->ms
.has_children
= !RB_EMPTY_ROOT(&self
->sorted_chain
);
182 callchain__init_have_children(&self
->sorted_chain
);
183 self
->init_have_children
= true;
187 static bool hist_browser__toggle_fold(struct hist_browser
*self
)
189 if (map_symbol__toggle_fold(self
->selection
)) {
190 struct hist_entry
*he
= self
->he_selection
;
192 hist_entry__init_have_children(he
);
193 self
->hists
->nr_entries
-= he
->nr_rows
;
196 he
->nr_rows
= callchain__count_rows(&he
->sorted_chain
);
199 self
->hists
->nr_entries
+= he
->nr_rows
;
200 self
->b
.nr_entries
= self
->hists
->nr_entries
;
205 /* If it doesn't have children, no toggling performed */
209 static int callchain_node__set_folding_rb_tree(struct callchain_node
*self
, bool unfold
)
214 for (nd
= rb_first(&self
->rb_root
); nd
; nd
= rb_next(nd
)) {
215 struct callchain_node
*child
= rb_entry(nd
, struct callchain_node
, rb_node
);
216 struct callchain_list
*chain
;
217 bool has_children
= false;
219 list_for_each_entry(chain
, &child
->val
, list
) {
221 map_symbol__set_folding(&chain
->ms
, unfold
);
222 has_children
= chain
->ms
.has_children
;
226 n
+= callchain_node__set_folding_rb_tree(child
, unfold
);
232 static int callchain_node__set_folding(struct callchain_node
*node
, bool unfold
)
234 struct callchain_list
*chain
;
235 bool has_children
= false;
238 list_for_each_entry(chain
, &node
->val
, list
) {
240 map_symbol__set_folding(&chain
->ms
, unfold
);
241 has_children
= chain
->ms
.has_children
;
245 n
+= callchain_node__set_folding_rb_tree(node
, unfold
);
250 static int callchain__set_folding(struct rb_root
*chain
, bool unfold
)
255 for (nd
= rb_first(chain
); nd
; nd
= rb_next(nd
)) {
256 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
257 n
+= callchain_node__set_folding(node
, unfold
);
263 static void hist_entry__set_folding(struct hist_entry
*self
, bool unfold
)
265 hist_entry__init_have_children(self
);
266 map_symbol__set_folding(&self
->ms
, unfold
);
268 if (self
->ms
.has_children
) {
269 int n
= callchain__set_folding(&self
->sorted_chain
, unfold
);
270 self
->nr_rows
= unfold
? n
: 0;
275 static void hists__set_folding(struct hists
*self
, bool unfold
)
279 self
->nr_entries
= 0;
281 for (nd
= rb_first(&self
->entries
); nd
; nd
= rb_next(nd
)) {
282 struct hist_entry
*he
= rb_entry(nd
, struct hist_entry
, rb_node
);
283 hist_entry__set_folding(he
, unfold
);
284 self
->nr_entries
+= 1 + he
->nr_rows
;
288 static void hist_browser__set_folding(struct hist_browser
*self
, bool unfold
)
290 hists__set_folding(self
->hists
, unfold
);
291 self
->b
.nr_entries
= self
->hists
->nr_entries
;
292 /* Go to the start, we may be way after valid entries after a collapse */
293 ui_browser__reset_index(&self
->b
);
296 static void ui_browser__warn_lost_events(struct ui_browser
*browser
)
298 ui_browser__warning(browser
, 4,
299 "Events are being lost, check IO/CPU overload!\n\n"
300 "You may want to run 'perf' using a RT scheduler policy:\n\n"
301 " perf top -r 80\n\n"
302 "Or reduce the sampling frequency.");
305 static int hist_browser__run(struct hist_browser
*self
, const char *ev_name
,
306 void(*timer
)(void *arg
), void *arg
, int delay_secs
)
311 self
->b
.entries
= &self
->hists
->entries
;
312 self
->b
.nr_entries
= self
->hists
->nr_entries
;
314 hist_browser__refresh_dimensions(self
);
315 hists__browser_title(self
->hists
, title
, sizeof(title
), ev_name
);
317 if (ui_browser__show(&self
->b
, title
,
318 "Press '?' for help on key bindings") < 0)
322 key
= ui_browser__run(&self
->b
, delay_secs
);
327 ui_browser__update_nr_entries(&self
->b
, self
->hists
->nr_entries
);
329 if (self
->hists
->stats
.nr_lost_warned
!=
330 self
->hists
->stats
.nr_events
[PERF_RECORD_LOST
]) {
331 self
->hists
->stats
.nr_lost_warned
=
332 self
->hists
->stats
.nr_events
[PERF_RECORD_LOST
];
333 ui_browser__warn_lost_events(&self
->b
);
336 hists__browser_title(self
->hists
, title
, sizeof(title
), ev_name
);
337 ui_browser__show_title(&self
->b
, title
);
339 case 'D': { /* Debug */
341 struct hist_entry
*h
= rb_entry(self
->b
.top
,
342 struct hist_entry
, rb_node
);
344 ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
345 seq
++, self
->b
.nr_entries
,
346 self
->hists
->nr_entries
,
350 h
->row_offset
, h
->nr_rows
);
354 /* Collapse the whole world. */
355 hist_browser__set_folding(self
, false);
358 /* Expand the whole world. */
359 hist_browser__set_folding(self
, true);
362 if (hist_browser__toggle_fold(self
))
370 ui_browser__hide(&self
->b
);
374 static char *callchain_list__sym_name(struct callchain_list
*self
,
375 char *bf
, size_t bfsize
)
378 return self
->ms
.sym
->name
;
380 snprintf(bf
, bfsize
, "%#" PRIx64
, self
->ip
);
384 #define LEVEL_OFFSET_STEP 3
386 static int hist_browser__show_callchain_node_rb_tree(struct hist_browser
*self
,
387 struct callchain_node
*chain_node
,
388 u64 total
, int level
,
391 bool *is_current_entry
)
393 struct rb_node
*node
;
394 int first_row
= row
, width
, offset
= level
* LEVEL_OFFSET_STEP
;
395 u64 new_total
, remaining
;
397 if (callchain_param
.mode
== CHAIN_GRAPH_REL
)
398 new_total
= chain_node
->children_hit
;
402 remaining
= new_total
;
403 node
= rb_first(&chain_node
->rb_root
);
405 struct callchain_node
*child
= rb_entry(node
, struct callchain_node
, rb_node
);
406 struct rb_node
*next
= rb_next(node
);
407 u64 cumul
= callchain_cumul_hits(child
);
408 struct callchain_list
*chain
;
409 char folded_sign
= ' ';
411 int extra_offset
= 0;
415 list_for_each_entry(chain
, &child
->val
, list
) {
416 char ipstr
[BITS_PER_LONG
/ 4 + 1], *alloc_str
;
419 bool was_first
= first
;
424 extra_offset
= LEVEL_OFFSET_STEP
;
426 folded_sign
= callchain_list__folded(chain
);
427 if (*row_offset
!= 0) {
433 str
= callchain_list__sym_name(chain
, ipstr
, sizeof(ipstr
));
435 double percent
= cumul
* 100.0 / new_total
;
437 if (asprintf(&alloc_str
, "%2.2f%% %s", percent
, str
) < 0)
438 str
= "Not enough memory!";
443 color
= HE_COLORSET_NORMAL
;
444 width
= self
->b
.width
- (offset
+ extra_offset
+ 2);
445 if (ui_browser__is_current_entry(&self
->b
, row
)) {
446 self
->selection
= &chain
->ms
;
447 color
= HE_COLORSET_SELECTED
;
448 *is_current_entry
= true;
451 ui_browser__set_color(&self
->b
, color
);
452 ui_browser__gotorc(&self
->b
, row
, 0);
453 slsmg_write_nstring(" ", offset
+ extra_offset
);
454 slsmg_printf("%c ", folded_sign
);
455 slsmg_write_nstring(str
, width
);
458 if (++row
== self
->b
.height
)
461 if (folded_sign
== '+')
465 if (folded_sign
== '-') {
466 const int new_level
= level
+ (extra_offset
? 2 : 1);
467 row
+= hist_browser__show_callchain_node_rb_tree(self
, child
, new_total
,
468 new_level
, row
, row_offset
,
471 if (row
== self
->b
.height
)
476 return row
- first_row
;
479 static int hist_browser__show_callchain_node(struct hist_browser
*self
,
480 struct callchain_node
*node
,
481 int level
, unsigned short row
,
483 bool *is_current_entry
)
485 struct callchain_list
*chain
;
487 offset
= level
* LEVEL_OFFSET_STEP
,
488 width
= self
->b
.width
- offset
;
489 char folded_sign
= ' ';
491 list_for_each_entry(chain
, &node
->val
, list
) {
492 char ipstr
[BITS_PER_LONG
/ 4 + 1], *s
;
495 folded_sign
= callchain_list__folded(chain
);
497 if (*row_offset
!= 0) {
502 color
= HE_COLORSET_NORMAL
;
503 if (ui_browser__is_current_entry(&self
->b
, row
)) {
504 self
->selection
= &chain
->ms
;
505 color
= HE_COLORSET_SELECTED
;
506 *is_current_entry
= true;
509 s
= callchain_list__sym_name(chain
, ipstr
, sizeof(ipstr
));
510 ui_browser__gotorc(&self
->b
, row
, 0);
511 ui_browser__set_color(&self
->b
, color
);
512 slsmg_write_nstring(" ", offset
);
513 slsmg_printf("%c ", folded_sign
);
514 slsmg_write_nstring(s
, width
- 2);
516 if (++row
== self
->b
.height
)
520 if (folded_sign
== '-')
521 row
+= hist_browser__show_callchain_node_rb_tree(self
, node
,
522 self
->hists
->stats
.total_period
,
527 return row
- first_row
;
530 static int hist_browser__show_callchain(struct hist_browser
*self
,
531 struct rb_root
*chain
,
532 int level
, unsigned short row
,
534 bool *is_current_entry
)
539 for (nd
= rb_first(chain
); nd
; nd
= rb_next(nd
)) {
540 struct callchain_node
*node
= rb_entry(nd
, struct callchain_node
, rb_node
);
542 row
+= hist_browser__show_callchain_node(self
, node
, level
,
545 if (row
== self
->b
.height
)
549 return row
- first_row
;
552 static int hist_browser__show_entry(struct hist_browser
*self
,
553 struct hist_entry
*entry
,
559 int width
= self
->b
.width
- 6; /* The percentage */
560 char folded_sign
= ' ';
561 bool current_entry
= ui_browser__is_current_entry(&self
->b
, row
);
562 off_t row_offset
= entry
->row_offset
;
565 self
->he_selection
= entry
;
566 self
->selection
= &entry
->ms
;
569 if (symbol_conf
.use_callchain
) {
570 hist_entry__init_have_children(entry
);
571 folded_sign
= hist_entry__folded(entry
);
574 if (row_offset
== 0) {
575 hist_entry__snprintf(entry
, s
, sizeof(s
), self
->hists
);
576 percent
= (entry
->period
* 100.0) / self
->hists
->stats
.total_period
;
578 ui_browser__set_percent_color(&self
->b
, percent
, current_entry
);
579 ui_browser__gotorc(&self
->b
, row
, 0);
580 if (symbol_conf
.use_callchain
) {
581 slsmg_printf("%c ", folded_sign
);
585 slsmg_printf(" %5.2f%%", percent
);
587 /* The scroll bar isn't being used */
588 if (!self
->b
.navkeypressed
)
591 if (!current_entry
|| !self
->b
.navkeypressed
)
592 ui_browser__set_color(&self
->b
, HE_COLORSET_NORMAL
);
594 if (symbol_conf
.show_nr_samples
) {
595 slsmg_printf(" %11u", entry
->nr_events
);
599 if (symbol_conf
.show_total_period
) {
600 slsmg_printf(" %12" PRIu64
, entry
->period
);
604 slsmg_write_nstring(s
, width
);
610 if (folded_sign
== '-' && row
!= self
->b
.height
) {
611 printed
+= hist_browser__show_callchain(self
, &entry
->sorted_chain
,
615 self
->he_selection
= entry
;
621 static void ui_browser__hists_init_top(struct ui_browser
*browser
)
623 if (browser
->top
== NULL
) {
624 struct hist_browser
*hb
;
626 hb
= container_of(browser
, struct hist_browser
, b
);
627 browser
->top
= rb_first(&hb
->hists
->entries
);
631 static unsigned int hist_browser__refresh(struct ui_browser
*self
)
635 struct hist_browser
*hb
= container_of(self
, struct hist_browser
, b
);
637 ui_browser__hists_init_top(self
);
639 for (nd
= self
->top
; nd
; nd
= rb_next(nd
)) {
640 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
645 row
+= hist_browser__show_entry(hb
, h
, row
);
646 if (row
== self
->height
)
653 static struct rb_node
*hists__filter_entries(struct rb_node
*nd
)
656 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
666 static struct rb_node
*hists__filter_prev_entries(struct rb_node
*nd
)
669 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
679 static void ui_browser__hists_seek(struct ui_browser
*self
,
680 off_t offset
, int whence
)
682 struct hist_entry
*h
;
686 if (self
->nr_entries
== 0)
689 ui_browser__hists_init_top(self
);
693 nd
= hists__filter_entries(rb_first(self
->entries
));
699 nd
= hists__filter_prev_entries(rb_last(self
->entries
));
707 * Moves not relative to the first visible entry invalidates its
710 h
= rb_entry(self
->top
, struct hist_entry
, rb_node
);
714 * Here we have to check if nd is expanded (+), if it is we can't go
715 * the next top level hist_entry, instead we must compute an offset of
716 * what _not_ to show and not change the first visible entry.
718 * This offset increments when we are going from top to bottom and
719 * decreases when we're going from bottom to top.
721 * As we don't have backpointers to the top level in the callchains
722 * structure, we need to always print the whole hist_entry callchain,
723 * skipping the first ones that are before the first visible entry
724 * and stop when we printed enough lines to fill the screen.
729 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
730 if (h
->ms
.unfolded
) {
731 u16 remaining
= h
->nr_rows
- h
->row_offset
;
732 if (offset
> remaining
) {
736 h
->row_offset
+= offset
;
742 nd
= hists__filter_entries(rb_next(nd
));
747 } while (offset
!= 0);
748 } else if (offset
< 0) {
750 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
751 if (h
->ms
.unfolded
) {
753 if (-offset
> h
->row_offset
) {
754 offset
+= h
->row_offset
;
757 h
->row_offset
+= offset
;
763 if (-offset
> h
->nr_rows
) {
764 offset
+= h
->nr_rows
;
767 h
->row_offset
= h
->nr_rows
+ offset
;
775 nd
= hists__filter_prev_entries(rb_prev(nd
));
782 * Last unfiltered hist_entry, check if it is
783 * unfolded, if it is then we should have
784 * row_offset at its last entry.
786 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
788 h
->row_offset
= h
->nr_rows
;
795 h
= rb_entry(nd
, struct hist_entry
, rb_node
);
800 static struct hist_browser
*hist_browser__new(struct hists
*hists
)
802 struct hist_browser
*self
= zalloc(sizeof(*self
));
806 self
->b
.refresh
= hist_browser__refresh
;
807 self
->b
.seek
= ui_browser__hists_seek
;
808 self
->b
.use_navkeypressed
= true;
809 if (sort__branch_mode
== 1)
810 self
->has_symbols
= sort_sym_from
.list
.next
!= NULL
;
812 self
->has_symbols
= sort_sym
.list
.next
!= NULL
;
818 static void hist_browser__delete(struct hist_browser
*self
)
823 static struct hist_entry
*hist_browser__selected_entry(struct hist_browser
*self
)
825 return self
->he_selection
;
828 static struct thread
*hist_browser__selected_thread(struct hist_browser
*self
)
830 return self
->he_selection
->thread
;
833 static int hists__browser_title(struct hists
*self
, char *bf
, size_t size
,
838 const struct dso
*dso
= self
->dso_filter
;
839 const struct thread
*thread
= self
->thread_filter
;
840 unsigned long nr_events
= self
->stats
.nr_events
[PERF_RECORD_SAMPLE
];
842 nr_events
= convert_unit(nr_events
, &unit
);
843 printed
= scnprintf(bf
, size
, "Events: %lu%c %s", nr_events
, unit
, ev_name
);
845 if (self
->uid_filter_str
)
846 printed
+= snprintf(bf
+ printed
, size
- printed
,
847 ", UID: %s", self
->uid_filter_str
);
849 printed
+= scnprintf(bf
+ printed
, size
- printed
,
851 (thread
->comm_set
? thread
->comm
: ""),
854 printed
+= scnprintf(bf
+ printed
, size
- printed
,
855 ", DSO: %s", dso
->short_name
);
859 static inline void free_popup_options(char **options
, int n
)
863 for (i
= 0; i
< n
; ++i
) {
869 static int perf_evsel__hists_browse(struct perf_evsel
*evsel
, int nr_events
,
870 const char *helpline
, const char *ev_name
,
872 void(*timer
)(void *arg
), void *arg
,
875 struct hists
*self
= &evsel
->hists
;
876 struct hist_browser
*browser
= hist_browser__new(self
);
877 struct branch_info
*bi
;
878 struct pstack
*fstack
;
886 fstack
= pstack__new(2);
890 ui_helpline__push(helpline
);
892 memset(options
, 0, sizeof(options
));
895 const struct thread
*thread
= NULL
;
896 const struct dso
*dso
= NULL
;
898 annotate
= -2, zoom_dso
= -2, zoom_thread
= -2,
899 annotate_f
= -2, annotate_t
= -2, browse_map
= -2;
903 key
= hist_browser__run(browser
, ev_name
, timer
, arg
, delay_secs
);
905 if (browser
->he_selection
!= NULL
) {
906 thread
= hist_browser__selected_thread(browser
);
907 dso
= browser
->selection
->map
? browser
->selection
->map
->dso
: NULL
;
915 * Exit the browser, let hists__browser_tree
916 * go to the next or previous
920 if (!browser
->has_symbols
) {
921 ui_browser__warning(&browser
->b
, delay_secs
* 2,
922 "Annotation is only available for symbolic views, "
923 "include \"sym*\" in --sort to use it.");
927 if (browser
->selection
== NULL
||
928 browser
->selection
->sym
== NULL
||
929 browser
->selection
->map
->dso
->annotate_warned
)
939 ui_browser__help_window(&browser
->b
,
940 "h/?/F1 Show this window\n"
942 "PGDN/SPACE Navigate\n"
943 "q/ESC/CTRL+C Exit browser\n\n"
944 "For multiple event sessions:\n\n"
945 "TAB/UNTAB Switch events\n\n"
946 "For symbolic views (--sort has sym):\n\n"
947 "-> Zoom into DSO/Threads & Annotate current symbol\n"
949 "a Annotate current symbol\n"
950 "C Collapse all callchains\n"
951 "E Expand all callchains\n"
952 "d Zoom into current DSO\n"
953 "t Zoom into current Thread");
962 if (pstack__empty(fstack
)) {
964 * Go back to the perf_evsel_menu__run or other user
970 top
= pstack__pop(fstack
);
971 if (top
== &browser
->hists
->dso_filter
)
973 if (top
== &browser
->hists
->thread_filter
)
974 goto zoom_out_thread
;
979 !ui_browser__dialog_yesno(&browser
->b
,
980 "Do you really want to exit?"))
990 if (!browser
->has_symbols
)
991 goto add_exit_option
;
993 if (sort__branch_mode
== 1) {
994 bi
= browser
->he_selection
->branch_info
;
995 if (browser
->selection
!= NULL
&&
997 bi
->from
.sym
!= NULL
&&
998 !bi
->from
.map
->dso
->annotate_warned
&&
999 asprintf(&options
[nr_options
], "Annotate %s",
1000 bi
->from
.sym
->name
) > 0)
1001 annotate_f
= nr_options
++;
1003 if (browser
->selection
!= NULL
&&
1005 bi
->to
.sym
!= NULL
&&
1006 !bi
->to
.map
->dso
->annotate_warned
&&
1007 (bi
->to
.sym
!= bi
->from
.sym
||
1008 bi
->to
.map
->dso
!= bi
->from
.map
->dso
) &&
1009 asprintf(&options
[nr_options
], "Annotate %s",
1010 bi
->to
.sym
->name
) > 0)
1011 annotate_t
= nr_options
++;
1014 if (browser
->selection
!= NULL
&&
1015 browser
->selection
->sym
!= NULL
&&
1016 !browser
->selection
->map
->dso
->annotate_warned
&&
1017 asprintf(&options
[nr_options
], "Annotate %s",
1018 browser
->selection
->sym
->name
) > 0)
1019 annotate
= nr_options
++;
1022 if (thread
!= NULL
&&
1023 asprintf(&options
[nr_options
], "Zoom %s %s(%d) thread",
1024 (browser
->hists
->thread_filter
? "out of" : "into"),
1025 (thread
->comm_set
? thread
->comm
: ""),
1027 zoom_thread
= nr_options
++;
1030 asprintf(&options
[nr_options
], "Zoom %s %s DSO",
1031 (browser
->hists
->dso_filter
? "out of" : "into"),
1032 (dso
->kernel
? "the Kernel" : dso
->short_name
)) > 0)
1033 zoom_dso
= nr_options
++;
1035 if (browser
->selection
!= NULL
&&
1036 browser
->selection
->map
!= NULL
&&
1037 asprintf(&options
[nr_options
], "Browse map details") > 0)
1038 browse_map
= nr_options
++;
1040 options
[nr_options
++] = (char *)"Exit";
1042 choice
= ui__popup_menu(nr_options
, options
);
1044 if (choice
== nr_options
- 1)
1048 free_popup_options(options
, nr_options
- 1);
1052 if (choice
== annotate
|| choice
== annotate_t
|| choice
== annotate_f
) {
1053 struct hist_entry
*he
;
1056 he
= hist_browser__selected_entry(browser
);
1061 * we stash the branch_info symbol + map into the
1062 * the ms so we don't have to rewrite all the annotation
1063 * code to use branch_info.
1064 * in branch mode, the ms struct is not used
1066 if (choice
== annotate_f
) {
1067 he
->ms
.sym
= he
->branch_info
->from
.sym
;
1068 he
->ms
.map
= he
->branch_info
->from
.map
;
1069 } else if (choice
== annotate_t
) {
1070 he
->ms
.sym
= he
->branch_info
->to
.sym
;
1071 he
->ms
.map
= he
->branch_info
->to
.map
;
1075 * Don't let this be freed, say, by hists__decay_entry.
1078 err
= hist_entry__tui_annotate(he
, evsel
->idx
,
1079 timer
, arg
, delay_secs
);
1082 * offer option to annotate the other branch source or target
1083 * (if they exists) when returning from annotate
1085 if ((err
== 'q' || err
== CTRL('c'))
1086 && annotate_t
!= -2 && annotate_f
!= -2)
1087 goto retry_popup_menu
;
1089 ui_browser__update_nr_entries(&browser
->b
, browser
->hists
->nr_entries
);
1091 ui_browser__handle_resize(&browser
->b
);
1093 } else if (choice
== browse_map
)
1094 map__browse(browser
->selection
->map
);
1095 else if (choice
== zoom_dso
) {
1097 if (browser
->hists
->dso_filter
) {
1098 pstack__remove(fstack
, &browser
->hists
->dso_filter
);
1101 browser
->hists
->dso_filter
= NULL
;
1102 sort_dso
.elide
= false;
1106 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1107 dso
->kernel
? "the Kernel" : dso
->short_name
);
1108 browser
->hists
->dso_filter
= dso
;
1109 sort_dso
.elide
= true;
1110 pstack__push(fstack
, &browser
->hists
->dso_filter
);
1112 hists__filter_by_dso(self
);
1113 hist_browser__reset(browser
);
1114 } else if (choice
== zoom_thread
) {
1116 if (browser
->hists
->thread_filter
) {
1117 pstack__remove(fstack
, &browser
->hists
->thread_filter
);
1120 browser
->hists
->thread_filter
= NULL
;
1121 sort_thread
.elide
= false;
1123 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1124 thread
->comm_set
? thread
->comm
: "",
1126 browser
->hists
->thread_filter
= thread
;
1127 sort_thread
.elide
= true;
1128 pstack__push(fstack
, &browser
->hists
->thread_filter
);
1130 hists__filter_by_thread(self
);
1131 hist_browser__reset(browser
);
1135 pstack__delete(fstack
);
1137 hist_browser__delete(browser
);
1138 free_popup_options(options
, nr_options
- 1);
1142 struct perf_evsel_menu
{
1143 struct ui_browser b
;
1144 struct perf_evsel
*selection
;
1145 bool lost_events
, lost_events_warned
;
1148 static void perf_evsel_menu__write(struct ui_browser
*browser
,
1149 void *entry
, int row
)
1151 struct perf_evsel_menu
*menu
= container_of(browser
,
1152 struct perf_evsel_menu
, b
);
1153 struct perf_evsel
*evsel
= list_entry(entry
, struct perf_evsel
, node
);
1154 bool current_entry
= ui_browser__is_current_entry(browser
, row
);
1155 unsigned long nr_events
= evsel
->hists
.stats
.nr_events
[PERF_RECORD_SAMPLE
];
1156 const char *ev_name
= event_name(evsel
);
1158 const char *warn
= " ";
1161 ui_browser__set_color(browser
, current_entry
? HE_COLORSET_SELECTED
:
1162 HE_COLORSET_NORMAL
);
1164 nr_events
= convert_unit(nr_events
, &unit
);
1165 printed
= scnprintf(bf
, sizeof(bf
), "%lu%c%s%s", nr_events
,
1166 unit
, unit
== ' ' ? "" : " ", ev_name
);
1167 slsmg_printf("%s", bf
);
1169 nr_events
= evsel
->hists
.stats
.nr_events
[PERF_RECORD_LOST
];
1170 if (nr_events
!= 0) {
1171 menu
->lost_events
= true;
1173 ui_browser__set_color(browser
, HE_COLORSET_TOP
);
1174 nr_events
= convert_unit(nr_events
, &unit
);
1175 printed
+= scnprintf(bf
, sizeof(bf
), ": %ld%c%schunks LOST!",
1176 nr_events
, unit
, unit
== ' ' ? "" : " ");
1180 slsmg_write_nstring(warn
, browser
->width
- printed
);
1183 menu
->selection
= evsel
;
1186 static int perf_evsel_menu__run(struct perf_evsel_menu
*menu
,
1187 int nr_events
, const char *help
,
1188 void(*timer
)(void *arg
), void *arg
, int delay_secs
)
1190 struct perf_evlist
*evlist
= menu
->b
.priv
;
1191 struct perf_evsel
*pos
;
1192 const char *ev_name
, *title
= "Available samples";
1195 if (ui_browser__show(&menu
->b
, title
,
1196 "ESC: exit, ENTER|->: Browse histograms") < 0)
1200 key
= ui_browser__run(&menu
->b
, delay_secs
);
1206 if (!menu
->lost_events_warned
&& menu
->lost_events
) {
1207 ui_browser__warn_lost_events(&menu
->b
);
1208 menu
->lost_events_warned
= true;
1213 if (!menu
->selection
)
1215 pos
= menu
->selection
;
1217 perf_evlist__set_selected(evlist
, pos
);
1219 * Give the calling tool a chance to populate the non
1220 * default evsel resorted hists tree.
1224 ev_name
= event_name(pos
);
1225 key
= perf_evsel__hists_browse(pos
, nr_events
, help
,
1226 ev_name
, true, timer
,
1228 ui_browser__show_title(&menu
->b
, title
);
1231 if (pos
->node
.next
== &evlist
->entries
)
1232 pos
= list_entry(evlist
->entries
.next
, struct perf_evsel
, node
);
1234 pos
= list_entry(pos
->node
.next
, struct perf_evsel
, node
);
1237 if (pos
->node
.prev
== &evlist
->entries
)
1238 pos
= list_entry(evlist
->entries
.prev
, struct perf_evsel
, node
);
1240 pos
= list_entry(pos
->node
.prev
, struct perf_evsel
, node
);
1243 if (!ui_browser__dialog_yesno(&menu
->b
,
1244 "Do you really want to exit?"))
1256 if (!ui_browser__dialog_yesno(&menu
->b
,
1257 "Do you really want to exit?"))
1269 ui_browser__hide(&menu
->b
);
1273 static int __perf_evlist__tui_browse_hists(struct perf_evlist
*evlist
,
1275 void(*timer
)(void *arg
), void *arg
,
1278 struct perf_evsel
*pos
;
1279 struct perf_evsel_menu menu
= {
1281 .entries
= &evlist
->entries
,
1282 .refresh
= ui_browser__list_head_refresh
,
1283 .seek
= ui_browser__list_head_seek
,
1284 .write
= perf_evsel_menu__write
,
1285 .nr_entries
= evlist
->nr_entries
,
1290 ui_helpline__push("Press ESC to exit");
1292 list_for_each_entry(pos
, &evlist
->entries
, node
) {
1293 const char *ev_name
= event_name(pos
);
1294 size_t line_len
= strlen(ev_name
) + 7;
1296 if (menu
.b
.width
< line_len
)
1297 menu
.b
.width
= line_len
;
1299 * Cache the evsel name, tracepoints have a _high_ cost per
1300 * event_name() call.
1302 if (pos
->name
== NULL
)
1303 pos
->name
= strdup(ev_name
);
1306 return perf_evsel_menu__run(&menu
, evlist
->nr_entries
, help
, timer
,
1310 int perf_evlist__tui_browse_hists(struct perf_evlist
*evlist
, const char *help
,
1311 void(*timer
)(void *arg
), void *arg
,
1315 if (evlist
->nr_entries
== 1) {
1316 struct perf_evsel
*first
= list_entry(evlist
->entries
.next
,
1317 struct perf_evsel
, node
);
1318 const char *ev_name
= event_name(first
);
1319 return perf_evsel__hists_browse(first
, evlist
->nr_entries
, help
,
1320 ev_name
, false, timer
, arg
,
1324 return __perf_evlist__tui_browse_hists(evlist
, help
,
1325 timer
, arg
, delay_secs
);