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