2dcbe3d15a5f34ffc2b8d9eba6c97e4e5831a3d4
[firefly-linux-kernel-4.4.55.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__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
620 {
621         struct hpp_arg *arg = hpp->ptr;
622         int ret;
623         va_list args;
624         double percent;
625
626         va_start(args, fmt);
627         percent = va_arg(args, double);
628         va_end(args);
629
630         ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
631
632         ret = scnprintf(hpp->buf, hpp->size, fmt, percent);
633         slsmg_printf("%s", hpp->buf);
634
635         advance_hpp(hpp, ret);
636         return ret;
637 }
638
639 #define __HPP_COLOR_PERCENT_FN(_type, _field)                           \
640 static u64 __hpp_get_##_field(struct hist_entry *he)                    \
641 {                                                                       \
642         return he->stat._field;                                         \
643 }                                                                       \
644                                                                         \
645 static int                                                              \
646 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,\
647                                 struct perf_hpp *hpp,                   \
648                                 struct hist_entry *he)                  \
649 {                                                                       \
650         return __hpp__fmt(hpp, he, __hpp_get_##_field, " %6.2f%%",      \
651                           __hpp__slsmg_color_printf, true);             \
652 }
653
654 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field)                       \
655 static u64 __hpp_get_acc_##_field(struct hist_entry *he)                \
656 {                                                                       \
657         return he->stat_acc->_field;                                    \
658 }                                                                       \
659                                                                         \
660 static int                                                              \
661 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,\
662                                 struct perf_hpp *hpp,                   \
663                                 struct hist_entry *he)                  \
664 {                                                                       \
665         if (!symbol_conf.cumulate_callchain) {                          \
666                 int ret = scnprintf(hpp->buf, hpp->size, "%8s", "N/A"); \
667                 slsmg_printf("%s", hpp->buf);                           \
668                                                                         \
669                 return ret;                                             \
670         }                                                               \
671         return __hpp__fmt(hpp, he, __hpp_get_acc_##_field, " %6.2f%%",  \
672                           __hpp__slsmg_color_printf, true);             \
673 }
674
675 __HPP_COLOR_PERCENT_FN(overhead, period)
676 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
677 __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
678 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
679 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
680 __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
681
682 #undef __HPP_COLOR_PERCENT_FN
683 #undef __HPP_COLOR_ACC_PERCENT_FN
684
685 void hist_browser__init_hpp(void)
686 {
687         perf_hpp__format[PERF_HPP__OVERHEAD].color =
688                                 hist_browser__hpp_color_overhead;
689         perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
690                                 hist_browser__hpp_color_overhead_sys;
691         perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
692                                 hist_browser__hpp_color_overhead_us;
693         perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
694                                 hist_browser__hpp_color_overhead_guest_sys;
695         perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
696                                 hist_browser__hpp_color_overhead_guest_us;
697         perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
698                                 hist_browser__hpp_color_overhead_acc;
699 }
700
701 static int hist_browser__show_entry(struct hist_browser *browser,
702                                     struct hist_entry *entry,
703                                     unsigned short row)
704 {
705         char s[256];
706         int printed = 0;
707         int width = browser->b.width;
708         char folded_sign = ' ';
709         bool current_entry = ui_browser__is_current_entry(&browser->b, row);
710         off_t row_offset = entry->row_offset;
711         bool first = true;
712         struct perf_hpp_fmt *fmt;
713
714         if (current_entry) {
715                 browser->he_selection = entry;
716                 browser->selection = &entry->ms;
717         }
718
719         if (symbol_conf.use_callchain) {
720                 hist_entry__init_have_children(entry);
721                 folded_sign = hist_entry__folded(entry);
722         }
723
724         if (row_offset == 0) {
725                 struct hpp_arg arg = {
726                         .b              = &browser->b,
727                         .folded_sign    = folded_sign,
728                         .current_entry  = current_entry,
729                 };
730                 struct perf_hpp hpp = {
731                         .buf            = s,
732                         .size           = sizeof(s),
733                         .ptr            = &arg,
734                 };
735
736                 ui_browser__gotorc(&browser->b, row, 0);
737
738                 perf_hpp__for_each_format(fmt) {
739                         if (perf_hpp__should_skip(fmt))
740                                 continue;
741
742                         if (current_entry && browser->b.navkeypressed) {
743                                 ui_browser__set_color(&browser->b,
744                                                       HE_COLORSET_SELECTED);
745                         } else {
746                                 ui_browser__set_color(&browser->b,
747                                                       HE_COLORSET_NORMAL);
748                         }
749
750                         if (first) {
751                                 if (symbol_conf.use_callchain) {
752                                         slsmg_printf("%c ", folded_sign);
753                                         width -= 2;
754                                 }
755                                 first = false;
756                         } else {
757                                 slsmg_printf("  ");
758                                 width -= 2;
759                         }
760
761                         if (fmt->color) {
762                                 width -= fmt->color(fmt, &hpp, entry);
763                         } else {
764                                 width -= fmt->entry(fmt, &hpp, entry);
765                                 slsmg_printf("%s", s);
766                         }
767                 }
768
769                 /* The scroll bar isn't being used */
770                 if (!browser->b.navkeypressed)
771                         width += 1;
772
773                 slsmg_write_nstring("", width);
774
775                 ++row;
776                 ++printed;
777         } else
778                 --row_offset;
779
780         if (folded_sign == '-' && row != browser->b.height) {
781                 printed += hist_browser__show_callchain(browser, &entry->sorted_chain,
782                                                         1, row, &row_offset,
783                                                         &current_entry);
784                 if (current_entry)
785                         browser->he_selection = entry;
786         }
787
788         return printed;
789 }
790
791 static void ui_browser__hists_init_top(struct ui_browser *browser)
792 {
793         if (browser->top == NULL) {
794                 struct hist_browser *hb;
795
796                 hb = container_of(browser, struct hist_browser, b);
797                 browser->top = rb_first(&hb->hists->entries);
798         }
799 }
800
801 static unsigned int hist_browser__refresh(struct ui_browser *browser)
802 {
803         unsigned row = 0;
804         struct rb_node *nd;
805         struct hist_browser *hb = container_of(browser, struct hist_browser, b);
806
807         ui_browser__hists_init_top(browser);
808
809         for (nd = browser->top; nd; nd = rb_next(nd)) {
810                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
811                 u64 total = hists__total_period(h->hists);
812                 float percent = 0.0;
813
814                 if (h->filtered)
815                         continue;
816
817                 if (total)
818                         percent = h->stat.period * 100.0 / total;
819
820                 if (percent < hb->min_pcnt)
821                         continue;
822
823                 row += hist_browser__show_entry(hb, h, row);
824                 if (row == browser->height)
825                         break;
826         }
827
828         return row;
829 }
830
831 static struct rb_node *hists__filter_entries(struct rb_node *nd,
832                                              struct hists *hists,
833                                              float min_pcnt)
834 {
835         while (nd != NULL) {
836                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
837                 u64 total = hists__total_period(hists);
838                 float percent = 0.0;
839
840                 if (total)
841                         percent = h->stat.period * 100.0 / total;
842
843                 if (!h->filtered && percent >= min_pcnt)
844                         return nd;
845
846                 nd = rb_next(nd);
847         }
848
849         return NULL;
850 }
851
852 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
853                                                   struct hists *hists,
854                                                   float min_pcnt)
855 {
856         while (nd != NULL) {
857                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
858                 u64 total = hists__total_period(hists);
859                 float percent = 0.0;
860
861                 if (total)
862                         percent = h->stat.period * 100.0 / total;
863
864                 if (!h->filtered && percent >= min_pcnt)
865                         return nd;
866
867                 nd = rb_prev(nd);
868         }
869
870         return NULL;
871 }
872
873 static void ui_browser__hists_seek(struct ui_browser *browser,
874                                    off_t offset, int whence)
875 {
876         struct hist_entry *h;
877         struct rb_node *nd;
878         bool first = true;
879         struct hist_browser *hb;
880
881         hb = container_of(browser, struct hist_browser, b);
882
883         if (browser->nr_entries == 0)
884                 return;
885
886         ui_browser__hists_init_top(browser);
887
888         switch (whence) {
889         case SEEK_SET:
890                 nd = hists__filter_entries(rb_first(browser->entries),
891                                            hb->hists, hb->min_pcnt);
892                 break;
893         case SEEK_CUR:
894                 nd = browser->top;
895                 goto do_offset;
896         case SEEK_END:
897                 nd = hists__filter_prev_entries(rb_last(browser->entries),
898                                                 hb->hists, hb->min_pcnt);
899                 first = false;
900                 break;
901         default:
902                 return;
903         }
904
905         /*
906          * Moves not relative to the first visible entry invalidates its
907          * row_offset:
908          */
909         h = rb_entry(browser->top, struct hist_entry, rb_node);
910         h->row_offset = 0;
911
912         /*
913          * Here we have to check if nd is expanded (+), if it is we can't go
914          * the next top level hist_entry, instead we must compute an offset of
915          * what _not_ to show and not change the first visible entry.
916          *
917          * This offset increments when we are going from top to bottom and
918          * decreases when we're going from bottom to top.
919          *
920          * As we don't have backpointers to the top level in the callchains
921          * structure, we need to always print the whole hist_entry callchain,
922          * skipping the first ones that are before the first visible entry
923          * and stop when we printed enough lines to fill the screen.
924          */
925 do_offset:
926         if (offset > 0) {
927                 do {
928                         h = rb_entry(nd, struct hist_entry, rb_node);
929                         if (h->ms.unfolded) {
930                                 u16 remaining = h->nr_rows - h->row_offset;
931                                 if (offset > remaining) {
932                                         offset -= remaining;
933                                         h->row_offset = 0;
934                                 } else {
935                                         h->row_offset += offset;
936                                         offset = 0;
937                                         browser->top = nd;
938                                         break;
939                                 }
940                         }
941                         nd = hists__filter_entries(rb_next(nd), hb->hists,
942                                                    hb->min_pcnt);
943                         if (nd == NULL)
944                                 break;
945                         --offset;
946                         browser->top = nd;
947                 } while (offset != 0);
948         } else if (offset < 0) {
949                 while (1) {
950                         h = rb_entry(nd, struct hist_entry, rb_node);
951                         if (h->ms.unfolded) {
952                                 if (first) {
953                                         if (-offset > h->row_offset) {
954                                                 offset += h->row_offset;
955                                                 h->row_offset = 0;
956                                         } else {
957                                                 h->row_offset += offset;
958                                                 offset = 0;
959                                                 browser->top = nd;
960                                                 break;
961                                         }
962                                 } else {
963                                         if (-offset > h->nr_rows) {
964                                                 offset += h->nr_rows;
965                                                 h->row_offset = 0;
966                                         } else {
967                                                 h->row_offset = h->nr_rows + offset;
968                                                 offset = 0;
969                                                 browser->top = nd;
970                                                 break;
971                                         }
972                                 }
973                         }
974
975                         nd = hists__filter_prev_entries(rb_prev(nd), hb->hists,
976                                                         hb->min_pcnt);
977                         if (nd == NULL)
978                                 break;
979                         ++offset;
980                         browser->top = nd;
981                         if (offset == 0) {
982                                 /*
983                                  * Last unfiltered hist_entry, check if it is
984                                  * unfolded, if it is then we should have
985                                  * row_offset at its last entry.
986                                  */
987                                 h = rb_entry(nd, struct hist_entry, rb_node);
988                                 if (h->ms.unfolded)
989                                         h->row_offset = h->nr_rows;
990                                 break;
991                         }
992                         first = false;
993                 }
994         } else {
995                 browser->top = nd;
996                 h = rb_entry(nd, struct hist_entry, rb_node);
997                 h->row_offset = 0;
998         }
999 }
1000
1001 static int hist_browser__fprintf_callchain_node_rb_tree(struct hist_browser *browser,
1002                                                         struct callchain_node *chain_node,
1003                                                         u64 total, int level,
1004                                                         FILE *fp)
1005 {
1006         struct rb_node *node;
1007         int offset = level * LEVEL_OFFSET_STEP;
1008         u64 new_total, remaining;
1009         int printed = 0;
1010
1011         if (callchain_param.mode == CHAIN_GRAPH_REL)
1012                 new_total = chain_node->children_hit;
1013         else
1014                 new_total = total;
1015
1016         remaining = new_total;
1017         node = rb_first(&chain_node->rb_root);
1018         while (node) {
1019                 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1020                 struct rb_node *next = rb_next(node);
1021                 u64 cumul = callchain_cumul_hits(child);
1022                 struct callchain_list *chain;
1023                 char folded_sign = ' ';
1024                 int first = true;
1025                 int extra_offset = 0;
1026
1027                 remaining -= cumul;
1028
1029                 list_for_each_entry(chain, &child->val, list) {
1030                         char bf[1024], *alloc_str;
1031                         const char *str;
1032                         bool was_first = first;
1033
1034                         if (first)
1035                                 first = false;
1036                         else
1037                                 extra_offset = LEVEL_OFFSET_STEP;
1038
1039                         folded_sign = callchain_list__folded(chain);
1040
1041                         alloc_str = NULL;
1042                         str = callchain_list__sym_name(chain, bf, sizeof(bf),
1043                                                        browser->show_dso);
1044                         if (was_first) {
1045                                 double percent = cumul * 100.0 / new_total;
1046
1047                                 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
1048                                         str = "Not enough memory!";
1049                                 else
1050                                         str = alloc_str;
1051                         }
1052
1053                         printed += fprintf(fp, "%*s%c %s\n", offset + extra_offset, " ", folded_sign, str);
1054                         free(alloc_str);
1055                         if (folded_sign == '+')
1056                                 break;
1057                 }
1058
1059                 if (folded_sign == '-') {
1060                         const int new_level = level + (extra_offset ? 2 : 1);
1061                         printed += hist_browser__fprintf_callchain_node_rb_tree(browser, child, new_total,
1062                                                                                 new_level, fp);
1063                 }
1064
1065                 node = next;
1066         }
1067
1068         return printed;
1069 }
1070
1071 static int hist_browser__fprintf_callchain_node(struct hist_browser *browser,
1072                                                 struct callchain_node *node,
1073                                                 int level, FILE *fp)
1074 {
1075         struct callchain_list *chain;
1076         int offset = level * LEVEL_OFFSET_STEP;
1077         char folded_sign = ' ';
1078         int printed = 0;
1079
1080         list_for_each_entry(chain, &node->val, list) {
1081                 char bf[1024], *s;
1082
1083                 folded_sign = callchain_list__folded(chain);
1084                 s = callchain_list__sym_name(chain, bf, sizeof(bf), browser->show_dso);
1085                 printed += fprintf(fp, "%*s%c %s\n", offset, " ", folded_sign, s);
1086         }
1087
1088         if (folded_sign == '-')
1089                 printed += hist_browser__fprintf_callchain_node_rb_tree(browser, node,
1090                                                                         browser->hists->stats.total_period,
1091                                                                         level + 1,  fp);
1092         return printed;
1093 }
1094
1095 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1096                                            struct rb_root *chain, int level, FILE *fp)
1097 {
1098         struct rb_node *nd;
1099         int printed = 0;
1100
1101         for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
1102                 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
1103
1104                 printed += hist_browser__fprintf_callchain_node(browser, node, level, fp);
1105         }
1106
1107         return printed;
1108 }
1109
1110 static int hist_browser__fprintf_entry(struct hist_browser *browser,
1111                                        struct hist_entry *he, FILE *fp)
1112 {
1113         char s[8192];
1114         int printed = 0;
1115         char folded_sign = ' ';
1116         struct perf_hpp hpp = {
1117                 .buf = s,
1118                 .size = sizeof(s),
1119         };
1120         struct perf_hpp_fmt *fmt;
1121         bool first = true;
1122         int ret;
1123
1124         if (symbol_conf.use_callchain)
1125                 folded_sign = hist_entry__folded(he);
1126
1127         if (symbol_conf.use_callchain)
1128                 printed += fprintf(fp, "%c ", folded_sign);
1129
1130         perf_hpp__for_each_format(fmt) {
1131                 if (perf_hpp__should_skip(fmt))
1132                         continue;
1133
1134                 if (!first) {
1135                         ret = scnprintf(hpp.buf, hpp.size, "  ");
1136                         advance_hpp(&hpp, ret);
1137                 } else
1138                         first = false;
1139
1140                 ret = fmt->entry(fmt, &hpp, he);
1141                 advance_hpp(&hpp, ret);
1142         }
1143         printed += fprintf(fp, "%s\n", rtrim(s));
1144
1145         if (folded_sign == '-')
1146                 printed += hist_browser__fprintf_callchain(browser, &he->sorted_chain, 1, fp);
1147
1148         return printed;
1149 }
1150
1151 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1152 {
1153         struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
1154                                                    browser->hists,
1155                                                    browser->min_pcnt);
1156         int printed = 0;
1157
1158         while (nd) {
1159                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1160
1161                 printed += hist_browser__fprintf_entry(browser, h, fp);
1162                 nd = hists__filter_entries(rb_next(nd), browser->hists,
1163                                            browser->min_pcnt);
1164         }
1165
1166         return printed;
1167 }
1168
1169 static int hist_browser__dump(struct hist_browser *browser)
1170 {
1171         char filename[64];
1172         FILE *fp;
1173
1174         while (1) {
1175                 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
1176                 if (access(filename, F_OK))
1177                         break;
1178                 /*
1179                  * XXX: Just an arbitrary lazy upper limit
1180                  */
1181                 if (++browser->print_seq == 8192) {
1182                         ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1183                         return -1;
1184                 }
1185         }
1186
1187         fp = fopen(filename, "w");
1188         if (fp == NULL) {
1189                 char bf[64];
1190                 const char *err = strerror_r(errno, bf, sizeof(bf));
1191                 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
1192                 return -1;
1193         }
1194
1195         ++browser->print_seq;
1196         hist_browser__fprintf(browser, fp);
1197         fclose(fp);
1198         ui_helpline__fpush("%s written!", filename);
1199
1200         return 0;
1201 }
1202
1203 static struct hist_browser *hist_browser__new(struct hists *hists)
1204 {
1205         struct hist_browser *browser = zalloc(sizeof(*browser));
1206
1207         if (browser) {
1208                 browser->hists = hists;
1209                 browser->b.refresh = hist_browser__refresh;
1210                 browser->b.seek = ui_browser__hists_seek;
1211                 browser->b.use_navkeypressed = true;
1212         }
1213
1214         return browser;
1215 }
1216
1217 static void hist_browser__delete(struct hist_browser *browser)
1218 {
1219         free(browser);
1220 }
1221
1222 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
1223 {
1224         return browser->he_selection;
1225 }
1226
1227 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
1228 {
1229         return browser->he_selection->thread;
1230 }
1231
1232 static int hists__browser_title(struct hists *hists, char *bf, size_t size,
1233                                 const char *ev_name)
1234 {
1235         char unit;
1236         int printed;
1237         const struct dso *dso = hists->dso_filter;
1238         const struct thread *thread = hists->thread_filter;
1239         unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1240         u64 nr_events = hists->stats.total_period;
1241         struct perf_evsel *evsel = hists_to_evsel(hists);
1242         char buf[512];
1243         size_t buflen = sizeof(buf);
1244
1245         if (symbol_conf.filter_relative) {
1246                 nr_samples = hists->stats.nr_non_filtered_samples;
1247                 nr_events = hists->stats.total_non_filtered_period;
1248         }
1249
1250         if (perf_evsel__is_group_event(evsel)) {
1251                 struct perf_evsel *pos;
1252
1253                 perf_evsel__group_desc(evsel, buf, buflen);
1254                 ev_name = buf;
1255
1256                 for_each_group_member(pos, evsel) {
1257                         if (symbol_conf.filter_relative) {
1258                                 nr_samples += pos->hists.stats.nr_non_filtered_samples;
1259                                 nr_events += pos->hists.stats.total_non_filtered_period;
1260                         } else {
1261                                 nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1262                                 nr_events += pos->hists.stats.total_period;
1263                         }
1264                 }
1265         }
1266
1267         nr_samples = convert_unit(nr_samples, &unit);
1268         printed = scnprintf(bf, size,
1269                            "Samples: %lu%c of event '%s', Event count (approx.): %lu",
1270                            nr_samples, unit, ev_name, nr_events);
1271
1272
1273         if (hists->uid_filter_str)
1274                 printed += snprintf(bf + printed, size - printed,
1275                                     ", UID: %s", hists->uid_filter_str);
1276         if (thread)
1277                 printed += scnprintf(bf + printed, size - printed,
1278                                     ", Thread: %s(%d)",
1279                                      (thread->comm_set ? thread__comm_str(thread) : ""),
1280                                     thread->tid);
1281         if (dso)
1282                 printed += scnprintf(bf + printed, size - printed,
1283                                     ", DSO: %s", dso->short_name);
1284         return printed;
1285 }
1286
1287 static inline void free_popup_options(char **options, int n)
1288 {
1289         int i;
1290
1291         for (i = 0; i < n; ++i)
1292                 zfree(&options[i]);
1293 }
1294
1295 /* Check whether the browser is for 'top' or 'report' */
1296 static inline bool is_report_browser(void *timer)
1297 {
1298         return timer == NULL;
1299 }
1300
1301 /*
1302  * Only runtime switching of perf data file will make "input_name" point
1303  * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1304  * whether we need to call free() for current "input_name" during the switch.
1305  */
1306 static bool is_input_name_malloced = false;
1307
1308 static int switch_data_file(void)
1309 {
1310         char *pwd, *options[32], *abs_path[32], *tmp;
1311         DIR *pwd_dir;
1312         int nr_options = 0, choice = -1, ret = -1;
1313         struct dirent *dent;
1314
1315         pwd = getenv("PWD");
1316         if (!pwd)
1317                 return ret;
1318
1319         pwd_dir = opendir(pwd);
1320         if (!pwd_dir)
1321                 return ret;
1322
1323         memset(options, 0, sizeof(options));
1324         memset(options, 0, sizeof(abs_path));
1325
1326         while ((dent = readdir(pwd_dir))) {
1327                 char path[PATH_MAX];
1328                 u64 magic;
1329                 char *name = dent->d_name;
1330                 FILE *file;
1331
1332                 if (!(dent->d_type == DT_REG))
1333                         continue;
1334
1335                 snprintf(path, sizeof(path), "%s/%s", pwd, name);
1336
1337                 file = fopen(path, "r");
1338                 if (!file)
1339                         continue;
1340
1341                 if (fread(&magic, 1, 8, file) < 8)
1342                         goto close_file_and_continue;
1343
1344                 if (is_perf_magic(magic)) {
1345                         options[nr_options] = strdup(name);
1346                         if (!options[nr_options])
1347                                 goto close_file_and_continue;
1348
1349                         abs_path[nr_options] = strdup(path);
1350                         if (!abs_path[nr_options]) {
1351                                 zfree(&options[nr_options]);
1352                                 ui__warning("Can't search all data files due to memory shortage.\n");
1353                                 fclose(file);
1354                                 break;
1355                         }
1356
1357                         nr_options++;
1358                 }
1359
1360 close_file_and_continue:
1361                 fclose(file);
1362                 if (nr_options >= 32) {
1363                         ui__warning("Too many perf data files in PWD!\n"
1364                                     "Only the first 32 files will be listed.\n");
1365                         break;
1366                 }
1367         }
1368         closedir(pwd_dir);
1369
1370         if (nr_options) {
1371                 choice = ui__popup_menu(nr_options, options);
1372                 if (choice < nr_options && choice >= 0) {
1373                         tmp = strdup(abs_path[choice]);
1374                         if (tmp) {
1375                                 if (is_input_name_malloced)
1376                                         free((void *)input_name);
1377                                 input_name = tmp;
1378                                 is_input_name_malloced = true;
1379                                 ret = 0;
1380                         } else
1381                                 ui__warning("Data switch failed due to memory shortage!\n");
1382                 }
1383         }
1384
1385         free_popup_options(options, nr_options);
1386         free_popup_options(abs_path, nr_options);
1387         return ret;
1388 }
1389
1390 static void hist_browser__update_nr_entries(struct hist_browser *hb)
1391 {
1392         u64 nr_entries = 0;
1393         struct rb_node *nd = rb_first(&hb->hists->entries);
1394
1395         if (hb->min_pcnt == 0) {
1396                 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
1397                 return;
1398         }
1399
1400         while ((nd = hists__filter_entries(nd, hb->hists,
1401                                            hb->min_pcnt)) != NULL) {
1402                 nr_entries++;
1403                 nd = rb_next(nd);
1404         }
1405
1406         hb->nr_non_filtered_entries = nr_entries;
1407 }
1408
1409 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
1410                                     const char *helpline, const char *ev_name,
1411                                     bool left_exits,
1412                                     struct hist_browser_timer *hbt,
1413                                     float min_pcnt,
1414                                     struct perf_session_env *env)
1415 {
1416         struct hists *hists = &evsel->hists;
1417         struct hist_browser *browser = hist_browser__new(hists);
1418         struct branch_info *bi;
1419         struct pstack *fstack;
1420         char *options[16];
1421         int nr_options = 0;
1422         int key = -1;
1423         char buf[64];
1424         char script_opt[64];
1425         int delay_secs = hbt ? hbt->refresh : 0;
1426
1427 #define HIST_BROWSER_HELP_COMMON                                        \
1428         "h/?/F1        Show this window\n"                              \
1429         "UP/DOWN/PGUP\n"                                                \
1430         "PGDN/SPACE    Navigate\n"                                      \
1431         "q/ESC/CTRL+C  Exit browser\n\n"                                \
1432         "For multiple event sessions:\n\n"                              \
1433         "TAB/UNTAB     Switch events\n\n"                               \
1434         "For symbolic views (--sort has sym):\n\n"                      \
1435         "->            Zoom into DSO/Threads & Annotate current symbol\n" \
1436         "<-            Zoom out\n"                                      \
1437         "a             Annotate current symbol\n"                       \
1438         "C             Collapse all callchains\n"                       \
1439         "d             Zoom into current DSO\n"                         \
1440         "E             Expand all callchains\n"                         \
1441         "F             Toggle percentage of filtered entries\n"         \
1442
1443         /* help messages are sorted by lexical order of the hotkey */
1444         const char report_help[] = HIST_BROWSER_HELP_COMMON
1445         "i             Show header information\n"
1446         "P             Print histograms to perf.hist.N\n"
1447         "r             Run available scripts\n"
1448         "s             Switch to another data file in PWD\n"
1449         "t             Zoom into current Thread\n"
1450         "V             Verbose (DSO names in callchains, etc)\n"
1451         "/             Filter symbol by name";
1452         const char top_help[] = HIST_BROWSER_HELP_COMMON
1453         "P             Print histograms to perf.hist.N\n"
1454         "t             Zoom into current Thread\n"
1455         "V             Verbose (DSO names in callchains, etc)\n"
1456         "/             Filter symbol by name";
1457
1458         if (browser == NULL)
1459                 return -1;
1460
1461         if (min_pcnt) {
1462                 browser->min_pcnt = min_pcnt;
1463                 hist_browser__update_nr_entries(browser);
1464         }
1465
1466         fstack = pstack__new(2);
1467         if (fstack == NULL)
1468                 goto out;
1469
1470         ui_helpline__push(helpline);
1471
1472         memset(options, 0, sizeof(options));
1473
1474         while (1) {
1475                 const struct thread *thread = NULL;
1476                 const struct dso *dso = NULL;
1477                 int choice = 0,
1478                     annotate = -2, zoom_dso = -2, zoom_thread = -2,
1479                     annotate_f = -2, annotate_t = -2, browse_map = -2;
1480                 int scripts_comm = -2, scripts_symbol = -2,
1481                     scripts_all = -2, switch_data = -2;
1482
1483                 nr_options = 0;
1484
1485                 key = hist_browser__run(browser, ev_name, hbt);
1486
1487                 if (browser->he_selection != NULL) {
1488                         thread = hist_browser__selected_thread(browser);
1489                         dso = browser->selection->map ? browser->selection->map->dso : NULL;
1490                 }
1491                 switch (key) {
1492                 case K_TAB:
1493                 case K_UNTAB:
1494                         if (nr_events == 1)
1495                                 continue;
1496                         /*
1497                          * Exit the browser, let hists__browser_tree
1498                          * go to the next or previous
1499                          */
1500                         goto out_free_stack;
1501                 case 'a':
1502                         if (!sort__has_sym) {
1503                                 ui_browser__warning(&browser->b, delay_secs * 2,
1504                         "Annotation is only available for symbolic views, "
1505                         "include \"sym*\" in --sort to use it.");
1506                                 continue;
1507                         }
1508
1509                         if (browser->selection == NULL ||
1510                             browser->selection->sym == NULL ||
1511                             browser->selection->map->dso->annotate_warned)
1512                                 continue;
1513                         goto do_annotate;
1514                 case 'P':
1515                         hist_browser__dump(browser);
1516                         continue;
1517                 case 'd':
1518                         goto zoom_dso;
1519                 case 'V':
1520                         browser->show_dso = !browser->show_dso;
1521                         continue;
1522                 case 't':
1523                         goto zoom_thread;
1524                 case '/':
1525                         if (ui_browser__input_window("Symbol to show",
1526                                         "Please enter the name of symbol you want to see",
1527                                         buf, "ENTER: OK, ESC: Cancel",
1528                                         delay_secs * 2) == K_ENTER) {
1529                                 hists->symbol_filter_str = *buf ? buf : NULL;
1530                                 hists__filter_by_symbol(hists);
1531                                 hist_browser__reset(browser);
1532                         }
1533                         continue;
1534                 case 'r':
1535                         if (is_report_browser(hbt))
1536                                 goto do_scripts;
1537                         continue;
1538                 case 's':
1539                         if (is_report_browser(hbt))
1540                                 goto do_data_switch;
1541                         continue;
1542                 case 'i':
1543                         /* env->arch is NULL for live-mode (i.e. perf top) */
1544                         if (env->arch)
1545                                 tui__header_window(env);
1546                         continue;
1547                 case 'F':
1548                         symbol_conf.filter_relative ^= 1;
1549                         continue;
1550                 case K_F1:
1551                 case 'h':
1552                 case '?':
1553                         ui_browser__help_window(&browser->b,
1554                                 is_report_browser(hbt) ? report_help : top_help);
1555                         continue;
1556                 case K_ENTER:
1557                 case K_RIGHT:
1558                         /* menu */
1559                         break;
1560                 case K_LEFT: {
1561                         const void *top;
1562
1563                         if (pstack__empty(fstack)) {
1564                                 /*
1565                                  * Go back to the perf_evsel_menu__run or other user
1566                                  */
1567                                 if (left_exits)
1568                                         goto out_free_stack;
1569                                 continue;
1570                         }
1571                         top = pstack__pop(fstack);
1572                         if (top == &browser->hists->dso_filter)
1573                                 goto zoom_out_dso;
1574                         if (top == &browser->hists->thread_filter)
1575                                 goto zoom_out_thread;
1576                         continue;
1577                 }
1578                 case K_ESC:
1579                         if (!left_exits &&
1580                             !ui_browser__dialog_yesno(&browser->b,
1581                                                "Do you really want to exit?"))
1582                                 continue;
1583                         /* Fall thru */
1584                 case 'q':
1585                 case CTRL('c'):
1586                         goto out_free_stack;
1587                 default:
1588                         continue;
1589                 }
1590
1591                 if (!sort__has_sym)
1592                         goto add_exit_option;
1593
1594                 if (sort__mode == SORT_MODE__BRANCH) {
1595                         bi = browser->he_selection->branch_info;
1596                         if (browser->selection != NULL &&
1597                             bi &&
1598                             bi->from.sym != NULL &&
1599                             !bi->from.map->dso->annotate_warned &&
1600                                 asprintf(&options[nr_options], "Annotate %s",
1601                                          bi->from.sym->name) > 0)
1602                                 annotate_f = nr_options++;
1603
1604                         if (browser->selection != NULL &&
1605                             bi &&
1606                             bi->to.sym != NULL &&
1607                             !bi->to.map->dso->annotate_warned &&
1608                             (bi->to.sym != bi->from.sym ||
1609                              bi->to.map->dso != bi->from.map->dso) &&
1610                                 asprintf(&options[nr_options], "Annotate %s",
1611                                          bi->to.sym->name) > 0)
1612                                 annotate_t = nr_options++;
1613                 } else {
1614
1615                         if (browser->selection != NULL &&
1616                             browser->selection->sym != NULL &&
1617                             !browser->selection->map->dso->annotate_warned &&
1618                                 asprintf(&options[nr_options], "Annotate %s",
1619                                          browser->selection->sym->name) > 0)
1620                                 annotate = nr_options++;
1621                 }
1622
1623                 if (thread != NULL &&
1624                     asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
1625                              (browser->hists->thread_filter ? "out of" : "into"),
1626                              (thread->comm_set ? thread__comm_str(thread) : ""),
1627                              thread->tid) > 0)
1628                         zoom_thread = nr_options++;
1629
1630                 if (dso != NULL &&
1631                     asprintf(&options[nr_options], "Zoom %s %s DSO",
1632                              (browser->hists->dso_filter ? "out of" : "into"),
1633                              (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
1634                         zoom_dso = nr_options++;
1635
1636                 if (browser->selection != NULL &&
1637                     browser->selection->map != NULL &&
1638                     asprintf(&options[nr_options], "Browse map details") > 0)
1639                         browse_map = nr_options++;
1640
1641                 /* perf script support */
1642                 if (browser->he_selection) {
1643                         struct symbol *sym;
1644
1645                         if (asprintf(&options[nr_options], "Run scripts for samples of thread [%s]",
1646                                      thread__comm_str(browser->he_selection->thread)) > 0)
1647                                 scripts_comm = nr_options++;
1648
1649                         sym = browser->he_selection->ms.sym;
1650                         if (sym && sym->namelen &&
1651                                 asprintf(&options[nr_options], "Run scripts for samples of symbol [%s]",
1652                                                 sym->name) > 0)
1653                                 scripts_symbol = nr_options++;
1654                 }
1655
1656                 if (asprintf(&options[nr_options], "Run scripts for all samples") > 0)
1657                         scripts_all = nr_options++;
1658
1659                 if (is_report_browser(hbt) && asprintf(&options[nr_options],
1660                                 "Switch to another data file in PWD") > 0)
1661                         switch_data = nr_options++;
1662 add_exit_option:
1663                 options[nr_options++] = (char *)"Exit";
1664 retry_popup_menu:
1665                 choice = ui__popup_menu(nr_options, options);
1666
1667                 if (choice == nr_options - 1)
1668                         break;
1669
1670                 if (choice == -1) {
1671                         free_popup_options(options, nr_options - 1);
1672                         continue;
1673                 }
1674
1675                 if (choice == annotate || choice == annotate_t || choice == annotate_f) {
1676                         struct hist_entry *he;
1677                         int err;
1678 do_annotate:
1679                         if (!objdump_path && perf_session_env__lookup_objdump(env))
1680                                 continue;
1681
1682                         he = hist_browser__selected_entry(browser);
1683                         if (he == NULL)
1684                                 continue;
1685
1686                         /*
1687                          * we stash the branch_info symbol + map into the
1688                          * the ms so we don't have to rewrite all the annotation
1689                          * code to use branch_info.
1690                          * in branch mode, the ms struct is not used
1691                          */
1692                         if (choice == annotate_f) {
1693                                 he->ms.sym = he->branch_info->from.sym;
1694                                 he->ms.map = he->branch_info->from.map;
1695                         }  else if (choice == annotate_t) {
1696                                 he->ms.sym = he->branch_info->to.sym;
1697                                 he->ms.map = he->branch_info->to.map;
1698                         }
1699
1700                         /*
1701                          * Don't let this be freed, say, by hists__decay_entry.
1702                          */
1703                         he->used = true;
1704                         err = hist_entry__tui_annotate(he, evsel, hbt);
1705                         he->used = false;
1706                         /*
1707                          * offer option to annotate the other branch source or target
1708                          * (if they exists) when returning from annotate
1709                          */
1710                         if ((err == 'q' || err == CTRL('c'))
1711                             && annotate_t != -2 && annotate_f != -2)
1712                                 goto retry_popup_menu;
1713
1714                         ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1715                         if (err)
1716                                 ui_browser__handle_resize(&browser->b);
1717
1718                 } else if (choice == browse_map)
1719                         map__browse(browser->selection->map);
1720                 else if (choice == zoom_dso) {
1721 zoom_dso:
1722                         if (browser->hists->dso_filter) {
1723                                 pstack__remove(fstack, &browser->hists->dso_filter);
1724 zoom_out_dso:
1725                                 ui_helpline__pop();
1726                                 browser->hists->dso_filter = NULL;
1727                                 sort_dso.elide = false;
1728                         } else {
1729                                 if (dso == NULL)
1730                                         continue;
1731                                 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1732                                                    dso->kernel ? "the Kernel" : dso->short_name);
1733                                 browser->hists->dso_filter = dso;
1734                                 sort_dso.elide = true;
1735                                 pstack__push(fstack, &browser->hists->dso_filter);
1736                         }
1737                         hists__filter_by_dso(hists);
1738                         hist_browser__reset(browser);
1739                 } else if (choice == zoom_thread) {
1740 zoom_thread:
1741                         if (browser->hists->thread_filter) {
1742                                 pstack__remove(fstack, &browser->hists->thread_filter);
1743 zoom_out_thread:
1744                                 ui_helpline__pop();
1745                                 browser->hists->thread_filter = NULL;
1746                                 sort_thread.elide = false;
1747                         } else {
1748                                 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1749                                                    thread->comm_set ? thread__comm_str(thread) : "",
1750                                                    thread->tid);
1751                                 browser->hists->thread_filter = thread;
1752                                 sort_thread.elide = true;
1753                                 pstack__push(fstack, &browser->hists->thread_filter);
1754                         }
1755                         hists__filter_by_thread(hists);
1756                         hist_browser__reset(browser);
1757                 }
1758                 /* perf scripts support */
1759                 else if (choice == scripts_all || choice == scripts_comm ||
1760                                 choice == scripts_symbol) {
1761 do_scripts:
1762                         memset(script_opt, 0, 64);
1763
1764                         if (choice == scripts_comm)
1765                                 sprintf(script_opt, " -c %s ", thread__comm_str(browser->he_selection->thread));
1766
1767                         if (choice == scripts_symbol)
1768                                 sprintf(script_opt, " -S %s ", browser->he_selection->ms.sym->name);
1769
1770                         script_browse(script_opt);
1771                 }
1772                 /* Switch to another data file */
1773                 else if (choice == switch_data) {
1774 do_data_switch:
1775                         if (!switch_data_file()) {
1776                                 key = K_SWITCH_INPUT_DATA;
1777                                 break;
1778                         } else
1779                                 ui__warning("Won't switch the data files due to\n"
1780                                         "no valid data file get selected!\n");
1781                 }
1782         }
1783 out_free_stack:
1784         pstack__delete(fstack);
1785 out:
1786         hist_browser__delete(browser);
1787         free_popup_options(options, nr_options - 1);
1788         return key;
1789 }
1790
1791 struct perf_evsel_menu {
1792         struct ui_browser b;
1793         struct perf_evsel *selection;
1794         bool lost_events, lost_events_warned;
1795         float min_pcnt;
1796         struct perf_session_env *env;
1797 };
1798
1799 static void perf_evsel_menu__write(struct ui_browser *browser,
1800                                    void *entry, int row)
1801 {
1802         struct perf_evsel_menu *menu = container_of(browser,
1803                                                     struct perf_evsel_menu, b);
1804         struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1805         bool current_entry = ui_browser__is_current_entry(browser, row);
1806         unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1807         const char *ev_name = perf_evsel__name(evsel);
1808         char bf[256], unit;
1809         const char *warn = " ";
1810         size_t printed;
1811
1812         ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
1813                                                        HE_COLORSET_NORMAL);
1814
1815         if (perf_evsel__is_group_event(evsel)) {
1816                 struct perf_evsel *pos;
1817
1818                 ev_name = perf_evsel__group_name(evsel);
1819
1820                 for_each_group_member(pos, evsel) {
1821                         nr_events += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1822                 }
1823         }
1824
1825         nr_events = convert_unit(nr_events, &unit);
1826         printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
1827                            unit, unit == ' ' ? "" : " ", ev_name);
1828         slsmg_printf("%s", bf);
1829
1830         nr_events = evsel->hists.stats.nr_events[PERF_RECORD_LOST];
1831         if (nr_events != 0) {
1832                 menu->lost_events = true;
1833                 if (!current_entry)
1834                         ui_browser__set_color(browser, HE_COLORSET_TOP);
1835                 nr_events = convert_unit(nr_events, &unit);
1836                 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
1837                                      nr_events, unit, unit == ' ' ? "" : " ");
1838                 warn = bf;
1839         }
1840
1841         slsmg_write_nstring(warn, browser->width - printed);
1842
1843         if (current_entry)
1844                 menu->selection = evsel;
1845 }
1846
1847 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
1848                                 int nr_events, const char *help,
1849                                 struct hist_browser_timer *hbt)
1850 {
1851         struct perf_evlist *evlist = menu->b.priv;
1852         struct perf_evsel *pos;
1853         const char *ev_name, *title = "Available samples";
1854         int delay_secs = hbt ? hbt->refresh : 0;
1855         int key;
1856
1857         if (ui_browser__show(&menu->b, title,
1858                              "ESC: exit, ENTER|->: Browse histograms") < 0)
1859                 return -1;
1860
1861         while (1) {
1862                 key = ui_browser__run(&menu->b, delay_secs);
1863
1864                 switch (key) {
1865                 case K_TIMER:
1866                         hbt->timer(hbt->arg);
1867
1868                         if (!menu->lost_events_warned && menu->lost_events) {
1869                                 ui_browser__warn_lost_events(&menu->b);
1870                                 menu->lost_events_warned = true;
1871                         }
1872                         continue;
1873                 case K_RIGHT:
1874                 case K_ENTER:
1875                         if (!menu->selection)
1876                                 continue;
1877                         pos = menu->selection;
1878 browse_hists:
1879                         perf_evlist__set_selected(evlist, pos);
1880                         /*
1881                          * Give the calling tool a chance to populate the non
1882                          * default evsel resorted hists tree.
1883                          */
1884                         if (hbt)
1885                                 hbt->timer(hbt->arg);
1886                         ev_name = perf_evsel__name(pos);
1887                         key = perf_evsel__hists_browse(pos, nr_events, help,
1888                                                        ev_name, true, hbt,
1889                                                        menu->min_pcnt,
1890                                                        menu->env);
1891                         ui_browser__show_title(&menu->b, title);
1892                         switch (key) {
1893                         case K_TAB:
1894                                 if (pos->node.next == &evlist->entries)
1895                                         pos = perf_evlist__first(evlist);
1896                                 else
1897                                         pos = perf_evsel__next(pos);
1898                                 goto browse_hists;
1899                         case K_UNTAB:
1900                                 if (pos->node.prev == &evlist->entries)
1901                                         pos = perf_evlist__last(evlist);
1902                                 else
1903                                         pos = perf_evsel__prev(pos);
1904                                 goto browse_hists;
1905                         case K_ESC:
1906                                 if (!ui_browser__dialog_yesno(&menu->b,
1907                                                 "Do you really want to exit?"))
1908                                         continue;
1909                                 /* Fall thru */
1910                         case K_SWITCH_INPUT_DATA:
1911                         case 'q':
1912                         case CTRL('c'):
1913                                 goto out;
1914                         default:
1915                                 continue;
1916                         }
1917                 case K_LEFT:
1918                         continue;
1919                 case K_ESC:
1920                         if (!ui_browser__dialog_yesno(&menu->b,
1921                                                "Do you really want to exit?"))
1922                                 continue;
1923                         /* Fall thru */
1924                 case 'q':
1925                 case CTRL('c'):
1926                         goto out;
1927                 default:
1928                         continue;
1929                 }
1930         }
1931
1932 out:
1933         ui_browser__hide(&menu->b);
1934         return key;
1935 }
1936
1937 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
1938                                  void *entry)
1939 {
1940         struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1941
1942         if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
1943                 return true;
1944
1945         return false;
1946 }
1947
1948 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
1949                                            int nr_entries, const char *help,
1950                                            struct hist_browser_timer *hbt,
1951                                            float min_pcnt,
1952                                            struct perf_session_env *env)
1953 {
1954         struct perf_evsel *pos;
1955         struct perf_evsel_menu menu = {
1956                 .b = {
1957                         .entries    = &evlist->entries,
1958                         .refresh    = ui_browser__list_head_refresh,
1959                         .seek       = ui_browser__list_head_seek,
1960                         .write      = perf_evsel_menu__write,
1961                         .filter     = filter_group_entries,
1962                         .nr_entries = nr_entries,
1963                         .priv       = evlist,
1964                 },
1965                 .min_pcnt = min_pcnt,
1966                 .env = env,
1967         };
1968
1969         ui_helpline__push("Press ESC to exit");
1970
1971         evlist__for_each(evlist, pos) {
1972                 const char *ev_name = perf_evsel__name(pos);
1973                 size_t line_len = strlen(ev_name) + 7;
1974
1975                 if (menu.b.width < line_len)
1976                         menu.b.width = line_len;
1977         }
1978
1979         return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
1980 }
1981
1982 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
1983                                   struct hist_browser_timer *hbt,
1984                                   float min_pcnt,
1985                                   struct perf_session_env *env)
1986 {
1987         int nr_entries = evlist->nr_entries;
1988
1989 single_entry:
1990         if (nr_entries == 1) {
1991                 struct perf_evsel *first = perf_evlist__first(evlist);
1992                 const char *ev_name = perf_evsel__name(first);
1993
1994                 return perf_evsel__hists_browse(first, nr_entries, help,
1995                                                 ev_name, false, hbt, min_pcnt,
1996                                                 env);
1997         }
1998
1999         if (symbol_conf.event_group) {
2000                 struct perf_evsel *pos;
2001
2002                 nr_entries = 0;
2003                 evlist__for_each(evlist, pos) {
2004                         if (perf_evsel__is_group_leader(pos))
2005                                 nr_entries++;
2006                 }
2007
2008                 if (nr_entries == 1)
2009                         goto single_entry;
2010         }
2011
2012         return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
2013                                                hbt, min_pcnt, env);
2014 }