Merge remote-tracking branches 'asoc/fix/tlv320aic3x' and 'asoc/fix/wm8962' into...
[linux-drm-fsl-dcu.git] / tools / perf / ui / browsers / hists.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <linux/rbtree.h>
5
6 #include "../../util/evsel.h"
7 #include "../../util/evlist.h"
8 #include "../../util/hist.h"
9 #include "../../util/pstack.h"
10 #include "../../util/sort.h"
11 #include "../../util/util.h"
12 #include "../../util/top.h"
13 #include "../../arch/common.h"
14
15 #include "../browser.h"
16 #include "../helpline.h"
17 #include "../util.h"
18 #include "../ui.h"
19 #include "map.h"
20 #include "annotate.h"
21
22 struct hist_browser {
23         struct ui_browser   b;
24         struct hists        *hists;
25         struct hist_entry   *he_selection;
26         struct map_symbol   *selection;
27         struct hist_browser_timer *hbt;
28         struct pstack       *pstack;
29         struct perf_env *env;
30         int                  print_seq;
31         bool                 show_dso;
32         bool                 show_headers;
33         float                min_pcnt;
34         u64                  nr_non_filtered_entries;
35         u64                  nr_callchain_rows;
36 };
37
38 extern void hist_browser__init_hpp(void);
39
40 static int hists__browser_title(struct hists *hists,
41                                 struct hist_browser_timer *hbt,
42                                 char *bf, size_t size);
43 static void hist_browser__update_nr_entries(struct hist_browser *hb);
44
45 static struct rb_node *hists__filter_entries(struct rb_node *nd,
46                                              float min_pcnt);
47
48 static bool hist_browser__has_filter(struct hist_browser *hb)
49 {
50         return hists__has_filter(hb->hists) || hb->min_pcnt || symbol_conf.has_filter;
51 }
52
53 static int hist_browser__get_folding(struct hist_browser *browser)
54 {
55         struct rb_node *nd;
56         struct hists *hists = browser->hists;
57         int unfolded_rows = 0;
58
59         for (nd = rb_first(&hists->entries);
60              (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
61              nd = rb_next(nd)) {
62                 struct hist_entry *he =
63                         rb_entry(nd, struct hist_entry, rb_node);
64
65                 if (he->unfolded)
66                         unfolded_rows += he->nr_rows;
67         }
68         return unfolded_rows;
69 }
70
71 static u32 hist_browser__nr_entries(struct hist_browser *hb)
72 {
73         u32 nr_entries;
74
75         if (hist_browser__has_filter(hb))
76                 nr_entries = hb->nr_non_filtered_entries;
77         else
78                 nr_entries = hb->hists->nr_entries;
79
80         hb->nr_callchain_rows = hist_browser__get_folding(hb);
81         return nr_entries + hb->nr_callchain_rows;
82 }
83
84 static void hist_browser__update_rows(struct hist_browser *hb)
85 {
86         struct ui_browser *browser = &hb->b;
87         u16 header_offset = hb->show_headers ? 1 : 0, index_row;
88
89         browser->rows = browser->height - header_offset;
90         /*
91          * Verify if we were at the last line and that line isn't
92          * visibe because we now show the header line(s).
93          */
94         index_row = browser->index - browser->top_idx;
95         if (index_row >= browser->rows)
96                 browser->index -= index_row - browser->rows + 1;
97 }
98
99 static void hist_browser__refresh_dimensions(struct ui_browser *browser)
100 {
101         struct hist_browser *hb = container_of(browser, struct hist_browser, b);
102
103         /* 3 == +/- toggle symbol before actual hist_entry rendering */
104         browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]"));
105         /*
106          * FIXME: Just keeping existing behaviour, but this really should be
107          *        before updating browser->width, as it will invalidate the
108          *        calculation above. Fix this and the fallout in another
109          *        changeset.
110          */
111         ui_browser__refresh_dimensions(browser);
112         hist_browser__update_rows(hb);
113 }
114
115 static void hist_browser__gotorc(struct hist_browser *browser, int row, int column)
116 {
117         u16 header_offset = browser->show_headers ? 1 : 0;
118
119         ui_browser__gotorc(&browser->b, row + header_offset, column);
120 }
121
122 static void hist_browser__reset(struct hist_browser *browser)
123 {
124         /*
125          * The hists__remove_entry_filter() already folds non-filtered
126          * entries so we can assume it has 0 callchain rows.
127          */
128         browser->nr_callchain_rows = 0;
129
130         hist_browser__update_nr_entries(browser);
131         browser->b.nr_entries = hist_browser__nr_entries(browser);
132         hist_browser__refresh_dimensions(&browser->b);
133         ui_browser__reset_index(&browser->b);
134 }
135
136 static char tree__folded_sign(bool unfolded)
137 {
138         return unfolded ? '-' : '+';
139 }
140
141 static char hist_entry__folded(const struct hist_entry *he)
142 {
143         return he->has_children ? tree__folded_sign(he->unfolded) : ' ';
144 }
145
146 static char callchain_list__folded(const struct callchain_list *cl)
147 {
148         return cl->has_children ? tree__folded_sign(cl->unfolded) : ' ';
149 }
150
151 static void callchain_list__set_folding(struct callchain_list *cl, bool unfold)
152 {
153         cl->unfolded = unfold ? cl->has_children : false;
154 }
155
156 static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
157 {
158         int n = 0;
159         struct rb_node *nd;
160
161         for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
162                 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
163                 struct callchain_list *chain;
164                 char folded_sign = ' '; /* No children */
165
166                 list_for_each_entry(chain, &child->val, list) {
167                         ++n;
168                         /* We need this because we may not have children */
169                         folded_sign = callchain_list__folded(chain);
170                         if (folded_sign == '+')
171                                 break;
172                 }
173
174                 if (folded_sign == '-') /* Have children and they're unfolded */
175                         n += callchain_node__count_rows_rb_tree(child);
176         }
177
178         return n;
179 }
180
181 static int callchain_node__count_rows(struct callchain_node *node)
182 {
183         struct callchain_list *chain;
184         bool unfolded = false;
185         int n = 0;
186
187         list_for_each_entry(chain, &node->val, list) {
188                 ++n;
189                 unfolded = chain->unfolded;
190         }
191
192         if (unfolded)
193                 n += callchain_node__count_rows_rb_tree(node);
194
195         return n;
196 }
197
198 static int callchain__count_rows(struct rb_root *chain)
199 {
200         struct rb_node *nd;
201         int n = 0;
202
203         for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
204                 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
205                 n += callchain_node__count_rows(node);
206         }
207
208         return n;
209 }
210
211 static bool hist_entry__toggle_fold(struct hist_entry *he)
212 {
213         if (!he)
214                 return false;
215
216         if (!he->has_children)
217                 return false;
218
219         he->unfolded = !he->unfolded;
220         return true;
221 }
222
223 static bool callchain_list__toggle_fold(struct callchain_list *cl)
224 {
225         if (!cl)
226                 return false;
227
228         if (!cl->has_children)
229                 return false;
230
231         cl->unfolded = !cl->unfolded;
232         return true;
233 }
234
235 static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
236 {
237         struct rb_node *nd = rb_first(&node->rb_root);
238
239         for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
240                 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
241                 struct callchain_list *chain;
242                 bool first = true;
243
244                 list_for_each_entry(chain, &child->val, list) {
245                         if (first) {
246                                 first = false;
247                                 chain->has_children = chain->list.next != &child->val ||
248                                                          !RB_EMPTY_ROOT(&child->rb_root);
249                         } else
250                                 chain->has_children = chain->list.next == &child->val &&
251                                                          !RB_EMPTY_ROOT(&child->rb_root);
252                 }
253
254                 callchain_node__init_have_children_rb_tree(child);
255         }
256 }
257
258 static void callchain_node__init_have_children(struct callchain_node *node,
259                                                bool has_sibling)
260 {
261         struct callchain_list *chain;
262
263         chain = list_entry(node->val.next, struct callchain_list, list);
264         chain->has_children = has_sibling;
265
266         if (!list_empty(&node->val)) {
267                 chain = list_entry(node->val.prev, struct callchain_list, list);
268                 chain->has_children = !RB_EMPTY_ROOT(&node->rb_root);
269         }
270
271         callchain_node__init_have_children_rb_tree(node);
272 }
273
274 static void callchain__init_have_children(struct rb_root *root)
275 {
276         struct rb_node *nd = rb_first(root);
277         bool has_sibling = nd && rb_next(nd);
278
279         for (nd = rb_first(root); nd; nd = rb_next(nd)) {
280                 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
281                 callchain_node__init_have_children(node, has_sibling);
282         }
283 }
284
285 static void hist_entry__init_have_children(struct hist_entry *he)
286 {
287         if (!he->init_have_children) {
288                 he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
289                 callchain__init_have_children(&he->sorted_chain);
290                 he->init_have_children = true;
291         }
292 }
293
294 static bool hist_browser__toggle_fold(struct hist_browser *browser)
295 {
296         struct hist_entry *he = browser->he_selection;
297         struct map_symbol *ms = browser->selection;
298         struct callchain_list *cl = container_of(ms, struct callchain_list, ms);
299         bool has_children;
300
301         if (ms == &he->ms)
302                 has_children = hist_entry__toggle_fold(he);
303         else
304                 has_children = callchain_list__toggle_fold(cl);
305
306         if (has_children) {
307                 hist_entry__init_have_children(he);
308                 browser->b.nr_entries -= he->nr_rows;
309                 browser->nr_callchain_rows -= he->nr_rows;
310
311                 if (he->unfolded)
312                         he->nr_rows = callchain__count_rows(&he->sorted_chain);
313                 else
314                         he->nr_rows = 0;
315
316                 browser->b.nr_entries += he->nr_rows;
317                 browser->nr_callchain_rows += he->nr_rows;
318
319                 return true;
320         }
321
322         /* If it doesn't have children, no toggling performed */
323         return false;
324 }
325
326 static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
327 {
328         int n = 0;
329         struct rb_node *nd;
330
331         for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
332                 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
333                 struct callchain_list *chain;
334                 bool has_children = false;
335
336                 list_for_each_entry(chain, &child->val, list) {
337                         ++n;
338                         callchain_list__set_folding(chain, unfold);
339                         has_children = chain->has_children;
340                 }
341
342                 if (has_children)
343                         n += callchain_node__set_folding_rb_tree(child, unfold);
344         }
345
346         return n;
347 }
348
349 static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
350 {
351         struct callchain_list *chain;
352         bool has_children = false;
353         int n = 0;
354
355         list_for_each_entry(chain, &node->val, list) {
356                 ++n;
357                 callchain_list__set_folding(chain, unfold);
358                 has_children = chain->has_children;
359         }
360
361         if (has_children)
362                 n += callchain_node__set_folding_rb_tree(node, unfold);
363
364         return n;
365 }
366
367 static int callchain__set_folding(struct rb_root *chain, bool unfold)
368 {
369         struct rb_node *nd;
370         int n = 0;
371
372         for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
373                 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
374                 n += callchain_node__set_folding(node, unfold);
375         }
376
377         return n;
378 }
379
380 static void hist_entry__set_folding(struct hist_entry *he, bool unfold)
381 {
382         hist_entry__init_have_children(he);
383         he->unfolded = unfold ? he->has_children : false;
384
385         if (he->has_children) {
386                 int n = callchain__set_folding(&he->sorted_chain, unfold);
387                 he->nr_rows = unfold ? n : 0;
388         } else
389                 he->nr_rows = 0;
390 }
391
392 static void
393 __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
394 {
395         struct rb_node *nd;
396         struct hists *hists = browser->hists;
397
398         for (nd = rb_first(&hists->entries);
399              (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
400              nd = rb_next(nd)) {
401                 struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
402                 hist_entry__set_folding(he, unfold);
403                 browser->nr_callchain_rows += he->nr_rows;
404         }
405 }
406
407 static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
408 {
409         browser->nr_callchain_rows = 0;
410         __hist_browser__set_folding(browser, unfold);
411
412         browser->b.nr_entries = hist_browser__nr_entries(browser);
413         /* Go to the start, we may be way after valid entries after a collapse */
414         ui_browser__reset_index(&browser->b);
415 }
416
417 static void ui_browser__warn_lost_events(struct ui_browser *browser)
418 {
419         ui_browser__warning(browser, 4,
420                 "Events are being lost, check IO/CPU overload!\n\n"
421                 "You may want to run 'perf' using a RT scheduler policy:\n\n"
422                 " perf top -r 80\n\n"
423                 "Or reduce the sampling frequency.");
424 }
425
426 static int hist_browser__run(struct hist_browser *browser, const char *help)
427 {
428         int key;
429         char title[160];
430         struct hist_browser_timer *hbt = browser->hbt;
431         int delay_secs = hbt ? hbt->refresh : 0;
432
433         browser->b.entries = &browser->hists->entries;
434         browser->b.nr_entries = hist_browser__nr_entries(browser);
435
436         hists__browser_title(browser->hists, hbt, title, sizeof(title));
437
438         if (ui_browser__show(&browser->b, title, help) < 0)
439                 return -1;
440
441         while (1) {
442                 key = ui_browser__run(&browser->b, delay_secs);
443
444                 switch (key) {
445                 case K_TIMER: {
446                         u64 nr_entries;
447                         hbt->timer(hbt->arg);
448
449                         if (hist_browser__has_filter(browser))
450                                 hist_browser__update_nr_entries(browser);
451
452                         nr_entries = hist_browser__nr_entries(browser);
453                         ui_browser__update_nr_entries(&browser->b, nr_entries);
454
455                         if (browser->hists->stats.nr_lost_warned !=
456                             browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
457                                 browser->hists->stats.nr_lost_warned =
458                                         browser->hists->stats.nr_events[PERF_RECORD_LOST];
459                                 ui_browser__warn_lost_events(&browser->b);
460                         }
461
462                         hists__browser_title(browser->hists,
463                                              hbt, title, sizeof(title));
464                         ui_browser__show_title(&browser->b, title);
465                         continue;
466                 }
467                 case 'D': { /* Debug */
468                         static int seq;
469                         struct hist_entry *h = rb_entry(browser->b.top,
470                                                         struct hist_entry, rb_node);
471                         ui_helpline__pop();
472                         ui_helpline__fpush("%d: nr_ent=(%d,%d), rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
473                                            seq++, browser->b.nr_entries,
474                                            browser->hists->nr_entries,
475                                            browser->b.rows,
476                                            browser->b.index,
477                                            browser->b.top_idx,
478                                            h->row_offset, h->nr_rows);
479                 }
480                         break;
481                 case 'C':
482                         /* Collapse the whole world. */
483                         hist_browser__set_folding(browser, false);
484                         break;
485                 case 'E':
486                         /* Expand the whole world. */
487                         hist_browser__set_folding(browser, true);
488                         break;
489                 case 'H':
490                         browser->show_headers = !browser->show_headers;
491                         hist_browser__update_rows(browser);
492                         break;
493                 case K_ENTER:
494                         if (hist_browser__toggle_fold(browser))
495                                 break;
496                         /* fall thru */
497                 default:
498                         goto out;
499                 }
500         }
501 out:
502         ui_browser__hide(&browser->b);
503         return key;
504 }
505
506 struct callchain_print_arg {
507         /* for hists browser */
508         off_t   row_offset;
509         bool    is_current_entry;
510
511         /* for file dump */
512         FILE    *fp;
513         int     printed;
514 };
515
516 typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
517                                          struct callchain_list *chain,
518                                          const char *str, int offset,
519                                          unsigned short row,
520                                          struct callchain_print_arg *arg);
521
522 static void hist_browser__show_callchain_entry(struct hist_browser *browser,
523                                                struct callchain_list *chain,
524                                                const char *str, int offset,
525                                                unsigned short row,
526                                                struct callchain_print_arg *arg)
527 {
528         int color, width;
529         char folded_sign = callchain_list__folded(chain);
530         bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
531
532         color = HE_COLORSET_NORMAL;
533         width = browser->b.width - (offset + 2);
534         if (ui_browser__is_current_entry(&browser->b, row)) {
535                 browser->selection = &chain->ms;
536                 color = HE_COLORSET_SELECTED;
537                 arg->is_current_entry = true;
538         }
539
540         ui_browser__set_color(&browser->b, color);
541         hist_browser__gotorc(browser, row, 0);
542         ui_browser__write_nstring(&browser->b, " ", offset);
543         ui_browser__printf(&browser->b, "%c", folded_sign);
544         ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
545         ui_browser__write_nstring(&browser->b, str, width);
546 }
547
548 static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
549                                                   struct callchain_list *chain,
550                                                   const char *str, int offset,
551                                                   unsigned short row __maybe_unused,
552                                                   struct callchain_print_arg *arg)
553 {
554         char folded_sign = callchain_list__folded(chain);
555
556         arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
557                                 folded_sign, str);
558 }
559
560 typedef bool (*check_output_full_fn)(struct hist_browser *browser,
561                                      unsigned short row);
562
563 static bool hist_browser__check_output_full(struct hist_browser *browser,
564                                             unsigned short row)
565 {
566         return browser->b.rows == row;
567 }
568
569 static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
570                                           unsigned short row __maybe_unused)
571 {
572         return false;
573 }
574
575 #define LEVEL_OFFSET_STEP 3
576
577 static int hist_browser__show_callchain(struct hist_browser *browser,
578                                         struct rb_root *root, int level,
579                                         unsigned short row, u64 total,
580                                         print_callchain_entry_fn print,
581                                         struct callchain_print_arg *arg,
582                                         check_output_full_fn is_output_full)
583 {
584         struct rb_node *node;
585         int first_row = row, offset = level * LEVEL_OFFSET_STEP;
586         u64 new_total;
587         bool need_percent;
588
589         node = rb_first(root);
590         need_percent = node && rb_next(node);
591
592         while (node) {
593                 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
594                 struct rb_node *next = rb_next(node);
595                 u64 cumul = callchain_cumul_hits(child);
596                 struct callchain_list *chain;
597                 char folded_sign = ' ';
598                 int first = true;
599                 int extra_offset = 0;
600
601                 list_for_each_entry(chain, &child->val, list) {
602                         char bf[1024], *alloc_str;
603                         const char *str;
604                         bool was_first = first;
605
606                         if (first)
607                                 first = false;
608                         else if (need_percent)
609                                 extra_offset = LEVEL_OFFSET_STEP;
610
611                         folded_sign = callchain_list__folded(chain);
612                         if (arg->row_offset != 0) {
613                                 arg->row_offset--;
614                                 goto do_next;
615                         }
616
617                         alloc_str = NULL;
618                         str = callchain_list__sym_name(chain, bf, sizeof(bf),
619                                                        browser->show_dso);
620
621                         if (was_first && need_percent) {
622                                 double percent = cumul * 100.0 / total;
623
624                                 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
625                                         str = "Not enough memory!";
626                                 else
627                                         str = alloc_str;
628                         }
629
630                         print(browser, chain, str, offset + extra_offset, row, arg);
631
632                         free(alloc_str);
633
634                         if (is_output_full(browser, ++row))
635                                 goto out;
636 do_next:
637                         if (folded_sign == '+')
638                                 break;
639                 }
640
641                 if (folded_sign == '-') {
642                         const int new_level = level + (extra_offset ? 2 : 1);
643
644                         if (callchain_param.mode == CHAIN_GRAPH_REL)
645                                 new_total = child->children_hit;
646                         else
647                                 new_total = total;
648
649                         row += hist_browser__show_callchain(browser, &child->rb_root,
650                                                             new_level, row, new_total,
651                                                             print, arg, is_output_full);
652                 }
653                 if (is_output_full(browser, row))
654                         break;
655                 node = next;
656         }
657 out:
658         return row - first_row;
659 }
660
661 struct hpp_arg {
662         struct ui_browser *b;
663         char folded_sign;
664         bool current_entry;
665 };
666
667 static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
668 {
669         struct hpp_arg *arg = hpp->ptr;
670         int ret, len;
671         va_list args;
672         double percent;
673
674         va_start(args, fmt);
675         len = va_arg(args, int);
676         percent = va_arg(args, double);
677         va_end(args);
678
679         ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
680
681         ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
682         ui_browser__printf(arg->b, "%s", hpp->buf);
683
684         advance_hpp(hpp, ret);
685         return ret;
686 }
687
688 #define __HPP_COLOR_PERCENT_FN(_type, _field)                           \
689 static u64 __hpp_get_##_field(struct hist_entry *he)                    \
690 {                                                                       \
691         return he->stat._field;                                         \
692 }                                                                       \
693                                                                         \
694 static int                                                              \
695 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,               \
696                                 struct perf_hpp *hpp,                   \
697                                 struct hist_entry *he)                  \
698 {                                                                       \
699         return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%",   \
700                         __hpp__slsmg_color_printf, true);               \
701 }
702
703 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field)                       \
704 static u64 __hpp_get_acc_##_field(struct hist_entry *he)                \
705 {                                                                       \
706         return he->stat_acc->_field;                                    \
707 }                                                                       \
708                                                                         \
709 static int                                                              \
710 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,               \
711                                 struct perf_hpp *hpp,                   \
712                                 struct hist_entry *he)                  \
713 {                                                                       \
714         if (!symbol_conf.cumulate_callchain) {                          \
715                 struct hpp_arg *arg = hpp->ptr;                         \
716                 int len = fmt->user_len ?: fmt->len;                    \
717                 int ret = scnprintf(hpp->buf, hpp->size,                \
718                                     "%*s", len, "N/A");                 \
719                 ui_browser__printf(arg->b, "%s", hpp->buf);             \
720                                                                         \
721                 return ret;                                             \
722         }                                                               \
723         return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field,           \
724                         " %*.2f%%", __hpp__slsmg_color_printf, true);   \
725 }
726
727 __HPP_COLOR_PERCENT_FN(overhead, period)
728 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
729 __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
730 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
731 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
732 __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
733
734 #undef __HPP_COLOR_PERCENT_FN
735 #undef __HPP_COLOR_ACC_PERCENT_FN
736
737 void hist_browser__init_hpp(void)
738 {
739         perf_hpp__format[PERF_HPP__OVERHEAD].color =
740                                 hist_browser__hpp_color_overhead;
741         perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
742                                 hist_browser__hpp_color_overhead_sys;
743         perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
744                                 hist_browser__hpp_color_overhead_us;
745         perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
746                                 hist_browser__hpp_color_overhead_guest_sys;
747         perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
748                                 hist_browser__hpp_color_overhead_guest_us;
749         perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
750                                 hist_browser__hpp_color_overhead_acc;
751 }
752
753 static int hist_browser__show_entry(struct hist_browser *browser,
754                                     struct hist_entry *entry,
755                                     unsigned short row)
756 {
757         char s[256];
758         int printed = 0;
759         int width = browser->b.width;
760         char folded_sign = ' ';
761         bool current_entry = ui_browser__is_current_entry(&browser->b, row);
762         off_t row_offset = entry->row_offset;
763         bool first = true;
764         struct perf_hpp_fmt *fmt;
765
766         if (current_entry) {
767                 browser->he_selection = entry;
768                 browser->selection = &entry->ms;
769         }
770
771         if (symbol_conf.use_callchain) {
772                 hist_entry__init_have_children(entry);
773                 folded_sign = hist_entry__folded(entry);
774         }
775
776         if (row_offset == 0) {
777                 struct hpp_arg arg = {
778                         .b              = &browser->b,
779                         .folded_sign    = folded_sign,
780                         .current_entry  = current_entry,
781                 };
782                 struct perf_hpp hpp = {
783                         .buf            = s,
784                         .size           = sizeof(s),
785                         .ptr            = &arg,
786                 };
787
788                 hist_browser__gotorc(browser, row, 0);
789
790                 perf_hpp__for_each_format(fmt) {
791                         if (perf_hpp__should_skip(fmt))
792                                 continue;
793
794                         if (current_entry && browser->b.navkeypressed) {
795                                 ui_browser__set_color(&browser->b,
796                                                       HE_COLORSET_SELECTED);
797                         } else {
798                                 ui_browser__set_color(&browser->b,
799                                                       HE_COLORSET_NORMAL);
800                         }
801
802                         if (first) {
803                                 if (symbol_conf.use_callchain) {
804                                         ui_browser__printf(&browser->b, "%c ", folded_sign);
805                                         width -= 2;
806                                 }
807                                 first = false;
808                         } else {
809                                 ui_browser__printf(&browser->b, "  ");
810                                 width -= 2;
811                         }
812
813                         if (fmt->color) {
814                                 width -= fmt->color(fmt, &hpp, entry);
815                         } else {
816                                 width -= fmt->entry(fmt, &hpp, entry);
817                                 ui_browser__printf(&browser->b, "%s", s);
818                         }
819                 }
820
821                 /* The scroll bar isn't being used */
822                 if (!browser->b.navkeypressed)
823                         width += 1;
824
825                 ui_browser__write_nstring(&browser->b, "", width);
826
827                 ++row;
828                 ++printed;
829         } else
830                 --row_offset;
831
832         if (folded_sign == '-' && row != browser->b.rows) {
833                 u64 total = hists__total_period(entry->hists);
834                 struct callchain_print_arg arg = {
835                         .row_offset = row_offset,
836                         .is_current_entry = current_entry,
837                 };
838
839                 if (callchain_param.mode == CHAIN_GRAPH_REL) {
840                         if (symbol_conf.cumulate_callchain)
841                                 total = entry->stat_acc->period;
842                         else
843                                 total = entry->stat.period;
844                 }
845
846                 printed += hist_browser__show_callchain(browser,
847                                         &entry->sorted_chain, 1, row, total,
848                                         hist_browser__show_callchain_entry, &arg,
849                                         hist_browser__check_output_full);
850
851                 if (arg.is_current_entry)
852                         browser->he_selection = entry;
853         }
854
855         return printed;
856 }
857
858 static int advance_hpp_check(struct perf_hpp *hpp, int inc)
859 {
860         advance_hpp(hpp, inc);
861         return hpp->size <= 0;
862 }
863
864 static int hists__scnprintf_headers(char *buf, size_t size, struct hists *hists)
865 {
866         struct perf_hpp dummy_hpp = {
867                 .buf    = buf,
868                 .size   = size,
869         };
870         struct perf_hpp_fmt *fmt;
871         size_t ret = 0;
872
873         if (symbol_conf.use_callchain) {
874                 ret = scnprintf(buf, size, "  ");
875                 if (advance_hpp_check(&dummy_hpp, ret))
876                         return ret;
877         }
878
879         perf_hpp__for_each_format(fmt) {
880                 if (perf_hpp__should_skip(fmt))
881                         continue;
882
883                 ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists));
884                 if (advance_hpp_check(&dummy_hpp, ret))
885                         break;
886
887                 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
888                 if (advance_hpp_check(&dummy_hpp, ret))
889                         break;
890         }
891
892         return ret;
893 }
894
895 static void hist_browser__show_headers(struct hist_browser *browser)
896 {
897         char headers[1024];
898
899         hists__scnprintf_headers(headers, sizeof(headers), browser->hists);
900         ui_browser__gotorc(&browser->b, 0, 0);
901         ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
902         ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
903 }
904
905 static void ui_browser__hists_init_top(struct ui_browser *browser)
906 {
907         if (browser->top == NULL) {
908                 struct hist_browser *hb;
909
910                 hb = container_of(browser, struct hist_browser, b);
911                 browser->top = rb_first(&hb->hists->entries);
912         }
913 }
914
915 static unsigned int hist_browser__refresh(struct ui_browser *browser)
916 {
917         unsigned row = 0;
918         u16 header_offset = 0;
919         struct rb_node *nd;
920         struct hist_browser *hb = container_of(browser, struct hist_browser, b);
921
922         if (hb->show_headers) {
923                 hist_browser__show_headers(hb);
924                 header_offset = 1;
925         }
926
927         ui_browser__hists_init_top(browser);
928
929         for (nd = browser->top; nd; nd = rb_next(nd)) {
930                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
931                 float percent;
932
933                 if (h->filtered)
934                         continue;
935
936                 percent = hist_entry__get_percent_limit(h);
937                 if (percent < hb->min_pcnt)
938                         continue;
939
940                 row += hist_browser__show_entry(hb, h, row);
941                 if (row == browser->rows)
942                         break;
943         }
944
945         return row + header_offset;
946 }
947
948 static struct rb_node *hists__filter_entries(struct rb_node *nd,
949                                              float min_pcnt)
950 {
951         while (nd != NULL) {
952                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
953                 float percent = hist_entry__get_percent_limit(h);
954
955                 if (!h->filtered && percent >= min_pcnt)
956                         return nd;
957
958                 nd = rb_next(nd);
959         }
960
961         return NULL;
962 }
963
964 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
965                                                   float min_pcnt)
966 {
967         while (nd != NULL) {
968                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
969                 float percent = hist_entry__get_percent_limit(h);
970
971                 if (!h->filtered && percent >= min_pcnt)
972                         return nd;
973
974                 nd = rb_prev(nd);
975         }
976
977         return NULL;
978 }
979
980 static void ui_browser__hists_seek(struct ui_browser *browser,
981                                    off_t offset, int whence)
982 {
983         struct hist_entry *h;
984         struct rb_node *nd;
985         bool first = true;
986         struct hist_browser *hb;
987
988         hb = container_of(browser, struct hist_browser, b);
989
990         if (browser->nr_entries == 0)
991                 return;
992
993         ui_browser__hists_init_top(browser);
994
995         switch (whence) {
996         case SEEK_SET:
997                 nd = hists__filter_entries(rb_first(browser->entries),
998                                            hb->min_pcnt);
999                 break;
1000         case SEEK_CUR:
1001                 nd = browser->top;
1002                 goto do_offset;
1003         case SEEK_END:
1004                 nd = hists__filter_prev_entries(rb_last(browser->entries),
1005                                                 hb->min_pcnt);
1006                 first = false;
1007                 break;
1008         default:
1009                 return;
1010         }
1011
1012         /*
1013          * Moves not relative to the first visible entry invalidates its
1014          * row_offset:
1015          */
1016         h = rb_entry(browser->top, struct hist_entry, rb_node);
1017         h->row_offset = 0;
1018
1019         /*
1020          * Here we have to check if nd is expanded (+), if it is we can't go
1021          * the next top level hist_entry, instead we must compute an offset of
1022          * what _not_ to show and not change the first visible entry.
1023          *
1024          * This offset increments when we are going from top to bottom and
1025          * decreases when we're going from bottom to top.
1026          *
1027          * As we don't have backpointers to the top level in the callchains
1028          * structure, we need to always print the whole hist_entry callchain,
1029          * skipping the first ones that are before the first visible entry
1030          * and stop when we printed enough lines to fill the screen.
1031          */
1032 do_offset:
1033         if (offset > 0) {
1034                 do {
1035                         h = rb_entry(nd, struct hist_entry, rb_node);
1036                         if (h->unfolded) {
1037                                 u16 remaining = h->nr_rows - h->row_offset;
1038                                 if (offset > remaining) {
1039                                         offset -= remaining;
1040                                         h->row_offset = 0;
1041                                 } else {
1042                                         h->row_offset += offset;
1043                                         offset = 0;
1044                                         browser->top = nd;
1045                                         break;
1046                                 }
1047                         }
1048                         nd = hists__filter_entries(rb_next(nd), hb->min_pcnt);
1049                         if (nd == NULL)
1050                                 break;
1051                         --offset;
1052                         browser->top = nd;
1053                 } while (offset != 0);
1054         } else if (offset < 0) {
1055                 while (1) {
1056                         h = rb_entry(nd, struct hist_entry, rb_node);
1057                         if (h->unfolded) {
1058                                 if (first) {
1059                                         if (-offset > h->row_offset) {
1060                                                 offset += h->row_offset;
1061                                                 h->row_offset = 0;
1062                                         } else {
1063                                                 h->row_offset += offset;
1064                                                 offset = 0;
1065                                                 browser->top = nd;
1066                                                 break;
1067                                         }
1068                                 } else {
1069                                         if (-offset > h->nr_rows) {
1070                                                 offset += h->nr_rows;
1071                                                 h->row_offset = 0;
1072                                         } else {
1073                                                 h->row_offset = h->nr_rows + offset;
1074                                                 offset = 0;
1075                                                 browser->top = nd;
1076                                                 break;
1077                                         }
1078                                 }
1079                         }
1080
1081                         nd = hists__filter_prev_entries(rb_prev(nd),
1082                                                         hb->min_pcnt);
1083                         if (nd == NULL)
1084                                 break;
1085                         ++offset;
1086                         browser->top = nd;
1087                         if (offset == 0) {
1088                                 /*
1089                                  * Last unfiltered hist_entry, check if it is
1090                                  * unfolded, if it is then we should have
1091                                  * row_offset at its last entry.
1092                                  */
1093                                 h = rb_entry(nd, struct hist_entry, rb_node);
1094                                 if (h->unfolded)
1095                                         h->row_offset = h->nr_rows;
1096                                 break;
1097                         }
1098                         first = false;
1099                 }
1100         } else {
1101                 browser->top = nd;
1102                 h = rb_entry(nd, struct hist_entry, rb_node);
1103                 h->row_offset = 0;
1104         }
1105 }
1106
1107 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1108                                            struct hist_entry *he, FILE *fp)
1109 {
1110         u64 total = hists__total_period(he->hists);
1111         struct callchain_print_arg arg  = {
1112                 .fp = fp,
1113         };
1114
1115         if (symbol_conf.cumulate_callchain)
1116                 total = he->stat_acc->period;
1117
1118         hist_browser__show_callchain(browser, &he->sorted_chain, 1, 0, total,
1119                                      hist_browser__fprintf_callchain_entry, &arg,
1120                                      hist_browser__check_dump_full);
1121         return arg.printed;
1122 }
1123
1124 static int hist_browser__fprintf_entry(struct hist_browser *browser,
1125                                        struct hist_entry *he, FILE *fp)
1126 {
1127         char s[8192];
1128         int printed = 0;
1129         char folded_sign = ' ';
1130         struct perf_hpp hpp = {
1131                 .buf = s,
1132                 .size = sizeof(s),
1133         };
1134         struct perf_hpp_fmt *fmt;
1135         bool first = true;
1136         int ret;
1137
1138         if (symbol_conf.use_callchain)
1139                 folded_sign = hist_entry__folded(he);
1140
1141         if (symbol_conf.use_callchain)
1142                 printed += fprintf(fp, "%c ", folded_sign);
1143
1144         perf_hpp__for_each_format(fmt) {
1145                 if (perf_hpp__should_skip(fmt))
1146                         continue;
1147
1148                 if (!first) {
1149                         ret = scnprintf(hpp.buf, hpp.size, "  ");
1150                         advance_hpp(&hpp, ret);
1151                 } else
1152                         first = false;
1153
1154                 ret = fmt->entry(fmt, &hpp, he);
1155                 advance_hpp(&hpp, ret);
1156         }
1157         printed += fprintf(fp, "%s\n", rtrim(s));
1158
1159         if (folded_sign == '-')
1160                 printed += hist_browser__fprintf_callchain(browser, he, fp);
1161
1162         return printed;
1163 }
1164
1165 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1166 {
1167         struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
1168                                                    browser->min_pcnt);
1169         int printed = 0;
1170
1171         while (nd) {
1172                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1173
1174                 printed += hist_browser__fprintf_entry(browser, h, fp);
1175                 nd = hists__filter_entries(rb_next(nd), browser->min_pcnt);
1176         }
1177
1178         return printed;
1179 }
1180
1181 static int hist_browser__dump(struct hist_browser *browser)
1182 {
1183         char filename[64];
1184         FILE *fp;
1185
1186         while (1) {
1187                 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
1188                 if (access(filename, F_OK))
1189                         break;
1190                 /*
1191                  * XXX: Just an arbitrary lazy upper limit
1192                  */
1193                 if (++browser->print_seq == 8192) {
1194                         ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1195                         return -1;
1196                 }
1197         }
1198
1199         fp = fopen(filename, "w");
1200         if (fp == NULL) {
1201                 char bf[64];
1202                 const char *err = strerror_r(errno, bf, sizeof(bf));
1203                 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
1204                 return -1;
1205         }
1206
1207         ++browser->print_seq;
1208         hist_browser__fprintf(browser, fp);
1209         fclose(fp);
1210         ui_helpline__fpush("%s written!", filename);
1211
1212         return 0;
1213 }
1214
1215 static struct hist_browser *hist_browser__new(struct hists *hists,
1216                                               struct hist_browser_timer *hbt,
1217                                               struct perf_env *env)
1218 {
1219         struct hist_browser *browser = zalloc(sizeof(*browser));
1220
1221         if (browser) {
1222                 browser->hists = hists;
1223                 browser->b.refresh = hist_browser__refresh;
1224                 browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
1225                 browser->b.seek = ui_browser__hists_seek;
1226                 browser->b.use_navkeypressed = true;
1227                 browser->show_headers = symbol_conf.show_hist_headers;
1228                 browser->hbt = hbt;
1229                 browser->env = env;
1230         }
1231
1232         return browser;
1233 }
1234
1235 static void hist_browser__delete(struct hist_browser *browser)
1236 {
1237         free(browser);
1238 }
1239
1240 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
1241 {
1242         return browser->he_selection;
1243 }
1244
1245 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
1246 {
1247         return browser->he_selection->thread;
1248 }
1249
1250 /* Check whether the browser is for 'top' or 'report' */
1251 static inline bool is_report_browser(void *timer)
1252 {
1253         return timer == NULL;
1254 }
1255
1256 static int hists__browser_title(struct hists *hists,
1257                                 struct hist_browser_timer *hbt,
1258                                 char *bf, size_t size)
1259 {
1260         char unit;
1261         int printed;
1262         const struct dso *dso = hists->dso_filter;
1263         const struct thread *thread = hists->thread_filter;
1264         unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1265         u64 nr_events = hists->stats.total_period;
1266         struct perf_evsel *evsel = hists_to_evsel(hists);
1267         const char *ev_name = perf_evsel__name(evsel);
1268         char buf[512];
1269         size_t buflen = sizeof(buf);
1270         char ref[30] = " show reference callgraph, ";
1271         bool enable_ref = false;
1272
1273         if (symbol_conf.filter_relative) {
1274                 nr_samples = hists->stats.nr_non_filtered_samples;
1275                 nr_events = hists->stats.total_non_filtered_period;
1276         }
1277
1278         if (perf_evsel__is_group_event(evsel)) {
1279                 struct perf_evsel *pos;
1280
1281                 perf_evsel__group_desc(evsel, buf, buflen);
1282                 ev_name = buf;
1283
1284                 for_each_group_member(pos, evsel) {
1285                         struct hists *pos_hists = evsel__hists(pos);
1286
1287                         if (symbol_conf.filter_relative) {
1288                                 nr_samples += pos_hists->stats.nr_non_filtered_samples;
1289                                 nr_events += pos_hists->stats.total_non_filtered_period;
1290                         } else {
1291                                 nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
1292                                 nr_events += pos_hists->stats.total_period;
1293                         }
1294                 }
1295         }
1296
1297         if (symbol_conf.show_ref_callgraph &&
1298             strstr(ev_name, "call-graph=no"))
1299                 enable_ref = true;
1300         nr_samples = convert_unit(nr_samples, &unit);
1301         printed = scnprintf(bf, size,
1302                            "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64,
1303                            nr_samples, unit, ev_name, enable_ref ? ref : " ", nr_events);
1304
1305
1306         if (hists->uid_filter_str)
1307                 printed += snprintf(bf + printed, size - printed,
1308                                     ", UID: %s", hists->uid_filter_str);
1309         if (thread)
1310                 printed += scnprintf(bf + printed, size - printed,
1311                                     ", Thread: %s(%d)",
1312                                      (thread->comm_set ? thread__comm_str(thread) : ""),
1313                                     thread->tid);
1314         if (dso)
1315                 printed += scnprintf(bf + printed, size - printed,
1316                                     ", DSO: %s", dso->short_name);
1317         if (!is_report_browser(hbt)) {
1318                 struct perf_top *top = hbt->arg;
1319
1320                 if (top->zero)
1321                         printed += scnprintf(bf + printed, size - printed, " [z]");
1322         }
1323
1324         return printed;
1325 }
1326
1327 static inline void free_popup_options(char **options, int n)
1328 {
1329         int i;
1330
1331         for (i = 0; i < n; ++i)
1332                 zfree(&options[i]);
1333 }
1334
1335 /*
1336  * Only runtime switching of perf data file will make "input_name" point
1337  * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1338  * whether we need to call free() for current "input_name" during the switch.
1339  */
1340 static bool is_input_name_malloced = false;
1341
1342 static int switch_data_file(void)
1343 {
1344         char *pwd, *options[32], *abs_path[32], *tmp;
1345         DIR *pwd_dir;
1346         int nr_options = 0, choice = -1, ret = -1;
1347         struct dirent *dent;
1348
1349         pwd = getenv("PWD");
1350         if (!pwd)
1351                 return ret;
1352
1353         pwd_dir = opendir(pwd);
1354         if (!pwd_dir)
1355                 return ret;
1356
1357         memset(options, 0, sizeof(options));
1358         memset(options, 0, sizeof(abs_path));
1359
1360         while ((dent = readdir(pwd_dir))) {
1361                 char path[PATH_MAX];
1362                 u64 magic;
1363                 char *name = dent->d_name;
1364                 FILE *file;
1365
1366                 if (!(dent->d_type == DT_REG))
1367                         continue;
1368
1369                 snprintf(path, sizeof(path), "%s/%s", pwd, name);
1370
1371                 file = fopen(path, "r");
1372                 if (!file)
1373                         continue;
1374
1375                 if (fread(&magic, 1, 8, file) < 8)
1376                         goto close_file_and_continue;
1377
1378                 if (is_perf_magic(magic)) {
1379                         options[nr_options] = strdup(name);
1380                         if (!options[nr_options])
1381                                 goto close_file_and_continue;
1382
1383                         abs_path[nr_options] = strdup(path);
1384                         if (!abs_path[nr_options]) {
1385                                 zfree(&options[nr_options]);
1386                                 ui__warning("Can't search all data files due to memory shortage.\n");
1387                                 fclose(file);
1388                                 break;
1389                         }
1390
1391                         nr_options++;
1392                 }
1393
1394 close_file_and_continue:
1395                 fclose(file);
1396                 if (nr_options >= 32) {
1397                         ui__warning("Too many perf data files in PWD!\n"
1398                                     "Only the first 32 files will be listed.\n");
1399                         break;
1400                 }
1401         }
1402         closedir(pwd_dir);
1403
1404         if (nr_options) {
1405                 choice = ui__popup_menu(nr_options, options);
1406                 if (choice < nr_options && choice >= 0) {
1407                         tmp = strdup(abs_path[choice]);
1408                         if (tmp) {
1409                                 if (is_input_name_malloced)
1410                                         free((void *)input_name);
1411                                 input_name = tmp;
1412                                 is_input_name_malloced = true;
1413                                 ret = 0;
1414                         } else
1415                                 ui__warning("Data switch failed due to memory shortage!\n");
1416                 }
1417         }
1418
1419         free_popup_options(options, nr_options);
1420         free_popup_options(abs_path, nr_options);
1421         return ret;
1422 }
1423
1424 struct popup_action {
1425         struct thread           *thread;
1426         struct dso              *dso;
1427         struct map_symbol       ms;
1428
1429         int (*fn)(struct hist_browser *browser, struct popup_action *act);
1430 };
1431
1432 static int
1433 do_annotate(struct hist_browser *browser, struct popup_action *act)
1434 {
1435         struct perf_evsel *evsel;
1436         struct annotation *notes;
1437         struct hist_entry *he;
1438         int err;
1439
1440         if (!objdump_path && perf_session_env__lookup_objdump(browser->env))
1441                 return 0;
1442
1443         notes = symbol__annotation(act->ms.sym);
1444         if (!notes->src)
1445                 return 0;
1446
1447         evsel = hists_to_evsel(browser->hists);
1448         err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
1449         he = hist_browser__selected_entry(browser);
1450         /*
1451          * offer option to annotate the other branch source or target
1452          * (if they exists) when returning from annotate
1453          */
1454         if ((err == 'q' || err == CTRL('c')) && he->branch_info)
1455                 return 1;
1456
1457         ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1458         if (err)
1459                 ui_browser__handle_resize(&browser->b);
1460         return 0;
1461 }
1462
1463 static int
1464 add_annotate_opt(struct hist_browser *browser __maybe_unused,
1465                  struct popup_action *act, char **optstr,
1466                  struct map *map, struct symbol *sym)
1467 {
1468         if (sym == NULL || map->dso->annotate_warned)
1469                 return 0;
1470
1471         if (asprintf(optstr, "Annotate %s", sym->name) < 0)
1472                 return 0;
1473
1474         act->ms.map = map;
1475         act->ms.sym = sym;
1476         act->fn = do_annotate;
1477         return 1;
1478 }
1479
1480 static int
1481 do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
1482 {
1483         struct thread *thread = act->thread;
1484
1485         if (browser->hists->thread_filter) {
1486                 pstack__remove(browser->pstack, &browser->hists->thread_filter);
1487                 perf_hpp__set_elide(HISTC_THREAD, false);
1488                 thread__zput(browser->hists->thread_filter);
1489                 ui_helpline__pop();
1490         } else {
1491                 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1492                                    thread->comm_set ? thread__comm_str(thread) : "",
1493                                    thread->tid);
1494                 browser->hists->thread_filter = thread__get(thread);
1495                 perf_hpp__set_elide(HISTC_THREAD, false);
1496                 pstack__push(browser->pstack, &browser->hists->thread_filter);
1497         }
1498
1499         hists__filter_by_thread(browser->hists);
1500         hist_browser__reset(browser);
1501         return 0;
1502 }
1503
1504 static int
1505 add_thread_opt(struct hist_browser *browser, struct popup_action *act,
1506                char **optstr, struct thread *thread)
1507 {
1508         if (thread == NULL)
1509                 return 0;
1510
1511         if (asprintf(optstr, "Zoom %s %s(%d) thread",
1512                      browser->hists->thread_filter ? "out of" : "into",
1513                      thread->comm_set ? thread__comm_str(thread) : "",
1514                      thread->tid) < 0)
1515                 return 0;
1516
1517         act->thread = thread;
1518         act->fn = do_zoom_thread;
1519         return 1;
1520 }
1521
1522 static int
1523 do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
1524 {
1525         struct dso *dso = act->dso;
1526
1527         if (browser->hists->dso_filter) {
1528                 pstack__remove(browser->pstack, &browser->hists->dso_filter);
1529                 perf_hpp__set_elide(HISTC_DSO, false);
1530                 browser->hists->dso_filter = NULL;
1531                 ui_helpline__pop();
1532         } else {
1533                 if (dso == NULL)
1534                         return 0;
1535                 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1536                                    dso->kernel ? "the Kernel" : dso->short_name);
1537                 browser->hists->dso_filter = dso;
1538                 perf_hpp__set_elide(HISTC_DSO, true);
1539                 pstack__push(browser->pstack, &browser->hists->dso_filter);
1540         }
1541
1542         hists__filter_by_dso(browser->hists);
1543         hist_browser__reset(browser);
1544         return 0;
1545 }
1546
1547 static int
1548 add_dso_opt(struct hist_browser *browser, struct popup_action *act,
1549             char **optstr, struct dso *dso)
1550 {
1551         if (dso == NULL)
1552                 return 0;
1553
1554         if (asprintf(optstr, "Zoom %s %s DSO",
1555                      browser->hists->dso_filter ? "out of" : "into",
1556                      dso->kernel ? "the Kernel" : dso->short_name) < 0)
1557                 return 0;
1558
1559         act->dso = dso;
1560         act->fn = do_zoom_dso;
1561         return 1;
1562 }
1563
1564 static int
1565 do_browse_map(struct hist_browser *browser __maybe_unused,
1566               struct popup_action *act)
1567 {
1568         map__browse(act->ms.map);
1569         return 0;
1570 }
1571
1572 static int
1573 add_map_opt(struct hist_browser *browser __maybe_unused,
1574             struct popup_action *act, char **optstr, struct map *map)
1575 {
1576         if (map == NULL)
1577                 return 0;
1578
1579         if (asprintf(optstr, "Browse map details") < 0)
1580                 return 0;
1581
1582         act->ms.map = map;
1583         act->fn = do_browse_map;
1584         return 1;
1585 }
1586
1587 static int
1588 do_run_script(struct hist_browser *browser __maybe_unused,
1589               struct popup_action *act)
1590 {
1591         char script_opt[64];
1592         memset(script_opt, 0, sizeof(script_opt));
1593
1594         if (act->thread) {
1595                 scnprintf(script_opt, sizeof(script_opt), " -c %s ",
1596                           thread__comm_str(act->thread));
1597         } else if (act->ms.sym) {
1598                 scnprintf(script_opt, sizeof(script_opt), " -S %s ",
1599                           act->ms.sym->name);
1600         }
1601
1602         script_browse(script_opt);
1603         return 0;
1604 }
1605
1606 static int
1607 add_script_opt(struct hist_browser *browser __maybe_unused,
1608                struct popup_action *act, char **optstr,
1609                struct thread *thread, struct symbol *sym)
1610 {
1611         if (thread) {
1612                 if (asprintf(optstr, "Run scripts for samples of thread [%s]",
1613                              thread__comm_str(thread)) < 0)
1614                         return 0;
1615         } else if (sym) {
1616                 if (asprintf(optstr, "Run scripts for samples of symbol [%s]",
1617                              sym->name) < 0)
1618                         return 0;
1619         } else {
1620                 if (asprintf(optstr, "Run scripts for all samples") < 0)
1621                         return 0;
1622         }
1623
1624         act->thread = thread;
1625         act->ms.sym = sym;
1626         act->fn = do_run_script;
1627         return 1;
1628 }
1629
1630 static int
1631 do_switch_data(struct hist_browser *browser __maybe_unused,
1632                struct popup_action *act __maybe_unused)
1633 {
1634         if (switch_data_file()) {
1635                 ui__warning("Won't switch the data files due to\n"
1636                             "no valid data file get selected!\n");
1637                 return 0;
1638         }
1639
1640         return K_SWITCH_INPUT_DATA;
1641 }
1642
1643 static int
1644 add_switch_opt(struct hist_browser *browser,
1645                struct popup_action *act, char **optstr)
1646 {
1647         if (!is_report_browser(browser->hbt))
1648                 return 0;
1649
1650         if (asprintf(optstr, "Switch to another data file in PWD") < 0)
1651                 return 0;
1652
1653         act->fn = do_switch_data;
1654         return 1;
1655 }
1656
1657 static int
1658 do_exit_browser(struct hist_browser *browser __maybe_unused,
1659                 struct popup_action *act __maybe_unused)
1660 {
1661         return 0;
1662 }
1663
1664 static int
1665 add_exit_opt(struct hist_browser *browser __maybe_unused,
1666              struct popup_action *act, char **optstr)
1667 {
1668         if (asprintf(optstr, "Exit") < 0)
1669                 return 0;
1670
1671         act->fn = do_exit_browser;
1672         return 1;
1673 }
1674
1675 static void hist_browser__update_nr_entries(struct hist_browser *hb)
1676 {
1677         u64 nr_entries = 0;
1678         struct rb_node *nd = rb_first(&hb->hists->entries);
1679
1680         if (hb->min_pcnt == 0) {
1681                 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
1682                 return;
1683         }
1684
1685         while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
1686                 nr_entries++;
1687                 nd = rb_next(nd);
1688         }
1689
1690         hb->nr_non_filtered_entries = nr_entries;
1691 }
1692
1693 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
1694                                     const char *helpline,
1695                                     bool left_exits,
1696                                     struct hist_browser_timer *hbt,
1697                                     float min_pcnt,
1698                                     struct perf_env *env)
1699 {
1700         struct hists *hists = evsel__hists(evsel);
1701         struct hist_browser *browser = hist_browser__new(hists, hbt, env);
1702         struct branch_info *bi;
1703 #define MAX_OPTIONS  16
1704         char *options[MAX_OPTIONS];
1705         struct popup_action actions[MAX_OPTIONS];
1706         int nr_options = 0;
1707         int key = -1;
1708         char buf[64];
1709         int delay_secs = hbt ? hbt->refresh : 0;
1710         struct perf_hpp_fmt *fmt;
1711
1712 #define HIST_BROWSER_HELP_COMMON                                        \
1713         "h/?/F1        Show this window\n"                              \
1714         "UP/DOWN/PGUP\n"                                                \
1715         "PGDN/SPACE    Navigate\n"                                      \
1716         "q/ESC/CTRL+C  Exit browser\n\n"                                \
1717         "For multiple event sessions:\n\n"                              \
1718         "TAB/UNTAB     Switch events\n\n"                               \
1719         "For symbolic views (--sort has sym):\n\n"                      \
1720         "->            Zoom into DSO/Threads & Annotate current symbol\n" \
1721         "<-            Zoom out\n"                                      \
1722         "a             Annotate current symbol\n"                       \
1723         "C             Collapse all callchains\n"                       \
1724         "d             Zoom into current DSO\n"                         \
1725         "E             Expand all callchains\n"                         \
1726         "F             Toggle percentage of filtered entries\n"         \
1727         "H             Display column headers\n"                        \
1728
1729         /* help messages are sorted by lexical order of the hotkey */
1730         const char report_help[] = HIST_BROWSER_HELP_COMMON
1731         "i             Show header information\n"
1732         "P             Print histograms to perf.hist.N\n"
1733         "r             Run available scripts\n"
1734         "s             Switch to another data file in PWD\n"
1735         "t             Zoom into current Thread\n"
1736         "V             Verbose (DSO names in callchains, etc)\n"
1737         "/             Filter symbol by name";
1738         const char top_help[] = HIST_BROWSER_HELP_COMMON
1739         "P             Print histograms to perf.hist.N\n"
1740         "t             Zoom into current Thread\n"
1741         "V             Verbose (DSO names in callchains, etc)\n"
1742         "z             Toggle zeroing of samples\n"
1743         "f             Enable/Disable events\n"
1744         "/             Filter symbol by name";
1745
1746         if (browser == NULL)
1747                 return -1;
1748
1749         /* reset abort key so that it can get Ctrl-C as a key */
1750         SLang_reset_tty();
1751         SLang_init_tty(0, 0, 0);
1752
1753         if (min_pcnt) {
1754                 browser->min_pcnt = min_pcnt;
1755                 hist_browser__update_nr_entries(browser);
1756         }
1757
1758         browser->pstack = pstack__new(2);
1759         if (browser->pstack == NULL)
1760                 goto out;
1761
1762         ui_helpline__push(helpline);
1763
1764         memset(options, 0, sizeof(options));
1765         memset(actions, 0, sizeof(actions));
1766
1767         perf_hpp__for_each_format(fmt)
1768                 perf_hpp__reset_width(fmt, hists);
1769
1770         if (symbol_conf.col_width_list_str)
1771                 perf_hpp__set_user_width(symbol_conf.col_width_list_str);
1772
1773         while (1) {
1774                 struct thread *thread = NULL;
1775                 struct dso *dso = NULL;
1776                 int choice = 0;
1777
1778                 nr_options = 0;
1779
1780                 key = hist_browser__run(browser, helpline);
1781
1782                 if (browser->he_selection != NULL) {
1783                         thread = hist_browser__selected_thread(browser);
1784                         dso = browser->selection->map ? browser->selection->map->dso : NULL;
1785                 }
1786                 switch (key) {
1787                 case K_TAB:
1788                 case K_UNTAB:
1789                         if (nr_events == 1)
1790                                 continue;
1791                         /*
1792                          * Exit the browser, let hists__browser_tree
1793                          * go to the next or previous
1794                          */
1795                         goto out_free_stack;
1796                 case 'a':
1797                         if (!sort__has_sym) {
1798                                 ui_browser__warning(&browser->b, delay_secs * 2,
1799                         "Annotation is only available for symbolic views, "
1800                         "include \"sym*\" in --sort to use it.");
1801                                 continue;
1802                         }
1803
1804                         if (browser->selection == NULL ||
1805                             browser->selection->sym == NULL ||
1806                             browser->selection->map->dso->annotate_warned)
1807                                 continue;
1808
1809                         actions->ms.map = browser->selection->map;
1810                         actions->ms.sym = browser->selection->sym;
1811                         do_annotate(browser, actions);
1812                         continue;
1813                 case 'P':
1814                         hist_browser__dump(browser);
1815                         continue;
1816                 case 'd':
1817                         actions->dso = dso;
1818                         do_zoom_dso(browser, actions);
1819                         continue;
1820                 case 'V':
1821                         browser->show_dso = !browser->show_dso;
1822                         continue;
1823                 case 't':
1824                         actions->thread = thread;
1825                         do_zoom_thread(browser, actions);
1826                         continue;
1827                 case '/':
1828                         if (ui_browser__input_window("Symbol to show",
1829                                         "Please enter the name of symbol you want to see",
1830                                         buf, "ENTER: OK, ESC: Cancel",
1831                                         delay_secs * 2) == K_ENTER) {
1832                                 hists->symbol_filter_str = *buf ? buf : NULL;
1833                                 hists__filter_by_symbol(hists);
1834                                 hist_browser__reset(browser);
1835                         }
1836                         continue;
1837                 case 'r':
1838                         if (is_report_browser(hbt)) {
1839                                 actions->thread = NULL;
1840                                 actions->ms.sym = NULL;
1841                                 do_run_script(browser, actions);
1842                         }
1843                         continue;
1844                 case 's':
1845                         if (is_report_browser(hbt)) {
1846                                 key = do_switch_data(browser, actions);
1847                                 if (key == K_SWITCH_INPUT_DATA)
1848                                         goto out_free_stack;
1849                         }
1850                         continue;
1851                 case 'i':
1852                         /* env->arch is NULL for live-mode (i.e. perf top) */
1853                         if (env->arch)
1854                                 tui__header_window(env);
1855                         continue;
1856                 case 'F':
1857                         symbol_conf.filter_relative ^= 1;
1858                         continue;
1859                 case 'z':
1860                         if (!is_report_browser(hbt)) {
1861                                 struct perf_top *top = hbt->arg;
1862
1863                                 top->zero = !top->zero;
1864                         }
1865                         continue;
1866                 case K_F1:
1867                 case 'h':
1868                 case '?':
1869                         ui_browser__help_window(&browser->b,
1870                                 is_report_browser(hbt) ? report_help : top_help);
1871                         continue;
1872                 case K_ENTER:
1873                 case K_RIGHT:
1874                         /* menu */
1875                         break;
1876                 case K_ESC:
1877                 case K_LEFT: {
1878                         const void *top;
1879
1880                         if (pstack__empty(browser->pstack)) {
1881                                 /*
1882                                  * Go back to the perf_evsel_menu__run or other user
1883                                  */
1884                                 if (left_exits)
1885                                         goto out_free_stack;
1886
1887                                 if (key == K_ESC &&
1888                                     ui_browser__dialog_yesno(&browser->b,
1889                                                              "Do you really want to exit?"))
1890                                         goto out_free_stack;
1891
1892                                 continue;
1893                         }
1894                         top = pstack__peek(browser->pstack);
1895                         if (top == &browser->hists->dso_filter) {
1896                                 /*
1897                                  * No need to set actions->dso here since
1898                                  * it's just to remove the current filter.
1899                                  * Ditto for thread below.
1900                                  */
1901                                 do_zoom_dso(browser, actions);
1902                         }
1903                         if (top == &browser->hists->thread_filter)
1904                                 do_zoom_thread(browser, actions);
1905                         continue;
1906                 }
1907                 case 'q':
1908                 case CTRL('c'):
1909                         goto out_free_stack;
1910                 case 'f':
1911                         if (!is_report_browser(hbt)) {
1912                                 struct perf_top *top = hbt->arg;
1913
1914                                 perf_evlist__toggle_enable(top->evlist);
1915                                 /*
1916                                  * No need to refresh, resort/decay histogram
1917                                  * entries if we are not collecting samples:
1918                                  */
1919                                 if (top->evlist->enabled) {
1920                                         helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
1921                                         hbt->refresh = delay_secs;
1922                                 } else {
1923                                         helpline = "Press 'f' again to re-enable the events";
1924                                         hbt->refresh = 0;
1925                                 }
1926                                 continue;
1927                         }
1928                         /* Fall thru */
1929                 default:
1930                         helpline = "Press '?' for help on key bindings";
1931                         continue;
1932                 }
1933
1934                 if (!sort__has_sym)
1935                         goto add_exit_option;
1936
1937                 if (browser->selection == NULL)
1938                         goto skip_annotation;
1939
1940                 if (sort__mode == SORT_MODE__BRANCH) {
1941                         bi = browser->he_selection->branch_info;
1942
1943                         if (bi == NULL)
1944                                 goto skip_annotation;
1945
1946                         nr_options += add_annotate_opt(browser,
1947                                                        &actions[nr_options],
1948                                                        &options[nr_options],
1949                                                        bi->from.map,
1950                                                        bi->from.sym);
1951                         if (bi->to.sym != bi->from.sym)
1952                                 nr_options += add_annotate_opt(browser,
1953                                                         &actions[nr_options],
1954                                                         &options[nr_options],
1955                                                         bi->to.map,
1956                                                         bi->to.sym);
1957                 } else {
1958                         nr_options += add_annotate_opt(browser,
1959                                                        &actions[nr_options],
1960                                                        &options[nr_options],
1961                                                        browser->selection->map,
1962                                                        browser->selection->sym);
1963                 }
1964 skip_annotation:
1965                 nr_options += add_thread_opt(browser, &actions[nr_options],
1966                                              &options[nr_options], thread);
1967                 nr_options += add_dso_opt(browser, &actions[nr_options],
1968                                           &options[nr_options], dso);
1969                 nr_options += add_map_opt(browser, &actions[nr_options],
1970                                           &options[nr_options],
1971                                           browser->selection ?
1972                                                 browser->selection->map : NULL);
1973
1974                 /* perf script support */
1975                 if (browser->he_selection) {
1976                         nr_options += add_script_opt(browser,
1977                                                      &actions[nr_options],
1978                                                      &options[nr_options],
1979                                                      thread, NULL);
1980                         /*
1981                          * Note that browser->selection != NULL
1982                          * when browser->he_selection is not NULL,
1983                          * so we don't need to check browser->selection
1984                          * before fetching browser->selection->sym like what
1985                          * we do before fetching browser->selection->map.
1986                          *
1987                          * See hist_browser__show_entry.
1988                          */
1989                         nr_options += add_script_opt(browser,
1990                                                      &actions[nr_options],
1991                                                      &options[nr_options],
1992                                                      NULL, browser->selection->sym);
1993                 }
1994                 nr_options += add_script_opt(browser, &actions[nr_options],
1995                                              &options[nr_options], NULL, NULL);
1996                 nr_options += add_switch_opt(browser, &actions[nr_options],
1997                                              &options[nr_options]);
1998 add_exit_option:
1999                 nr_options += add_exit_opt(browser, &actions[nr_options],
2000                                            &options[nr_options]);
2001
2002                 do {
2003                         struct popup_action *act;
2004
2005                         choice = ui__popup_menu(nr_options, options);
2006                         if (choice == -1 || choice >= nr_options)
2007                                 break;
2008
2009                         act = &actions[choice];
2010                         key = act->fn(browser, act);
2011                 } while (key == 1);
2012
2013                 if (key == K_SWITCH_INPUT_DATA)
2014                         break;
2015         }
2016 out_free_stack:
2017         pstack__delete(browser->pstack);
2018 out:
2019         hist_browser__delete(browser);
2020         free_popup_options(options, MAX_OPTIONS);
2021         return key;
2022 }
2023
2024 struct perf_evsel_menu {
2025         struct ui_browser b;
2026         struct perf_evsel *selection;
2027         bool lost_events, lost_events_warned;
2028         float min_pcnt;
2029         struct perf_env *env;
2030 };
2031
2032 static void perf_evsel_menu__write(struct ui_browser *browser,
2033                                    void *entry, int row)
2034 {
2035         struct perf_evsel_menu *menu = container_of(browser,
2036                                                     struct perf_evsel_menu, b);
2037         struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
2038         struct hists *hists = evsel__hists(evsel);
2039         bool current_entry = ui_browser__is_current_entry(browser, row);
2040         unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
2041         const char *ev_name = perf_evsel__name(evsel);
2042         char bf[256], unit;
2043         const char *warn = " ";
2044         size_t printed;
2045
2046         ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
2047                                                        HE_COLORSET_NORMAL);
2048
2049         if (perf_evsel__is_group_event(evsel)) {
2050                 struct perf_evsel *pos;
2051
2052                 ev_name = perf_evsel__group_name(evsel);
2053
2054                 for_each_group_member(pos, evsel) {
2055                         struct hists *pos_hists = evsel__hists(pos);
2056                         nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
2057                 }
2058         }
2059
2060         nr_events = convert_unit(nr_events, &unit);
2061         printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
2062                            unit, unit == ' ' ? "" : " ", ev_name);
2063         ui_browser__printf(browser, "%s", bf);
2064
2065         nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
2066         if (nr_events != 0) {
2067                 menu->lost_events = true;
2068                 if (!current_entry)
2069                         ui_browser__set_color(browser, HE_COLORSET_TOP);
2070                 nr_events = convert_unit(nr_events, &unit);
2071                 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
2072                                      nr_events, unit, unit == ' ' ? "" : " ");
2073                 warn = bf;
2074         }
2075
2076         ui_browser__write_nstring(browser, warn, browser->width - printed);
2077
2078         if (current_entry)
2079                 menu->selection = evsel;
2080 }
2081
2082 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
2083                                 int nr_events, const char *help,
2084                                 struct hist_browser_timer *hbt)
2085 {
2086         struct perf_evlist *evlist = menu->b.priv;
2087         struct perf_evsel *pos;
2088         const char *title = "Available samples";
2089         int delay_secs = hbt ? hbt->refresh : 0;
2090         int key;
2091
2092         if (ui_browser__show(&menu->b, title,
2093                              "ESC: exit, ENTER|->: Browse histograms") < 0)
2094                 return -1;
2095
2096         while (1) {
2097                 key = ui_browser__run(&menu->b, delay_secs);
2098
2099                 switch (key) {
2100                 case K_TIMER:
2101                         hbt->timer(hbt->arg);
2102
2103                         if (!menu->lost_events_warned && menu->lost_events) {
2104                                 ui_browser__warn_lost_events(&menu->b);
2105                                 menu->lost_events_warned = true;
2106                         }
2107                         continue;
2108                 case K_RIGHT:
2109                 case K_ENTER:
2110                         if (!menu->selection)
2111                                 continue;
2112                         pos = menu->selection;
2113 browse_hists:
2114                         perf_evlist__set_selected(evlist, pos);
2115                         /*
2116                          * Give the calling tool a chance to populate the non
2117                          * default evsel resorted hists tree.
2118                          */
2119                         if (hbt)
2120                                 hbt->timer(hbt->arg);
2121                         key = perf_evsel__hists_browse(pos, nr_events, help,
2122                                                        true, hbt,
2123                                                        menu->min_pcnt,
2124                                                        menu->env);
2125                         ui_browser__show_title(&menu->b, title);
2126                         switch (key) {
2127                         case K_TAB:
2128                                 if (pos->node.next == &evlist->entries)
2129                                         pos = perf_evlist__first(evlist);
2130                                 else
2131                                         pos = perf_evsel__next(pos);
2132                                 goto browse_hists;
2133                         case K_UNTAB:
2134                                 if (pos->node.prev == &evlist->entries)
2135                                         pos = perf_evlist__last(evlist);
2136                                 else
2137                                         pos = perf_evsel__prev(pos);
2138                                 goto browse_hists;
2139                         case K_SWITCH_INPUT_DATA:
2140                         case 'q':
2141                         case CTRL('c'):
2142                                 goto out;
2143                         case K_ESC:
2144                         default:
2145                                 continue;
2146                         }
2147                 case K_LEFT:
2148                         continue;
2149                 case K_ESC:
2150                         if (!ui_browser__dialog_yesno(&menu->b,
2151                                                "Do you really want to exit?"))
2152                                 continue;
2153                         /* Fall thru */
2154                 case 'q':
2155                 case CTRL('c'):
2156                         goto out;
2157                 default:
2158                         continue;
2159                 }
2160         }
2161
2162 out:
2163         ui_browser__hide(&menu->b);
2164         return key;
2165 }
2166
2167 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
2168                                  void *entry)
2169 {
2170         struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
2171
2172         if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
2173                 return true;
2174
2175         return false;
2176 }
2177
2178 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
2179                                            int nr_entries, const char *help,
2180                                            struct hist_browser_timer *hbt,
2181                                            float min_pcnt,
2182                                            struct perf_env *env)
2183 {
2184         struct perf_evsel *pos;
2185         struct perf_evsel_menu menu = {
2186                 .b = {
2187                         .entries    = &evlist->entries,
2188                         .refresh    = ui_browser__list_head_refresh,
2189                         .seek       = ui_browser__list_head_seek,
2190                         .write      = perf_evsel_menu__write,
2191                         .filter     = filter_group_entries,
2192                         .nr_entries = nr_entries,
2193                         .priv       = evlist,
2194                 },
2195                 .min_pcnt = min_pcnt,
2196                 .env = env,
2197         };
2198
2199         ui_helpline__push("Press ESC to exit");
2200
2201         evlist__for_each(evlist, pos) {
2202                 const char *ev_name = perf_evsel__name(pos);
2203                 size_t line_len = strlen(ev_name) + 7;
2204
2205                 if (menu.b.width < line_len)
2206                         menu.b.width = line_len;
2207         }
2208
2209         return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
2210 }
2211
2212 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
2213                                   struct hist_browser_timer *hbt,
2214                                   float min_pcnt,
2215                                   struct perf_env *env)
2216 {
2217         int nr_entries = evlist->nr_entries;
2218
2219 single_entry:
2220         if (nr_entries == 1) {
2221                 struct perf_evsel *first = perf_evlist__first(evlist);
2222
2223                 return perf_evsel__hists_browse(first, nr_entries, help,
2224                                                 false, hbt, min_pcnt,
2225                                                 env);
2226         }
2227
2228         if (symbol_conf.event_group) {
2229                 struct perf_evsel *pos;
2230
2231                 nr_entries = 0;
2232                 evlist__for_each(evlist, pos) {
2233                         if (perf_evsel__is_group_leader(pos))
2234                                 nr_entries++;
2235                 }
2236
2237                 if (nr_entries == 1)
2238                         goto single_entry;
2239         }
2240
2241         return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
2242                                                hbt, min_pcnt, env);
2243 }