OSDN Git Service

Merge tag 'scsi-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi
[uclinux-h8/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 || symbol_conf.has_filter;
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, const char *help)
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, help) < 0)
440                 return -1;
441
442         while (1) {
443                 key = ui_browser__run(&browser->b, delay_secs);
444
445                 switch (key) {
446                 case K_TIMER: {
447                         u64 nr_entries;
448                         hbt->timer(hbt->arg);
449
450                         if (hist_browser__has_filter(browser))
451                                 hist_browser__update_nr_entries(browser);
452
453                         nr_entries = hist_browser__nr_entries(browser);
454                         ui_browser__update_nr_entries(&browser->b, nr_entries);
455
456                         if (browser->hists->stats.nr_lost_warned !=
457                             browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
458                                 browser->hists->stats.nr_lost_warned =
459                                         browser->hists->stats.nr_events[PERF_RECORD_LOST];
460                                 ui_browser__warn_lost_events(&browser->b);
461                         }
462
463                         hists__browser_title(browser->hists,
464                                              hbt, title, sizeof(title));
465                         ui_browser__show_title(&browser->b, title);
466                         continue;
467                 }
468                 case 'D': { /* Debug */
469                         static int seq;
470                         struct hist_entry *h = rb_entry(browser->b.top,
471                                                         struct hist_entry, rb_node);
472                         ui_helpline__pop();
473                         ui_helpline__fpush("%d: nr_ent=(%d,%d), rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
474                                            seq++, browser->b.nr_entries,
475                                            browser->hists->nr_entries,
476                                            browser->b.rows,
477                                            browser->b.index,
478                                            browser->b.top_idx,
479                                            h->row_offset, h->nr_rows);
480                 }
481                         break;
482                 case 'C':
483                         /* Collapse the whole world. */
484                         hist_browser__set_folding(browser, false);
485                         break;
486                 case 'E':
487                         /* Expand the whole world. */
488                         hist_browser__set_folding(browser, true);
489                         break;
490                 case 'H':
491                         browser->show_headers = !browser->show_headers;
492                         hist_browser__update_rows(browser);
493                         break;
494                 case K_ENTER:
495                         if (hist_browser__toggle_fold(browser))
496                                 break;
497                         /* fall thru */
498                 default:
499                         goto out;
500                 }
501         }
502 out:
503         ui_browser__hide(&browser->b);
504         return key;
505 }
506
507 struct callchain_print_arg {
508         /* for hists browser */
509         off_t   row_offset;
510         bool    is_current_entry;
511
512         /* for file dump */
513         FILE    *fp;
514         int     printed;
515 };
516
517 typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
518                                          struct callchain_list *chain,
519                                          const char *str, int offset,
520                                          unsigned short row,
521                                          struct callchain_print_arg *arg);
522
523 static void hist_browser__show_callchain_entry(struct hist_browser *browser,
524                                                struct callchain_list *chain,
525                                                const char *str, int offset,
526                                                unsigned short row,
527                                                struct callchain_print_arg *arg)
528 {
529         int color, width;
530         char folded_sign = callchain_list__folded(chain);
531         bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
532
533         color = HE_COLORSET_NORMAL;
534         width = browser->b.width - (offset + 2);
535         if (ui_browser__is_current_entry(&browser->b, row)) {
536                 browser->selection = &chain->ms;
537                 color = HE_COLORSET_SELECTED;
538                 arg->is_current_entry = true;
539         }
540
541         ui_browser__set_color(&browser->b, color);
542         hist_browser__gotorc(browser, row, 0);
543         slsmg_write_nstring(" ", offset);
544         slsmg_printf("%c", folded_sign);
545         ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
546         slsmg_write_nstring(str, width);
547 }
548
549 static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
550                                                   struct callchain_list *chain,
551                                                   const char *str, int offset,
552                                                   unsigned short row __maybe_unused,
553                                                   struct callchain_print_arg *arg)
554 {
555         char folded_sign = callchain_list__folded(chain);
556
557         arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
558                                 folded_sign, str);
559 }
560
561 typedef bool (*check_output_full_fn)(struct hist_browser *browser,
562                                      unsigned short row);
563
564 static bool hist_browser__check_output_full(struct hist_browser *browser,
565                                             unsigned short row)
566 {
567         return browser->b.rows == row;
568 }
569
570 static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
571                                           unsigned short row __maybe_unused)
572 {
573         return false;
574 }
575
576 #define LEVEL_OFFSET_STEP 3
577
578 static int hist_browser__show_callchain(struct hist_browser *browser,
579                                         struct rb_root *root, int level,
580                                         unsigned short row, u64 total,
581                                         print_callchain_entry_fn print,
582                                         struct callchain_print_arg *arg,
583                                         check_output_full_fn is_output_full)
584 {
585         struct rb_node *node;
586         int first_row = row, offset = level * LEVEL_OFFSET_STEP;
587         u64 new_total;
588         bool need_percent;
589
590         node = rb_first(root);
591         need_percent = node && rb_next(node);
592
593         while (node) {
594                 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
595                 struct rb_node *next = rb_next(node);
596                 u64 cumul = callchain_cumul_hits(child);
597                 struct callchain_list *chain;
598                 char folded_sign = ' ';
599                 int first = true;
600                 int extra_offset = 0;
601
602                 list_for_each_entry(chain, &child->val, list) {
603                         char bf[1024], *alloc_str;
604                         const char *str;
605                         bool was_first = first;
606
607                         if (first)
608                                 first = false;
609                         else if (need_percent)
610                                 extra_offset = LEVEL_OFFSET_STEP;
611
612                         folded_sign = callchain_list__folded(chain);
613                         if (arg->row_offset != 0) {
614                                 arg->row_offset--;
615                                 goto do_next;
616                         }
617
618                         alloc_str = NULL;
619                         str = callchain_list__sym_name(chain, bf, sizeof(bf),
620                                                        browser->show_dso);
621
622                         if (was_first && need_percent) {
623                                 double percent = cumul * 100.0 / total;
624
625                                 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
626                                         str = "Not enough memory!";
627                                 else
628                                         str = alloc_str;
629                         }
630
631                         print(browser, chain, str, offset + extra_offset, row, arg);
632
633                         free(alloc_str);
634
635                         if (is_output_full(browser, ++row))
636                                 goto out;
637 do_next:
638                         if (folded_sign == '+')
639                                 break;
640                 }
641
642                 if (folded_sign == '-') {
643                         const int new_level = level + (extra_offset ? 2 : 1);
644
645                         if (callchain_param.mode == CHAIN_GRAPH_REL)
646                                 new_total = child->children_hit;
647                         else
648                                 new_total = total;
649
650                         row += hist_browser__show_callchain(browser, &child->rb_root,
651                                                             new_level, row, new_total,
652                                                             print, arg, is_output_full);
653                 }
654                 if (is_output_full(browser, row))
655                         break;
656                 node = next;
657         }
658 out:
659         return row - first_row;
660 }
661
662 struct hpp_arg {
663         struct ui_browser *b;
664         char folded_sign;
665         bool current_entry;
666 };
667
668 static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
669 {
670         struct hpp_arg *arg = hpp->ptr;
671         int ret, len;
672         va_list args;
673         double percent;
674
675         va_start(args, fmt);
676         len = va_arg(args, int);
677         percent = va_arg(args, double);
678         va_end(args);
679
680         ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
681
682         ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
683         slsmg_printf("%s", hpp->buf);
684
685         advance_hpp(hpp, ret);
686         return ret;
687 }
688
689 #define __HPP_COLOR_PERCENT_FN(_type, _field)                           \
690 static u64 __hpp_get_##_field(struct hist_entry *he)                    \
691 {                                                                       \
692         return he->stat._field;                                         \
693 }                                                                       \
694                                                                         \
695 static int                                                              \
696 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,               \
697                                 struct perf_hpp *hpp,                   \
698                                 struct hist_entry *he)                  \
699 {                                                                       \
700         return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%",   \
701                         __hpp__slsmg_color_printf, true);               \
702 }
703
704 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field)                       \
705 static u64 __hpp_get_acc_##_field(struct hist_entry *he)                \
706 {                                                                       \
707         return he->stat_acc->_field;                                    \
708 }                                                                       \
709                                                                         \
710 static int                                                              \
711 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,               \
712                                 struct perf_hpp *hpp,                   \
713                                 struct hist_entry *he)                  \
714 {                                                                       \
715         if (!symbol_conf.cumulate_callchain) {                          \
716                 int len = fmt->user_len ?: fmt->len;                    \
717                 int ret = scnprintf(hpp->buf, hpp->size,                \
718                                     "%*s", len, "N/A");                 \
719                 slsmg_printf("%s", hpp->buf);                           \
720                                                                         \
721                 return ret;                                             \
722         }                                                               \
723         return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field,           \
724                         " %*.2f%%", __hpp__slsmg_color_printf, true);   \
725 }
726
727 __HPP_COLOR_PERCENT_FN(overhead, period)
728 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
729 __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
730 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
731 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
732 __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
733
734 #undef __HPP_COLOR_PERCENT_FN
735 #undef __HPP_COLOR_ACC_PERCENT_FN
736
737 void hist_browser__init_hpp(void)
738 {
739         perf_hpp__format[PERF_HPP__OVERHEAD].color =
740                                 hist_browser__hpp_color_overhead;
741         perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
742                                 hist_browser__hpp_color_overhead_sys;
743         perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
744                                 hist_browser__hpp_color_overhead_us;
745         perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
746                                 hist_browser__hpp_color_overhead_guest_sys;
747         perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
748                                 hist_browser__hpp_color_overhead_guest_us;
749         perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
750                                 hist_browser__hpp_color_overhead_acc;
751 }
752
753 static int hist_browser__show_entry(struct hist_browser *browser,
754                                     struct hist_entry *entry,
755                                     unsigned short row)
756 {
757         char s[256];
758         int printed = 0;
759         int width = browser->b.width;
760         char folded_sign = ' ';
761         bool current_entry = ui_browser__is_current_entry(&browser->b, row);
762         off_t row_offset = entry->row_offset;
763         bool first = true;
764         struct perf_hpp_fmt *fmt;
765
766         if (current_entry) {
767                 browser->he_selection = entry;
768                 browser->selection = &entry->ms;
769         }
770
771         if (symbol_conf.use_callchain) {
772                 hist_entry__init_have_children(entry);
773                 folded_sign = hist_entry__folded(entry);
774         }
775
776         if (row_offset == 0) {
777                 struct hpp_arg arg = {
778                         .b              = &browser->b,
779                         .folded_sign    = folded_sign,
780                         .current_entry  = current_entry,
781                 };
782                 struct perf_hpp hpp = {
783                         .buf            = s,
784                         .size           = sizeof(s),
785                         .ptr            = &arg,
786                 };
787
788                 hist_browser__gotorc(browser, row, 0);
789
790                 perf_hpp__for_each_format(fmt) {
791                         if (perf_hpp__should_skip(fmt))
792                                 continue;
793
794                         if (current_entry && browser->b.navkeypressed) {
795                                 ui_browser__set_color(&browser->b,
796                                                       HE_COLORSET_SELECTED);
797                         } else {
798                                 ui_browser__set_color(&browser->b,
799                                                       HE_COLORSET_NORMAL);
800                         }
801
802                         if (first) {
803                                 if (symbol_conf.use_callchain) {
804                                         slsmg_printf("%c ", folded_sign);
805                                         width -= 2;
806                                 }
807                                 first = false;
808                         } else {
809                                 slsmg_printf("  ");
810                                 width -= 2;
811                         }
812
813                         if (fmt->color) {
814                                 width -= fmt->color(fmt, &hpp, entry);
815                         } else {
816                                 width -= fmt->entry(fmt, &hpp, entry);
817                                 slsmg_printf("%s", s);
818                         }
819                 }
820
821                 /* The scroll bar isn't being used */
822                 if (!browser->b.navkeypressed)
823                         width += 1;
824
825                 slsmg_write_nstring("", width);
826
827                 ++row;
828                 ++printed;
829         } else
830                 --row_offset;
831
832         if (folded_sign == '-' && row != browser->b.rows) {
833                 u64 total = hists__total_period(entry->hists);
834                 struct callchain_print_arg arg = {
835                         .row_offset = row_offset,
836                         .is_current_entry = current_entry,
837                 };
838
839                 if (callchain_param.mode == CHAIN_GRAPH_REL) {
840                         if (symbol_conf.cumulate_callchain)
841                                 total = entry->stat_acc->period;
842                         else
843                                 total = entry->stat.period;
844                 }
845
846                 printed += hist_browser__show_callchain(browser,
847                                         &entry->sorted_chain, 1, row, total,
848                                         hist_browser__show_callchain_entry, &arg,
849                                         hist_browser__check_output_full);
850
851                 if (arg.is_current_entry)
852                         browser->he_selection = entry;
853         }
854
855         return printed;
856 }
857
858 static int advance_hpp_check(struct perf_hpp *hpp, int inc)
859 {
860         advance_hpp(hpp, inc);
861         return hpp->size <= 0;
862 }
863
864 static int hists__scnprintf_headers(char *buf, size_t size, struct hists *hists)
865 {
866         struct perf_hpp dummy_hpp = {
867                 .buf    = buf,
868                 .size   = size,
869         };
870         struct perf_hpp_fmt *fmt;
871         size_t ret = 0;
872
873         if (symbol_conf.use_callchain) {
874                 ret = scnprintf(buf, size, "  ");
875                 if (advance_hpp_check(&dummy_hpp, ret))
876                         return ret;
877         }
878
879         perf_hpp__for_each_format(fmt) {
880                 if (perf_hpp__should_skip(fmt))
881                         continue;
882
883                 ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists));
884                 if (advance_hpp_check(&dummy_hpp, ret))
885                         break;
886
887                 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
888                 if (advance_hpp_check(&dummy_hpp, ret))
889                         break;
890         }
891
892         return ret;
893 }
894
895 static void hist_browser__show_headers(struct hist_browser *browser)
896 {
897         char headers[1024];
898
899         hists__scnprintf_headers(headers, sizeof(headers), browser->hists);
900         ui_browser__gotorc(&browser->b, 0, 0);
901         ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
902         slsmg_write_nstring(headers, browser->b.width + 1);
903 }
904
905 static void ui_browser__hists_init_top(struct ui_browser *browser)
906 {
907         if (browser->top == NULL) {
908                 struct hist_browser *hb;
909
910                 hb = container_of(browser, struct hist_browser, b);
911                 browser->top = rb_first(&hb->hists->entries);
912         }
913 }
914
915 static unsigned int hist_browser__refresh(struct ui_browser *browser)
916 {
917         unsigned row = 0;
918         u16 header_offset = 0;
919         struct rb_node *nd;
920         struct hist_browser *hb = container_of(browser, struct hist_browser, b);
921
922         if (hb->show_headers) {
923                 hist_browser__show_headers(hb);
924                 header_offset = 1;
925         }
926
927         ui_browser__hists_init_top(browser);
928
929         for (nd = browser->top; nd; nd = rb_next(nd)) {
930                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
931                 float percent;
932
933                 if (h->filtered)
934                         continue;
935
936                 percent = hist_entry__get_percent_limit(h);
937                 if (percent < hb->min_pcnt)
938                         continue;
939
940                 row += hist_browser__show_entry(hb, h, row);
941                 if (row == browser->rows)
942                         break;
943         }
944
945         return row + header_offset;
946 }
947
948 static struct rb_node *hists__filter_entries(struct rb_node *nd,
949                                              float min_pcnt)
950 {
951         while (nd != NULL) {
952                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
953                 float percent = hist_entry__get_percent_limit(h);
954
955                 if (!h->filtered && percent >= min_pcnt)
956                         return nd;
957
958                 nd = rb_next(nd);
959         }
960
961         return NULL;
962 }
963
964 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
965                                                   float min_pcnt)
966 {
967         while (nd != NULL) {
968                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
969                 float percent = hist_entry__get_percent_limit(h);
970
971                 if (!h->filtered && percent >= min_pcnt)
972                         return nd;
973
974                 nd = rb_prev(nd);
975         }
976
977         return NULL;
978 }
979
980 static void ui_browser__hists_seek(struct ui_browser *browser,
981                                    off_t offset, int whence)
982 {
983         struct hist_entry *h;
984         struct rb_node *nd;
985         bool first = true;
986         struct hist_browser *hb;
987
988         hb = container_of(browser, struct hist_browser, b);
989
990         if (browser->nr_entries == 0)
991                 return;
992
993         ui_browser__hists_init_top(browser);
994
995         switch (whence) {
996         case SEEK_SET:
997                 nd = hists__filter_entries(rb_first(browser->entries),
998                                            hb->min_pcnt);
999                 break;
1000         case SEEK_CUR:
1001                 nd = browser->top;
1002                 goto do_offset;
1003         case SEEK_END:
1004                 nd = hists__filter_prev_entries(rb_last(browser->entries),
1005                                                 hb->min_pcnt);
1006                 first = false;
1007                 break;
1008         default:
1009                 return;
1010         }
1011
1012         /*
1013          * Moves not relative to the first visible entry invalidates its
1014          * row_offset:
1015          */
1016         h = rb_entry(browser->top, struct hist_entry, rb_node);
1017         h->row_offset = 0;
1018
1019         /*
1020          * Here we have to check if nd is expanded (+), if it is we can't go
1021          * the next top level hist_entry, instead we must compute an offset of
1022          * what _not_ to show and not change the first visible entry.
1023          *
1024          * This offset increments when we are going from top to bottom and
1025          * decreases when we're going from bottom to top.
1026          *
1027          * As we don't have backpointers to the top level in the callchains
1028          * structure, we need to always print the whole hist_entry callchain,
1029          * skipping the first ones that are before the first visible entry
1030          * and stop when we printed enough lines to fill the screen.
1031          */
1032 do_offset:
1033         if (offset > 0) {
1034                 do {
1035                         h = rb_entry(nd, struct hist_entry, rb_node);
1036                         if (h->unfolded) {
1037                                 u16 remaining = h->nr_rows - h->row_offset;
1038                                 if (offset > remaining) {
1039                                         offset -= remaining;
1040                                         h->row_offset = 0;
1041                                 } else {
1042                                         h->row_offset += offset;
1043                                         offset = 0;
1044                                         browser->top = nd;
1045                                         break;
1046                                 }
1047                         }
1048                         nd = hists__filter_entries(rb_next(nd), hb->min_pcnt);
1049                         if (nd == NULL)
1050                                 break;
1051                         --offset;
1052                         browser->top = nd;
1053                 } while (offset != 0);
1054         } else if (offset < 0) {
1055                 while (1) {
1056                         h = rb_entry(nd, struct hist_entry, rb_node);
1057                         if (h->unfolded) {
1058                                 if (first) {
1059                                         if (-offset > h->row_offset) {
1060                                                 offset += h->row_offset;
1061                                                 h->row_offset = 0;
1062                                         } else {
1063                                                 h->row_offset += offset;
1064                                                 offset = 0;
1065                                                 browser->top = nd;
1066                                                 break;
1067                                         }
1068                                 } else {
1069                                         if (-offset > h->nr_rows) {
1070                                                 offset += h->nr_rows;
1071                                                 h->row_offset = 0;
1072                                         } else {
1073                                                 h->row_offset = h->nr_rows + offset;
1074                                                 offset = 0;
1075                                                 browser->top = nd;
1076                                                 break;
1077                                         }
1078                                 }
1079                         }
1080
1081                         nd = hists__filter_prev_entries(rb_prev(nd),
1082                                                         hb->min_pcnt);
1083                         if (nd == NULL)
1084                                 break;
1085                         ++offset;
1086                         browser->top = nd;
1087                         if (offset == 0) {
1088                                 /*
1089                                  * Last unfiltered hist_entry, check if it is
1090                                  * unfolded, if it is then we should have
1091                                  * row_offset at its last entry.
1092                                  */
1093                                 h = rb_entry(nd, struct hist_entry, rb_node);
1094                                 if (h->unfolded)
1095                                         h->row_offset = h->nr_rows;
1096                                 break;
1097                         }
1098                         first = false;
1099                 }
1100         } else {
1101                 browser->top = nd;
1102                 h = rb_entry(nd, struct hist_entry, rb_node);
1103                 h->row_offset = 0;
1104         }
1105 }
1106
1107 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1108                                            struct hist_entry *he, FILE *fp)
1109 {
1110         u64 total = hists__total_period(he->hists);
1111         struct callchain_print_arg arg  = {
1112                 .fp = fp,
1113         };
1114
1115         if (symbol_conf.cumulate_callchain)
1116                 total = he->stat_acc->period;
1117
1118         hist_browser__show_callchain(browser, &he->sorted_chain, 1, 0, total,
1119                                      hist_browser__fprintf_callchain_entry, &arg,
1120                                      hist_browser__check_dump_full);
1121         return arg.printed;
1122 }
1123
1124 static int hist_browser__fprintf_entry(struct hist_browser *browser,
1125                                        struct hist_entry *he, FILE *fp)
1126 {
1127         char s[8192];
1128         int printed = 0;
1129         char folded_sign = ' ';
1130         struct perf_hpp hpp = {
1131                 .buf = s,
1132                 .size = sizeof(s),
1133         };
1134         struct perf_hpp_fmt *fmt;
1135         bool first = true;
1136         int ret;
1137
1138         if (symbol_conf.use_callchain)
1139                 folded_sign = hist_entry__folded(he);
1140
1141         if (symbol_conf.use_callchain)
1142                 printed += fprintf(fp, "%c ", folded_sign);
1143
1144         perf_hpp__for_each_format(fmt) {
1145                 if (perf_hpp__should_skip(fmt))
1146                         continue;
1147
1148                 if (!first) {
1149                         ret = scnprintf(hpp.buf, hpp.size, "  ");
1150                         advance_hpp(&hpp, ret);
1151                 } else
1152                         first = false;
1153
1154                 ret = fmt->entry(fmt, &hpp, he);
1155                 advance_hpp(&hpp, ret);
1156         }
1157         printed += fprintf(fp, "%s\n", rtrim(s));
1158
1159         if (folded_sign == '-')
1160                 printed += hist_browser__fprintf_callchain(browser, he, fp);
1161
1162         return printed;
1163 }
1164
1165 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1166 {
1167         struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
1168                                                    browser->min_pcnt);
1169         int printed = 0;
1170
1171         while (nd) {
1172                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1173
1174                 printed += hist_browser__fprintf_entry(browser, h, fp);
1175                 nd = hists__filter_entries(rb_next(nd), browser->min_pcnt);
1176         }
1177
1178         return printed;
1179 }
1180
1181 static int hist_browser__dump(struct hist_browser *browser)
1182 {
1183         char filename[64];
1184         FILE *fp;
1185
1186         while (1) {
1187                 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
1188                 if (access(filename, F_OK))
1189                         break;
1190                 /*
1191                  * XXX: Just an arbitrary lazy upper limit
1192                  */
1193                 if (++browser->print_seq == 8192) {
1194                         ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1195                         return -1;
1196                 }
1197         }
1198
1199         fp = fopen(filename, "w");
1200         if (fp == NULL) {
1201                 char bf[64];
1202                 const char *err = strerror_r(errno, bf, sizeof(bf));
1203                 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
1204                 return -1;
1205         }
1206
1207         ++browser->print_seq;
1208         hist_browser__fprintf(browser, fp);
1209         fclose(fp);
1210         ui_helpline__fpush("%s written!", filename);
1211
1212         return 0;
1213 }
1214
1215 static struct hist_browser *hist_browser__new(struct hists *hists,
1216                                               struct hist_browser_timer *hbt,
1217                                               struct perf_session_env *env)
1218 {
1219         struct hist_browser *browser = zalloc(sizeof(*browser));
1220
1221         if (browser) {
1222                 browser->hists = hists;
1223                 browser->b.refresh = hist_browser__refresh;
1224                 browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
1225                 browser->b.seek = ui_browser__hists_seek;
1226                 browser->b.use_navkeypressed = true;
1227                 browser->show_headers = symbol_conf.show_hist_headers;
1228                 browser->hbt = hbt;
1229                 browser->env = env;
1230         }
1231
1232         return browser;
1233 }
1234
1235 static void hist_browser__delete(struct hist_browser *browser)
1236 {
1237         free(browser);
1238 }
1239
1240 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
1241 {
1242         return browser->he_selection;
1243 }
1244
1245 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
1246 {
1247         return browser->he_selection->thread;
1248 }
1249
1250 /* Check whether the browser is for 'top' or 'report' */
1251 static inline bool is_report_browser(void *timer)
1252 {
1253         return timer == NULL;
1254 }
1255
1256 static int hists__browser_title(struct hists *hists,
1257                                 struct hist_browser_timer *hbt,
1258                                 char *bf, size_t size)
1259 {
1260         char unit;
1261         int printed;
1262         const struct dso *dso = hists->dso_filter;
1263         const struct thread *thread = hists->thread_filter;
1264         unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1265         u64 nr_events = hists->stats.total_period;
1266         struct perf_evsel *evsel = hists_to_evsel(hists);
1267         const char *ev_name = perf_evsel__name(evsel);
1268         char buf[512];
1269         size_t buflen = sizeof(buf);
1270
1271         if (symbol_conf.filter_relative) {
1272                 nr_samples = hists->stats.nr_non_filtered_samples;
1273                 nr_events = hists->stats.total_non_filtered_period;
1274         }
1275
1276         if (perf_evsel__is_group_event(evsel)) {
1277                 struct perf_evsel *pos;
1278
1279                 perf_evsel__group_desc(evsel, buf, buflen);
1280                 ev_name = buf;
1281
1282                 for_each_group_member(pos, evsel) {
1283                         struct hists *pos_hists = evsel__hists(pos);
1284
1285                         if (symbol_conf.filter_relative) {
1286                                 nr_samples += pos_hists->stats.nr_non_filtered_samples;
1287                                 nr_events += pos_hists->stats.total_non_filtered_period;
1288                         } else {
1289                                 nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
1290                                 nr_events += pos_hists->stats.total_period;
1291                         }
1292                 }
1293         }
1294
1295         nr_samples = convert_unit(nr_samples, &unit);
1296         printed = scnprintf(bf, size,
1297                            "Samples: %lu%c of event '%s', Event count (approx.): %" PRIu64,
1298                            nr_samples, unit, ev_name, nr_events);
1299
1300
1301         if (hists->uid_filter_str)
1302                 printed += snprintf(bf + printed, size - printed,
1303                                     ", UID: %s", hists->uid_filter_str);
1304         if (thread)
1305                 printed += scnprintf(bf + printed, size - printed,
1306                                     ", Thread: %s(%d)",
1307                                      (thread->comm_set ? thread__comm_str(thread) : ""),
1308                                     thread->tid);
1309         if (dso)
1310                 printed += scnprintf(bf + printed, size - printed,
1311                                     ", DSO: %s", dso->short_name);
1312         if (!is_report_browser(hbt)) {
1313                 struct perf_top *top = hbt->arg;
1314
1315                 if (top->zero)
1316                         printed += scnprintf(bf + printed, size - printed, " [z]");
1317         }
1318
1319         return printed;
1320 }
1321
1322 static inline void free_popup_options(char **options, int n)
1323 {
1324         int i;
1325
1326         for (i = 0; i < n; ++i)
1327                 zfree(&options[i]);
1328 }
1329
1330 /*
1331  * Only runtime switching of perf data file will make "input_name" point
1332  * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1333  * whether we need to call free() for current "input_name" during the switch.
1334  */
1335 static bool is_input_name_malloced = false;
1336
1337 static int switch_data_file(void)
1338 {
1339         char *pwd, *options[32], *abs_path[32], *tmp;
1340         DIR *pwd_dir;
1341         int nr_options = 0, choice = -1, ret = -1;
1342         struct dirent *dent;
1343
1344         pwd = getenv("PWD");
1345         if (!pwd)
1346                 return ret;
1347
1348         pwd_dir = opendir(pwd);
1349         if (!pwd_dir)
1350                 return ret;
1351
1352         memset(options, 0, sizeof(options));
1353         memset(options, 0, sizeof(abs_path));
1354
1355         while ((dent = readdir(pwd_dir))) {
1356                 char path[PATH_MAX];
1357                 u64 magic;
1358                 char *name = dent->d_name;
1359                 FILE *file;
1360
1361                 if (!(dent->d_type == DT_REG))
1362                         continue;
1363
1364                 snprintf(path, sizeof(path), "%s/%s", pwd, name);
1365
1366                 file = fopen(path, "r");
1367                 if (!file)
1368                         continue;
1369
1370                 if (fread(&magic, 1, 8, file) < 8)
1371                         goto close_file_and_continue;
1372
1373                 if (is_perf_magic(magic)) {
1374                         options[nr_options] = strdup(name);
1375                         if (!options[nr_options])
1376                                 goto close_file_and_continue;
1377
1378                         abs_path[nr_options] = strdup(path);
1379                         if (!abs_path[nr_options]) {
1380                                 zfree(&options[nr_options]);
1381                                 ui__warning("Can't search all data files due to memory shortage.\n");
1382                                 fclose(file);
1383                                 break;
1384                         }
1385
1386                         nr_options++;
1387                 }
1388
1389 close_file_and_continue:
1390                 fclose(file);
1391                 if (nr_options >= 32) {
1392                         ui__warning("Too many perf data files in PWD!\n"
1393                                     "Only the first 32 files will be listed.\n");
1394                         break;
1395                 }
1396         }
1397         closedir(pwd_dir);
1398
1399         if (nr_options) {
1400                 choice = ui__popup_menu(nr_options, options);
1401                 if (choice < nr_options && choice >= 0) {
1402                         tmp = strdup(abs_path[choice]);
1403                         if (tmp) {
1404                                 if (is_input_name_malloced)
1405                                         free((void *)input_name);
1406                                 input_name = tmp;
1407                                 is_input_name_malloced = true;
1408                                 ret = 0;
1409                         } else
1410                                 ui__warning("Data switch failed due to memory shortage!\n");
1411                 }
1412         }
1413
1414         free_popup_options(options, nr_options);
1415         free_popup_options(abs_path, nr_options);
1416         return ret;
1417 }
1418
1419 struct popup_action {
1420         struct thread           *thread;
1421         struct dso              *dso;
1422         struct map_symbol       ms;
1423
1424         int (*fn)(struct hist_browser *browser, struct popup_action *act);
1425 };
1426
1427 static int
1428 do_annotate(struct hist_browser *browser, struct popup_action *act)
1429 {
1430         struct perf_evsel *evsel;
1431         struct annotation *notes;
1432         struct hist_entry *he;
1433         int err;
1434
1435         if (!objdump_path && perf_session_env__lookup_objdump(browser->env))
1436                 return 0;
1437
1438         notes = symbol__annotation(act->ms.sym);
1439         if (!notes->src)
1440                 return 0;
1441
1442         evsel = hists_to_evsel(browser->hists);
1443         err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
1444         he = hist_browser__selected_entry(browser);
1445         /*
1446          * offer option to annotate the other branch source or target
1447          * (if they exists) when returning from annotate
1448          */
1449         if ((err == 'q' || err == CTRL('c')) && he->branch_info)
1450                 return 1;
1451
1452         ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1453         if (err)
1454                 ui_browser__handle_resize(&browser->b);
1455         return 0;
1456 }
1457
1458 static int
1459 add_annotate_opt(struct hist_browser *browser __maybe_unused,
1460                  struct popup_action *act, char **optstr,
1461                  struct map *map, struct symbol *sym)
1462 {
1463         if (sym == NULL || map->dso->annotate_warned)
1464                 return 0;
1465
1466         if (asprintf(optstr, "Annotate %s", sym->name) < 0)
1467                 return 0;
1468
1469         act->ms.map = map;
1470         act->ms.sym = sym;
1471         act->fn = do_annotate;
1472         return 1;
1473 }
1474
1475 static int
1476 do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
1477 {
1478         struct thread *thread = act->thread;
1479
1480         if (browser->hists->thread_filter) {
1481                 pstack__remove(browser->pstack, &browser->hists->thread_filter);
1482                 perf_hpp__set_elide(HISTC_THREAD, false);
1483                 thread__zput(browser->hists->thread_filter);
1484                 ui_helpline__pop();
1485         } else {
1486                 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1487                                    thread->comm_set ? thread__comm_str(thread) : "",
1488                                    thread->tid);
1489                 browser->hists->thread_filter = thread__get(thread);
1490                 perf_hpp__set_elide(HISTC_THREAD, false);
1491                 pstack__push(browser->pstack, &browser->hists->thread_filter);
1492         }
1493
1494         hists__filter_by_thread(browser->hists);
1495         hist_browser__reset(browser);
1496         return 0;
1497 }
1498
1499 static int
1500 add_thread_opt(struct hist_browser *browser, struct popup_action *act,
1501                char **optstr, struct thread *thread)
1502 {
1503         if (thread == NULL)
1504                 return 0;
1505
1506         if (asprintf(optstr, "Zoom %s %s(%d) thread",
1507                      browser->hists->thread_filter ? "out of" : "into",
1508                      thread->comm_set ? thread__comm_str(thread) : "",
1509                      thread->tid) < 0)
1510                 return 0;
1511
1512         act->thread = thread;
1513         act->fn = do_zoom_thread;
1514         return 1;
1515 }
1516
1517 static int
1518 do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
1519 {
1520         struct dso *dso = act->dso;
1521
1522         if (browser->hists->dso_filter) {
1523                 pstack__remove(browser->pstack, &browser->hists->dso_filter);
1524                 perf_hpp__set_elide(HISTC_DSO, false);
1525                 browser->hists->dso_filter = NULL;
1526                 ui_helpline__pop();
1527         } else {
1528                 if (dso == NULL)
1529                         return 0;
1530                 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1531                                    dso->kernel ? "the Kernel" : dso->short_name);
1532                 browser->hists->dso_filter = dso;
1533                 perf_hpp__set_elide(HISTC_DSO, true);
1534                 pstack__push(browser->pstack, &browser->hists->dso_filter);
1535         }
1536
1537         hists__filter_by_dso(browser->hists);
1538         hist_browser__reset(browser);
1539         return 0;
1540 }
1541
1542 static int
1543 add_dso_opt(struct hist_browser *browser, struct popup_action *act,
1544             char **optstr, struct dso *dso)
1545 {
1546         if (dso == NULL)
1547                 return 0;
1548
1549         if (asprintf(optstr, "Zoom %s %s DSO",
1550                      browser->hists->dso_filter ? "out of" : "into",
1551                      dso->kernel ? "the Kernel" : dso->short_name) < 0)
1552                 return 0;
1553
1554         act->dso = dso;
1555         act->fn = do_zoom_dso;
1556         return 1;
1557 }
1558
1559 static int
1560 do_browse_map(struct hist_browser *browser __maybe_unused,
1561               struct popup_action *act)
1562 {
1563         map__browse(act->ms.map);
1564         return 0;
1565 }
1566
1567 static int
1568 add_map_opt(struct hist_browser *browser __maybe_unused,
1569             struct popup_action *act, char **optstr, struct map *map)
1570 {
1571         if (map == NULL)
1572                 return 0;
1573
1574         if (asprintf(optstr, "Browse map details") < 0)
1575                 return 0;
1576
1577         act->ms.map = map;
1578         act->fn = do_browse_map;
1579         return 1;
1580 }
1581
1582 static int
1583 do_run_script(struct hist_browser *browser __maybe_unused,
1584               struct popup_action *act)
1585 {
1586         char script_opt[64];
1587         memset(script_opt, 0, sizeof(script_opt));
1588
1589         if (act->thread) {
1590                 scnprintf(script_opt, sizeof(script_opt), " -c %s ",
1591                           thread__comm_str(act->thread));
1592         } else if (act->ms.sym) {
1593                 scnprintf(script_opt, sizeof(script_opt), " -S %s ",
1594                           act->ms.sym->name);
1595         }
1596
1597         script_browse(script_opt);
1598         return 0;
1599 }
1600
1601 static int
1602 add_script_opt(struct hist_browser *browser __maybe_unused,
1603                struct popup_action *act, char **optstr,
1604                struct thread *thread, struct symbol *sym)
1605 {
1606         if (thread) {
1607                 if (asprintf(optstr, "Run scripts for samples of thread [%s]",
1608                              thread__comm_str(thread)) < 0)
1609                         return 0;
1610         } else if (sym) {
1611                 if (asprintf(optstr, "Run scripts for samples of symbol [%s]",
1612                              sym->name) < 0)
1613                         return 0;
1614         } else {
1615                 if (asprintf(optstr, "Run scripts for all samples") < 0)
1616                         return 0;
1617         }
1618
1619         act->thread = thread;
1620         act->ms.sym = sym;
1621         act->fn = do_run_script;
1622         return 1;
1623 }
1624
1625 static int
1626 do_switch_data(struct hist_browser *browser __maybe_unused,
1627                struct popup_action *act __maybe_unused)
1628 {
1629         if (switch_data_file()) {
1630                 ui__warning("Won't switch the data files due to\n"
1631                             "no valid data file get selected!\n");
1632                 return 0;
1633         }
1634
1635         return K_SWITCH_INPUT_DATA;
1636 }
1637
1638 static int
1639 add_switch_opt(struct hist_browser *browser,
1640                struct popup_action *act, char **optstr)
1641 {
1642         if (!is_report_browser(browser->hbt))
1643                 return 0;
1644
1645         if (asprintf(optstr, "Switch to another data file in PWD") < 0)
1646                 return 0;
1647
1648         act->fn = do_switch_data;
1649         return 1;
1650 }
1651
1652 static int
1653 do_exit_browser(struct hist_browser *browser __maybe_unused,
1654                 struct popup_action *act __maybe_unused)
1655 {
1656         return 0;
1657 }
1658
1659 static int
1660 add_exit_opt(struct hist_browser *browser __maybe_unused,
1661              struct popup_action *act, char **optstr)
1662 {
1663         if (asprintf(optstr, "Exit") < 0)
1664                 return 0;
1665
1666         act->fn = do_exit_browser;
1667         return 1;
1668 }
1669
1670 static void hist_browser__update_nr_entries(struct hist_browser *hb)
1671 {
1672         u64 nr_entries = 0;
1673         struct rb_node *nd = rb_first(&hb->hists->entries);
1674
1675         if (hb->min_pcnt == 0) {
1676                 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
1677                 return;
1678         }
1679
1680         while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
1681                 nr_entries++;
1682                 nd = rb_next(nd);
1683         }
1684
1685         hb->nr_non_filtered_entries = nr_entries;
1686 }
1687
1688 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
1689                                     const char *helpline,
1690                                     bool left_exits,
1691                                     struct hist_browser_timer *hbt,
1692                                     float min_pcnt,
1693                                     struct perf_session_env *env)
1694 {
1695         struct hists *hists = evsel__hists(evsel);
1696         struct hist_browser *browser = hist_browser__new(hists, hbt, env);
1697         struct branch_info *bi;
1698 #define MAX_OPTIONS  16
1699         char *options[MAX_OPTIONS];
1700         struct popup_action actions[MAX_OPTIONS];
1701         int nr_options = 0;
1702         int key = -1;
1703         char buf[64];
1704         int delay_secs = hbt ? hbt->refresh : 0;
1705         struct perf_hpp_fmt *fmt;
1706
1707 #define HIST_BROWSER_HELP_COMMON                                        \
1708         "h/?/F1        Show this window\n"                              \
1709         "UP/DOWN/PGUP\n"                                                \
1710         "PGDN/SPACE    Navigate\n"                                      \
1711         "q/ESC/CTRL+C  Exit browser\n\n"                                \
1712         "For multiple event sessions:\n\n"                              \
1713         "TAB/UNTAB     Switch events\n\n"                               \
1714         "For symbolic views (--sort has sym):\n\n"                      \
1715         "->            Zoom into DSO/Threads & Annotate current symbol\n" \
1716         "<-            Zoom out\n"                                      \
1717         "a             Annotate current symbol\n"                       \
1718         "C             Collapse all callchains\n"                       \
1719         "d             Zoom into current DSO\n"                         \
1720         "E             Expand all callchains\n"                         \
1721         "F             Toggle percentage of filtered entries\n"         \
1722         "H             Display column headers\n"                        \
1723
1724         /* help messages are sorted by lexical order of the hotkey */
1725         const char report_help[] = HIST_BROWSER_HELP_COMMON
1726         "i             Show header information\n"
1727         "P             Print histograms to perf.hist.N\n"
1728         "r             Run available scripts\n"
1729         "s             Switch to another data file in PWD\n"
1730         "t             Zoom into current Thread\n"
1731         "V             Verbose (DSO names in callchains, etc)\n"
1732         "/             Filter symbol by name";
1733         const char top_help[] = HIST_BROWSER_HELP_COMMON
1734         "P             Print histograms to perf.hist.N\n"
1735         "t             Zoom into current Thread\n"
1736         "V             Verbose (DSO names in callchains, etc)\n"
1737         "z             Toggle zeroing of samples\n"
1738         "f             Enable/Disable events\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, helpline);
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                 case 'f':
1905                         if (!is_report_browser(hbt)) {
1906                                 struct perf_top *top = hbt->arg;
1907
1908                                 perf_evlist__toggle_enable(top->evlist);
1909                                 /*
1910                                  * No need to refresh, resort/decay histogram
1911                                  * entries if we are not collecting samples:
1912                                  */
1913                                 if (top->evlist->enabled) {
1914                                         helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
1915                                         hbt->refresh = delay_secs;
1916                                 } else {
1917                                         helpline = "Press 'f' again to re-enable the events";
1918                                         hbt->refresh = 0;
1919                                 }
1920                                 continue;
1921                         }
1922                         /* Fall thru */
1923                 default:
1924                         helpline = "Press '?' for help on key bindings";
1925                         continue;
1926                 }
1927
1928                 if (!sort__has_sym)
1929                         goto add_exit_option;
1930
1931                 if (browser->selection == NULL)
1932                         goto skip_annotation;
1933
1934                 if (sort__mode == SORT_MODE__BRANCH) {
1935                         bi = browser->he_selection->branch_info;
1936
1937                         if (bi == NULL)
1938                                 goto skip_annotation;
1939
1940                         nr_options += add_annotate_opt(browser,
1941                                                        &actions[nr_options],
1942                                                        &options[nr_options],
1943                                                        bi->from.map,
1944                                                        bi->from.sym);
1945                         if (bi->to.sym != bi->from.sym)
1946                                 nr_options += add_annotate_opt(browser,
1947                                                         &actions[nr_options],
1948                                                         &options[nr_options],
1949                                                         bi->to.map,
1950                                                         bi->to.sym);
1951                 } else {
1952                         nr_options += add_annotate_opt(browser,
1953                                                        &actions[nr_options],
1954                                                        &options[nr_options],
1955                                                        browser->selection->map,
1956                                                        browser->selection->sym);
1957                 }
1958 skip_annotation:
1959                 nr_options += add_thread_opt(browser, &actions[nr_options],
1960                                              &options[nr_options], thread);
1961                 nr_options += add_dso_opt(browser, &actions[nr_options],
1962                                           &options[nr_options], dso);
1963                 nr_options += add_map_opt(browser, &actions[nr_options],
1964                                           &options[nr_options],
1965                                           browser->selection->map);
1966
1967                 /* perf script support */
1968                 if (browser->he_selection) {
1969                         nr_options += add_script_opt(browser,
1970                                                      &actions[nr_options],
1971                                                      &options[nr_options],
1972                                                      thread, NULL);
1973                         nr_options += add_script_opt(browser,
1974                                                      &actions[nr_options],
1975                                                      &options[nr_options],
1976                                                      NULL, browser->selection->sym);
1977                 }
1978                 nr_options += add_script_opt(browser, &actions[nr_options],
1979                                              &options[nr_options], NULL, NULL);
1980                 nr_options += add_switch_opt(browser, &actions[nr_options],
1981                                              &options[nr_options]);
1982 add_exit_option:
1983                 nr_options += add_exit_opt(browser, &actions[nr_options],
1984                                            &options[nr_options]);
1985
1986                 do {
1987                         struct popup_action *act;
1988
1989                         choice = ui__popup_menu(nr_options, options);
1990                         if (choice == -1 || choice >= nr_options)
1991                                 break;
1992
1993                         act = &actions[choice];
1994                         key = act->fn(browser, act);
1995                 } while (key == 1);
1996
1997                 if (key == K_SWITCH_INPUT_DATA)
1998                         break;
1999         }
2000 out_free_stack:
2001         pstack__delete(browser->pstack);
2002 out:
2003         hist_browser__delete(browser);
2004         free_popup_options(options, MAX_OPTIONS);
2005         return key;
2006 }
2007
2008 struct perf_evsel_menu {
2009         struct ui_browser b;
2010         struct perf_evsel *selection;
2011         bool lost_events, lost_events_warned;
2012         float min_pcnt;
2013         struct perf_session_env *env;
2014 };
2015
2016 static void perf_evsel_menu__write(struct ui_browser *browser,
2017                                    void *entry, int row)
2018 {
2019         struct perf_evsel_menu *menu = container_of(browser,
2020                                                     struct perf_evsel_menu, b);
2021         struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
2022         struct hists *hists = evsel__hists(evsel);
2023         bool current_entry = ui_browser__is_current_entry(browser, row);
2024         unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
2025         const char *ev_name = perf_evsel__name(evsel);
2026         char bf[256], unit;
2027         const char *warn = " ";
2028         size_t printed;
2029
2030         ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
2031                                                        HE_COLORSET_NORMAL);
2032
2033         if (perf_evsel__is_group_event(evsel)) {
2034                 struct perf_evsel *pos;
2035
2036                 ev_name = perf_evsel__group_name(evsel);
2037
2038                 for_each_group_member(pos, evsel) {
2039                         struct hists *pos_hists = evsel__hists(pos);
2040                         nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
2041                 }
2042         }
2043
2044         nr_events = convert_unit(nr_events, &unit);
2045         printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
2046                            unit, unit == ' ' ? "" : " ", ev_name);
2047         slsmg_printf("%s", bf);
2048
2049         nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
2050         if (nr_events != 0) {
2051                 menu->lost_events = true;
2052                 if (!current_entry)
2053                         ui_browser__set_color(browser, HE_COLORSET_TOP);
2054                 nr_events = convert_unit(nr_events, &unit);
2055                 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
2056                                      nr_events, unit, unit == ' ' ? "" : " ");
2057                 warn = bf;
2058         }
2059
2060         slsmg_write_nstring(warn, browser->width - printed);
2061
2062         if (current_entry)
2063                 menu->selection = evsel;
2064 }
2065
2066 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
2067                                 int nr_events, const char *help,
2068                                 struct hist_browser_timer *hbt)
2069 {
2070         struct perf_evlist *evlist = menu->b.priv;
2071         struct perf_evsel *pos;
2072         const char *title = "Available samples";
2073         int delay_secs = hbt ? hbt->refresh : 0;
2074         int key;
2075
2076         if (ui_browser__show(&menu->b, title,
2077                              "ESC: exit, ENTER|->: Browse histograms") < 0)
2078                 return -1;
2079
2080         while (1) {
2081                 key = ui_browser__run(&menu->b, delay_secs);
2082
2083                 switch (key) {
2084                 case K_TIMER:
2085                         hbt->timer(hbt->arg);
2086
2087                         if (!menu->lost_events_warned && menu->lost_events) {
2088                                 ui_browser__warn_lost_events(&menu->b);
2089                                 menu->lost_events_warned = true;
2090                         }
2091                         continue;
2092                 case K_RIGHT:
2093                 case K_ENTER:
2094                         if (!menu->selection)
2095                                 continue;
2096                         pos = menu->selection;
2097 browse_hists:
2098                         perf_evlist__set_selected(evlist, pos);
2099                         /*
2100                          * Give the calling tool a chance to populate the non
2101                          * default evsel resorted hists tree.
2102                          */
2103                         if (hbt)
2104                                 hbt->timer(hbt->arg);
2105                         key = perf_evsel__hists_browse(pos, nr_events, help,
2106                                                        true, hbt,
2107                                                        menu->min_pcnt,
2108                                                        menu->env);
2109                         ui_browser__show_title(&menu->b, title);
2110                         switch (key) {
2111                         case K_TAB:
2112                                 if (pos->node.next == &evlist->entries)
2113                                         pos = perf_evlist__first(evlist);
2114                                 else
2115                                         pos = perf_evsel__next(pos);
2116                                 goto browse_hists;
2117                         case K_UNTAB:
2118                                 if (pos->node.prev == &evlist->entries)
2119                                         pos = perf_evlist__last(evlist);
2120                                 else
2121                                         pos = perf_evsel__prev(pos);
2122                                 goto browse_hists;
2123                         case K_ESC:
2124                                 if (!ui_browser__dialog_yesno(&menu->b,
2125                                                 "Do you really want to exit?"))
2126                                         continue;
2127                                 /* Fall thru */
2128                         case K_SWITCH_INPUT_DATA:
2129                         case 'q':
2130                         case CTRL('c'):
2131                                 goto out;
2132                         default:
2133                                 continue;
2134                         }
2135                 case K_LEFT:
2136                         continue;
2137                 case K_ESC:
2138                         if (!ui_browser__dialog_yesno(&menu->b,
2139                                                "Do you really want to exit?"))
2140                                 continue;
2141                         /* Fall thru */
2142                 case 'q':
2143                 case CTRL('c'):
2144                         goto out;
2145                 default:
2146                         continue;
2147                 }
2148         }
2149
2150 out:
2151         ui_browser__hide(&menu->b);
2152         return key;
2153 }
2154
2155 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
2156                                  void *entry)
2157 {
2158         struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
2159
2160         if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
2161                 return true;
2162
2163         return false;
2164 }
2165
2166 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
2167                                            int nr_entries, const char *help,
2168                                            struct hist_browser_timer *hbt,
2169                                            float min_pcnt,
2170                                            struct perf_session_env *env)
2171 {
2172         struct perf_evsel *pos;
2173         struct perf_evsel_menu menu = {
2174                 .b = {
2175                         .entries    = &evlist->entries,
2176                         .refresh    = ui_browser__list_head_refresh,
2177                         .seek       = ui_browser__list_head_seek,
2178                         .write      = perf_evsel_menu__write,
2179                         .filter     = filter_group_entries,
2180                         .nr_entries = nr_entries,
2181                         .priv       = evlist,
2182                 },
2183                 .min_pcnt = min_pcnt,
2184                 .env = env,
2185         };
2186
2187         ui_helpline__push("Press ESC to exit");
2188
2189         evlist__for_each(evlist, pos) {
2190                 const char *ev_name = perf_evsel__name(pos);
2191                 size_t line_len = strlen(ev_name) + 7;
2192
2193                 if (menu.b.width < line_len)
2194                         menu.b.width = line_len;
2195         }
2196
2197         return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
2198 }
2199
2200 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
2201                                   struct hist_browser_timer *hbt,
2202                                   float min_pcnt,
2203                                   struct perf_session_env *env)
2204 {
2205         int nr_entries = evlist->nr_entries;
2206
2207 single_entry:
2208         if (nr_entries == 1) {
2209                 struct perf_evsel *first = perf_evlist__first(evlist);
2210
2211                 return perf_evsel__hists_browse(first, nr_entries, help,
2212                                                 false, hbt, min_pcnt,
2213                                                 env);
2214         }
2215
2216         if (symbol_conf.event_group) {
2217                 struct perf_evsel *pos;
2218
2219                 nr_entries = 0;
2220                 evlist__for_each(evlist, pos) {
2221                         if (perf_evsel__is_group_leader(pos))
2222                                 nr_entries++;
2223                 }
2224
2225                 if (nr_entries == 1)
2226                         goto single_entry;
2227         }
2228
2229         return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
2230                                                hbt, min_pcnt, env);
2231 }