Merge tag 'pinctrl-v4.4-2' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw...
[firefly-linux-kernel-4.4.55.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                 int column = 0;
788
789                 hist_browser__gotorc(browser, row, 0);
790
791                 perf_hpp__for_each_format(fmt) {
792                         if (perf_hpp__should_skip(fmt) || column++ < browser->b.horiz_scroll)
793                                 continue;
794
795                         if (current_entry && browser->b.navkeypressed) {
796                                 ui_browser__set_color(&browser->b,
797                                                       HE_COLORSET_SELECTED);
798                         } else {
799                                 ui_browser__set_color(&browser->b,
800                                                       HE_COLORSET_NORMAL);
801                         }
802
803                         if (first) {
804                                 if (symbol_conf.use_callchain) {
805                                         ui_browser__printf(&browser->b, "%c ", folded_sign);
806                                         width -= 2;
807                                 }
808                                 first = false;
809                         } else {
810                                 ui_browser__printf(&browser->b, "  ");
811                                 width -= 2;
812                         }
813
814                         if (fmt->color) {
815                                 width -= fmt->color(fmt, &hpp, entry);
816                         } else {
817                                 width -= fmt->entry(fmt, &hpp, entry);
818                                 ui_browser__printf(&browser->b, "%s", s);
819                         }
820                 }
821
822                 /* The scroll bar isn't being used */
823                 if (!browser->b.navkeypressed)
824                         width += 1;
825
826                 ui_browser__write_nstring(&browser->b, "", width);
827
828                 ++row;
829                 ++printed;
830         } else
831                 --row_offset;
832
833         if (folded_sign == '-' && row != browser->b.rows) {
834                 u64 total = hists__total_period(entry->hists);
835                 struct callchain_print_arg arg = {
836                         .row_offset = row_offset,
837                         .is_current_entry = current_entry,
838                 };
839
840                 if (callchain_param.mode == CHAIN_GRAPH_REL) {
841                         if (symbol_conf.cumulate_callchain)
842                                 total = entry->stat_acc->period;
843                         else
844                                 total = entry->stat.period;
845                 }
846
847                 printed += hist_browser__show_callchain(browser,
848                                         &entry->sorted_chain, 1, row, total,
849                                         hist_browser__show_callchain_entry, &arg,
850                                         hist_browser__check_output_full);
851
852                 if (arg.is_current_entry)
853                         browser->he_selection = entry;
854         }
855
856         return printed;
857 }
858
859 static int advance_hpp_check(struct perf_hpp *hpp, int inc)
860 {
861         advance_hpp(hpp, inc);
862         return hpp->size <= 0;
863 }
864
865 static int hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf, size_t size)
866 {
867         struct hists *hists = browser->hists;
868         struct perf_hpp dummy_hpp = {
869                 .buf    = buf,
870                 .size   = size,
871         };
872         struct perf_hpp_fmt *fmt;
873         size_t ret = 0;
874         int column = 0;
875
876         if (symbol_conf.use_callchain) {
877                 ret = scnprintf(buf, size, "  ");
878                 if (advance_hpp_check(&dummy_hpp, ret))
879                         return ret;
880         }
881
882         perf_hpp__for_each_format(fmt) {
883                 if (perf_hpp__should_skip(fmt)  || column++ < browser->b.horiz_scroll)
884                         continue;
885
886                 ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists));
887                 if (advance_hpp_check(&dummy_hpp, ret))
888                         break;
889
890                 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
891                 if (advance_hpp_check(&dummy_hpp, ret))
892                         break;
893         }
894
895         return ret;
896 }
897
898 static void hist_browser__show_headers(struct hist_browser *browser)
899 {
900         char headers[1024];
901
902         hists_browser__scnprintf_headers(browser, headers, sizeof(headers));
903         ui_browser__gotorc(&browser->b, 0, 0);
904         ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
905         ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
906 }
907
908 static void ui_browser__hists_init_top(struct ui_browser *browser)
909 {
910         if (browser->top == NULL) {
911                 struct hist_browser *hb;
912
913                 hb = container_of(browser, struct hist_browser, b);
914                 browser->top = rb_first(&hb->hists->entries);
915         }
916 }
917
918 static unsigned int hist_browser__refresh(struct ui_browser *browser)
919 {
920         unsigned row = 0;
921         u16 header_offset = 0;
922         struct rb_node *nd;
923         struct hist_browser *hb = container_of(browser, struct hist_browser, b);
924
925         if (hb->show_headers) {
926                 hist_browser__show_headers(hb);
927                 header_offset = 1;
928         }
929
930         ui_browser__hists_init_top(browser);
931
932         for (nd = browser->top; nd; nd = rb_next(nd)) {
933                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
934                 float percent;
935
936                 if (h->filtered)
937                         continue;
938
939                 percent = hist_entry__get_percent_limit(h);
940                 if (percent < hb->min_pcnt)
941                         continue;
942
943                 row += hist_browser__show_entry(hb, h, row);
944                 if (row == browser->rows)
945                         break;
946         }
947
948         return row + header_offset;
949 }
950
951 static struct rb_node *hists__filter_entries(struct rb_node *nd,
952                                              float min_pcnt)
953 {
954         while (nd != NULL) {
955                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
956                 float percent = hist_entry__get_percent_limit(h);
957
958                 if (!h->filtered && percent >= min_pcnt)
959                         return nd;
960
961                 nd = rb_next(nd);
962         }
963
964         return NULL;
965 }
966
967 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
968                                                   float min_pcnt)
969 {
970         while (nd != NULL) {
971                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
972                 float percent = hist_entry__get_percent_limit(h);
973
974                 if (!h->filtered && percent >= min_pcnt)
975                         return nd;
976
977                 nd = rb_prev(nd);
978         }
979
980         return NULL;
981 }
982
983 static void ui_browser__hists_seek(struct ui_browser *browser,
984                                    off_t offset, int whence)
985 {
986         struct hist_entry *h;
987         struct rb_node *nd;
988         bool first = true;
989         struct hist_browser *hb;
990
991         hb = container_of(browser, struct hist_browser, b);
992
993         if (browser->nr_entries == 0)
994                 return;
995
996         ui_browser__hists_init_top(browser);
997
998         switch (whence) {
999         case SEEK_SET:
1000                 nd = hists__filter_entries(rb_first(browser->entries),
1001                                            hb->min_pcnt);
1002                 break;
1003         case SEEK_CUR:
1004                 nd = browser->top;
1005                 goto do_offset;
1006         case SEEK_END:
1007                 nd = hists__filter_prev_entries(rb_last(browser->entries),
1008                                                 hb->min_pcnt);
1009                 first = false;
1010                 break;
1011         default:
1012                 return;
1013         }
1014
1015         /*
1016          * Moves not relative to the first visible entry invalidates its
1017          * row_offset:
1018          */
1019         h = rb_entry(browser->top, struct hist_entry, rb_node);
1020         h->row_offset = 0;
1021
1022         /*
1023          * Here we have to check if nd is expanded (+), if it is we can't go
1024          * the next top level hist_entry, instead we must compute an offset of
1025          * what _not_ to show and not change the first visible entry.
1026          *
1027          * This offset increments when we are going from top to bottom and
1028          * decreases when we're going from bottom to top.
1029          *
1030          * As we don't have backpointers to the top level in the callchains
1031          * structure, we need to always print the whole hist_entry callchain,
1032          * skipping the first ones that are before the first visible entry
1033          * and stop when we printed enough lines to fill the screen.
1034          */
1035 do_offset:
1036         if (offset > 0) {
1037                 do {
1038                         h = rb_entry(nd, struct hist_entry, rb_node);
1039                         if (h->unfolded) {
1040                                 u16 remaining = h->nr_rows - h->row_offset;
1041                                 if (offset > remaining) {
1042                                         offset -= remaining;
1043                                         h->row_offset = 0;
1044                                 } else {
1045                                         h->row_offset += offset;
1046                                         offset = 0;
1047                                         browser->top = nd;
1048                                         break;
1049                                 }
1050                         }
1051                         nd = hists__filter_entries(rb_next(nd), hb->min_pcnt);
1052                         if (nd == NULL)
1053                                 break;
1054                         --offset;
1055                         browser->top = nd;
1056                 } while (offset != 0);
1057         } else if (offset < 0) {
1058                 while (1) {
1059                         h = rb_entry(nd, struct hist_entry, rb_node);
1060                         if (h->unfolded) {
1061                                 if (first) {
1062                                         if (-offset > h->row_offset) {
1063                                                 offset += h->row_offset;
1064                                                 h->row_offset = 0;
1065                                         } else {
1066                                                 h->row_offset += offset;
1067                                                 offset = 0;
1068                                                 browser->top = nd;
1069                                                 break;
1070                                         }
1071                                 } else {
1072                                         if (-offset > h->nr_rows) {
1073                                                 offset += h->nr_rows;
1074                                                 h->row_offset = 0;
1075                                         } else {
1076                                                 h->row_offset = h->nr_rows + offset;
1077                                                 offset = 0;
1078                                                 browser->top = nd;
1079                                                 break;
1080                                         }
1081                                 }
1082                         }
1083
1084                         nd = hists__filter_prev_entries(rb_prev(nd),
1085                                                         hb->min_pcnt);
1086                         if (nd == NULL)
1087                                 break;
1088                         ++offset;
1089                         browser->top = nd;
1090                         if (offset == 0) {
1091                                 /*
1092                                  * Last unfiltered hist_entry, check if it is
1093                                  * unfolded, if it is then we should have
1094                                  * row_offset at its last entry.
1095                                  */
1096                                 h = rb_entry(nd, struct hist_entry, rb_node);
1097                                 if (h->unfolded)
1098                                         h->row_offset = h->nr_rows;
1099                                 break;
1100                         }
1101                         first = false;
1102                 }
1103         } else {
1104                 browser->top = nd;
1105                 h = rb_entry(nd, struct hist_entry, rb_node);
1106                 h->row_offset = 0;
1107         }
1108 }
1109
1110 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1111                                            struct hist_entry *he, FILE *fp)
1112 {
1113         u64 total = hists__total_period(he->hists);
1114         struct callchain_print_arg arg  = {
1115                 .fp = fp,
1116         };
1117
1118         if (symbol_conf.cumulate_callchain)
1119                 total = he->stat_acc->period;
1120
1121         hist_browser__show_callchain(browser, &he->sorted_chain, 1, 0, total,
1122                                      hist_browser__fprintf_callchain_entry, &arg,
1123                                      hist_browser__check_dump_full);
1124         return arg.printed;
1125 }
1126
1127 static int hist_browser__fprintf_entry(struct hist_browser *browser,
1128                                        struct hist_entry *he, FILE *fp)
1129 {
1130         char s[8192];
1131         int printed = 0;
1132         char folded_sign = ' ';
1133         struct perf_hpp hpp = {
1134                 .buf = s,
1135                 .size = sizeof(s),
1136         };
1137         struct perf_hpp_fmt *fmt;
1138         bool first = true;
1139         int ret;
1140
1141         if (symbol_conf.use_callchain)
1142                 folded_sign = hist_entry__folded(he);
1143
1144         if (symbol_conf.use_callchain)
1145                 printed += fprintf(fp, "%c ", folded_sign);
1146
1147         perf_hpp__for_each_format(fmt) {
1148                 if (perf_hpp__should_skip(fmt))
1149                         continue;
1150
1151                 if (!first) {
1152                         ret = scnprintf(hpp.buf, hpp.size, "  ");
1153                         advance_hpp(&hpp, ret);
1154                 } else
1155                         first = false;
1156
1157                 ret = fmt->entry(fmt, &hpp, he);
1158                 advance_hpp(&hpp, ret);
1159         }
1160         printed += fprintf(fp, "%s\n", rtrim(s));
1161
1162         if (folded_sign == '-')
1163                 printed += hist_browser__fprintf_callchain(browser, he, fp);
1164
1165         return printed;
1166 }
1167
1168 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1169 {
1170         struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
1171                                                    browser->min_pcnt);
1172         int printed = 0;
1173
1174         while (nd) {
1175                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1176
1177                 printed += hist_browser__fprintf_entry(browser, h, fp);
1178                 nd = hists__filter_entries(rb_next(nd), browser->min_pcnt);
1179         }
1180
1181         return printed;
1182 }
1183
1184 static int hist_browser__dump(struct hist_browser *browser)
1185 {
1186         char filename[64];
1187         FILE *fp;
1188
1189         while (1) {
1190                 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
1191                 if (access(filename, F_OK))
1192                         break;
1193                 /*
1194                  * XXX: Just an arbitrary lazy upper limit
1195                  */
1196                 if (++browser->print_seq == 8192) {
1197                         ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1198                         return -1;
1199                 }
1200         }
1201
1202         fp = fopen(filename, "w");
1203         if (fp == NULL) {
1204                 char bf[64];
1205                 const char *err = strerror_r(errno, bf, sizeof(bf));
1206                 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
1207                 return -1;
1208         }
1209
1210         ++browser->print_seq;
1211         hist_browser__fprintf(browser, fp);
1212         fclose(fp);
1213         ui_helpline__fpush("%s written!", filename);
1214
1215         return 0;
1216 }
1217
1218 static struct hist_browser *hist_browser__new(struct hists *hists,
1219                                               struct hist_browser_timer *hbt,
1220                                               struct perf_env *env)
1221 {
1222         struct hist_browser *browser = zalloc(sizeof(*browser));
1223
1224         if (browser) {
1225                 browser->hists = hists;
1226                 browser->b.refresh = hist_browser__refresh;
1227                 browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
1228                 browser->b.seek = ui_browser__hists_seek;
1229                 browser->b.use_navkeypressed = true;
1230                 browser->show_headers = symbol_conf.show_hist_headers;
1231                 browser->hbt = hbt;
1232                 browser->env = env;
1233         }
1234
1235         return browser;
1236 }
1237
1238 static void hist_browser__delete(struct hist_browser *browser)
1239 {
1240         free(browser);
1241 }
1242
1243 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
1244 {
1245         return browser->he_selection;
1246 }
1247
1248 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
1249 {
1250         return browser->he_selection->thread;
1251 }
1252
1253 /* Check whether the browser is for 'top' or 'report' */
1254 static inline bool is_report_browser(void *timer)
1255 {
1256         return timer == NULL;
1257 }
1258
1259 static int hists__browser_title(struct hists *hists,
1260                                 struct hist_browser_timer *hbt,
1261                                 char *bf, size_t size)
1262 {
1263         char unit;
1264         int printed;
1265         const struct dso *dso = hists->dso_filter;
1266         const struct thread *thread = hists->thread_filter;
1267         int socket_id = hists->socket_filter;
1268         unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1269         u64 nr_events = hists->stats.total_period;
1270         struct perf_evsel *evsel = hists_to_evsel(hists);
1271         const char *ev_name = perf_evsel__name(evsel);
1272         char buf[512];
1273         size_t buflen = sizeof(buf);
1274         char ref[30] = " show reference callgraph, ";
1275         bool enable_ref = false;
1276
1277         if (symbol_conf.filter_relative) {
1278                 nr_samples = hists->stats.nr_non_filtered_samples;
1279                 nr_events = hists->stats.total_non_filtered_period;
1280         }
1281
1282         if (perf_evsel__is_group_event(evsel)) {
1283                 struct perf_evsel *pos;
1284
1285                 perf_evsel__group_desc(evsel, buf, buflen);
1286                 ev_name = buf;
1287
1288                 for_each_group_member(pos, evsel) {
1289                         struct hists *pos_hists = evsel__hists(pos);
1290
1291                         if (symbol_conf.filter_relative) {
1292                                 nr_samples += pos_hists->stats.nr_non_filtered_samples;
1293                                 nr_events += pos_hists->stats.total_non_filtered_period;
1294                         } else {
1295                                 nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
1296                                 nr_events += pos_hists->stats.total_period;
1297                         }
1298                 }
1299         }
1300
1301         if (symbol_conf.show_ref_callgraph &&
1302             strstr(ev_name, "call-graph=no"))
1303                 enable_ref = true;
1304         nr_samples = convert_unit(nr_samples, &unit);
1305         printed = scnprintf(bf, size,
1306                            "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64,
1307                            nr_samples, unit, ev_name, enable_ref ? ref : " ", nr_events);
1308
1309
1310         if (hists->uid_filter_str)
1311                 printed += snprintf(bf + printed, size - printed,
1312                                     ", UID: %s", hists->uid_filter_str);
1313         if (thread)
1314                 printed += scnprintf(bf + printed, size - printed,
1315                                     ", Thread: %s(%d)",
1316                                      (thread->comm_set ? thread__comm_str(thread) : ""),
1317                                     thread->tid);
1318         if (dso)
1319                 printed += scnprintf(bf + printed, size - printed,
1320                                     ", DSO: %s", dso->short_name);
1321         if (socket_id > -1)
1322                 printed += scnprintf(bf + printed, size - printed,
1323                                     ", Processor Socket: %d", socket_id);
1324         if (!is_report_browser(hbt)) {
1325                 struct perf_top *top = hbt->arg;
1326
1327                 if (top->zero)
1328                         printed += scnprintf(bf + printed, size - printed, " [z]");
1329         }
1330
1331         return printed;
1332 }
1333
1334 static inline void free_popup_options(char **options, int n)
1335 {
1336         int i;
1337
1338         for (i = 0; i < n; ++i)
1339                 zfree(&options[i]);
1340 }
1341
1342 /*
1343  * Only runtime switching of perf data file will make "input_name" point
1344  * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1345  * whether we need to call free() for current "input_name" during the switch.
1346  */
1347 static bool is_input_name_malloced = false;
1348
1349 static int switch_data_file(void)
1350 {
1351         char *pwd, *options[32], *abs_path[32], *tmp;
1352         DIR *pwd_dir;
1353         int nr_options = 0, choice = -1, ret = -1;
1354         struct dirent *dent;
1355
1356         pwd = getenv("PWD");
1357         if (!pwd)
1358                 return ret;
1359
1360         pwd_dir = opendir(pwd);
1361         if (!pwd_dir)
1362                 return ret;
1363
1364         memset(options, 0, sizeof(options));
1365         memset(options, 0, sizeof(abs_path));
1366
1367         while ((dent = readdir(pwd_dir))) {
1368                 char path[PATH_MAX];
1369                 u64 magic;
1370                 char *name = dent->d_name;
1371                 FILE *file;
1372
1373                 if (!(dent->d_type == DT_REG))
1374                         continue;
1375
1376                 snprintf(path, sizeof(path), "%s/%s", pwd, name);
1377
1378                 file = fopen(path, "r");
1379                 if (!file)
1380                         continue;
1381
1382                 if (fread(&magic, 1, 8, file) < 8)
1383                         goto close_file_and_continue;
1384
1385                 if (is_perf_magic(magic)) {
1386                         options[nr_options] = strdup(name);
1387                         if (!options[nr_options])
1388                                 goto close_file_and_continue;
1389
1390                         abs_path[nr_options] = strdup(path);
1391                         if (!abs_path[nr_options]) {
1392                                 zfree(&options[nr_options]);
1393                                 ui__warning("Can't search all data files due to memory shortage.\n");
1394                                 fclose(file);
1395                                 break;
1396                         }
1397
1398                         nr_options++;
1399                 }
1400
1401 close_file_and_continue:
1402                 fclose(file);
1403                 if (nr_options >= 32) {
1404                         ui__warning("Too many perf data files in PWD!\n"
1405                                     "Only the first 32 files will be listed.\n");
1406                         break;
1407                 }
1408         }
1409         closedir(pwd_dir);
1410
1411         if (nr_options) {
1412                 choice = ui__popup_menu(nr_options, options);
1413                 if (choice < nr_options && choice >= 0) {
1414                         tmp = strdup(abs_path[choice]);
1415                         if (tmp) {
1416                                 if (is_input_name_malloced)
1417                                         free((void *)input_name);
1418                                 input_name = tmp;
1419                                 is_input_name_malloced = true;
1420                                 ret = 0;
1421                         } else
1422                                 ui__warning("Data switch failed due to memory shortage!\n");
1423                 }
1424         }
1425
1426         free_popup_options(options, nr_options);
1427         free_popup_options(abs_path, nr_options);
1428         return ret;
1429 }
1430
1431 struct popup_action {
1432         struct thread           *thread;
1433         struct map_symbol       ms;
1434         int                     socket;
1435
1436         int (*fn)(struct hist_browser *browser, struct popup_action *act);
1437 };
1438
1439 static int
1440 do_annotate(struct hist_browser *browser, struct popup_action *act)
1441 {
1442         struct perf_evsel *evsel;
1443         struct annotation *notes;
1444         struct hist_entry *he;
1445         int err;
1446
1447         if (!objdump_path && perf_env__lookup_objdump(browser->env))
1448                 return 0;
1449
1450         notes = symbol__annotation(act->ms.sym);
1451         if (!notes->src)
1452                 return 0;
1453
1454         evsel = hists_to_evsel(browser->hists);
1455         err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
1456         he = hist_browser__selected_entry(browser);
1457         /*
1458          * offer option to annotate the other branch source or target
1459          * (if they exists) when returning from annotate
1460          */
1461         if ((err == 'q' || err == CTRL('c')) && he->branch_info)
1462                 return 1;
1463
1464         ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1465         if (err)
1466                 ui_browser__handle_resize(&browser->b);
1467         return 0;
1468 }
1469
1470 static int
1471 add_annotate_opt(struct hist_browser *browser __maybe_unused,
1472                  struct popup_action *act, char **optstr,
1473                  struct map *map, struct symbol *sym)
1474 {
1475         if (sym == NULL || map->dso->annotate_warned)
1476                 return 0;
1477
1478         if (asprintf(optstr, "Annotate %s", sym->name) < 0)
1479                 return 0;
1480
1481         act->ms.map = map;
1482         act->ms.sym = sym;
1483         act->fn = do_annotate;
1484         return 1;
1485 }
1486
1487 static int
1488 do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
1489 {
1490         struct thread *thread = act->thread;
1491
1492         if (browser->hists->thread_filter) {
1493                 pstack__remove(browser->pstack, &browser->hists->thread_filter);
1494                 perf_hpp__set_elide(HISTC_THREAD, false);
1495                 thread__zput(browser->hists->thread_filter);
1496                 ui_helpline__pop();
1497         } else {
1498                 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
1499                                    thread->comm_set ? thread__comm_str(thread) : "",
1500                                    thread->tid);
1501                 browser->hists->thread_filter = thread__get(thread);
1502                 perf_hpp__set_elide(HISTC_THREAD, false);
1503                 pstack__push(browser->pstack, &browser->hists->thread_filter);
1504         }
1505
1506         hists__filter_by_thread(browser->hists);
1507         hist_browser__reset(browser);
1508         return 0;
1509 }
1510
1511 static int
1512 add_thread_opt(struct hist_browser *browser, struct popup_action *act,
1513                char **optstr, struct thread *thread)
1514 {
1515         if (thread == NULL)
1516                 return 0;
1517
1518         if (asprintf(optstr, "Zoom %s %s(%d) thread",
1519                      browser->hists->thread_filter ? "out of" : "into",
1520                      thread->comm_set ? thread__comm_str(thread) : "",
1521                      thread->tid) < 0)
1522                 return 0;
1523
1524         act->thread = thread;
1525         act->fn = do_zoom_thread;
1526         return 1;
1527 }
1528
1529 static int
1530 do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
1531 {
1532         struct map *map = act->ms.map;
1533
1534         if (browser->hists->dso_filter) {
1535                 pstack__remove(browser->pstack, &browser->hists->dso_filter);
1536                 perf_hpp__set_elide(HISTC_DSO, false);
1537                 browser->hists->dso_filter = NULL;
1538                 ui_helpline__pop();
1539         } else {
1540                 if (map == NULL)
1541                         return 0;
1542                 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
1543                                    __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
1544                 browser->hists->dso_filter = map->dso;
1545                 perf_hpp__set_elide(HISTC_DSO, true);
1546                 pstack__push(browser->pstack, &browser->hists->dso_filter);
1547         }
1548
1549         hists__filter_by_dso(browser->hists);
1550         hist_browser__reset(browser);
1551         return 0;
1552 }
1553
1554 static int
1555 add_dso_opt(struct hist_browser *browser, struct popup_action *act,
1556             char **optstr, struct map *map)
1557 {
1558         if (map == NULL)
1559                 return 0;
1560
1561         if (asprintf(optstr, "Zoom %s %s DSO",
1562                      browser->hists->dso_filter ? "out of" : "into",
1563                      __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
1564                 return 0;
1565
1566         act->ms.map = map;
1567         act->fn = do_zoom_dso;
1568         return 1;
1569 }
1570
1571 static int
1572 do_browse_map(struct hist_browser *browser __maybe_unused,
1573               struct popup_action *act)
1574 {
1575         map__browse(act->ms.map);
1576         return 0;
1577 }
1578
1579 static int
1580 add_map_opt(struct hist_browser *browser __maybe_unused,
1581             struct popup_action *act, char **optstr, struct map *map)
1582 {
1583         if (map == NULL)
1584                 return 0;
1585
1586         if (asprintf(optstr, "Browse map details") < 0)
1587                 return 0;
1588
1589         act->ms.map = map;
1590         act->fn = do_browse_map;
1591         return 1;
1592 }
1593
1594 static int
1595 do_run_script(struct hist_browser *browser __maybe_unused,
1596               struct popup_action *act)
1597 {
1598         char script_opt[64];
1599         memset(script_opt, 0, sizeof(script_opt));
1600
1601         if (act->thread) {
1602                 scnprintf(script_opt, sizeof(script_opt), " -c %s ",
1603                           thread__comm_str(act->thread));
1604         } else if (act->ms.sym) {
1605                 scnprintf(script_opt, sizeof(script_opt), " -S %s ",
1606                           act->ms.sym->name);
1607         }
1608
1609         script_browse(script_opt);
1610         return 0;
1611 }
1612
1613 static int
1614 add_script_opt(struct hist_browser *browser __maybe_unused,
1615                struct popup_action *act, char **optstr,
1616                struct thread *thread, struct symbol *sym)
1617 {
1618         if (thread) {
1619                 if (asprintf(optstr, "Run scripts for samples of thread [%s]",
1620                              thread__comm_str(thread)) < 0)
1621                         return 0;
1622         } else if (sym) {
1623                 if (asprintf(optstr, "Run scripts for samples of symbol [%s]",
1624                              sym->name) < 0)
1625                         return 0;
1626         } else {
1627                 if (asprintf(optstr, "Run scripts for all samples") < 0)
1628                         return 0;
1629         }
1630
1631         act->thread = thread;
1632         act->ms.sym = sym;
1633         act->fn = do_run_script;
1634         return 1;
1635 }
1636
1637 static int
1638 do_switch_data(struct hist_browser *browser __maybe_unused,
1639                struct popup_action *act __maybe_unused)
1640 {
1641         if (switch_data_file()) {
1642                 ui__warning("Won't switch the data files due to\n"
1643                             "no valid data file get selected!\n");
1644                 return 0;
1645         }
1646
1647         return K_SWITCH_INPUT_DATA;
1648 }
1649
1650 static int
1651 add_switch_opt(struct hist_browser *browser,
1652                struct popup_action *act, char **optstr)
1653 {
1654         if (!is_report_browser(browser->hbt))
1655                 return 0;
1656
1657         if (asprintf(optstr, "Switch to another data file in PWD") < 0)
1658                 return 0;
1659
1660         act->fn = do_switch_data;
1661         return 1;
1662 }
1663
1664 static int
1665 do_exit_browser(struct hist_browser *browser __maybe_unused,
1666                 struct popup_action *act __maybe_unused)
1667 {
1668         return 0;
1669 }
1670
1671 static int
1672 add_exit_opt(struct hist_browser *browser __maybe_unused,
1673              struct popup_action *act, char **optstr)
1674 {
1675         if (asprintf(optstr, "Exit") < 0)
1676                 return 0;
1677
1678         act->fn = do_exit_browser;
1679         return 1;
1680 }
1681
1682 static int
1683 do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
1684 {
1685         if (browser->hists->socket_filter > -1) {
1686                 pstack__remove(browser->pstack, &browser->hists->socket_filter);
1687                 browser->hists->socket_filter = -1;
1688                 perf_hpp__set_elide(HISTC_SOCKET, false);
1689         } else {
1690                 browser->hists->socket_filter = act->socket;
1691                 perf_hpp__set_elide(HISTC_SOCKET, true);
1692                 pstack__push(browser->pstack, &browser->hists->socket_filter);
1693         }
1694
1695         hists__filter_by_socket(browser->hists);
1696         hist_browser__reset(browser);
1697         return 0;
1698 }
1699
1700 static int
1701 add_socket_opt(struct hist_browser *browser, struct popup_action *act,
1702                char **optstr, int socket_id)
1703 {
1704         if (socket_id < 0)
1705                 return 0;
1706
1707         if (asprintf(optstr, "Zoom %s Processor Socket %d",
1708                      (browser->hists->socket_filter > -1) ? "out of" : "into",
1709                      socket_id) < 0)
1710                 return 0;
1711
1712         act->socket = socket_id;
1713         act->fn = do_zoom_socket;
1714         return 1;
1715 }
1716
1717 static void hist_browser__update_nr_entries(struct hist_browser *hb)
1718 {
1719         u64 nr_entries = 0;
1720         struct rb_node *nd = rb_first(&hb->hists->entries);
1721
1722         if (hb->min_pcnt == 0) {
1723                 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
1724                 return;
1725         }
1726
1727         while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
1728                 nr_entries++;
1729                 nd = rb_next(nd);
1730         }
1731
1732         hb->nr_non_filtered_entries = nr_entries;
1733 }
1734
1735 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
1736                                     const char *helpline,
1737                                     bool left_exits,
1738                                     struct hist_browser_timer *hbt,
1739                                     float min_pcnt,
1740                                     struct perf_env *env)
1741 {
1742         struct hists *hists = evsel__hists(evsel);
1743         struct hist_browser *browser = hist_browser__new(hists, hbt, env);
1744         struct branch_info *bi;
1745 #define MAX_OPTIONS  16
1746         char *options[MAX_OPTIONS];
1747         struct popup_action actions[MAX_OPTIONS];
1748         int nr_options = 0;
1749         int key = -1;
1750         char buf[64];
1751         int delay_secs = hbt ? hbt->refresh : 0;
1752         struct perf_hpp_fmt *fmt;
1753
1754 #define HIST_BROWSER_HELP_COMMON                                        \
1755         "h/?/F1        Show this window\n"                              \
1756         "UP/DOWN/PGUP\n"                                                \
1757         "PGDN/SPACE    Navigate\n"                                      \
1758         "q/ESC/CTRL+C  Exit browser\n\n"                                \
1759         "For multiple event sessions:\n\n"                              \
1760         "TAB/UNTAB     Switch events\n\n"                               \
1761         "For symbolic views (--sort has sym):\n\n"                      \
1762         "ENTER         Zoom into DSO/Threads & Annotate current symbol\n" \
1763         "ESC           Zoom out\n"                                      \
1764         "a             Annotate current symbol\n"                       \
1765         "C             Collapse all callchains\n"                       \
1766         "d             Zoom into current DSO\n"                         \
1767         "E             Expand all callchains\n"                         \
1768         "F             Toggle percentage of filtered entries\n"         \
1769         "H             Display column headers\n"                        \
1770         "m             Display context menu\n"                          \
1771         "S             Zoom into current Processor Socket\n"            \
1772
1773         /* help messages are sorted by lexical order of the hotkey */
1774         const char report_help[] = HIST_BROWSER_HELP_COMMON
1775         "i             Show header information\n"
1776         "P             Print histograms to perf.hist.N\n"
1777         "r             Run available scripts\n"
1778         "s             Switch to another data file in PWD\n"
1779         "t             Zoom into current Thread\n"
1780         "V             Verbose (DSO names in callchains, etc)\n"
1781         "/             Filter symbol by name";
1782         const char top_help[] = HIST_BROWSER_HELP_COMMON
1783         "P             Print histograms to perf.hist.N\n"
1784         "t             Zoom into current Thread\n"
1785         "V             Verbose (DSO names in callchains, etc)\n"
1786         "z             Toggle zeroing of samples\n"
1787         "f             Enable/Disable events\n"
1788         "/             Filter symbol by name";
1789
1790         if (browser == NULL)
1791                 return -1;
1792
1793         /* reset abort key so that it can get Ctrl-C as a key */
1794         SLang_reset_tty();
1795         SLang_init_tty(0, 0, 0);
1796
1797         if (min_pcnt) {
1798                 browser->min_pcnt = min_pcnt;
1799                 hist_browser__update_nr_entries(browser);
1800         }
1801
1802         browser->pstack = pstack__new(3);
1803         if (browser->pstack == NULL)
1804                 goto out;
1805
1806         ui_helpline__push(helpline);
1807
1808         memset(options, 0, sizeof(options));
1809         memset(actions, 0, sizeof(actions));
1810
1811         perf_hpp__for_each_format(fmt) {
1812                 perf_hpp__reset_width(fmt, hists);
1813                 /*
1814                  * This is done just once, and activates the horizontal scrolling
1815                  * code in the ui_browser code, it would be better to have a the
1816                  * counter in the perf_hpp code, but I couldn't find doing it here
1817                  * works, FIXME by setting this in hist_browser__new, for now, be
1818                  * clever 8-)
1819                  */
1820                 ++browser->b.columns;
1821         }
1822
1823         if (symbol_conf.col_width_list_str)
1824                 perf_hpp__set_user_width(symbol_conf.col_width_list_str);
1825
1826         while (1) {
1827                 struct thread *thread = NULL;
1828                 struct map *map = NULL;
1829                 int choice = 0;
1830                 int socked_id = -1;
1831
1832                 nr_options = 0;
1833
1834                 key = hist_browser__run(browser, helpline);
1835
1836                 if (browser->he_selection != NULL) {
1837                         thread = hist_browser__selected_thread(browser);
1838                         map = browser->selection->map;
1839                         socked_id = browser->he_selection->socket;
1840                 }
1841                 switch (key) {
1842                 case K_TAB:
1843                 case K_UNTAB:
1844                         if (nr_events == 1)
1845                                 continue;
1846                         /*
1847                          * Exit the browser, let hists__browser_tree
1848                          * go to the next or previous
1849                          */
1850                         goto out_free_stack;
1851                 case 'a':
1852                         if (!sort__has_sym) {
1853                                 ui_browser__warning(&browser->b, delay_secs * 2,
1854                         "Annotation is only available for symbolic views, "
1855                         "include \"sym*\" in --sort to use it.");
1856                                 continue;
1857                         }
1858
1859                         if (browser->selection == NULL ||
1860                             browser->selection->sym == NULL ||
1861                             browser->selection->map->dso->annotate_warned)
1862                                 continue;
1863
1864                         actions->ms.map = browser->selection->map;
1865                         actions->ms.sym = browser->selection->sym;
1866                         do_annotate(browser, actions);
1867                         continue;
1868                 case 'P':
1869                         hist_browser__dump(browser);
1870                         continue;
1871                 case 'd':
1872                         actions->ms.map = map;
1873                         do_zoom_dso(browser, actions);
1874                         continue;
1875                 case 'V':
1876                         browser->show_dso = !browser->show_dso;
1877                         continue;
1878                 case 't':
1879                         actions->thread = thread;
1880                         do_zoom_thread(browser, actions);
1881                         continue;
1882                 case 'S':
1883                         actions->socket = socked_id;
1884                         do_zoom_socket(browser, actions);
1885                         continue;
1886                 case '/':
1887                         if (ui_browser__input_window("Symbol to show",
1888                                         "Please enter the name of symbol you want to see.\n"
1889                                         "To remove the filter later, press / + ENTER.",
1890                                         buf, "ENTER: OK, ESC: Cancel",
1891                                         delay_secs * 2) == K_ENTER) {
1892                                 hists->symbol_filter_str = *buf ? buf : NULL;
1893                                 hists__filter_by_symbol(hists);
1894                                 hist_browser__reset(browser);
1895                         }
1896                         continue;
1897                 case 'r':
1898                         if (is_report_browser(hbt)) {
1899                                 actions->thread = NULL;
1900                                 actions->ms.sym = NULL;
1901                                 do_run_script(browser, actions);
1902                         }
1903                         continue;
1904                 case 's':
1905                         if (is_report_browser(hbt)) {
1906                                 key = do_switch_data(browser, actions);
1907                                 if (key == K_SWITCH_INPUT_DATA)
1908                                         goto out_free_stack;
1909                         }
1910                         continue;
1911                 case 'i':
1912                         /* env->arch is NULL for live-mode (i.e. perf top) */
1913                         if (env->arch)
1914                                 tui__header_window(env);
1915                         continue;
1916                 case 'F':
1917                         symbol_conf.filter_relative ^= 1;
1918                         continue;
1919                 case 'z':
1920                         if (!is_report_browser(hbt)) {
1921                                 struct perf_top *top = hbt->arg;
1922
1923                                 top->zero = !top->zero;
1924                         }
1925                         continue;
1926                 case K_F1:
1927                 case 'h':
1928                 case '?':
1929                         ui_browser__help_window(&browser->b,
1930                                 is_report_browser(hbt) ? report_help : top_help);
1931                         continue;
1932                 case K_ENTER:
1933                 case K_RIGHT:
1934                 case 'm':
1935                         /* menu */
1936                         break;
1937                 case K_ESC:
1938                 case K_LEFT: {
1939                         const void *top;
1940
1941                         if (pstack__empty(browser->pstack)) {
1942                                 /*
1943                                  * Go back to the perf_evsel_menu__run or other user
1944                                  */
1945                                 if (left_exits)
1946                                         goto out_free_stack;
1947
1948                                 if (key == K_ESC &&
1949                                     ui_browser__dialog_yesno(&browser->b,
1950                                                              "Do you really want to exit?"))
1951                                         goto out_free_stack;
1952
1953                                 continue;
1954                         }
1955                         top = pstack__peek(browser->pstack);
1956                         if (top == &browser->hists->dso_filter) {
1957                                 /*
1958                                  * No need to set actions->dso here since
1959                                  * it's just to remove the current filter.
1960                                  * Ditto for thread below.
1961                                  */
1962                                 do_zoom_dso(browser, actions);
1963                         } else if (top == &browser->hists->thread_filter) {
1964                                 do_zoom_thread(browser, actions);
1965                         } else if (top == &browser->hists->socket_filter) {
1966                                 do_zoom_socket(browser, actions);
1967                         }
1968                         continue;
1969                 }
1970                 case 'q':
1971                 case CTRL('c'):
1972                         goto out_free_stack;
1973                 case 'f':
1974                         if (!is_report_browser(hbt)) {
1975                                 struct perf_top *top = hbt->arg;
1976
1977                                 perf_evlist__toggle_enable(top->evlist);
1978                                 /*
1979                                  * No need to refresh, resort/decay histogram
1980                                  * entries if we are not collecting samples:
1981                                  */
1982                                 if (top->evlist->enabled) {
1983                                         helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
1984                                         hbt->refresh = delay_secs;
1985                                 } else {
1986                                         helpline = "Press 'f' again to re-enable the events";
1987                                         hbt->refresh = 0;
1988                                 }
1989                                 continue;
1990                         }
1991                         /* Fall thru */
1992                 default:
1993                         helpline = "Press '?' for help on key bindings";
1994                         continue;
1995                 }
1996
1997                 if (!sort__has_sym)
1998                         goto add_exit_option;
1999
2000                 if (browser->selection == NULL)
2001                         goto skip_annotation;
2002
2003                 if (sort__mode == SORT_MODE__BRANCH) {
2004                         bi = browser->he_selection->branch_info;
2005
2006                         if (bi == NULL)
2007                                 goto skip_annotation;
2008
2009                         nr_options += add_annotate_opt(browser,
2010                                                        &actions[nr_options],
2011                                                        &options[nr_options],
2012                                                        bi->from.map,
2013                                                        bi->from.sym);
2014                         if (bi->to.sym != bi->from.sym)
2015                                 nr_options += add_annotate_opt(browser,
2016                                                         &actions[nr_options],
2017                                                         &options[nr_options],
2018                                                         bi->to.map,
2019                                                         bi->to.sym);
2020                 } else {
2021                         nr_options += add_annotate_opt(browser,
2022                                                        &actions[nr_options],
2023                                                        &options[nr_options],
2024                                                        browser->selection->map,
2025                                                        browser->selection->sym);
2026                 }
2027 skip_annotation:
2028                 nr_options += add_thread_opt(browser, &actions[nr_options],
2029                                              &options[nr_options], thread);
2030                 nr_options += add_dso_opt(browser, &actions[nr_options],
2031                                           &options[nr_options], map);
2032                 nr_options += add_map_opt(browser, &actions[nr_options],
2033                                           &options[nr_options],
2034                                           browser->selection ?
2035                                                 browser->selection->map : NULL);
2036                 nr_options += add_socket_opt(browser, &actions[nr_options],
2037                                              &options[nr_options],
2038                                              socked_id);
2039                 /* perf script support */
2040                 if (browser->he_selection) {
2041                         nr_options += add_script_opt(browser,
2042                                                      &actions[nr_options],
2043                                                      &options[nr_options],
2044                                                      thread, NULL);
2045                         /*
2046                          * Note that browser->selection != NULL
2047                          * when browser->he_selection is not NULL,
2048                          * so we don't need to check browser->selection
2049                          * before fetching browser->selection->sym like what
2050                          * we do before fetching browser->selection->map.
2051                          *
2052                          * See hist_browser__show_entry.
2053                          */
2054                         nr_options += add_script_opt(browser,
2055                                                      &actions[nr_options],
2056                                                      &options[nr_options],
2057                                                      NULL, browser->selection->sym);
2058                 }
2059                 nr_options += add_script_opt(browser, &actions[nr_options],
2060                                              &options[nr_options], NULL, NULL);
2061                 nr_options += add_switch_opt(browser, &actions[nr_options],
2062                                              &options[nr_options]);
2063 add_exit_option:
2064                 nr_options += add_exit_opt(browser, &actions[nr_options],
2065                                            &options[nr_options]);
2066
2067                 do {
2068                         struct popup_action *act;
2069
2070                         choice = ui__popup_menu(nr_options, options);
2071                         if (choice == -1 || choice >= nr_options)
2072                                 break;
2073
2074                         act = &actions[choice];
2075                         key = act->fn(browser, act);
2076                 } while (key == 1);
2077
2078                 if (key == K_SWITCH_INPUT_DATA)
2079                         break;
2080         }
2081 out_free_stack:
2082         pstack__delete(browser->pstack);
2083 out:
2084         hist_browser__delete(browser);
2085         free_popup_options(options, MAX_OPTIONS);
2086         return key;
2087 }
2088
2089 struct perf_evsel_menu {
2090         struct ui_browser b;
2091         struct perf_evsel *selection;
2092         bool lost_events, lost_events_warned;
2093         float min_pcnt;
2094         struct perf_env *env;
2095 };
2096
2097 static void perf_evsel_menu__write(struct ui_browser *browser,
2098                                    void *entry, int row)
2099 {
2100         struct perf_evsel_menu *menu = container_of(browser,
2101                                                     struct perf_evsel_menu, b);
2102         struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
2103         struct hists *hists = evsel__hists(evsel);
2104         bool current_entry = ui_browser__is_current_entry(browser, row);
2105         unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
2106         const char *ev_name = perf_evsel__name(evsel);
2107         char bf[256], unit;
2108         const char *warn = " ";
2109         size_t printed;
2110
2111         ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
2112                                                        HE_COLORSET_NORMAL);
2113
2114         if (perf_evsel__is_group_event(evsel)) {
2115                 struct perf_evsel *pos;
2116
2117                 ev_name = perf_evsel__group_name(evsel);
2118
2119                 for_each_group_member(pos, evsel) {
2120                         struct hists *pos_hists = evsel__hists(pos);
2121                         nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
2122                 }
2123         }
2124
2125         nr_events = convert_unit(nr_events, &unit);
2126         printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
2127                            unit, unit == ' ' ? "" : " ", ev_name);
2128         ui_browser__printf(browser, "%s", bf);
2129
2130         nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
2131         if (nr_events != 0) {
2132                 menu->lost_events = true;
2133                 if (!current_entry)
2134                         ui_browser__set_color(browser, HE_COLORSET_TOP);
2135                 nr_events = convert_unit(nr_events, &unit);
2136                 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
2137                                      nr_events, unit, unit == ' ' ? "" : " ");
2138                 warn = bf;
2139         }
2140
2141         ui_browser__write_nstring(browser, warn, browser->width - printed);
2142
2143         if (current_entry)
2144                 menu->selection = evsel;
2145 }
2146
2147 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
2148                                 int nr_events, const char *help,
2149                                 struct hist_browser_timer *hbt)
2150 {
2151         struct perf_evlist *evlist = menu->b.priv;
2152         struct perf_evsel *pos;
2153         const char *title = "Available samples";
2154         int delay_secs = hbt ? hbt->refresh : 0;
2155         int key;
2156
2157         if (ui_browser__show(&menu->b, title,
2158                              "ESC: exit, ENTER|->: Browse histograms") < 0)
2159                 return -1;
2160
2161         while (1) {
2162                 key = ui_browser__run(&menu->b, delay_secs);
2163
2164                 switch (key) {
2165                 case K_TIMER:
2166                         hbt->timer(hbt->arg);
2167
2168                         if (!menu->lost_events_warned && menu->lost_events) {
2169                                 ui_browser__warn_lost_events(&menu->b);
2170                                 menu->lost_events_warned = true;
2171                         }
2172                         continue;
2173                 case K_RIGHT:
2174                 case K_ENTER:
2175                         if (!menu->selection)
2176                                 continue;
2177                         pos = menu->selection;
2178 browse_hists:
2179                         perf_evlist__set_selected(evlist, pos);
2180                         /*
2181                          * Give the calling tool a chance to populate the non
2182                          * default evsel resorted hists tree.
2183                          */
2184                         if (hbt)
2185                                 hbt->timer(hbt->arg);
2186                         key = perf_evsel__hists_browse(pos, nr_events, help,
2187                                                        true, hbt,
2188                                                        menu->min_pcnt,
2189                                                        menu->env);
2190                         ui_browser__show_title(&menu->b, title);
2191                         switch (key) {
2192                         case K_TAB:
2193                                 if (pos->node.next == &evlist->entries)
2194                                         pos = perf_evlist__first(evlist);
2195                                 else
2196                                         pos = perf_evsel__next(pos);
2197                                 goto browse_hists;
2198                         case K_UNTAB:
2199                                 if (pos->node.prev == &evlist->entries)
2200                                         pos = perf_evlist__last(evlist);
2201                                 else
2202                                         pos = perf_evsel__prev(pos);
2203                                 goto browse_hists;
2204                         case K_SWITCH_INPUT_DATA:
2205                         case 'q':
2206                         case CTRL('c'):
2207                                 goto out;
2208                         case K_ESC:
2209                         default:
2210                                 continue;
2211                         }
2212                 case K_LEFT:
2213                         continue;
2214                 case K_ESC:
2215                         if (!ui_browser__dialog_yesno(&menu->b,
2216                                                "Do you really want to exit?"))
2217                                 continue;
2218                         /* Fall thru */
2219                 case 'q':
2220                 case CTRL('c'):
2221                         goto out;
2222                 default:
2223                         continue;
2224                 }
2225         }
2226
2227 out:
2228         ui_browser__hide(&menu->b);
2229         return key;
2230 }
2231
2232 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
2233                                  void *entry)
2234 {
2235         struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
2236
2237         if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
2238                 return true;
2239
2240         return false;
2241 }
2242
2243 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
2244                                            int nr_entries, const char *help,
2245                                            struct hist_browser_timer *hbt,
2246                                            float min_pcnt,
2247                                            struct perf_env *env)
2248 {
2249         struct perf_evsel *pos;
2250         struct perf_evsel_menu menu = {
2251                 .b = {
2252                         .entries    = &evlist->entries,
2253                         .refresh    = ui_browser__list_head_refresh,
2254                         .seek       = ui_browser__list_head_seek,
2255                         .write      = perf_evsel_menu__write,
2256                         .filter     = filter_group_entries,
2257                         .nr_entries = nr_entries,
2258                         .priv       = evlist,
2259                 },
2260                 .min_pcnt = min_pcnt,
2261                 .env = env,
2262         };
2263
2264         ui_helpline__push("Press ESC to exit");
2265
2266         evlist__for_each(evlist, pos) {
2267                 const char *ev_name = perf_evsel__name(pos);
2268                 size_t line_len = strlen(ev_name) + 7;
2269
2270                 if (menu.b.width < line_len)
2271                         menu.b.width = line_len;
2272         }
2273
2274         return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
2275 }
2276
2277 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
2278                                   struct hist_browser_timer *hbt,
2279                                   float min_pcnt,
2280                                   struct perf_env *env)
2281 {
2282         int nr_entries = evlist->nr_entries;
2283
2284 single_entry:
2285         if (nr_entries == 1) {
2286                 struct perf_evsel *first = perf_evlist__first(evlist);
2287
2288                 return perf_evsel__hists_browse(first, nr_entries, help,
2289                                                 false, hbt, min_pcnt,
2290                                                 env);
2291         }
2292
2293         if (symbol_conf.event_group) {
2294                 struct perf_evsel *pos;
2295
2296                 nr_entries = 0;
2297                 evlist__for_each(evlist, pos) {
2298                         if (perf_evsel__is_group_leader(pos))
2299                                 nr_entries++;
2300                 }
2301
2302                 if (nr_entries == 1)
2303                         goto single_entry;
2304         }
2305
2306         return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
2307                                                hbt, min_pcnt, env);
2308 }