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