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