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