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