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