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