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