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