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