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