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;
29 static int hists__browser_title(struct hists *hists, char *bf, size_t size,
32 static void hist_browser__refresh_dimensions(struct hist_browser *browser)
34 /* 3 == +/- toggle symbol before actual hist_entry rendering */
35 browser->b.width = 3 + (hists__sort_list_width(browser->hists) +
39 static void hist_browser__reset(struct hist_browser *browser)
41 browser->b.nr_entries = browser->hists->nr_entries;
42 hist_browser__refresh_dimensions(browser);
43 ui_browser__reset_index(&browser->b);
46 static char tree__folded_sign(bool unfolded)
48 return unfolded ? '-' : '+';
51 static char map_symbol__folded(const struct map_symbol *ms)
53 return ms->has_children ? tree__folded_sign(ms->unfolded) : ' ';
56 static char hist_entry__folded(const struct hist_entry *he)
58 return map_symbol__folded(&he->ms);
61 static char callchain_list__folded(const struct callchain_list *cl)
63 return map_symbol__folded(&cl->ms);
66 static void map_symbol__set_folding(struct map_symbol *ms, bool unfold)
68 ms->unfolded = unfold ? ms->has_children : false;
71 static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
76 for (nd = rb_first(&node->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 *ms)
131 if (!ms->has_children)
134 ms->unfolded = !ms->unfolded;
138 static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
140 struct rb_node *nd = rb_first(&node->rb_root);
142 for (nd = rb_first(&node->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 *node)
163 struct callchain_list *chain;
165 list_for_each_entry(chain, &node->val, list)
166 chain->ms.has_children = !RB_EMPTY_ROOT(&node->rb_root);
168 callchain_node__init_have_children_rb_tree(node);
171 static void callchain__init_have_children(struct rb_root *root)
175 for (nd = rb_first(root); 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 *he)
183 if (!he->init_have_children) {
184 he->ms.has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
185 callchain__init_have_children(&he->sorted_chain);
186 he->init_have_children = true;
190 static bool hist_browser__toggle_fold(struct hist_browser *browser)
192 if (map_symbol__toggle_fold(browser->selection)) {
193 struct hist_entry *he = browser->he_selection;
195 hist_entry__init_have_children(he);
196 browser->hists->nr_entries -= he->nr_rows;
199 he->nr_rows = callchain__count_rows(&he->sorted_chain);
202 browser->hists->nr_entries += he->nr_rows;
203 browser->b.nr_entries = browser->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 *node, bool unfold)
217 for (nd = rb_first(&node->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 *he, bool unfold)
268 hist_entry__init_have_children(he);
269 map_symbol__set_folding(&he->ms, unfold);
271 if (he->ms.has_children) {
272 int n = callchain__set_folding(&he->sorted_chain, unfold);
273 he->nr_rows = unfold ? n : 0;
278 static void hists__set_folding(struct hists *hists, bool unfold)
282 hists->nr_entries = 0;
284 for (nd = rb_first(&hists->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 hists->nr_entries += 1 + he->nr_rows;
291 static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
293 hists__set_folding(browser->hists, unfold);
294 browser->b.nr_entries = browser->hists->nr_entries;
295 /* Go to the start, we may be way after valid entries after a collapse */
296 ui_browser__reset_index(&browser->b);
299 static void ui_browser__warn_lost_events(struct ui_browser *browser)
301 ui_browser__warning(browser, 4,
302 "Events are being lost, check IO/CPU overload!\n\n"
303 "You may want to run 'perf' using a RT scheduler policy:\n\n"
304 " perf top -r 80\n\n"
305 "Or reduce the sampling frequency.");
308 static int hist_browser__run(struct hist_browser *browser, const char *ev_name,
309 void(*timer)(void *arg), void *arg, int delay_secs)
314 browser->b.entries = &browser->hists->entries;
315 browser->b.nr_entries = browser->hists->nr_entries;
317 hist_browser__refresh_dimensions(browser);
318 hists__browser_title(browser->hists, title, sizeof(title), ev_name);
320 if (ui_browser__show(&browser->b, title,
321 "Press '?' for help on key bindings") < 0)
325 key = ui_browser__run(&browser->b, delay_secs);
330 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
332 if (browser->hists->stats.nr_lost_warned !=
333 browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
334 browser->hists->stats.nr_lost_warned =
335 browser->hists->stats.nr_events[PERF_RECORD_LOST];
336 ui_browser__warn_lost_events(&browser->b);
339 hists__browser_title(browser->hists, title, sizeof(title), ev_name);
340 ui_browser__show_title(&browser->b, title);
342 case 'D': { /* Debug */
344 struct hist_entry *h = rb_entry(browser->b.top,
345 struct hist_entry, rb_node);
347 ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
348 seq++, browser->b.nr_entries,
349 browser->hists->nr_entries,
353 h->row_offset, h->nr_rows);
357 /* Collapse the whole world. */
358 hist_browser__set_folding(browser, false);
361 /* Expand the whole world. */
362 hist_browser__set_folding(browser, true);
365 if (hist_browser__toggle_fold(browser))
373 ui_browser__hide(&browser->b);
377 static char *callchain_list__sym_name(struct callchain_list *cl,
378 char *bf, size_t bfsize)
381 return cl->ms.sym->name;
383 snprintf(bf, bfsize, "%#" PRIx64, cl->ip);
387 #define LEVEL_OFFSET_STEP 3
389 static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browser,
390 struct callchain_node *chain_node,
391 u64 total, int level,
394 bool *is_current_entry)
396 struct rb_node *node;
397 int first_row = row, width, offset = level * LEVEL_OFFSET_STEP;
398 u64 new_total, remaining;
400 if (callchain_param.mode == CHAIN_GRAPH_REL)
401 new_total = chain_node->children_hit;
405 remaining = new_total;
406 node = rb_first(&chain_node->rb_root);
408 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
409 struct rb_node *next = rb_next(node);
410 u64 cumul = callchain_cumul_hits(child);
411 struct callchain_list *chain;
412 char folded_sign = ' ';
414 int extra_offset = 0;
418 list_for_each_entry(chain, &child->val, list) {
419 char ipstr[BITS_PER_LONG / 4 + 1], *alloc_str;
422 bool was_first = first;
427 extra_offset = LEVEL_OFFSET_STEP;
429 folded_sign = callchain_list__folded(chain);
430 if (*row_offset != 0) {
436 str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
438 double percent = cumul * 100.0 / new_total;
440 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
441 str = "Not enough memory!";
446 color = HE_COLORSET_NORMAL;
447 width = browser->b.width - (offset + extra_offset + 2);
448 if (ui_browser__is_current_entry(&browser->b, row)) {
449 browser->selection = &chain->ms;
450 color = HE_COLORSET_SELECTED;
451 *is_current_entry = true;
454 ui_browser__set_color(&browser->b, color);
455 ui_browser__gotorc(&browser->b, row, 0);
456 slsmg_write_nstring(" ", offset + extra_offset);
457 slsmg_printf("%c ", folded_sign);
458 slsmg_write_nstring(str, width);
461 if (++row == browser->b.height)
464 if (folded_sign == '+')
468 if (folded_sign == '-') {
469 const int new_level = level + (extra_offset ? 2 : 1);
470 row += hist_browser__show_callchain_node_rb_tree(browser, child, new_total,
471 new_level, row, row_offset,
474 if (row == browser->b.height)
479 return row - first_row;
482 static int hist_browser__show_callchain_node(struct hist_browser *browser,
483 struct callchain_node *node,
484 int level, unsigned short row,
486 bool *is_current_entry)
488 struct callchain_list *chain;
490 offset = level * LEVEL_OFFSET_STEP,
491 width = browser->b.width - offset;
492 char folded_sign = ' ';
494 list_for_each_entry(chain, &node->val, list) {
495 char ipstr[BITS_PER_LONG / 4 + 1], *s;
498 folded_sign = callchain_list__folded(chain);
500 if (*row_offset != 0) {
505 color = HE_COLORSET_NORMAL;
506 if (ui_browser__is_current_entry(&browser->b, row)) {
507 browser->selection = &chain->ms;
508 color = HE_COLORSET_SELECTED;
509 *is_current_entry = true;
512 s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
513 ui_browser__gotorc(&browser->b, row, 0);
514 ui_browser__set_color(&browser->b, color);
515 slsmg_write_nstring(" ", offset);
516 slsmg_printf("%c ", folded_sign);
517 slsmg_write_nstring(s, width - 2);
519 if (++row == browser->b.height)
523 if (folded_sign == '-')
524 row += hist_browser__show_callchain_node_rb_tree(browser, node,
525 browser->hists->stats.total_period,
530 return row - first_row;
533 static int hist_browser__show_callchain(struct hist_browser *browser,
534 struct rb_root *chain,
535 int level, unsigned short row,
537 bool *is_current_entry)
542 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
543 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
545 row += hist_browser__show_callchain_node(browser, node, level,
548 if (row == browser->b.height)
552 return row - first_row;
555 static int hist_browser__show_entry(struct hist_browser *browser,
556 struct hist_entry *entry,
562 int width = browser->b.width - 6; /* The percentage */
563 char folded_sign = ' ';
564 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
565 off_t row_offset = entry->row_offset;
568 browser->he_selection = entry;
569 browser->selection = &entry->ms;
572 if (symbol_conf.use_callchain) {
573 hist_entry__init_have_children(entry);
574 folded_sign = hist_entry__folded(entry);
577 if (row_offset == 0) {
578 hist_entry__snprintf(entry, s, sizeof(s), browser->hists);
579 percent = (entry->period * 100.0) / browser->hists->stats.total_period;
581 ui_browser__set_percent_color(&browser->b, percent, current_entry);
582 ui_browser__gotorc(&browser->b, row, 0);
583 if (symbol_conf.use_callchain) {
584 slsmg_printf("%c ", folded_sign);
588 slsmg_printf(" %5.2f%%", percent);
590 /* The scroll bar isn't being used */
591 if (!browser->b.navkeypressed)
594 if (!current_entry || !browser->b.navkeypressed)
595 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
597 if (symbol_conf.show_nr_samples) {
598 slsmg_printf(" %11u", entry->nr_events);
602 if (symbol_conf.show_total_period) {
603 slsmg_printf(" %12" PRIu64, entry->period);
607 slsmg_write_nstring(s, width);
613 if (folded_sign == '-' && row != browser->b.height) {
614 printed += hist_browser__show_callchain(browser, &entry->sorted_chain,
618 browser->he_selection = entry;
624 static void ui_browser__hists_init_top(struct ui_browser *browser)
626 if (browser->top == NULL) {
627 struct hist_browser *hb;
629 hb = container_of(browser, struct hist_browser, b);
630 browser->top = rb_first(&hb->hists->entries);
634 static unsigned int hist_browser__refresh(struct ui_browser *browser)
638 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
640 ui_browser__hists_init_top(browser);
642 for (nd = browser->top; nd; nd = rb_next(nd)) {
643 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
648 row += hist_browser__show_entry(hb, h, row);
649 if (row == browser->height)
656 static struct rb_node *hists__filter_entries(struct rb_node *nd)
659 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
669 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd)
672 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
682 static void ui_browser__hists_seek(struct ui_browser *browser,
683 off_t offset, int whence)
685 struct hist_entry *h;
689 if (browser->nr_entries == 0)
692 ui_browser__hists_init_top(browser);
696 nd = hists__filter_entries(rb_first(browser->entries));
702 nd = hists__filter_prev_entries(rb_last(browser->entries));
710 * Moves not relative to the first visible entry invalidates its
713 h = rb_entry(browser->top, struct hist_entry, rb_node);
717 * Here we have to check if nd is expanded (+), if it is we can't go
718 * the next top level hist_entry, instead we must compute an offset of
719 * what _not_ to show and not change the first visible entry.
721 * This offset increments when we are going from top to bottom and
722 * decreases when we're going from bottom to top.
724 * As we don't have backpointers to the top level in the callchains
725 * structure, we need to always print the whole hist_entry callchain,
726 * skipping the first ones that are before the first visible entry
727 * and stop when we printed enough lines to fill the screen.
732 h = rb_entry(nd, struct hist_entry, rb_node);
733 if (h->ms.unfolded) {
734 u16 remaining = h->nr_rows - h->row_offset;
735 if (offset > remaining) {
739 h->row_offset += offset;
745 nd = hists__filter_entries(rb_next(nd));
750 } while (offset != 0);
751 } else if (offset < 0) {
753 h = rb_entry(nd, struct hist_entry, rb_node);
754 if (h->ms.unfolded) {
756 if (-offset > h->row_offset) {
757 offset += h->row_offset;
760 h->row_offset += offset;
766 if (-offset > h->nr_rows) {
767 offset += h->nr_rows;
770 h->row_offset = h->nr_rows + offset;
778 nd = hists__filter_prev_entries(rb_prev(nd));
785 * Last unfiltered hist_entry, check if it is
786 * unfolded, if it is then we should have
787 * row_offset at its last entry.
789 h = rb_entry(nd, struct hist_entry, rb_node);
791 h->row_offset = h->nr_rows;
798 h = rb_entry(nd, struct hist_entry, rb_node);
803 static struct hist_browser *hist_browser__new(struct hists *hists)
805 struct hist_browser *browser = zalloc(sizeof(*browser));
808 browser->hists = hists;
809 browser->b.refresh = hist_browser__refresh;
810 browser->b.seek = ui_browser__hists_seek;
811 browser->b.use_navkeypressed = true;
812 if (sort__branch_mode == 1)
813 browser->has_symbols = sort_sym_from.list.next != NULL;
815 browser->has_symbols = sort_sym.list.next != NULL;
821 static void hist_browser__delete(struct hist_browser *browser)
826 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
828 return browser->he_selection;
831 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
833 return browser->he_selection->thread;
836 static int hists__browser_title(struct hists *hists, char *bf, size_t size,
841 const struct dso *dso = hists->dso_filter;
842 const struct thread *thread = hists->thread_filter;
843 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
844 u64 nr_events = hists->stats.total_period;
846 nr_samples = convert_unit(nr_samples, &unit);
847 printed = scnprintf(bf, size,
848 "Samples: %lu%c of event '%s', Event count (approx.): %lu",
849 nr_samples, unit, ev_name, nr_events);
852 if (hists->uid_filter_str)
853 printed += snprintf(bf + printed, size - printed,
854 ", UID: %s", hists->uid_filter_str);
856 printed += scnprintf(bf + printed, size - printed,
858 (thread->comm_set ? thread->comm : ""),
861 printed += scnprintf(bf + printed, size - printed,
862 ", DSO: %s", dso->short_name);
866 static inline void free_popup_options(char **options, int n)
870 for (i = 0; i < n; ++i) {
876 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
877 const char *helpline, const char *ev_name,
879 void(*timer)(void *arg), void *arg,
882 struct hists *hists = &evsel->hists;
883 struct hist_browser *browser = hist_browser__new(hists);
884 struct branch_info *bi;
885 struct pstack *fstack;
894 fstack = pstack__new(2);
898 ui_helpline__push(helpline);
900 memset(options, 0, sizeof(options));
903 const struct thread *thread = NULL;
904 const struct dso *dso = NULL;
906 annotate = -2, zoom_dso = -2, zoom_thread = -2,
907 annotate_f = -2, annotate_t = -2, browse_map = -2;
911 key = hist_browser__run(browser, ev_name, timer, arg, delay_secs);
913 if (browser->he_selection != NULL) {
914 thread = hist_browser__selected_thread(browser);
915 dso = browser->selection->map ? browser->selection->map->dso : NULL;
923 * Exit the browser, let hists__browser_tree
924 * go to the next or previous
928 if (!browser->has_symbols) {
929 ui_browser__warning(&browser->b, delay_secs * 2,
930 "Annotation is only available for symbolic views, "
931 "include \"sym*\" in --sort to use it.");
935 if (browser->selection == NULL ||
936 browser->selection->sym == NULL ||
937 browser->selection->map->dso->annotate_warned)
945 if (ui_browser__input_window("Symbol to show",
946 "Please enter the name of symbol you want to see",
947 buf, "ENTER: OK, ESC: Cancel",
948 delay_secs * 2) == K_ENTER) {
949 hists->symbol_filter_str = *buf ? buf : NULL;
950 hists__filter_by_symbol(hists);
951 hist_browser__reset(browser);
957 ui_browser__help_window(&browser->b,
958 "h/?/F1 Show this window\n"
960 "PGDN/SPACE Navigate\n"
961 "q/ESC/CTRL+C Exit browser\n\n"
962 "For multiple event sessions:\n\n"
963 "TAB/UNTAB Switch events\n\n"
964 "For symbolic views (--sort has sym):\n\n"
965 "-> Zoom into DSO/Threads & Annotate current symbol\n"
967 "a Annotate current symbol\n"
968 "C Collapse all callchains\n"
969 "E Expand all callchains\n"
970 "d Zoom into current DSO\n"
971 "t Zoom into current Thread\n"
972 "/ Filter symbol by name");
981 if (pstack__empty(fstack)) {
983 * Go back to the perf_evsel_menu__run or other user
989 top = pstack__pop(fstack);
990 if (top == &browser->hists->dso_filter)
992 if (top == &browser->hists->thread_filter)
993 goto zoom_out_thread;
998 !ui_browser__dialog_yesno(&browser->b,
999 "Do you really want to exit?"))
1004 goto out_free_stack;
1009 if (!browser->has_symbols)
1010 goto add_exit_option;
1012 if (sort__branch_mode == 1) {
1013 bi = browser->he_selection->branch_info;
1014 if (browser->selection != NULL &&
1016 bi->from.sym != NULL &&
1017 !bi->from.map->dso->annotate_warned &&
1018 asprintf(&options[nr_options], "Annotate %s",
1019 bi->from.sym->name) > 0)
1020 annotate_f = nr_options++;
1022 if (browser->selection != NULL &&
1024 bi->to.sym != NULL &&
1025 !bi->to.map->dso->annotate_warned &&
1026 (bi->to.sym != bi->from.sym ||
1027 bi->to.map->dso != bi->from.map->dso) &&
1028 asprintf(&options[nr_options], "Annotate %s",
1029 bi->to.sym->name) > 0)
1030 annotate_t = nr_options++;
1033 if (browser->selection != NULL &&
1034 browser->selection->sym != NULL &&
1035 !browser->selection->map->dso->annotate_warned &&
1036 asprintf(&options[nr_options], "Annotate %s",
1037 browser->selection->sym->name) > 0)
1038 annotate = nr_options++;
1041 if (thread != NULL &&
1042 asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
1043 (browser->hists->thread_filter ? "out of" : "into"),
1044 (thread->comm_set ? thread->comm : ""),
1046 zoom_thread = nr_options++;
1049 asprintf(&options[nr_options], "Zoom %s %s DSO",
1050 (browser->hists->dso_filter ? "out of" : "into"),
1051 (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
1052 zoom_dso = nr_options++;
1054 if (browser->selection != NULL &&
1055 browser->selection->map != NULL &&
1056 asprintf(&options[nr_options], "Browse map details") > 0)
1057 browse_map = nr_options++;
1059 options[nr_options++] = (char *)"Exit";
1061 choice = ui__popup_menu(nr_options, options);
1063 if (choice == nr_options - 1)
1067 free_popup_options(options, nr_options - 1);
1071 if (choice == annotate || choice == annotate_t || choice == annotate_f) {
1072 struct hist_entry *he;
1075 he = hist_browser__selected_entry(browser);
1080 * we stash the branch_info symbol + map into the
1081 * the ms so we don't have to rewrite all the annotation
1082 * code to use branch_info.
1083 * in branch mode, the ms struct is not used
1085 if (choice == annotate_f) {
1086 he->ms.sym = he->branch_info->from.sym;
1087 he->ms.map = he->branch_info->from.map;
1088 } else if (choice == annotate_t) {
1089 he->ms.sym = he->branch_info->to.sym;
1090 he->ms.map = he->branch_info->to.map;
1094 * Don't let this be freed, say, by hists__decay_entry.
1097 err = hist_entry__tui_annotate(he, evsel->idx,
1098 timer, arg, delay_secs);
1101 * offer option to annotate the other branch source or target
1102 * (if they exists) when returning from annotate
1104 if ((err == 'q' || err == CTRL('c'))
1105 && annotate_t != -2 && annotate_f != -2)
1106 goto retry_popup_menu;
1108 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1110 ui_browser__handle_resize(&browser->b);
1112 } else if (choice == browse_map)
1113 map__browse(browser->selection->map);
1114 else if (choice == zoom_dso) {
1116 if (browser->hists->dso_filter) {
1117 pstack__remove(fstack, &browser->hists->dso_filter);
1120 browser->hists->dso_filter = NULL;
1121 sort_dso.elide = false;
1125 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1126 dso->kernel ? "the Kernel" : dso->short_name);
1127 browser->hists->dso_filter = dso;
1128 sort_dso.elide = true;
1129 pstack__push(fstack, &browser->hists->dso_filter);
1131 hists__filter_by_dso(hists);
1132 hist_browser__reset(browser);
1133 } else if (choice == zoom_thread) {
1135 if (browser->hists->thread_filter) {
1136 pstack__remove(fstack, &browser->hists->thread_filter);
1139 browser->hists->thread_filter = NULL;
1140 sort_thread.elide = false;
1142 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1143 thread->comm_set ? thread->comm : "",
1145 browser->hists->thread_filter = thread;
1146 sort_thread.elide = true;
1147 pstack__push(fstack, &browser->hists->thread_filter);
1149 hists__filter_by_thread(hists);
1150 hist_browser__reset(browser);
1154 pstack__delete(fstack);
1156 hist_browser__delete(browser);
1157 free_popup_options(options, nr_options - 1);
1161 struct perf_evsel_menu {
1162 struct ui_browser b;
1163 struct perf_evsel *selection;
1164 bool lost_events, lost_events_warned;
1167 static void perf_evsel_menu__write(struct ui_browser *browser,
1168 void *entry, int row)
1170 struct perf_evsel_menu *menu = container_of(browser,
1171 struct perf_evsel_menu, b);
1172 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1173 bool current_entry = ui_browser__is_current_entry(browser, row);
1174 unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1175 const char *ev_name = event_name(evsel);
1177 const char *warn = " ";
1180 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
1181 HE_COLORSET_NORMAL);
1183 nr_events = convert_unit(nr_events, &unit);
1184 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
1185 unit, unit == ' ' ? "" : " ", ev_name);
1186 slsmg_printf("%s", bf);
1188 nr_events = evsel->hists.stats.nr_events[PERF_RECORD_LOST];
1189 if (nr_events != 0) {
1190 menu->lost_events = true;
1192 ui_browser__set_color(browser, HE_COLORSET_TOP);
1193 nr_events = convert_unit(nr_events, &unit);
1194 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
1195 nr_events, unit, unit == ' ' ? "" : " ");
1199 slsmg_write_nstring(warn, browser->width - printed);
1202 menu->selection = evsel;
1205 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
1206 int nr_events, const char *help,
1207 void(*timer)(void *arg), void *arg, int delay_secs)
1209 struct perf_evlist *evlist = menu->b.priv;
1210 struct perf_evsel *pos;
1211 const char *ev_name, *title = "Available samples";
1214 if (ui_browser__show(&menu->b, title,
1215 "ESC: exit, ENTER|->: Browse histograms") < 0)
1219 key = ui_browser__run(&menu->b, delay_secs);
1225 if (!menu->lost_events_warned && menu->lost_events) {
1226 ui_browser__warn_lost_events(&menu->b);
1227 menu->lost_events_warned = true;
1232 if (!menu->selection)
1234 pos = menu->selection;
1236 perf_evlist__set_selected(evlist, pos);
1238 * Give the calling tool a chance to populate the non
1239 * default evsel resorted hists tree.
1243 ev_name = event_name(pos);
1244 key = perf_evsel__hists_browse(pos, nr_events, help,
1245 ev_name, true, timer,
1247 ui_browser__show_title(&menu->b, title);
1250 if (pos->node.next == &evlist->entries)
1251 pos = list_entry(evlist->entries.next, struct perf_evsel, node);
1253 pos = list_entry(pos->node.next, struct perf_evsel, node);
1256 if (pos->node.prev == &evlist->entries)
1257 pos = list_entry(evlist->entries.prev, struct perf_evsel, node);
1259 pos = list_entry(pos->node.prev, struct perf_evsel, node);
1262 if (!ui_browser__dialog_yesno(&menu->b,
1263 "Do you really want to exit?"))
1275 if (!ui_browser__dialog_yesno(&menu->b,
1276 "Do you really want to exit?"))
1288 ui_browser__hide(&menu->b);
1292 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
1294 void(*timer)(void *arg), void *arg,
1297 struct perf_evsel *pos;
1298 struct perf_evsel_menu menu = {
1300 .entries = &evlist->entries,
1301 .refresh = ui_browser__list_head_refresh,
1302 .seek = ui_browser__list_head_seek,
1303 .write = perf_evsel_menu__write,
1304 .nr_entries = evlist->nr_entries,
1309 ui_helpline__push("Press ESC to exit");
1311 list_for_each_entry(pos, &evlist->entries, node) {
1312 const char *ev_name = event_name(pos);
1313 size_t line_len = strlen(ev_name) + 7;
1315 if (menu.b.width < line_len)
1316 menu.b.width = line_len;
1318 * Cache the evsel name, tracepoints have a _high_ cost per
1319 * event_name() call.
1321 if (pos->name == NULL)
1322 pos->name = strdup(ev_name);
1325 return perf_evsel_menu__run(&menu, evlist->nr_entries, help, timer,
1329 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
1330 void(*timer)(void *arg), void *arg,
1334 if (evlist->nr_entries == 1) {
1335 struct perf_evsel *first = list_entry(evlist->entries.next,
1336 struct perf_evsel, node);
1337 const char *ev_name = event_name(first);
1338 return perf_evsel__hists_browse(first, evlist->nr_entries, help,
1339 ev_name, false, timer, arg,
1343 return __perf_evlist__tui_browse_hists(evlist, help,
1344 timer, arg, delay_secs);