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