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