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