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