perf report: Show group description when event group is enabled
[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 <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 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
1245                                     const char *helpline, const char *ev_name,
1246                                     bool left_exits,
1247                                     struct hist_browser_timer *hbt,
1248                                     struct perf_session_env *env)
1249 {
1250         struct hists *hists = &evsel->hists;
1251         struct hist_browser *browser = hist_browser__new(hists);
1252         struct branch_info *bi;
1253         struct pstack *fstack;
1254         char *options[16];
1255         int nr_options = 0;
1256         int key = -1;
1257         char buf[64];
1258         char script_opt[64];
1259         int delay_secs = hbt ? hbt->refresh : 0;
1260
1261         if (browser == NULL)
1262                 return -1;
1263
1264         fstack = pstack__new(2);
1265         if (fstack == NULL)
1266                 goto out;
1267
1268         ui_helpline__push(helpline);
1269
1270         memset(options, 0, sizeof(options));
1271
1272         while (1) {
1273                 const struct thread *thread = NULL;
1274                 const struct dso *dso = NULL;
1275                 int choice = 0,
1276                     annotate = -2, zoom_dso = -2, zoom_thread = -2,
1277                     annotate_f = -2, annotate_t = -2, browse_map = -2;
1278                 int scripts_comm = -2, scripts_symbol = -2, scripts_all = -2;
1279
1280                 nr_options = 0;
1281
1282                 key = hist_browser__run(browser, ev_name, hbt);
1283
1284                 if (browser->he_selection != NULL) {
1285                         thread = hist_browser__selected_thread(browser);
1286                         dso = browser->selection->map ? browser->selection->map->dso : NULL;
1287                 }
1288                 switch (key) {
1289                 case K_TAB:
1290                 case K_UNTAB:
1291                         if (nr_events == 1)
1292                                 continue;
1293                         /*
1294                          * Exit the browser, let hists__browser_tree
1295                          * go to the next or previous
1296                          */
1297                         goto out_free_stack;
1298                 case 'a':
1299                         if (!browser->has_symbols) {
1300                                 ui_browser__warning(&browser->b, delay_secs * 2,
1301                         "Annotation is only available for symbolic views, "
1302                         "include \"sym*\" in --sort to use it.");
1303                                 continue;
1304                         }
1305
1306                         if (browser->selection == NULL ||
1307                             browser->selection->sym == NULL ||
1308                             browser->selection->map->dso->annotate_warned)
1309                                 continue;
1310                         goto do_annotate;
1311                 case 'P':
1312                         hist_browser__dump(browser);
1313                         continue;
1314                 case 'd':
1315                         goto zoom_dso;
1316                 case 'V':
1317                         browser->show_dso = !browser->show_dso;
1318                         continue;
1319                 case 't':
1320                         goto zoom_thread;
1321                 case '/':
1322                         if (ui_browser__input_window("Symbol to show",
1323                                         "Please enter the name of symbol you want to see",
1324                                         buf, "ENTER: OK, ESC: Cancel",
1325                                         delay_secs * 2) == K_ENTER) {
1326                                 hists->symbol_filter_str = *buf ? buf : NULL;
1327                                 hists__filter_by_symbol(hists);
1328                                 hist_browser__reset(browser);
1329                         }
1330                         continue;
1331                 case 'r':
1332                         if (is_report_browser(hbt))
1333                                 goto do_scripts;
1334                         continue;
1335                 case K_F1:
1336                 case 'h':
1337                 case '?':
1338                         ui_browser__help_window(&browser->b,
1339                                         "h/?/F1        Show this window\n"
1340                                         "UP/DOWN/PGUP\n"
1341                                         "PGDN/SPACE    Navigate\n"
1342                                         "q/ESC/CTRL+C  Exit browser\n\n"
1343                                         "For multiple event sessions:\n\n"
1344                                         "TAB/UNTAB Switch events\n\n"
1345                                         "For symbolic views (--sort has sym):\n\n"
1346                                         "->            Zoom into DSO/Threads & Annotate current symbol\n"
1347                                         "<-            Zoom out\n"
1348                                         "a             Annotate current symbol\n"
1349                                         "C             Collapse all callchains\n"
1350                                         "E             Expand all callchains\n"
1351                                         "d             Zoom into current DSO\n"
1352                                         "t             Zoom into current Thread\n"
1353                                         "r             Run available scripts('perf report' only)\n"
1354                                         "P             Print histograms to perf.hist.N\n"
1355                                         "V             Verbose (DSO names in callchains, etc)\n"
1356                                         "/             Filter symbol by name");
1357                         continue;
1358                 case K_ENTER:
1359                 case K_RIGHT:
1360                         /* menu */
1361                         break;
1362                 case K_LEFT: {
1363                         const void *top;
1364
1365                         if (pstack__empty(fstack)) {
1366                                 /*
1367                                  * Go back to the perf_evsel_menu__run or other user
1368                                  */
1369                                 if (left_exits)
1370                                         goto out_free_stack;
1371                                 continue;
1372                         }
1373                         top = pstack__pop(fstack);
1374                         if (top == &browser->hists->dso_filter)
1375                                 goto zoom_out_dso;
1376                         if (top == &browser->hists->thread_filter)
1377                                 goto zoom_out_thread;
1378                         continue;
1379                 }
1380                 case K_ESC:
1381                         if (!left_exits &&
1382                             !ui_browser__dialog_yesno(&browser->b,
1383                                                "Do you really want to exit?"))
1384                                 continue;
1385                         /* Fall thru */
1386                 case 'q':
1387                 case CTRL('c'):
1388                         goto out_free_stack;
1389                 default:
1390                         continue;
1391                 }
1392
1393                 if (!browser->has_symbols)
1394                         goto add_exit_option;
1395
1396                 if (sort__branch_mode == 1) {
1397                         bi = browser->he_selection->branch_info;
1398                         if (browser->selection != NULL &&
1399                             bi &&
1400                             bi->from.sym != NULL &&
1401                             !bi->from.map->dso->annotate_warned &&
1402                                 asprintf(&options[nr_options], "Annotate %s",
1403                                          bi->from.sym->name) > 0)
1404                                 annotate_f = nr_options++;
1405
1406                         if (browser->selection != NULL &&
1407                             bi &&
1408                             bi->to.sym != NULL &&
1409                             !bi->to.map->dso->annotate_warned &&
1410                             (bi->to.sym != bi->from.sym ||
1411                              bi->to.map->dso != bi->from.map->dso) &&
1412                                 asprintf(&options[nr_options], "Annotate %s",
1413                                          bi->to.sym->name) > 0)
1414                                 annotate_t = nr_options++;
1415                 } else {
1416
1417                         if (browser->selection != NULL &&
1418                             browser->selection->sym != NULL &&
1419                             !browser->selection->map->dso->annotate_warned &&
1420                                 asprintf(&options[nr_options], "Annotate %s",
1421                                          browser->selection->sym->name) > 0)
1422                                 annotate = nr_options++;
1423                 }
1424
1425                 if (thread != NULL &&
1426                     asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
1427                              (browser->hists->thread_filter ? "out of" : "into"),
1428                              (thread->comm_set ? thread->comm : ""),
1429                              thread->pid) > 0)
1430                         zoom_thread = nr_options++;
1431
1432                 if (dso != NULL &&
1433                     asprintf(&options[nr_options], "Zoom %s %s DSO",
1434                              (browser->hists->dso_filter ? "out of" : "into"),
1435                              (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
1436                         zoom_dso = nr_options++;
1437
1438                 if (browser->selection != NULL &&
1439                     browser->selection->map != NULL &&
1440                     asprintf(&options[nr_options], "Browse map details") > 0)
1441                         browse_map = nr_options++;
1442
1443                 /* perf script support */
1444                 if (browser->he_selection) {
1445                         struct symbol *sym;
1446
1447                         if (asprintf(&options[nr_options], "Run scripts for samples of thread [%s]",
1448                                 browser->he_selection->thread->comm) > 0)
1449                                 scripts_comm = nr_options++;
1450
1451                         sym = browser->he_selection->ms.sym;
1452                         if (sym && sym->namelen &&
1453                                 asprintf(&options[nr_options], "Run scripts for samples of symbol [%s]",
1454                                                 sym->name) > 0)
1455                                 scripts_symbol = nr_options++;
1456                 }
1457
1458                 if (asprintf(&options[nr_options], "Run scripts for all samples") > 0)
1459                         scripts_all = nr_options++;
1460
1461 add_exit_option:
1462                 options[nr_options++] = (char *)"Exit";
1463 retry_popup_menu:
1464                 choice = ui__popup_menu(nr_options, options);
1465
1466                 if (choice == nr_options - 1)
1467                         break;
1468
1469                 if (choice == -1) {
1470                         free_popup_options(options, nr_options - 1);
1471                         continue;
1472                 }
1473
1474                 if (choice == annotate || choice == annotate_t || choice == annotate_f) {
1475                         struct hist_entry *he;
1476                         int err;
1477 do_annotate:
1478                         if (!objdump_path && perf_session_env__lookup_objdump(env))
1479                                 continue;
1480
1481                         he = hist_browser__selected_entry(browser);
1482                         if (he == NULL)
1483                                 continue;
1484
1485                         /*
1486                          * we stash the branch_info symbol + map into the
1487                          * the ms so we don't have to rewrite all the annotation
1488                          * code to use branch_info.
1489                          * in branch mode, the ms struct is not used
1490                          */
1491                         if (choice == annotate_f) {
1492                                 he->ms.sym = he->branch_info->from.sym;
1493                                 he->ms.map = he->branch_info->from.map;
1494                         }  else if (choice == annotate_t) {
1495                                 he->ms.sym = he->branch_info->to.sym;
1496                                 he->ms.map = he->branch_info->to.map;
1497                         }
1498
1499                         /*
1500                          * Don't let this be freed, say, by hists__decay_entry.
1501                          */
1502                         he->used = true;
1503                         err = hist_entry__tui_annotate(he, evsel->idx, hbt);
1504                         he->used = false;
1505                         /*
1506                          * offer option to annotate the other branch source or target
1507                          * (if they exists) when returning from annotate
1508                          */
1509                         if ((err == 'q' || err == CTRL('c'))
1510                             && annotate_t != -2 && annotate_f != -2)
1511                                 goto retry_popup_menu;
1512
1513                         ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1514                         if (err)
1515                                 ui_browser__handle_resize(&browser->b);
1516
1517                 } else if (choice == browse_map)
1518                         map__browse(browser->selection->map);
1519                 else if (choice == zoom_dso) {
1520 zoom_dso:
1521                         if (browser->hists->dso_filter) {
1522                                 pstack__remove(fstack, &browser->hists->dso_filter);
1523 zoom_out_dso:
1524                                 ui_helpline__pop();
1525                                 browser->hists->dso_filter = NULL;
1526                                 sort_dso.elide = false;
1527                         } else {
1528                                 if (dso == NULL)
1529                                         continue;
1530                                 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1531                                                    dso->kernel ? "the Kernel" : dso->short_name);
1532                                 browser->hists->dso_filter = dso;
1533                                 sort_dso.elide = true;
1534                                 pstack__push(fstack, &browser->hists->dso_filter);
1535                         }
1536                         hists__filter_by_dso(hists);
1537                         hist_browser__reset(browser);
1538                 } else if (choice == zoom_thread) {
1539 zoom_thread:
1540                         if (browser->hists->thread_filter) {
1541                                 pstack__remove(fstack, &browser->hists->thread_filter);
1542 zoom_out_thread:
1543                                 ui_helpline__pop();
1544                                 browser->hists->thread_filter = NULL;
1545                                 sort_thread.elide = false;
1546                         } else {
1547                                 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1548                                                    thread->comm_set ? thread->comm : "",
1549                                                    thread->pid);
1550                                 browser->hists->thread_filter = thread;
1551                                 sort_thread.elide = true;
1552                                 pstack__push(fstack, &browser->hists->thread_filter);
1553                         }
1554                         hists__filter_by_thread(hists);
1555                         hist_browser__reset(browser);
1556                 }
1557                 /* perf scripts support */
1558                 else if (choice == scripts_all || choice == scripts_comm ||
1559                                 choice == scripts_symbol) {
1560 do_scripts:
1561                         memset(script_opt, 0, 64);
1562
1563                         if (choice == scripts_comm)
1564                                 sprintf(script_opt, " -c %s ", browser->he_selection->thread->comm);
1565
1566                         if (choice == scripts_symbol)
1567                                 sprintf(script_opt, " -S %s ", browser->he_selection->ms.sym->name);
1568
1569                         script_browse(script_opt);
1570                 }
1571         }
1572 out_free_stack:
1573         pstack__delete(fstack);
1574 out:
1575         hist_browser__delete(browser);
1576         free_popup_options(options, nr_options - 1);
1577         return key;
1578 }
1579
1580 struct perf_evsel_menu {
1581         struct ui_browser b;
1582         struct perf_evsel *selection;
1583         bool lost_events, lost_events_warned;
1584         struct perf_session_env *env;
1585 };
1586
1587 static void perf_evsel_menu__write(struct ui_browser *browser,
1588                                    void *entry, int row)
1589 {
1590         struct perf_evsel_menu *menu = container_of(browser,
1591                                                     struct perf_evsel_menu, b);
1592         struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1593         bool current_entry = ui_browser__is_current_entry(browser, row);
1594         unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1595         const char *ev_name = perf_evsel__name(evsel);
1596         char bf[256], unit;
1597         const char *warn = " ";
1598         size_t printed;
1599
1600         ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
1601                                                        HE_COLORSET_NORMAL);
1602
1603         if (symbol_conf.event_group && evsel->nr_members > 1) {
1604                 struct perf_evsel *pos;
1605
1606                 ev_name = perf_evsel__group_name(evsel);
1607
1608                 for_each_group_member(pos, evsel) {
1609                         nr_events += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1610                 }
1611         }
1612
1613         nr_events = convert_unit(nr_events, &unit);
1614         printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
1615                            unit, unit == ' ' ? "" : " ", ev_name);
1616         slsmg_printf("%s", bf);
1617
1618         nr_events = evsel->hists.stats.nr_events[PERF_RECORD_LOST];
1619         if (nr_events != 0) {
1620                 menu->lost_events = true;
1621                 if (!current_entry)
1622                         ui_browser__set_color(browser, HE_COLORSET_TOP);
1623                 nr_events = convert_unit(nr_events, &unit);
1624                 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
1625                                      nr_events, unit, unit == ' ' ? "" : " ");
1626                 warn = bf;
1627         }
1628
1629         slsmg_write_nstring(warn, browser->width - printed);
1630
1631         if (current_entry)
1632                 menu->selection = evsel;
1633 }
1634
1635 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
1636                                 int nr_events, const char *help,
1637                                 struct hist_browser_timer *hbt)
1638 {
1639         struct perf_evlist *evlist = menu->b.priv;
1640         struct perf_evsel *pos;
1641         const char *ev_name, *title = "Available samples";
1642         int delay_secs = hbt ? hbt->refresh : 0;
1643         int key;
1644
1645         if (ui_browser__show(&menu->b, title,
1646                              "ESC: exit, ENTER|->: Browse histograms") < 0)
1647                 return -1;
1648
1649         while (1) {
1650                 key = ui_browser__run(&menu->b, delay_secs);
1651
1652                 switch (key) {
1653                 case K_TIMER:
1654                         hbt->timer(hbt->arg);
1655
1656                         if (!menu->lost_events_warned && menu->lost_events) {
1657                                 ui_browser__warn_lost_events(&menu->b);
1658                                 menu->lost_events_warned = true;
1659                         }
1660                         continue;
1661                 case K_RIGHT:
1662                 case K_ENTER:
1663                         if (!menu->selection)
1664                                 continue;
1665                         pos = menu->selection;
1666 browse_hists:
1667                         perf_evlist__set_selected(evlist, pos);
1668                         /*
1669                          * Give the calling tool a chance to populate the non
1670                          * default evsel resorted hists tree.
1671                          */
1672                         if (hbt)
1673                                 hbt->timer(hbt->arg);
1674                         ev_name = perf_evsel__name(pos);
1675                         key = perf_evsel__hists_browse(pos, nr_events, help,
1676                                                        ev_name, true, hbt,
1677                                                        menu->env);
1678                         ui_browser__show_title(&menu->b, title);
1679                         switch (key) {
1680                         case K_TAB:
1681                                 if (pos->node.next == &evlist->entries)
1682                                         pos = list_entry(evlist->entries.next, struct perf_evsel, node);
1683                                 else
1684                                         pos = list_entry(pos->node.next, struct perf_evsel, node);
1685                                 goto browse_hists;
1686                         case K_UNTAB:
1687                                 if (pos->node.prev == &evlist->entries)
1688                                         pos = list_entry(evlist->entries.prev, struct perf_evsel, node);
1689                                 else
1690                                         pos = list_entry(pos->node.prev, struct perf_evsel, node);
1691                                 goto browse_hists;
1692                         case K_ESC:
1693                                 if (!ui_browser__dialog_yesno(&menu->b,
1694                                                 "Do you really want to exit?"))
1695                                         continue;
1696                                 /* Fall thru */
1697                         case 'q':
1698                         case CTRL('c'):
1699                                 goto out;
1700                         default:
1701                                 continue;
1702                         }
1703                 case K_LEFT:
1704                         continue;
1705                 case K_ESC:
1706                         if (!ui_browser__dialog_yesno(&menu->b,
1707                                                "Do you really want to exit?"))
1708                                 continue;
1709                         /* Fall thru */
1710                 case 'q':
1711                 case CTRL('c'):
1712                         goto out;
1713                 default:
1714                         continue;
1715                 }
1716         }
1717
1718 out:
1719         ui_browser__hide(&menu->b);
1720         return key;
1721 }
1722
1723 static bool filter_group_entries(struct ui_browser *self __maybe_unused,
1724                                  void *entry)
1725 {
1726         struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1727
1728         if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
1729                 return true;
1730
1731         return false;
1732 }
1733
1734 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
1735                                            int nr_entries, const char *help,
1736                                            struct hist_browser_timer *hbt,
1737                                            struct perf_session_env *env)
1738 {
1739         struct perf_evsel *pos;
1740         struct perf_evsel_menu menu = {
1741                 .b = {
1742                         .entries    = &evlist->entries,
1743                         .refresh    = ui_browser__list_head_refresh,
1744                         .seek       = ui_browser__list_head_seek,
1745                         .write      = perf_evsel_menu__write,
1746                         .filter     = filter_group_entries,
1747                         .nr_entries = nr_entries,
1748                         .priv       = evlist,
1749                 },
1750                 .env = env,
1751         };
1752
1753         ui_helpline__push("Press ESC to exit");
1754
1755         list_for_each_entry(pos, &evlist->entries, node) {
1756                 const char *ev_name = perf_evsel__name(pos);
1757                 size_t line_len = strlen(ev_name) + 7;
1758
1759                 if (menu.b.width < line_len)
1760                         menu.b.width = line_len;
1761         }
1762
1763         return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
1764 }
1765
1766 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
1767                                   struct hist_browser_timer *hbt,
1768                                   struct perf_session_env *env)
1769 {
1770         int nr_entries = evlist->nr_entries;
1771
1772 single_entry:
1773         if (nr_entries == 1) {
1774                 struct perf_evsel *first = list_entry(evlist->entries.next,
1775                                                       struct perf_evsel, node);
1776                 const char *ev_name = perf_evsel__name(first);
1777
1778                 return perf_evsel__hists_browse(first, nr_entries, help,
1779                                                 ev_name, false, hbt, env);
1780         }
1781
1782         if (symbol_conf.event_group) {
1783                 struct perf_evsel *pos;
1784
1785                 nr_entries = 0;
1786                 list_for_each_entry(pos, &evlist->entries, node)
1787                         if (perf_evsel__is_group_leader(pos))
1788                                 nr_entries++;
1789
1790                 if (nr_entries == 1)
1791                         goto single_entry;
1792         }
1793
1794         return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
1795                                                hbt, env);
1796 }