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