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