b72ee68222228492bb5c86be509dd29373e2b908
[platform/kernel/linux-starfive.git] / tools / perf / ui / browsers / hists.c
1 // SPDX-License-Identifier: GPL-2.0
2 #include <dirent.h>
3 #include <errno.h>
4 #include <inttypes.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <linux/rbtree.h>
9 #include <linux/string.h>
10 #include <sys/ttydefaults.h>
11 #include <linux/time64.h>
12 #include <linux/zalloc.h>
13
14 #include "../../util/debug.h"
15 #include "../../util/dso.h"
16 #include "../../util/callchain.h"
17 #include "../../util/evsel.h"
18 #include "../../util/evlist.h"
19 #include "../../util/header.h"
20 #include "../../util/hist.h"
21 #include "../../util/machine.h"
22 #include "../../util/map.h"
23 #include "../../util/maps.h"
24 #include "../../util/symbol.h"
25 #include "../../util/map_symbol.h"
26 #include "../../util/branch.h"
27 #include "../../util/pstack.h"
28 #include "../../util/sort.h"
29 #include "../../util/top.h"
30 #include "../../util/thread.h"
31 #include "../../util/block-info.h"
32 #include "../../arch/common.h"
33 #include "../../perf.h"
34
35 #include "../browsers/hists.h"
36 #include "../helpline.h"
37 #include "../util.h"
38 #include "../ui.h"
39 #include "map.h"
40 #include "annotate.h"
41 #include "srcline.h"
42 #include "string2.h"
43 #include "units.h"
44 #include "time-utils.h"
45
46 #include <linux/ctype.h>
47
48 extern void hist_browser__init_hpp(void);
49
50 static int hists_browser__scnprintf_title(struct hist_browser *browser, char *bf, size_t size);
51 static void hist_browser__update_nr_entries(struct hist_browser *hb);
52
53 static struct rb_node *hists__filter_entries(struct rb_node *nd,
54                                              float min_pcnt);
55
56 static bool hist_browser__has_filter(struct hist_browser *hb)
57 {
58         return hists__has_filter(hb->hists) || hb->min_pcnt || symbol_conf.has_filter || hb->c2c_filter;
59 }
60
61 static int hist_browser__get_folding(struct hist_browser *browser)
62 {
63         struct rb_node *nd;
64         struct hists *hists = browser->hists;
65         int unfolded_rows = 0;
66
67         for (nd = rb_first_cached(&hists->entries);
68              (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
69              nd = rb_hierarchy_next(nd)) {
70                 struct hist_entry *he =
71                         rb_entry(nd, struct hist_entry, rb_node);
72
73                 if (he->leaf && he->unfolded)
74                         unfolded_rows += he->nr_rows;
75         }
76         return unfolded_rows;
77 }
78
79 static void hist_browser__set_title_space(struct hist_browser *hb)
80 {
81         struct ui_browser *browser = &hb->b;
82         struct hists *hists = hb->hists;
83         struct perf_hpp_list *hpp_list = hists->hpp_list;
84
85         browser->extra_title_lines = hb->show_headers ? hpp_list->nr_header_lines : 0;
86 }
87
88 static u32 hist_browser__nr_entries(struct hist_browser *hb)
89 {
90         u32 nr_entries;
91
92         if (symbol_conf.report_hierarchy)
93                 nr_entries = hb->nr_hierarchy_entries;
94         else if (hist_browser__has_filter(hb))
95                 nr_entries = hb->nr_non_filtered_entries;
96         else
97                 nr_entries = hb->hists->nr_entries;
98
99         hb->nr_callchain_rows = hist_browser__get_folding(hb);
100         return nr_entries + hb->nr_callchain_rows;
101 }
102
103 static void hist_browser__update_rows(struct hist_browser *hb)
104 {
105         struct ui_browser *browser = &hb->b;
106         struct hists *hists = hb->hists;
107         struct perf_hpp_list *hpp_list = hists->hpp_list;
108         u16 index_row;
109
110         if (!hb->show_headers) {
111                 browser->rows += browser->extra_title_lines;
112                 browser->extra_title_lines = 0;
113                 return;
114         }
115
116         browser->extra_title_lines = hpp_list->nr_header_lines;
117         browser->rows -= browser->extra_title_lines;
118         /*
119          * Verify if we were at the last line and that line isn't
120          * visible because we now show the header line(s).
121          */
122         index_row = browser->index - browser->top_idx;
123         if (index_row >= browser->rows)
124                 browser->index -= index_row - browser->rows + 1;
125 }
126
127 static void hist_browser__refresh_dimensions(struct ui_browser *browser)
128 {
129         struct hist_browser *hb = container_of(browser, struct hist_browser, b);
130
131         /* 3 == +/- toggle symbol before actual hist_entry rendering */
132         browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]"));
133         /*
134          * FIXME: Just keeping existing behaviour, but this really should be
135          *        before updating browser->width, as it will invalidate the
136          *        calculation above. Fix this and the fallout in another
137          *        changeset.
138          */
139         ui_browser__refresh_dimensions(browser);
140 }
141
142 static void hist_browser__reset(struct hist_browser *browser)
143 {
144         /*
145          * The hists__remove_entry_filter() already folds non-filtered
146          * entries so we can assume it has 0 callchain rows.
147          */
148         browser->nr_callchain_rows = 0;
149
150         hist_browser__update_nr_entries(browser);
151         browser->b.nr_entries = hist_browser__nr_entries(browser);
152         hist_browser__refresh_dimensions(&browser->b);
153         ui_browser__reset_index(&browser->b);
154 }
155
156 static char tree__folded_sign(bool unfolded)
157 {
158         return unfolded ? '-' : '+';
159 }
160
161 static char hist_entry__folded(const struct hist_entry *he)
162 {
163         return he->has_children ? tree__folded_sign(he->unfolded) : ' ';
164 }
165
166 static char callchain_list__folded(const struct callchain_list *cl)
167 {
168         return cl->has_children ? tree__folded_sign(cl->unfolded) : ' ';
169 }
170
171 static void callchain_list__set_folding(struct callchain_list *cl, bool unfold)
172 {
173         cl->unfolded = unfold ? cl->has_children : false;
174 }
175
176 static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
177 {
178         int n = 0;
179         struct rb_node *nd;
180
181         for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
182                 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
183                 struct callchain_list *chain;
184                 char folded_sign = ' '; /* No children */
185
186                 list_for_each_entry(chain, &child->val, list) {
187                         ++n;
188
189                         /* We need this because we may not have children */
190                         folded_sign = callchain_list__folded(chain);
191                         if (folded_sign == '+')
192                                 break;
193                 }
194
195                 if (folded_sign == '-') /* Have children and they're unfolded */
196                         n += callchain_node__count_rows_rb_tree(child);
197         }
198
199         return n;
200 }
201
202 static int callchain_node__count_flat_rows(struct callchain_node *node)
203 {
204         struct callchain_list *chain;
205         char folded_sign = 0;
206         int n = 0;
207
208         list_for_each_entry(chain, &node->parent_val, list) {
209                 if (!folded_sign) {
210                         /* only check first chain list entry */
211                         folded_sign = callchain_list__folded(chain);
212                         if (folded_sign == '+')
213                                 return 1;
214                 }
215                 n++;
216         }
217
218         list_for_each_entry(chain, &node->val, list) {
219                 if (!folded_sign) {
220                         /* node->parent_val list might be empty */
221                         folded_sign = callchain_list__folded(chain);
222                         if (folded_sign == '+')
223                                 return 1;
224                 }
225                 n++;
226         }
227
228         return n;
229 }
230
231 static int callchain_node__count_folded_rows(struct callchain_node *node __maybe_unused)
232 {
233         return 1;
234 }
235
236 static int callchain_node__count_rows(struct callchain_node *node)
237 {
238         struct callchain_list *chain;
239         bool unfolded = false;
240         int n = 0;
241
242         if (callchain_param.mode == CHAIN_FLAT)
243                 return callchain_node__count_flat_rows(node);
244         else if (callchain_param.mode == CHAIN_FOLDED)
245                 return callchain_node__count_folded_rows(node);
246
247         list_for_each_entry(chain, &node->val, list) {
248                 ++n;
249
250                 unfolded = chain->unfolded;
251         }
252
253         if (unfolded)
254                 n += callchain_node__count_rows_rb_tree(node);
255
256         return n;
257 }
258
259 static int callchain__count_rows(struct rb_root *chain)
260 {
261         struct rb_node *nd;
262         int n = 0;
263
264         for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
265                 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
266                 n += callchain_node__count_rows(node);
267         }
268
269         return n;
270 }
271
272 static int hierarchy_count_rows(struct hist_browser *hb, struct hist_entry *he,
273                                 bool include_children)
274 {
275         int count = 0;
276         struct rb_node *node;
277         struct hist_entry *child;
278
279         if (he->leaf)
280                 return callchain__count_rows(&he->sorted_chain);
281
282         if (he->has_no_entry)
283                 return 1;
284
285         node = rb_first_cached(&he->hroot_out);
286         while (node) {
287                 float percent;
288
289                 child = rb_entry(node, struct hist_entry, rb_node);
290                 percent = hist_entry__get_percent_limit(child);
291
292                 if (!child->filtered && percent >= hb->min_pcnt) {
293                         count++;
294
295                         if (include_children && child->unfolded)
296                                 count += hierarchy_count_rows(hb, child, true);
297                 }
298
299                 node = rb_next(node);
300         }
301         return count;
302 }
303
304 static bool hist_entry__toggle_fold(struct hist_entry *he)
305 {
306         if (!he)
307                 return false;
308
309         if (!he->has_children)
310                 return false;
311
312         he->unfolded = !he->unfolded;
313         return true;
314 }
315
316 static bool callchain_list__toggle_fold(struct callchain_list *cl)
317 {
318         if (!cl)
319                 return false;
320
321         if (!cl->has_children)
322                 return false;
323
324         cl->unfolded = !cl->unfolded;
325         return true;
326 }
327
328 static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
329 {
330         struct rb_node *nd = rb_first(&node->rb_root);
331
332         for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
333                 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
334                 struct callchain_list *chain;
335                 bool first = true;
336
337                 list_for_each_entry(chain, &child->val, list) {
338                         if (first) {
339                                 first = false;
340                                 chain->has_children = chain->list.next != &child->val ||
341                                                          !RB_EMPTY_ROOT(&child->rb_root);
342                         } else
343                                 chain->has_children = chain->list.next == &child->val &&
344                                                          !RB_EMPTY_ROOT(&child->rb_root);
345                 }
346
347                 callchain_node__init_have_children_rb_tree(child);
348         }
349 }
350
351 static void callchain_node__init_have_children(struct callchain_node *node,
352                                                bool has_sibling)
353 {
354         struct callchain_list *chain;
355
356         chain = list_entry(node->val.next, struct callchain_list, list);
357         chain->has_children = has_sibling;
358
359         if (!list_empty(&node->val)) {
360                 chain = list_entry(node->val.prev, struct callchain_list, list);
361                 chain->has_children = !RB_EMPTY_ROOT(&node->rb_root);
362         }
363
364         callchain_node__init_have_children_rb_tree(node);
365 }
366
367 static void callchain__init_have_children(struct rb_root *root)
368 {
369         struct rb_node *nd = rb_first(root);
370         bool has_sibling = nd && rb_next(nd);
371
372         for (nd = rb_first(root); nd; nd = rb_next(nd)) {
373                 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
374                 callchain_node__init_have_children(node, has_sibling);
375                 if (callchain_param.mode == CHAIN_FLAT ||
376                     callchain_param.mode == CHAIN_FOLDED)
377                         callchain_node__make_parent_list(node);
378         }
379 }
380
381 static void hist_entry__init_have_children(struct hist_entry *he)
382 {
383         if (he->init_have_children)
384                 return;
385
386         if (he->leaf) {
387                 he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
388                 callchain__init_have_children(&he->sorted_chain);
389         } else {
390                 he->has_children = !RB_EMPTY_ROOT(&he->hroot_out.rb_root);
391         }
392
393         he->init_have_children = true;
394 }
395
396 static bool hist_browser__selection_has_children(struct hist_browser *browser)
397 {
398         struct hist_entry *he = browser->he_selection;
399         struct map_symbol *ms = browser->selection;
400
401         if (!he || !ms)
402                 return false;
403
404         if (ms == &he->ms)
405                return he->has_children;
406
407         return container_of(ms, struct callchain_list, ms)->has_children;
408 }
409
410 static bool hist_browser__he_selection_unfolded(struct hist_browser *browser)
411 {
412         return browser->he_selection ? browser->he_selection->unfolded : false;
413 }
414
415 static bool hist_browser__selection_unfolded(struct hist_browser *browser)
416 {
417         struct hist_entry *he = browser->he_selection;
418         struct map_symbol *ms = browser->selection;
419
420         if (!he || !ms)
421                 return false;
422
423         if (ms == &he->ms)
424                return he->unfolded;
425
426         return container_of(ms, struct callchain_list, ms)->unfolded;
427 }
428
429 static char *hist_browser__selection_sym_name(struct hist_browser *browser, char *bf, size_t size)
430 {
431         struct hist_entry *he = browser->he_selection;
432         struct map_symbol *ms = browser->selection;
433         struct callchain_list *callchain_entry;
434
435         if (!he || !ms)
436                 return NULL;
437
438         if (ms == &he->ms) {
439                hist_entry__sym_snprintf(he, bf, size, 0);
440                return bf + 4; // skip the level, e.g. '[k] '
441         }
442
443         callchain_entry = container_of(ms, struct callchain_list, ms);
444         return callchain_list__sym_name(callchain_entry, bf, size, browser->show_dso);
445 }
446
447 static bool hist_browser__toggle_fold(struct hist_browser *browser)
448 {
449         struct hist_entry *he = browser->he_selection;
450         struct map_symbol *ms = browser->selection;
451         struct callchain_list *cl = container_of(ms, struct callchain_list, ms);
452         bool has_children;
453
454         if (!he || !ms)
455                 return false;
456
457         if (ms == &he->ms)
458                 has_children = hist_entry__toggle_fold(he);
459         else
460                 has_children = callchain_list__toggle_fold(cl);
461
462         if (has_children) {
463                 int child_rows = 0;
464
465                 hist_entry__init_have_children(he);
466                 browser->b.nr_entries -= he->nr_rows;
467
468                 if (he->leaf)
469                         browser->nr_callchain_rows -= he->nr_rows;
470                 else
471                         browser->nr_hierarchy_entries -= he->nr_rows;
472
473                 if (symbol_conf.report_hierarchy)
474                         child_rows = hierarchy_count_rows(browser, he, true);
475
476                 if (he->unfolded) {
477                         if (he->leaf)
478                                 he->nr_rows = callchain__count_rows(
479                                                 &he->sorted_chain);
480                         else
481                                 he->nr_rows = hierarchy_count_rows(browser, he, false);
482
483                         /* account grand children */
484                         if (symbol_conf.report_hierarchy)
485                                 browser->b.nr_entries += child_rows - he->nr_rows;
486
487                         if (!he->leaf && he->nr_rows == 0) {
488                                 he->has_no_entry = true;
489                                 he->nr_rows = 1;
490                         }
491                 } else {
492                         if (symbol_conf.report_hierarchy)
493                                 browser->b.nr_entries -= child_rows - he->nr_rows;
494
495                         if (he->has_no_entry)
496                                 he->has_no_entry = false;
497
498                         he->nr_rows = 0;
499                 }
500
501                 browser->b.nr_entries += he->nr_rows;
502
503                 if (he->leaf)
504                         browser->nr_callchain_rows += he->nr_rows;
505                 else
506                         browser->nr_hierarchy_entries += he->nr_rows;
507
508                 return true;
509         }
510
511         /* If it doesn't have children, no toggling performed */
512         return false;
513 }
514
515 static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
516 {
517         int n = 0;
518         struct rb_node *nd;
519
520         for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
521                 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
522                 struct callchain_list *chain;
523                 bool has_children = false;
524
525                 list_for_each_entry(chain, &child->val, list) {
526                         ++n;
527                         callchain_list__set_folding(chain, unfold);
528                         has_children = chain->has_children;
529                 }
530
531                 if (has_children)
532                         n += callchain_node__set_folding_rb_tree(child, unfold);
533         }
534
535         return n;
536 }
537
538 static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
539 {
540         struct callchain_list *chain;
541         bool has_children = false;
542         int n = 0;
543
544         list_for_each_entry(chain, &node->val, list) {
545                 ++n;
546                 callchain_list__set_folding(chain, unfold);
547                 has_children = chain->has_children;
548         }
549
550         if (has_children)
551                 n += callchain_node__set_folding_rb_tree(node, unfold);
552
553         return n;
554 }
555
556 static int callchain__set_folding(struct rb_root *chain, bool unfold)
557 {
558         struct rb_node *nd;
559         int n = 0;
560
561         for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
562                 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
563                 n += callchain_node__set_folding(node, unfold);
564         }
565
566         return n;
567 }
568
569 static int hierarchy_set_folding(struct hist_browser *hb, struct hist_entry *he,
570                                  bool unfold __maybe_unused)
571 {
572         float percent;
573         struct rb_node *nd;
574         struct hist_entry *child;
575         int n = 0;
576
577         for (nd = rb_first_cached(&he->hroot_out); nd; nd = rb_next(nd)) {
578                 child = rb_entry(nd, struct hist_entry, rb_node);
579                 percent = hist_entry__get_percent_limit(child);
580                 if (!child->filtered && percent >= hb->min_pcnt)
581                         n++;
582         }
583
584         return n;
585 }
586
587 static void __hist_entry__set_folding(struct hist_entry *he,
588                                       struct hist_browser *hb, bool unfold)
589 {
590         hist_entry__init_have_children(he);
591         he->unfolded = unfold ? he->has_children : false;
592
593         if (he->has_children) {
594                 int n;
595
596                 if (he->leaf)
597                         n = callchain__set_folding(&he->sorted_chain, unfold);
598                 else
599                         n = hierarchy_set_folding(hb, he, unfold);
600
601                 he->nr_rows = unfold ? n : 0;
602         } else
603                 he->nr_rows = 0;
604 }
605
606 static void hist_entry__set_folding(struct hist_entry *he,
607                                     struct hist_browser *browser, bool unfold)
608 {
609         double percent;
610
611         percent = hist_entry__get_percent_limit(he);
612         if (he->filtered || percent < browser->min_pcnt)
613                 return;
614
615         __hist_entry__set_folding(he, browser, unfold);
616
617         if (!he->depth || unfold)
618                 browser->nr_hierarchy_entries++;
619         if (he->leaf)
620                 browser->nr_callchain_rows += he->nr_rows;
621         else if (unfold && !hist_entry__has_hierarchy_children(he, browser->min_pcnt)) {
622                 browser->nr_hierarchy_entries++;
623                 he->has_no_entry = true;
624                 he->nr_rows = 1;
625         } else
626                 he->has_no_entry = false;
627 }
628
629 static void
630 __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
631 {
632         struct rb_node *nd;
633         struct hist_entry *he;
634
635         nd = rb_first_cached(&browser->hists->entries);
636         while (nd) {
637                 he = rb_entry(nd, struct hist_entry, rb_node);
638
639                 /* set folding state even if it's currently folded */
640                 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
641
642                 hist_entry__set_folding(he, browser, unfold);
643         }
644 }
645
646 static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
647 {
648         browser->nr_hierarchy_entries = 0;
649         browser->nr_callchain_rows = 0;
650         __hist_browser__set_folding(browser, unfold);
651
652         browser->b.nr_entries = hist_browser__nr_entries(browser);
653         /* Go to the start, we may be way after valid entries after a collapse */
654         ui_browser__reset_index(&browser->b);
655 }
656
657 static void hist_browser__set_folding_selected(struct hist_browser *browser, bool unfold)
658 {
659         if (!browser->he_selection)
660                 return;
661
662         hist_entry__set_folding(browser->he_selection, browser, unfold);
663         browser->b.nr_entries = hist_browser__nr_entries(browser);
664 }
665
666 static void ui_browser__warn_lost_events(struct ui_browser *browser)
667 {
668         ui_browser__warning(browser, 4,
669                 "Events are being lost, check IO/CPU overload!\n\n"
670                 "You may want to run 'perf' using a RT scheduler policy:\n\n"
671                 " perf top -r 80\n\n"
672                 "Or reduce the sampling frequency.");
673 }
674
675 static int hist_browser__title(struct hist_browser *browser, char *bf, size_t size)
676 {
677         return browser->title ? browser->title(browser, bf, size) : 0;
678 }
679
680 static int hist_browser__handle_hotkey(struct hist_browser *browser, bool warn_lost_event, char *title, size_t size, int key)
681 {
682         switch (key) {
683         case K_TIMER: {
684                 struct hist_browser_timer *hbt = browser->hbt;
685                 struct evsel *evsel = hists_to_evsel(browser->hists);
686                 u64 nr_entries;
687
688                 WARN_ON_ONCE(!hbt);
689
690                 if (hbt)
691                         hbt->timer(hbt->arg);
692
693                 if (hist_browser__has_filter(browser) || symbol_conf.report_hierarchy)
694                         hist_browser__update_nr_entries(browser);
695
696                 nr_entries = hist_browser__nr_entries(browser);
697                 ui_browser__update_nr_entries(&browser->b, nr_entries);
698
699                 if (warn_lost_event &&
700                     (evsel->evlist->stats.nr_lost_warned !=
701                      evsel->evlist->stats.nr_events[PERF_RECORD_LOST])) {
702                         evsel->evlist->stats.nr_lost_warned =
703                                 evsel->evlist->stats.nr_events[PERF_RECORD_LOST];
704                         ui_browser__warn_lost_events(&browser->b);
705                 }
706
707                 hist_browser__title(browser, title, size);
708                 ui_browser__show_title(&browser->b, title);
709                 break;
710         }
711         case 'D': { /* Debug */
712                 struct hist_entry *h = rb_entry(browser->b.top, struct hist_entry, rb_node);
713                 static int seq;
714
715                 ui_helpline__pop();
716                 ui_helpline__fpush("%d: nr_ent=(%d,%d), etl: %d, rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
717                                    seq++, browser->b.nr_entries, browser->hists->nr_entries,
718                                    browser->b.extra_title_lines, browser->b.rows,
719                                    browser->b.index, browser->b.top_idx, h->row_offset, h->nr_rows);
720         }
721                 break;
722         case 'C':
723                 /* Collapse the whole world. */
724                 hist_browser__set_folding(browser, false);
725                 break;
726         case 'c':
727                 /* Collapse the selected entry. */
728                 hist_browser__set_folding_selected(browser, false);
729                 break;
730         case 'E':
731                 /* Expand the whole world. */
732                 hist_browser__set_folding(browser, true);
733                 break;
734         case 'e':
735                 /* Expand the selected entry. */
736                 hist_browser__set_folding_selected(browser, !hist_browser__he_selection_unfolded(browser));
737                 break;
738         case 'H':
739                 browser->show_headers = !browser->show_headers;
740                 hist_browser__update_rows(browser);
741                 break;
742         case '+':
743                 if (hist_browser__toggle_fold(browser))
744                         break;
745                 /* fall thru */
746         default:
747                 return -1;
748         }
749
750         return 0;
751 }
752
753 int hist_browser__run(struct hist_browser *browser, const char *help,
754                       bool warn_lost_event, int key)
755 {
756         char title[160];
757         struct hist_browser_timer *hbt = browser->hbt;
758         int delay_secs = hbt ? hbt->refresh : 0;
759
760         browser->b.entries = &browser->hists->entries;
761         browser->b.nr_entries = hist_browser__nr_entries(browser);
762
763         hist_browser__title(browser, title, sizeof(title));
764
765         if (ui_browser__show(&browser->b, title, "%s", help) < 0)
766                 return -1;
767
768         if (key && hist_browser__handle_hotkey(browser, warn_lost_event, title, sizeof(title), key))
769                 goto out;
770
771         while (1) {
772                 key = ui_browser__run(&browser->b, delay_secs);
773
774                 if (hist_browser__handle_hotkey(browser, warn_lost_event, title, sizeof(title), key))
775                         break;
776         }
777 out:
778         ui_browser__hide(&browser->b);
779         return key;
780 }
781
782 struct callchain_print_arg {
783         /* for hists browser */
784         off_t   row_offset;
785         bool    is_current_entry;
786
787         /* for file dump */
788         FILE    *fp;
789         int     printed;
790 };
791
792 typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
793                                          struct callchain_list *chain,
794                                          const char *str, int offset,
795                                          unsigned short row,
796                                          struct callchain_print_arg *arg);
797
798 static void hist_browser__show_callchain_entry(struct hist_browser *browser,
799                                                struct callchain_list *chain,
800                                                const char *str, int offset,
801                                                unsigned short row,
802                                                struct callchain_print_arg *arg)
803 {
804         int color, width;
805         char folded_sign = callchain_list__folded(chain);
806         bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
807
808         color = HE_COLORSET_NORMAL;
809         width = browser->b.width - (offset + 2);
810         if (ui_browser__is_current_entry(&browser->b, row)) {
811                 browser->selection = &chain->ms;
812                 color = HE_COLORSET_SELECTED;
813                 arg->is_current_entry = true;
814         }
815
816         ui_browser__set_color(&browser->b, color);
817         ui_browser__gotorc(&browser->b, row, 0);
818         ui_browser__write_nstring(&browser->b, " ", offset);
819         ui_browser__printf(&browser->b, "%c", folded_sign);
820         ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
821         ui_browser__write_nstring(&browser->b, str, width);
822 }
823
824 static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
825                                                   struct callchain_list *chain,
826                                                   const char *str, int offset,
827                                                   unsigned short row __maybe_unused,
828                                                   struct callchain_print_arg *arg)
829 {
830         char folded_sign = callchain_list__folded(chain);
831
832         arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
833                                 folded_sign, str);
834 }
835
836 typedef bool (*check_output_full_fn)(struct hist_browser *browser,
837                                      unsigned short row);
838
839 static bool hist_browser__check_output_full(struct hist_browser *browser,
840                                             unsigned short row)
841 {
842         return browser->b.rows == row;
843 }
844
845 static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
846                                           unsigned short row __maybe_unused)
847 {
848         return false;
849 }
850
851 #define LEVEL_OFFSET_STEP 3
852
853 static int hist_browser__show_callchain_list(struct hist_browser *browser,
854                                              struct callchain_node *node,
855                                              struct callchain_list *chain,
856                                              unsigned short row, u64 total,
857                                              bool need_percent, int offset,
858                                              print_callchain_entry_fn print,
859                                              struct callchain_print_arg *arg)
860 {
861         char bf[1024], *alloc_str;
862         char buf[64], *alloc_str2;
863         const char *str;
864         int ret = 1;
865
866         if (arg->row_offset != 0) {
867                 arg->row_offset--;
868                 return 0;
869         }
870
871         alloc_str = NULL;
872         alloc_str2 = NULL;
873
874         str = callchain_list__sym_name(chain, bf, sizeof(bf),
875                                        browser->show_dso);
876
877         if (symbol_conf.show_branchflag_count) {
878                 callchain_list_counts__printf_value(chain, NULL,
879                                                     buf, sizeof(buf));
880
881                 if (asprintf(&alloc_str2, "%s%s", str, buf) < 0)
882                         str = "Not enough memory!";
883                 else
884                         str = alloc_str2;
885         }
886
887         if (need_percent) {
888                 callchain_node__scnprintf_value(node, buf, sizeof(buf),
889                                                 total);
890
891                 if (asprintf(&alloc_str, "%s %s", buf, str) < 0)
892                         str = "Not enough memory!";
893                 else
894                         str = alloc_str;
895         }
896
897         print(browser, chain, str, offset, row, arg);
898         free(alloc_str);
899         free(alloc_str2);
900
901         return ret;
902 }
903
904 static bool check_percent_display(struct rb_node *node, u64 parent_total)
905 {
906         struct callchain_node *child;
907
908         if (node == NULL)
909                 return false;
910
911         if (rb_next(node))
912                 return true;
913
914         child = rb_entry(node, struct callchain_node, rb_node);
915         return callchain_cumul_hits(child) != parent_total;
916 }
917
918 static int hist_browser__show_callchain_flat(struct hist_browser *browser,
919                                              struct rb_root *root,
920                                              unsigned short row, u64 total,
921                                              u64 parent_total,
922                                              print_callchain_entry_fn print,
923                                              struct callchain_print_arg *arg,
924                                              check_output_full_fn is_output_full)
925 {
926         struct rb_node *node;
927         int first_row = row, offset = LEVEL_OFFSET_STEP;
928         bool need_percent;
929
930         node = rb_first(root);
931         need_percent = check_percent_display(node, parent_total);
932
933         while (node) {
934                 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
935                 struct rb_node *next = rb_next(node);
936                 struct callchain_list *chain;
937                 char folded_sign = ' ';
938                 int first = true;
939                 int extra_offset = 0;
940
941                 list_for_each_entry(chain, &child->parent_val, list) {
942                         bool was_first = first;
943
944                         if (first)
945                                 first = false;
946                         else if (need_percent)
947                                 extra_offset = LEVEL_OFFSET_STEP;
948
949                         folded_sign = callchain_list__folded(chain);
950
951                         row += hist_browser__show_callchain_list(browser, child,
952                                                         chain, row, total,
953                                                         was_first && need_percent,
954                                                         offset + extra_offset,
955                                                         print, arg);
956
957                         if (is_output_full(browser, row))
958                                 goto out;
959
960                         if (folded_sign == '+')
961                                 goto next;
962                 }
963
964                 list_for_each_entry(chain, &child->val, list) {
965                         bool was_first = first;
966
967                         if (first)
968                                 first = false;
969                         else if (need_percent)
970                                 extra_offset = LEVEL_OFFSET_STEP;
971
972                         folded_sign = callchain_list__folded(chain);
973
974                         row += hist_browser__show_callchain_list(browser, child,
975                                                         chain, row, total,
976                                                         was_first && need_percent,
977                                                         offset + extra_offset,
978                                                         print, arg);
979
980                         if (is_output_full(browser, row))
981                                 goto out;
982
983                         if (folded_sign == '+')
984                                 break;
985                 }
986
987 next:
988                 if (is_output_full(browser, row))
989                         break;
990                 node = next;
991         }
992 out:
993         return row - first_row;
994 }
995
996 static char *hist_browser__folded_callchain_str(struct hist_browser *browser,
997                                                 struct callchain_list *chain,
998                                                 char *value_str, char *old_str)
999 {
1000         char bf[1024];
1001         const char *str;
1002         char *new;
1003
1004         str = callchain_list__sym_name(chain, bf, sizeof(bf),
1005                                        browser->show_dso);
1006         if (old_str) {
1007                 if (asprintf(&new, "%s%s%s", old_str,
1008                              symbol_conf.field_sep ?: ";", str) < 0)
1009                         new = NULL;
1010         } else {
1011                 if (value_str) {
1012                         if (asprintf(&new, "%s %s", value_str, str) < 0)
1013                                 new = NULL;
1014                 } else {
1015                         if (asprintf(&new, "%s", str) < 0)
1016                                 new = NULL;
1017                 }
1018         }
1019         return new;
1020 }
1021
1022 static int hist_browser__show_callchain_folded(struct hist_browser *browser,
1023                                                struct rb_root *root,
1024                                                unsigned short row, u64 total,
1025                                                u64 parent_total,
1026                                                print_callchain_entry_fn print,
1027                                                struct callchain_print_arg *arg,
1028                                                check_output_full_fn is_output_full)
1029 {
1030         struct rb_node *node;
1031         int first_row = row, offset = LEVEL_OFFSET_STEP;
1032         bool need_percent;
1033
1034         node = rb_first(root);
1035         need_percent = check_percent_display(node, parent_total);
1036
1037         while (node) {
1038                 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1039                 struct rb_node *next = rb_next(node);
1040                 struct callchain_list *chain, *first_chain = NULL;
1041                 int first = true;
1042                 char *value_str = NULL, *value_str_alloc = NULL;
1043                 char *chain_str = NULL, *chain_str_alloc = NULL;
1044
1045                 if (arg->row_offset != 0) {
1046                         arg->row_offset--;
1047                         goto next;
1048                 }
1049
1050                 if (need_percent) {
1051                         char buf[64];
1052
1053                         callchain_node__scnprintf_value(child, buf, sizeof(buf), total);
1054                         if (asprintf(&value_str, "%s", buf) < 0) {
1055                                 value_str = (char *)"<...>";
1056                                 goto do_print;
1057                         }
1058                         value_str_alloc = value_str;
1059                 }
1060
1061                 list_for_each_entry(chain, &child->parent_val, list) {
1062                         chain_str = hist_browser__folded_callchain_str(browser,
1063                                                 chain, value_str, chain_str);
1064                         if (first) {
1065                                 first = false;
1066                                 first_chain = chain;
1067                         }
1068
1069                         if (chain_str == NULL) {
1070                                 chain_str = (char *)"Not enough memory!";
1071                                 goto do_print;
1072                         }
1073
1074                         chain_str_alloc = chain_str;
1075                 }
1076
1077                 list_for_each_entry(chain, &child->val, list) {
1078                         chain_str = hist_browser__folded_callchain_str(browser,
1079                                                 chain, value_str, chain_str);
1080                         if (first) {
1081                                 first = false;
1082                                 first_chain = chain;
1083                         }
1084
1085                         if (chain_str == NULL) {
1086                                 chain_str = (char *)"Not enough memory!";
1087                                 goto do_print;
1088                         }
1089
1090                         chain_str_alloc = chain_str;
1091                 }
1092
1093 do_print:
1094                 print(browser, first_chain, chain_str, offset, row++, arg);
1095                 free(value_str_alloc);
1096                 free(chain_str_alloc);
1097
1098 next:
1099                 if (is_output_full(browser, row))
1100                         break;
1101                 node = next;
1102         }
1103
1104         return row - first_row;
1105 }
1106
1107 static int hist_browser__show_callchain_graph(struct hist_browser *browser,
1108                                         struct rb_root *root, int level,
1109                                         unsigned short row, u64 total,
1110                                         u64 parent_total,
1111                                         print_callchain_entry_fn print,
1112                                         struct callchain_print_arg *arg,
1113                                         check_output_full_fn is_output_full)
1114 {
1115         struct rb_node *node;
1116         int first_row = row, offset = level * LEVEL_OFFSET_STEP;
1117         bool need_percent;
1118         u64 percent_total = total;
1119
1120         if (callchain_param.mode == CHAIN_GRAPH_REL)
1121                 percent_total = parent_total;
1122
1123         node = rb_first(root);
1124         need_percent = check_percent_display(node, parent_total);
1125
1126         while (node) {
1127                 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1128                 struct rb_node *next = rb_next(node);
1129                 struct callchain_list *chain;
1130                 char folded_sign = ' ';
1131                 int first = true;
1132                 int extra_offset = 0;
1133
1134                 list_for_each_entry(chain, &child->val, list) {
1135                         bool was_first = first;
1136
1137                         if (first)
1138                                 first = false;
1139                         else if (need_percent)
1140                                 extra_offset = LEVEL_OFFSET_STEP;
1141
1142                         folded_sign = callchain_list__folded(chain);
1143
1144                         row += hist_browser__show_callchain_list(browser, child,
1145                                                         chain, row, percent_total,
1146                                                         was_first && need_percent,
1147                                                         offset + extra_offset,
1148                                                         print, arg);
1149
1150                         if (is_output_full(browser, row))
1151                                 goto out;
1152
1153                         if (folded_sign == '+')
1154                                 break;
1155                 }
1156
1157                 if (folded_sign == '-') {
1158                         const int new_level = level + (extra_offset ? 2 : 1);
1159
1160                         row += hist_browser__show_callchain_graph(browser, &child->rb_root,
1161                                                             new_level, row, total,
1162                                                             child->children_hit,
1163                                                             print, arg, is_output_full);
1164                 }
1165                 if (is_output_full(browser, row))
1166                         break;
1167                 node = next;
1168         }
1169 out:
1170         return row - first_row;
1171 }
1172
1173 static int hist_browser__show_callchain(struct hist_browser *browser,
1174                                         struct hist_entry *entry, int level,
1175                                         unsigned short row,
1176                                         print_callchain_entry_fn print,
1177                                         struct callchain_print_arg *arg,
1178                                         check_output_full_fn is_output_full)
1179 {
1180         u64 total = hists__total_period(entry->hists);
1181         u64 parent_total;
1182         int printed;
1183
1184         if (symbol_conf.cumulate_callchain)
1185                 parent_total = entry->stat_acc->period;
1186         else
1187                 parent_total = entry->stat.period;
1188
1189         if (callchain_param.mode == CHAIN_FLAT) {
1190                 printed = hist_browser__show_callchain_flat(browser,
1191                                                 &entry->sorted_chain, row,
1192                                                 total, parent_total, print, arg,
1193                                                 is_output_full);
1194         } else if (callchain_param.mode == CHAIN_FOLDED) {
1195                 printed = hist_browser__show_callchain_folded(browser,
1196                                                 &entry->sorted_chain, row,
1197                                                 total, parent_total, print, arg,
1198                                                 is_output_full);
1199         } else {
1200                 printed = hist_browser__show_callchain_graph(browser,
1201                                                 &entry->sorted_chain, level, row,
1202                                                 total, parent_total, print, arg,
1203                                                 is_output_full);
1204         }
1205
1206         if (arg->is_current_entry)
1207                 browser->he_selection = entry;
1208
1209         return printed;
1210 }
1211
1212 struct hpp_arg {
1213         struct ui_browser *b;
1214         char folded_sign;
1215         bool current_entry;
1216 };
1217
1218 int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
1219 {
1220         struct hpp_arg *arg = hpp->ptr;
1221         int ret, len;
1222         va_list args;
1223         double percent;
1224
1225         va_start(args, fmt);
1226         len = va_arg(args, int);
1227         percent = va_arg(args, double);
1228         va_end(args);
1229
1230         ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
1231
1232         ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
1233         ui_browser__printf(arg->b, "%s", hpp->buf);
1234
1235         return ret;
1236 }
1237
1238 #define __HPP_COLOR_PERCENT_FN(_type, _field)                           \
1239 static u64 __hpp_get_##_field(struct hist_entry *he)                    \
1240 {                                                                       \
1241         return he->stat._field;                                         \
1242 }                                                                       \
1243                                                                         \
1244 static int                                                              \
1245 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,               \
1246                                 struct perf_hpp *hpp,                   \
1247                                 struct hist_entry *he)                  \
1248 {                                                                       \
1249         return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%",   \
1250                         __hpp__slsmg_color_printf, true);               \
1251 }
1252
1253 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field)                       \
1254 static u64 __hpp_get_acc_##_field(struct hist_entry *he)                \
1255 {                                                                       \
1256         return he->stat_acc->_field;                                    \
1257 }                                                                       \
1258                                                                         \
1259 static int                                                              \
1260 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,               \
1261                                 struct perf_hpp *hpp,                   \
1262                                 struct hist_entry *he)                  \
1263 {                                                                       \
1264         if (!symbol_conf.cumulate_callchain) {                          \
1265                 struct hpp_arg *arg = hpp->ptr;                         \
1266                 int len = fmt->user_len ?: fmt->len;                    \
1267                 int ret = scnprintf(hpp->buf, hpp->size,                \
1268                                     "%*s", len, "N/A");                 \
1269                 ui_browser__printf(arg->b, "%s", hpp->buf);             \
1270                                                                         \
1271                 return ret;                                             \
1272         }                                                               \
1273         return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field,           \
1274                         " %*.2f%%", __hpp__slsmg_color_printf, true);   \
1275 }
1276
1277 __HPP_COLOR_PERCENT_FN(overhead, period)
1278 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
1279 __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
1280 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
1281 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
1282 __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
1283
1284 #undef __HPP_COLOR_PERCENT_FN
1285 #undef __HPP_COLOR_ACC_PERCENT_FN
1286
1287 void hist_browser__init_hpp(void)
1288 {
1289         perf_hpp__format[PERF_HPP__OVERHEAD].color =
1290                                 hist_browser__hpp_color_overhead;
1291         perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
1292                                 hist_browser__hpp_color_overhead_sys;
1293         perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
1294                                 hist_browser__hpp_color_overhead_us;
1295         perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
1296                                 hist_browser__hpp_color_overhead_guest_sys;
1297         perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
1298                                 hist_browser__hpp_color_overhead_guest_us;
1299         perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
1300                                 hist_browser__hpp_color_overhead_acc;
1301
1302         res_sample_init();
1303 }
1304
1305 static int hist_browser__show_entry(struct hist_browser *browser,
1306                                     struct hist_entry *entry,
1307                                     unsigned short row)
1308 {
1309         int printed = 0;
1310         int width = browser->b.width;
1311         char folded_sign = ' ';
1312         bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1313         bool use_callchain = hist_entry__has_callchains(entry) && symbol_conf.use_callchain;
1314         off_t row_offset = entry->row_offset;
1315         bool first = true;
1316         struct perf_hpp_fmt *fmt;
1317
1318         if (current_entry) {
1319                 browser->he_selection = entry;
1320                 browser->selection = &entry->ms;
1321         }
1322
1323         if (use_callchain) {
1324                 hist_entry__init_have_children(entry);
1325                 folded_sign = hist_entry__folded(entry);
1326         }
1327
1328         if (row_offset == 0) {
1329                 struct hpp_arg arg = {
1330                         .b              = &browser->b,
1331                         .folded_sign    = folded_sign,
1332                         .current_entry  = current_entry,
1333                 };
1334                 int column = 0;
1335
1336                 ui_browser__gotorc(&browser->b, row, 0);
1337
1338                 hists__for_each_format(browser->hists, fmt) {
1339                         char s[2048];
1340                         struct perf_hpp hpp = {
1341                                 .buf    = s,
1342                                 .size   = sizeof(s),
1343                                 .ptr    = &arg,
1344                         };
1345
1346                         if (perf_hpp__should_skip(fmt, entry->hists) ||
1347                             column++ < browser->b.horiz_scroll)
1348                                 continue;
1349
1350                         if (current_entry && browser->b.navkeypressed) {
1351                                 ui_browser__set_color(&browser->b,
1352                                                       HE_COLORSET_SELECTED);
1353                         } else {
1354                                 ui_browser__set_color(&browser->b,
1355                                                       HE_COLORSET_NORMAL);
1356                         }
1357
1358                         if (first) {
1359                                 if (use_callchain) {
1360                                         ui_browser__printf(&browser->b, "%c ", folded_sign);
1361                                         width -= 2;
1362                                 }
1363                                 first = false;
1364                         } else {
1365                                 ui_browser__printf(&browser->b, "  ");
1366                                 width -= 2;
1367                         }
1368
1369                         if (fmt->color) {
1370                                 int ret = fmt->color(fmt, &hpp, entry);
1371                                 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1372                                 /*
1373                                  * fmt->color() already used ui_browser to
1374                                  * print the non alignment bits, skip it (+ret):
1375                                  */
1376                                 ui_browser__printf(&browser->b, "%s", s + ret);
1377                         } else {
1378                                 hist_entry__snprintf_alignment(entry, &hpp, fmt, fmt->entry(fmt, &hpp, entry));
1379                                 ui_browser__printf(&browser->b, "%s", s);
1380                         }
1381                         width -= hpp.buf - s;
1382                 }
1383
1384                 /* The scroll bar isn't being used */
1385                 if (!browser->b.navkeypressed)
1386                         width += 1;
1387
1388                 ui_browser__write_nstring(&browser->b, "", width);
1389
1390                 ++row;
1391                 ++printed;
1392         } else
1393                 --row_offset;
1394
1395         if (folded_sign == '-' && row != browser->b.rows) {
1396                 struct callchain_print_arg arg = {
1397                         .row_offset = row_offset,
1398                         .is_current_entry = current_entry,
1399                 };
1400
1401                 printed += hist_browser__show_callchain(browser,
1402                                 entry, 1, row,
1403                                 hist_browser__show_callchain_entry,
1404                                 &arg,
1405                                 hist_browser__check_output_full);
1406         }
1407
1408         return printed;
1409 }
1410
1411 static int hist_browser__show_hierarchy_entry(struct hist_browser *browser,
1412                                               struct hist_entry *entry,
1413                                               unsigned short row,
1414                                               int level)
1415 {
1416         int printed = 0;
1417         int width = browser->b.width;
1418         char folded_sign = ' ';
1419         bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1420         off_t row_offset = entry->row_offset;
1421         bool first = true;
1422         struct perf_hpp_fmt *fmt;
1423         struct perf_hpp_list_node *fmt_node;
1424         struct hpp_arg arg = {
1425                 .b              = &browser->b,
1426                 .current_entry  = current_entry,
1427         };
1428         int column = 0;
1429         int hierarchy_indent = (entry->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
1430
1431         if (current_entry) {
1432                 browser->he_selection = entry;
1433                 browser->selection = &entry->ms;
1434         }
1435
1436         hist_entry__init_have_children(entry);
1437         folded_sign = hist_entry__folded(entry);
1438         arg.folded_sign = folded_sign;
1439
1440         if (entry->leaf && row_offset) {
1441                 row_offset--;
1442                 goto show_callchain;
1443         }
1444
1445         ui_browser__gotorc(&browser->b, row, 0);
1446
1447         if (current_entry && browser->b.navkeypressed)
1448                 ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1449         else
1450                 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1451
1452         ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1453         width -= level * HIERARCHY_INDENT;
1454
1455         /* the first hpp_list_node is for overhead columns */
1456         fmt_node = list_first_entry(&entry->hists->hpp_formats,
1457                                     struct perf_hpp_list_node, list);
1458         perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1459                 char s[2048];
1460                 struct perf_hpp hpp = {
1461                         .buf            = s,
1462                         .size           = sizeof(s),
1463                         .ptr            = &arg,
1464                 };
1465
1466                 if (perf_hpp__should_skip(fmt, entry->hists) ||
1467                     column++ < browser->b.horiz_scroll)
1468                         continue;
1469
1470                 if (current_entry && browser->b.navkeypressed) {
1471                         ui_browser__set_color(&browser->b,
1472                                               HE_COLORSET_SELECTED);
1473                 } else {
1474                         ui_browser__set_color(&browser->b,
1475                                               HE_COLORSET_NORMAL);
1476                 }
1477
1478                 if (first) {
1479                         ui_browser__printf(&browser->b, "%c ", folded_sign);
1480                         width -= 2;
1481                         first = false;
1482                 } else {
1483                         ui_browser__printf(&browser->b, "  ");
1484                         width -= 2;
1485                 }
1486
1487                 if (fmt->color) {
1488                         int ret = fmt->color(fmt, &hpp, entry);
1489                         hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1490                         /*
1491                          * fmt->color() already used ui_browser to
1492                          * print the non alignment bits, skip it (+ret):
1493                          */
1494                         ui_browser__printf(&browser->b, "%s", s + ret);
1495                 } else {
1496                         int ret = fmt->entry(fmt, &hpp, entry);
1497                         hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1498                         ui_browser__printf(&browser->b, "%s", s);
1499                 }
1500                 width -= hpp.buf - s;
1501         }
1502
1503         if (!first) {
1504                 ui_browser__write_nstring(&browser->b, "", hierarchy_indent);
1505                 width -= hierarchy_indent;
1506         }
1507
1508         if (column >= browser->b.horiz_scroll) {
1509                 char s[2048];
1510                 struct perf_hpp hpp = {
1511                         .buf            = s,
1512                         .size           = sizeof(s),
1513                         .ptr            = &arg,
1514                 };
1515
1516                 if (current_entry && browser->b.navkeypressed) {
1517                         ui_browser__set_color(&browser->b,
1518                                               HE_COLORSET_SELECTED);
1519                 } else {
1520                         ui_browser__set_color(&browser->b,
1521                                               HE_COLORSET_NORMAL);
1522                 }
1523
1524                 perf_hpp_list__for_each_format(entry->hpp_list, fmt) {
1525                         if (first) {
1526                                 ui_browser__printf(&browser->b, "%c ", folded_sign);
1527                                 first = false;
1528                         } else {
1529                                 ui_browser__write_nstring(&browser->b, "", 2);
1530                         }
1531
1532                         width -= 2;
1533
1534                         /*
1535                          * No need to call hist_entry__snprintf_alignment()
1536                          * since this fmt is always the last column in the
1537                          * hierarchy mode.
1538                          */
1539                         if (fmt->color) {
1540                                 width -= fmt->color(fmt, &hpp, entry);
1541                         } else {
1542                                 int i = 0;
1543
1544                                 width -= fmt->entry(fmt, &hpp, entry);
1545                                 ui_browser__printf(&browser->b, "%s", skip_spaces(s));
1546
1547                                 while (isspace(s[i++]))
1548                                         width++;
1549                         }
1550                 }
1551         }
1552
1553         /* The scroll bar isn't being used */
1554         if (!browser->b.navkeypressed)
1555                 width += 1;
1556
1557         ui_browser__write_nstring(&browser->b, "", width);
1558
1559         ++row;
1560         ++printed;
1561
1562 show_callchain:
1563         if (entry->leaf && folded_sign == '-' && row != browser->b.rows) {
1564                 struct callchain_print_arg carg = {
1565                         .row_offset = row_offset,
1566                 };
1567
1568                 printed += hist_browser__show_callchain(browser, entry,
1569                                         level + 1, row,
1570                                         hist_browser__show_callchain_entry, &carg,
1571                                         hist_browser__check_output_full);
1572         }
1573
1574         return printed;
1575 }
1576
1577 static int hist_browser__show_no_entry(struct hist_browser *browser,
1578                                        unsigned short row, int level)
1579 {
1580         int width = browser->b.width;
1581         bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1582         bool first = true;
1583         int column = 0;
1584         int ret;
1585         struct perf_hpp_fmt *fmt;
1586         struct perf_hpp_list_node *fmt_node;
1587         int indent = browser->hists->nr_hpp_node - 2;
1588
1589         if (current_entry) {
1590                 browser->he_selection = NULL;
1591                 browser->selection = NULL;
1592         }
1593
1594         ui_browser__gotorc(&browser->b, row, 0);
1595
1596         if (current_entry && browser->b.navkeypressed)
1597                 ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1598         else
1599                 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1600
1601         ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1602         width -= level * HIERARCHY_INDENT;
1603
1604         /* the first hpp_list_node is for overhead columns */
1605         fmt_node = list_first_entry(&browser->hists->hpp_formats,
1606                                     struct perf_hpp_list_node, list);
1607         perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1608                 if (perf_hpp__should_skip(fmt, browser->hists) ||
1609                     column++ < browser->b.horiz_scroll)
1610                         continue;
1611
1612                 ret = fmt->width(fmt, NULL, browser->hists);
1613
1614                 if (first) {
1615                         /* for folded sign */
1616                         first = false;
1617                         ret++;
1618                 } else {
1619                         /* space between columns */
1620                         ret += 2;
1621                 }
1622
1623                 ui_browser__write_nstring(&browser->b, "", ret);
1624                 width -= ret;
1625         }
1626
1627         ui_browser__write_nstring(&browser->b, "", indent * HIERARCHY_INDENT);
1628         width -= indent * HIERARCHY_INDENT;
1629
1630         if (column >= browser->b.horiz_scroll) {
1631                 char buf[32];
1632
1633                 ret = snprintf(buf, sizeof(buf), "no entry >= %.2f%%", browser->min_pcnt);
1634                 ui_browser__printf(&browser->b, "  %s", buf);
1635                 width -= ret + 2;
1636         }
1637
1638         /* The scroll bar isn't being used */
1639         if (!browser->b.navkeypressed)
1640                 width += 1;
1641
1642         ui_browser__write_nstring(&browser->b, "", width);
1643         return 1;
1644 }
1645
1646 static int advance_hpp_check(struct perf_hpp *hpp, int inc)
1647 {
1648         advance_hpp(hpp, inc);
1649         return hpp->size <= 0;
1650 }
1651
1652 static int
1653 hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf,
1654                                  size_t size, int line)
1655 {
1656         struct hists *hists = browser->hists;
1657         struct perf_hpp dummy_hpp = {
1658                 .buf    = buf,
1659                 .size   = size,
1660         };
1661         struct perf_hpp_fmt *fmt;
1662         size_t ret = 0;
1663         int column = 0;
1664         int span = 0;
1665
1666         if (hists__has_callchains(hists) && symbol_conf.use_callchain) {
1667                 ret = scnprintf(buf, size, "  ");
1668                 if (advance_hpp_check(&dummy_hpp, ret))
1669                         return ret;
1670         }
1671
1672         hists__for_each_format(browser->hists, fmt) {
1673                 if (perf_hpp__should_skip(fmt, hists)  || column++ < browser->b.horiz_scroll)
1674                         continue;
1675
1676                 ret = fmt->header(fmt, &dummy_hpp, hists, line, &span);
1677                 if (advance_hpp_check(&dummy_hpp, ret))
1678                         break;
1679
1680                 if (span)
1681                         continue;
1682
1683                 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
1684                 if (advance_hpp_check(&dummy_hpp, ret))
1685                         break;
1686         }
1687
1688         return ret;
1689 }
1690
1691 static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser, char *buf, size_t size)
1692 {
1693         struct hists *hists = browser->hists;
1694         struct perf_hpp dummy_hpp = {
1695                 .buf    = buf,
1696                 .size   = size,
1697         };
1698         struct perf_hpp_fmt *fmt;
1699         struct perf_hpp_list_node *fmt_node;
1700         size_t ret = 0;
1701         int column = 0;
1702         int indent = hists->nr_hpp_node - 2;
1703         bool first_node, first_col;
1704
1705         ret = scnprintf(buf, size, "  ");
1706         if (advance_hpp_check(&dummy_hpp, ret))
1707                 return ret;
1708
1709         first_node = true;
1710         /* the first hpp_list_node is for overhead columns */
1711         fmt_node = list_first_entry(&hists->hpp_formats,
1712                                     struct perf_hpp_list_node, list);
1713         perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1714                 if (column++ < browser->b.horiz_scroll)
1715                         continue;
1716
1717                 ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1718                 if (advance_hpp_check(&dummy_hpp, ret))
1719                         break;
1720
1721                 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
1722                 if (advance_hpp_check(&dummy_hpp, ret))
1723                         break;
1724
1725                 first_node = false;
1726         }
1727
1728         if (!first_node) {
1729                 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "%*s",
1730                                 indent * HIERARCHY_INDENT, "");
1731                 if (advance_hpp_check(&dummy_hpp, ret))
1732                         return ret;
1733         }
1734
1735         first_node = true;
1736         list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
1737                 if (!first_node) {
1738                         ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " / ");
1739                         if (advance_hpp_check(&dummy_hpp, ret))
1740                                 break;
1741                 }
1742                 first_node = false;
1743
1744                 first_col = true;
1745                 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1746                         char *start;
1747
1748                         if (perf_hpp__should_skip(fmt, hists))
1749                                 continue;
1750
1751                         if (!first_col) {
1752                                 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "+");
1753                                 if (advance_hpp_check(&dummy_hpp, ret))
1754                                         break;
1755                         }
1756                         first_col = false;
1757
1758                         ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1759                         dummy_hpp.buf[ret] = '\0';
1760
1761                         start = strim(dummy_hpp.buf);
1762                         ret = strlen(start);
1763
1764                         if (start != dummy_hpp.buf)
1765                                 memmove(dummy_hpp.buf, start, ret + 1);
1766
1767                         if (advance_hpp_check(&dummy_hpp, ret))
1768                                 break;
1769                 }
1770         }
1771
1772         return ret;
1773 }
1774
1775 static void hists_browser__hierarchy_headers(struct hist_browser *browser)
1776 {
1777         char headers[1024];
1778
1779         hists_browser__scnprintf_hierarchy_headers(browser, headers,
1780                                                    sizeof(headers));
1781
1782         ui_browser__gotorc(&browser->b, 0, 0);
1783         ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1784         ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1785 }
1786
1787 static void hists_browser__headers(struct hist_browser *browser)
1788 {
1789         struct hists *hists = browser->hists;
1790         struct perf_hpp_list *hpp_list = hists->hpp_list;
1791
1792         int line;
1793
1794         for (line = 0; line < hpp_list->nr_header_lines; line++) {
1795                 char headers[1024];
1796
1797                 hists_browser__scnprintf_headers(browser, headers,
1798                                                  sizeof(headers), line);
1799
1800                 ui_browser__gotorc_title(&browser->b, line, 0);
1801                 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1802                 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1803         }
1804 }
1805
1806 static void hist_browser__show_headers(struct hist_browser *browser)
1807 {
1808         if (symbol_conf.report_hierarchy)
1809                 hists_browser__hierarchy_headers(browser);
1810         else
1811                 hists_browser__headers(browser);
1812 }
1813
1814 static void ui_browser__hists_init_top(struct ui_browser *browser)
1815 {
1816         if (browser->top == NULL) {
1817                 struct hist_browser *hb;
1818
1819                 hb = container_of(browser, struct hist_browser, b);
1820                 browser->top = rb_first_cached(&hb->hists->entries);
1821         }
1822 }
1823
1824 static unsigned int hist_browser__refresh(struct ui_browser *browser)
1825 {
1826         unsigned row = 0;
1827         struct rb_node *nd;
1828         struct hist_browser *hb = container_of(browser, struct hist_browser, b);
1829
1830         if (hb->show_headers)
1831                 hist_browser__show_headers(hb);
1832
1833         ui_browser__hists_init_top(browser);
1834         hb->he_selection = NULL;
1835         hb->selection = NULL;
1836
1837         for (nd = browser->top; nd; nd = rb_hierarchy_next(nd)) {
1838                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1839                 float percent;
1840
1841                 if (h->filtered) {
1842                         /* let it move to sibling */
1843                         h->unfolded = false;
1844                         continue;
1845                 }
1846
1847                 if (symbol_conf.report_individual_block)
1848                         percent = block_info__total_cycles_percent(h);
1849                 else
1850                         percent = hist_entry__get_percent_limit(h);
1851
1852                 if (percent < hb->min_pcnt)
1853                         continue;
1854
1855                 if (symbol_conf.report_hierarchy) {
1856                         row += hist_browser__show_hierarchy_entry(hb, h, row,
1857                                                                   h->depth);
1858                         if (row == browser->rows)
1859                                 break;
1860
1861                         if (h->has_no_entry) {
1862                                 hist_browser__show_no_entry(hb, row, h->depth + 1);
1863                                 row++;
1864                         }
1865                 } else {
1866                         row += hist_browser__show_entry(hb, h, row);
1867                 }
1868
1869                 if (row == browser->rows)
1870                         break;
1871         }
1872
1873         return row;
1874 }
1875
1876 static struct rb_node *hists__filter_entries(struct rb_node *nd,
1877                                              float min_pcnt)
1878 {
1879         while (nd != NULL) {
1880                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1881                 float percent = hist_entry__get_percent_limit(h);
1882
1883                 if (!h->filtered && percent >= min_pcnt)
1884                         return nd;
1885
1886                 /*
1887                  * If it's filtered, its all children also were filtered.
1888                  * So move to sibling node.
1889                  */
1890                 if (rb_next(nd))
1891                         nd = rb_next(nd);
1892                 else
1893                         nd = rb_hierarchy_next(nd);
1894         }
1895
1896         return NULL;
1897 }
1898
1899 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
1900                                                   float min_pcnt)
1901 {
1902         while (nd != NULL) {
1903                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1904                 float percent = hist_entry__get_percent_limit(h);
1905
1906                 if (!h->filtered && percent >= min_pcnt)
1907                         return nd;
1908
1909                 nd = rb_hierarchy_prev(nd);
1910         }
1911
1912         return NULL;
1913 }
1914
1915 static void ui_browser__hists_seek(struct ui_browser *browser,
1916                                    off_t offset, int whence)
1917 {
1918         struct hist_entry *h;
1919         struct rb_node *nd;
1920         bool first = true;
1921         struct hist_browser *hb;
1922
1923         hb = container_of(browser, struct hist_browser, b);
1924
1925         if (browser->nr_entries == 0)
1926                 return;
1927
1928         ui_browser__hists_init_top(browser);
1929
1930         switch (whence) {
1931         case SEEK_SET:
1932                 nd = hists__filter_entries(rb_first(browser->entries),
1933                                            hb->min_pcnt);
1934                 break;
1935         case SEEK_CUR:
1936                 nd = browser->top;
1937                 goto do_offset;
1938         case SEEK_END:
1939                 nd = rb_hierarchy_last(rb_last(browser->entries));
1940                 nd = hists__filter_prev_entries(nd, hb->min_pcnt);
1941                 first = false;
1942                 break;
1943         default:
1944                 return;
1945         }
1946
1947         /*
1948          * Moves not relative to the first visible entry invalidates its
1949          * row_offset:
1950          */
1951         h = rb_entry(browser->top, struct hist_entry, rb_node);
1952         h->row_offset = 0;
1953
1954         /*
1955          * Here we have to check if nd is expanded (+), if it is we can't go
1956          * the next top level hist_entry, instead we must compute an offset of
1957          * what _not_ to show and not change the first visible entry.
1958          *
1959          * This offset increments when we are going from top to bottom and
1960          * decreases when we're going from bottom to top.
1961          *
1962          * As we don't have backpointers to the top level in the callchains
1963          * structure, we need to always print the whole hist_entry callchain,
1964          * skipping the first ones that are before the first visible entry
1965          * and stop when we printed enough lines to fill the screen.
1966          */
1967 do_offset:
1968         if (!nd)
1969                 return;
1970
1971         if (offset > 0) {
1972                 do {
1973                         h = rb_entry(nd, struct hist_entry, rb_node);
1974                         if (h->unfolded && h->leaf) {
1975                                 u16 remaining = h->nr_rows - h->row_offset;
1976                                 if (offset > remaining) {
1977                                         offset -= remaining;
1978                                         h->row_offset = 0;
1979                                 } else {
1980                                         h->row_offset += offset;
1981                                         offset = 0;
1982                                         browser->top = nd;
1983                                         break;
1984                                 }
1985                         }
1986                         nd = hists__filter_entries(rb_hierarchy_next(nd),
1987                                                    hb->min_pcnt);
1988                         if (nd == NULL)
1989                                 break;
1990                         --offset;
1991                         browser->top = nd;
1992                 } while (offset != 0);
1993         } else if (offset < 0) {
1994                 while (1) {
1995                         h = rb_entry(nd, struct hist_entry, rb_node);
1996                         if (h->unfolded && h->leaf) {
1997                                 if (first) {
1998                                         if (-offset > h->row_offset) {
1999                                                 offset += h->row_offset;
2000                                                 h->row_offset = 0;
2001                                         } else {
2002                                                 h->row_offset += offset;
2003                                                 offset = 0;
2004                                                 browser->top = nd;
2005                                                 break;
2006                                         }
2007                                 } else {
2008                                         if (-offset > h->nr_rows) {
2009                                                 offset += h->nr_rows;
2010                                                 h->row_offset = 0;
2011                                         } else {
2012                                                 h->row_offset = h->nr_rows + offset;
2013                                                 offset = 0;
2014                                                 browser->top = nd;
2015                                                 break;
2016                                         }
2017                                 }
2018                         }
2019
2020                         nd = hists__filter_prev_entries(rb_hierarchy_prev(nd),
2021                                                         hb->min_pcnt);
2022                         if (nd == NULL)
2023                                 break;
2024                         ++offset;
2025                         browser->top = nd;
2026                         if (offset == 0) {
2027                                 /*
2028                                  * Last unfiltered hist_entry, check if it is
2029                                  * unfolded, if it is then we should have
2030                                  * row_offset at its last entry.
2031                                  */
2032                                 h = rb_entry(nd, struct hist_entry, rb_node);
2033                                 if (h->unfolded && h->leaf)
2034                                         h->row_offset = h->nr_rows;
2035                                 break;
2036                         }
2037                         first = false;
2038                 }
2039         } else {
2040                 browser->top = nd;
2041                 h = rb_entry(nd, struct hist_entry, rb_node);
2042                 h->row_offset = 0;
2043         }
2044 }
2045
2046 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
2047                                            struct hist_entry *he, FILE *fp,
2048                                            int level)
2049 {
2050         struct callchain_print_arg arg  = {
2051                 .fp = fp,
2052         };
2053
2054         hist_browser__show_callchain(browser, he, level, 0,
2055                                      hist_browser__fprintf_callchain_entry, &arg,
2056                                      hist_browser__check_dump_full);
2057         return arg.printed;
2058 }
2059
2060 static int hist_browser__fprintf_entry(struct hist_browser *browser,
2061                                        struct hist_entry *he, FILE *fp)
2062 {
2063         char s[8192];
2064         int printed = 0;
2065         char folded_sign = ' ';
2066         struct perf_hpp hpp = {
2067                 .buf = s,
2068                 .size = sizeof(s),
2069         };
2070         struct perf_hpp_fmt *fmt;
2071         bool first = true;
2072         int ret;
2073
2074         if (hist_entry__has_callchains(he) && symbol_conf.use_callchain) {
2075                 folded_sign = hist_entry__folded(he);
2076                 printed += fprintf(fp, "%c ", folded_sign);
2077         }
2078
2079         hists__for_each_format(browser->hists, fmt) {
2080                 if (perf_hpp__should_skip(fmt, he->hists))
2081                         continue;
2082
2083                 if (!first) {
2084                         ret = scnprintf(hpp.buf, hpp.size, "  ");
2085                         advance_hpp(&hpp, ret);
2086                 } else
2087                         first = false;
2088
2089                 ret = fmt->entry(fmt, &hpp, he);
2090                 ret = hist_entry__snprintf_alignment(he, &hpp, fmt, ret);
2091                 advance_hpp(&hpp, ret);
2092         }
2093         printed += fprintf(fp, "%s\n", s);
2094
2095         if (folded_sign == '-')
2096                 printed += hist_browser__fprintf_callchain(browser, he, fp, 1);
2097
2098         return printed;
2099 }
2100
2101
2102 static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,
2103                                                  struct hist_entry *he,
2104                                                  FILE *fp, int level)
2105 {
2106         char s[8192];
2107         int printed = 0;
2108         char folded_sign = ' ';
2109         struct perf_hpp hpp = {
2110                 .buf = s,
2111                 .size = sizeof(s),
2112         };
2113         struct perf_hpp_fmt *fmt;
2114         struct perf_hpp_list_node *fmt_node;
2115         bool first = true;
2116         int ret;
2117         int hierarchy_indent = (he->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
2118
2119         printed = fprintf(fp, "%*s", level * HIERARCHY_INDENT, "");
2120
2121         folded_sign = hist_entry__folded(he);
2122         printed += fprintf(fp, "%c", folded_sign);
2123
2124         /* the first hpp_list_node is for overhead columns */
2125         fmt_node = list_first_entry(&he->hists->hpp_formats,
2126                                     struct perf_hpp_list_node, list);
2127         perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
2128                 if (!first) {
2129                         ret = scnprintf(hpp.buf, hpp.size, "  ");
2130                         advance_hpp(&hpp, ret);
2131                 } else
2132                         first = false;
2133
2134                 ret = fmt->entry(fmt, &hpp, he);
2135                 advance_hpp(&hpp, ret);
2136         }
2137
2138         ret = scnprintf(hpp.buf, hpp.size, "%*s", hierarchy_indent, "");
2139         advance_hpp(&hpp, ret);
2140
2141         perf_hpp_list__for_each_format(he->hpp_list, fmt) {
2142                 ret = scnprintf(hpp.buf, hpp.size, "  ");
2143                 advance_hpp(&hpp, ret);
2144
2145                 ret = fmt->entry(fmt, &hpp, he);
2146                 advance_hpp(&hpp, ret);
2147         }
2148
2149         strim(s);
2150         printed += fprintf(fp, "%s\n", s);
2151
2152         if (he->leaf && folded_sign == '-') {
2153                 printed += hist_browser__fprintf_callchain(browser, he, fp,
2154                                                            he->depth + 1);
2155         }
2156
2157         return printed;
2158 }
2159
2160 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
2161 {
2162         struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
2163                                                    browser->min_pcnt);
2164         int printed = 0;
2165
2166         while (nd) {
2167                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
2168
2169                 if (symbol_conf.report_hierarchy) {
2170                         printed += hist_browser__fprintf_hierarchy_entry(browser,
2171                                                                          h, fp,
2172                                                                          h->depth);
2173                 } else {
2174                         printed += hist_browser__fprintf_entry(browser, h, fp);
2175                 }
2176
2177                 nd = hists__filter_entries(rb_hierarchy_next(nd),
2178                                            browser->min_pcnt);
2179         }
2180
2181         return printed;
2182 }
2183
2184 static int hist_browser__dump(struct hist_browser *browser)
2185 {
2186         char filename[64];
2187         FILE *fp;
2188
2189         while (1) {
2190                 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
2191                 if (access(filename, F_OK))
2192                         break;
2193                 /*
2194                  * XXX: Just an arbitrary lazy upper limit
2195                  */
2196                 if (++browser->print_seq == 8192) {
2197                         ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
2198                         return -1;
2199                 }
2200         }
2201
2202         fp = fopen(filename, "w");
2203         if (fp == NULL) {
2204                 char bf[64];
2205                 const char *err = str_error_r(errno, bf, sizeof(bf));
2206                 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
2207                 return -1;
2208         }
2209
2210         ++browser->print_seq;
2211         hist_browser__fprintf(browser, fp);
2212         fclose(fp);
2213         ui_helpline__fpush("%s written!", filename);
2214
2215         return 0;
2216 }
2217
2218 void hist_browser__init(struct hist_browser *browser,
2219                         struct hists *hists)
2220 {
2221         struct perf_hpp_fmt *fmt;
2222
2223         browser->hists                  = hists;
2224         browser->b.refresh              = hist_browser__refresh;
2225         browser->b.refresh_dimensions   = hist_browser__refresh_dimensions;
2226         browser->b.seek                 = ui_browser__hists_seek;
2227         browser->b.use_navkeypressed    = true;
2228         browser->show_headers           = symbol_conf.show_hist_headers;
2229         hist_browser__set_title_space(browser);
2230
2231         if (symbol_conf.report_hierarchy) {
2232                 struct perf_hpp_list_node *fmt_node;
2233
2234                 /* count overhead columns (in the first node) */
2235                 fmt_node = list_first_entry(&hists->hpp_formats,
2236                                             struct perf_hpp_list_node, list);
2237                 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt)
2238                         ++browser->b.columns;
2239
2240                 /* add a single column for whole hierarchy sort keys*/
2241                 ++browser->b.columns;
2242         } else {
2243                 hists__for_each_format(hists, fmt)
2244                         ++browser->b.columns;
2245         }
2246
2247         hists__reset_column_width(hists);
2248 }
2249
2250 struct hist_browser *hist_browser__new(struct hists *hists)
2251 {
2252         struct hist_browser *browser = zalloc(sizeof(*browser));
2253
2254         if (browser)
2255                 hist_browser__init(browser, hists);
2256
2257         return browser;
2258 }
2259
2260 static struct hist_browser *
2261 perf_evsel_browser__new(struct evsel *evsel,
2262                         struct hist_browser_timer *hbt,
2263                         struct perf_env *env,
2264                         struct annotation_options *annotation_opts)
2265 {
2266         struct hist_browser *browser = hist_browser__new(evsel__hists(evsel));
2267
2268         if (browser) {
2269                 browser->hbt   = hbt;
2270                 browser->env   = env;
2271                 browser->title = hists_browser__scnprintf_title;
2272                 browser->annotation_opts = annotation_opts;
2273         }
2274         return browser;
2275 }
2276
2277 void hist_browser__delete(struct hist_browser *browser)
2278 {
2279         free(browser);
2280 }
2281
2282 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
2283 {
2284         return browser->he_selection;
2285 }
2286
2287 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
2288 {
2289         return browser->he_selection->thread;
2290 }
2291
2292 static struct res_sample *hist_browser__selected_res_sample(struct hist_browser *browser)
2293 {
2294         return browser->he_selection ? browser->he_selection->res_samples : NULL;
2295 }
2296
2297 /* Check whether the browser is for 'top' or 'report' */
2298 static inline bool is_report_browser(void *timer)
2299 {
2300         return timer == NULL;
2301 }
2302
2303 static int hists_browser__scnprintf_title(struct hist_browser *browser, char *bf, size_t size)
2304 {
2305         struct hist_browser_timer *hbt = browser->hbt;
2306         int printed = __hists__scnprintf_title(browser->hists, bf, size, !is_report_browser(hbt));
2307
2308         if (!is_report_browser(hbt)) {
2309                 struct perf_top *top = hbt->arg;
2310
2311                 printed += scnprintf(bf + printed, size - printed,
2312                                      " lost: %" PRIu64 "/%" PRIu64,
2313                                      top->lost, top->lost_total);
2314
2315                 printed += scnprintf(bf + printed, size - printed,
2316                                      " drop: %" PRIu64 "/%" PRIu64,
2317                                      top->drop, top->drop_total);
2318
2319                 if (top->zero)
2320                         printed += scnprintf(bf + printed, size - printed, " [z]");
2321
2322                 perf_top__reset_sample_counters(top);
2323         }
2324
2325
2326         return printed;
2327 }
2328
2329 static inline void free_popup_options(char **options, int n)
2330 {
2331         int i;
2332
2333         for (i = 0; i < n; ++i)
2334                 zfree(&options[i]);
2335 }
2336
2337 /*
2338  * Only runtime switching of perf data file will make "input_name" point
2339  * to a malloced buffer. So add "is_input_name_malloced" flag to decide
2340  * whether we need to call free() for current "input_name" during the switch.
2341  */
2342 static bool is_input_name_malloced = false;
2343
2344 static int switch_data_file(void)
2345 {
2346         char *pwd, *options[32], *abs_path[32], *tmp;
2347         DIR *pwd_dir;
2348         int nr_options = 0, choice = -1, ret = -1;
2349         struct dirent *dent;
2350
2351         pwd = getenv("PWD");
2352         if (!pwd)
2353                 return ret;
2354
2355         pwd_dir = opendir(pwd);
2356         if (!pwd_dir)
2357                 return ret;
2358
2359         memset(options, 0, sizeof(options));
2360         memset(abs_path, 0, sizeof(abs_path));
2361
2362         while ((dent = readdir(pwd_dir))) {
2363                 char path[PATH_MAX];
2364                 u64 magic;
2365                 char *name = dent->d_name;
2366                 FILE *file;
2367
2368                 if (!(dent->d_type == DT_REG))
2369                         continue;
2370
2371                 snprintf(path, sizeof(path), "%s/%s", pwd, name);
2372
2373                 file = fopen(path, "r");
2374                 if (!file)
2375                         continue;
2376
2377                 if (fread(&magic, 1, 8, file) < 8)
2378                         goto close_file_and_continue;
2379
2380                 if (is_perf_magic(magic)) {
2381                         options[nr_options] = strdup(name);
2382                         if (!options[nr_options])
2383                                 goto close_file_and_continue;
2384
2385                         abs_path[nr_options] = strdup(path);
2386                         if (!abs_path[nr_options]) {
2387                                 zfree(&options[nr_options]);
2388                                 ui__warning("Can't search all data files due to memory shortage.\n");
2389                                 fclose(file);
2390                                 break;
2391                         }
2392
2393                         nr_options++;
2394                 }
2395
2396 close_file_and_continue:
2397                 fclose(file);
2398                 if (nr_options >= 32) {
2399                         ui__warning("Too many perf data files in PWD!\n"
2400                                     "Only the first 32 files will be listed.\n");
2401                         break;
2402                 }
2403         }
2404         closedir(pwd_dir);
2405
2406         if (nr_options) {
2407                 choice = ui__popup_menu(nr_options, options, NULL);
2408                 if (choice < nr_options && choice >= 0) {
2409                         tmp = strdup(abs_path[choice]);
2410                         if (tmp) {
2411                                 if (is_input_name_malloced)
2412                                         free((void *)input_name);
2413                                 input_name = tmp;
2414                                 is_input_name_malloced = true;
2415                                 ret = 0;
2416                         } else
2417                                 ui__warning("Data switch failed due to memory shortage!\n");
2418                 }
2419         }
2420
2421         free_popup_options(options, nr_options);
2422         free_popup_options(abs_path, nr_options);
2423         return ret;
2424 }
2425
2426 struct popup_action {
2427         unsigned long           time;
2428         struct thread           *thread;
2429         struct map_symbol       ms;
2430         int                     socket;
2431         struct evsel    *evsel;
2432         enum rstype             rstype;
2433
2434         int (*fn)(struct hist_browser *browser, struct popup_action *act);
2435 };
2436
2437 static int
2438 do_annotate(struct hist_browser *browser, struct popup_action *act)
2439 {
2440         struct evsel *evsel;
2441         struct annotation *notes;
2442         struct hist_entry *he;
2443         int err;
2444
2445         if (!browser->annotation_opts->objdump_path &&
2446             perf_env__lookup_objdump(browser->env, &browser->annotation_opts->objdump_path))
2447                 return 0;
2448
2449         notes = symbol__annotation(act->ms.sym);
2450         if (!notes->src)
2451                 return 0;
2452
2453         if (browser->block_evsel)
2454                 evsel = browser->block_evsel;
2455         else
2456                 evsel = hists_to_evsel(browser->hists);
2457
2458         err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt,
2459                                        browser->annotation_opts);
2460         he = hist_browser__selected_entry(browser);
2461         /*
2462          * offer option to annotate the other branch source or target
2463          * (if they exists) when returning from annotate
2464          */
2465         if ((err == 'q' || err == CTRL('c')) && he->branch_info)
2466                 return 1;
2467
2468         ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
2469         if (err)
2470                 ui_browser__handle_resize(&browser->b);
2471         return 0;
2472 }
2473
2474 static struct symbol *symbol__new_unresolved(u64 addr, struct map *map)
2475 {
2476         struct annotated_source *src;
2477         struct symbol *sym;
2478         char name[64];
2479
2480         snprintf(name, sizeof(name), "%.*" PRIx64, BITS_PER_LONG / 4, addr);
2481
2482         sym = symbol__new(addr, ANNOTATION_DUMMY_LEN, 0, 0, name);
2483         if (sym) {
2484                 src = symbol__hists(sym, 1);
2485                 if (!src) {
2486                         symbol__delete(sym);
2487                         return NULL;
2488                 }
2489
2490                 dso__insert_symbol(map->dso, sym);
2491         }
2492
2493         return sym;
2494 }
2495
2496 static int
2497 add_annotate_opt(struct hist_browser *browser __maybe_unused,
2498                  struct popup_action *act, char **optstr,
2499                  struct map_symbol *ms,
2500                  u64 addr)
2501 {
2502         if (!ms->map || !ms->map->dso || ms->map->dso->annotate_warned)
2503                 return 0;
2504
2505         if (!ms->sym)
2506                 ms->sym = symbol__new_unresolved(addr, ms->map);
2507
2508         if (ms->sym == NULL || symbol__annotation(ms->sym)->src == NULL)
2509                 return 0;
2510
2511         if (asprintf(optstr, "Annotate %s", ms->sym->name) < 0)
2512                 return 0;
2513
2514         act->ms = *ms;
2515         act->fn = do_annotate;
2516         return 1;
2517 }
2518
2519 static int
2520 do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
2521 {
2522         struct thread *thread = act->thread;
2523
2524         if ((!hists__has(browser->hists, thread) &&
2525              !hists__has(browser->hists, comm)) || thread == NULL)
2526                 return 0;
2527
2528         if (browser->hists->thread_filter) {
2529                 pstack__remove(browser->pstack, &browser->hists->thread_filter);
2530                 perf_hpp__set_elide(HISTC_THREAD, false);
2531                 thread__zput(browser->hists->thread_filter);
2532                 ui_helpline__pop();
2533         } else {
2534                 if (hists__has(browser->hists, thread)) {
2535                         ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
2536                                            thread->comm_set ? thread__comm_str(thread) : "",
2537                                            thread->tid);
2538                 } else {
2539                         ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
2540                                            thread->comm_set ? thread__comm_str(thread) : "");
2541                 }
2542
2543                 browser->hists->thread_filter = thread__get(thread);
2544                 perf_hpp__set_elide(HISTC_THREAD, false);
2545                 pstack__push(browser->pstack, &browser->hists->thread_filter);
2546         }
2547
2548         hists__filter_by_thread(browser->hists);
2549         hist_browser__reset(browser);
2550         return 0;
2551 }
2552
2553 static int
2554 add_thread_opt(struct hist_browser *browser, struct popup_action *act,
2555                char **optstr, struct thread *thread)
2556 {
2557         int ret;
2558
2559         if ((!hists__has(browser->hists, thread) &&
2560              !hists__has(browser->hists, comm)) || thread == NULL)
2561                 return 0;
2562
2563         if (hists__has(browser->hists, thread)) {
2564                 ret = asprintf(optstr, "Zoom %s %s(%d) thread",
2565                                browser->hists->thread_filter ? "out of" : "into",
2566                                thread->comm_set ? thread__comm_str(thread) : "",
2567                                thread->tid);
2568         } else {
2569                 ret = asprintf(optstr, "Zoom %s %s thread",
2570                                browser->hists->thread_filter ? "out of" : "into",
2571                                thread->comm_set ? thread__comm_str(thread) : "");
2572         }
2573         if (ret < 0)
2574                 return 0;
2575
2576         act->thread = thread;
2577         act->fn = do_zoom_thread;
2578         return 1;
2579 }
2580
2581 static int hists_browser__zoom_map(struct hist_browser *browser, struct map *map)
2582 {
2583         if (!hists__has(browser->hists, dso) || map == NULL)
2584                 return 0;
2585
2586         if (browser->hists->dso_filter) {
2587                 pstack__remove(browser->pstack, &browser->hists->dso_filter);
2588                 perf_hpp__set_elide(HISTC_DSO, false);
2589                 browser->hists->dso_filter = NULL;
2590                 ui_helpline__pop();
2591         } else {
2592                 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
2593                                    __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
2594                 browser->hists->dso_filter = map->dso;
2595                 perf_hpp__set_elide(HISTC_DSO, true);
2596                 pstack__push(browser->pstack, &browser->hists->dso_filter);
2597         }
2598
2599         hists__filter_by_dso(browser->hists);
2600         hist_browser__reset(browser);
2601         return 0;
2602 }
2603
2604 static int
2605 do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
2606 {
2607         return hists_browser__zoom_map(browser, act->ms.map);
2608 }
2609
2610 static int
2611 add_dso_opt(struct hist_browser *browser, struct popup_action *act,
2612             char **optstr, struct map *map)
2613 {
2614         if (!hists__has(browser->hists, dso) || map == NULL)
2615                 return 0;
2616
2617         if (asprintf(optstr, "Zoom %s %s DSO (use the 'k' hotkey to zoom directly into the kernel)",
2618                      browser->hists->dso_filter ? "out of" : "into",
2619                      __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
2620                 return 0;
2621
2622         act->ms.map = map;
2623         act->fn = do_zoom_dso;
2624         return 1;
2625 }
2626
2627 static int do_toggle_callchain(struct hist_browser *browser, struct popup_action *act __maybe_unused)
2628 {
2629         hist_browser__toggle_fold(browser);
2630         return 0;
2631 }
2632
2633 static int add_callchain_toggle_opt(struct hist_browser *browser, struct popup_action *act, char **optstr)
2634 {
2635         char sym_name[512];
2636
2637         if (!hist_browser__selection_has_children(browser))
2638                 return 0;
2639
2640         if (asprintf(optstr, "%s [%s] callchain (one level, same as '+' hotkey, use 'e'/'c' for the whole main level entry)",
2641                      hist_browser__selection_unfolded(browser) ? "Collapse" : "Expand",
2642                      hist_browser__selection_sym_name(browser, sym_name, sizeof(sym_name))) < 0)
2643                 return 0;
2644
2645         act->fn = do_toggle_callchain;
2646         return 1;
2647 }
2648
2649 static int
2650 do_browse_map(struct hist_browser *browser __maybe_unused,
2651               struct popup_action *act)
2652 {
2653         map__browse(act->ms.map);
2654         return 0;
2655 }
2656
2657 static int
2658 add_map_opt(struct hist_browser *browser,
2659             struct popup_action *act, char **optstr, struct map *map)
2660 {
2661         if (!hists__has(browser->hists, dso) || map == NULL)
2662                 return 0;
2663
2664         if (asprintf(optstr, "Browse map details") < 0)
2665                 return 0;
2666
2667         act->ms.map = map;
2668         act->fn = do_browse_map;
2669         return 1;
2670 }
2671
2672 static int
2673 do_run_script(struct hist_browser *browser __maybe_unused,
2674               struct popup_action *act)
2675 {
2676         char *script_opt;
2677         int len;
2678         int n = 0;
2679
2680         len = 100;
2681         if (act->thread)
2682                 len += strlen(thread__comm_str(act->thread));
2683         else if (act->ms.sym)
2684                 len += strlen(act->ms.sym->name);
2685         script_opt = malloc(len);
2686         if (!script_opt)
2687                 return -1;
2688
2689         script_opt[0] = 0;
2690         if (act->thread) {
2691                 n = scnprintf(script_opt, len, " -c %s ",
2692                           thread__comm_str(act->thread));
2693         } else if (act->ms.sym) {
2694                 n = scnprintf(script_opt, len, " -S %s ",
2695                           act->ms.sym->name);
2696         }
2697
2698         if (act->time) {
2699                 char start[32], end[32];
2700                 unsigned long starttime = act->time;
2701                 unsigned long endtime = act->time + symbol_conf.time_quantum;
2702
2703                 if (starttime == endtime) { /* Display 1ms as fallback */
2704                         starttime -= 1*NSEC_PER_MSEC;
2705                         endtime += 1*NSEC_PER_MSEC;
2706                 }
2707                 timestamp__scnprintf_usec(starttime, start, sizeof start);
2708                 timestamp__scnprintf_usec(endtime, end, sizeof end);
2709                 n += snprintf(script_opt + n, len - n, " --time %s,%s", start, end);
2710         }
2711
2712         script_browse(script_opt, act->evsel);
2713         free(script_opt);
2714         return 0;
2715 }
2716
2717 static int
2718 do_res_sample_script(struct hist_browser *browser __maybe_unused,
2719                      struct popup_action *act)
2720 {
2721         struct hist_entry *he;
2722
2723         he = hist_browser__selected_entry(browser);
2724         res_sample_browse(he->res_samples, he->num_res, act->evsel, act->rstype);
2725         return 0;
2726 }
2727
2728 static int
2729 add_script_opt_2(struct hist_browser *browser __maybe_unused,
2730                struct popup_action *act, char **optstr,
2731                struct thread *thread, struct symbol *sym,
2732                struct evsel *evsel, const char *tstr)
2733 {
2734
2735         if (thread) {
2736                 if (asprintf(optstr, "Run scripts for samples of thread [%s]%s",
2737                              thread__comm_str(thread), tstr) < 0)
2738                         return 0;
2739         } else if (sym) {
2740                 if (asprintf(optstr, "Run scripts for samples of symbol [%s]%s",
2741                              sym->name, tstr) < 0)
2742                         return 0;
2743         } else {
2744                 if (asprintf(optstr, "Run scripts for all samples%s", tstr) < 0)
2745                         return 0;
2746         }
2747
2748         act->thread = thread;
2749         act->ms.sym = sym;
2750         act->evsel = evsel;
2751         act->fn = do_run_script;
2752         return 1;
2753 }
2754
2755 static int
2756 add_script_opt(struct hist_browser *browser,
2757                struct popup_action *act, char **optstr,
2758                struct thread *thread, struct symbol *sym,
2759                struct evsel *evsel)
2760 {
2761         int n, j;
2762         struct hist_entry *he;
2763
2764         n = add_script_opt_2(browser, act, optstr, thread, sym, evsel, "");
2765
2766         he = hist_browser__selected_entry(browser);
2767         if (sort_order && strstr(sort_order, "time")) {
2768                 char tstr[128];
2769
2770                 optstr++;
2771                 act++;
2772                 j = sprintf(tstr, " in ");
2773                 j += timestamp__scnprintf_usec(he->time, tstr + j,
2774                                                sizeof tstr - j);
2775                 j += sprintf(tstr + j, "-");
2776                 timestamp__scnprintf_usec(he->time + symbol_conf.time_quantum,
2777                                           tstr + j, sizeof tstr - j);
2778                 n += add_script_opt_2(browser, act, optstr, thread, sym,
2779                                           evsel, tstr);
2780                 act->time = he->time;
2781         }
2782         return n;
2783 }
2784
2785 static int
2786 add_res_sample_opt(struct hist_browser *browser __maybe_unused,
2787                    struct popup_action *act, char **optstr,
2788                    struct res_sample *res_sample,
2789                    struct evsel *evsel,
2790                    enum rstype type)
2791 {
2792         if (!res_sample)
2793                 return 0;
2794
2795         if (asprintf(optstr, "Show context for individual samples %s",
2796                 type == A_ASM ? "with assembler" :
2797                 type == A_SOURCE ? "with source" : "") < 0)
2798                 return 0;
2799
2800         act->fn = do_res_sample_script;
2801         act->evsel = evsel;
2802         act->rstype = type;
2803         return 1;
2804 }
2805
2806 static int
2807 do_switch_data(struct hist_browser *browser __maybe_unused,
2808                struct popup_action *act __maybe_unused)
2809 {
2810         if (switch_data_file()) {
2811                 ui__warning("Won't switch the data files due to\n"
2812                             "no valid data file get selected!\n");
2813                 return 0;
2814         }
2815
2816         return K_SWITCH_INPUT_DATA;
2817 }
2818
2819 static int
2820 add_switch_opt(struct hist_browser *browser,
2821                struct popup_action *act, char **optstr)
2822 {
2823         if (!is_report_browser(browser->hbt))
2824                 return 0;
2825
2826         if (asprintf(optstr, "Switch to another data file in PWD") < 0)
2827                 return 0;
2828
2829         act->fn = do_switch_data;
2830         return 1;
2831 }
2832
2833 static int
2834 do_exit_browser(struct hist_browser *browser __maybe_unused,
2835                 struct popup_action *act __maybe_unused)
2836 {
2837         return 0;
2838 }
2839
2840 static int
2841 add_exit_opt(struct hist_browser *browser __maybe_unused,
2842              struct popup_action *act, char **optstr)
2843 {
2844         if (asprintf(optstr, "Exit") < 0)
2845                 return 0;
2846
2847         act->fn = do_exit_browser;
2848         return 1;
2849 }
2850
2851 static int
2852 do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
2853 {
2854         if (!hists__has(browser->hists, socket) || act->socket < 0)
2855                 return 0;
2856
2857         if (browser->hists->socket_filter > -1) {
2858                 pstack__remove(browser->pstack, &browser->hists->socket_filter);
2859                 browser->hists->socket_filter = -1;
2860                 perf_hpp__set_elide(HISTC_SOCKET, false);
2861         } else {
2862                 browser->hists->socket_filter = act->socket;
2863                 perf_hpp__set_elide(HISTC_SOCKET, true);
2864                 pstack__push(browser->pstack, &browser->hists->socket_filter);
2865         }
2866
2867         hists__filter_by_socket(browser->hists);
2868         hist_browser__reset(browser);
2869         return 0;
2870 }
2871
2872 static int
2873 add_socket_opt(struct hist_browser *browser, struct popup_action *act,
2874                char **optstr, int socket_id)
2875 {
2876         if (!hists__has(browser->hists, socket) || socket_id < 0)
2877                 return 0;
2878
2879         if (asprintf(optstr, "Zoom %s Processor Socket %d",
2880                      (browser->hists->socket_filter > -1) ? "out of" : "into",
2881                      socket_id) < 0)
2882                 return 0;
2883
2884         act->socket = socket_id;
2885         act->fn = do_zoom_socket;
2886         return 1;
2887 }
2888
2889 static void hist_browser__update_nr_entries(struct hist_browser *hb)
2890 {
2891         u64 nr_entries = 0;
2892         struct rb_node *nd = rb_first_cached(&hb->hists->entries);
2893
2894         if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
2895                 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
2896                 return;
2897         }
2898
2899         while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2900                 nr_entries++;
2901                 nd = rb_hierarchy_next(nd);
2902         }
2903
2904         hb->nr_non_filtered_entries = nr_entries;
2905         hb->nr_hierarchy_entries = nr_entries;
2906 }
2907
2908 static void hist_browser__update_percent_limit(struct hist_browser *hb,
2909                                                double percent)
2910 {
2911         struct hist_entry *he;
2912         struct rb_node *nd = rb_first_cached(&hb->hists->entries);
2913         u64 total = hists__total_period(hb->hists);
2914         u64 min_callchain_hits = total * (percent / 100);
2915
2916         hb->min_pcnt = callchain_param.min_percent = percent;
2917
2918         while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2919                 he = rb_entry(nd, struct hist_entry, rb_node);
2920
2921                 if (he->has_no_entry) {
2922                         he->has_no_entry = false;
2923                         he->nr_rows = 0;
2924                 }
2925
2926                 if (!he->leaf || !hist_entry__has_callchains(he) || !symbol_conf.use_callchain)
2927                         goto next;
2928
2929                 if (callchain_param.mode == CHAIN_GRAPH_REL) {
2930                         total = he->stat.period;
2931
2932                         if (symbol_conf.cumulate_callchain)
2933                                 total = he->stat_acc->period;
2934
2935                         min_callchain_hits = total * (percent / 100);
2936                 }
2937
2938                 callchain_param.sort(&he->sorted_chain, he->callchain,
2939                                      min_callchain_hits, &callchain_param);
2940
2941 next:
2942                 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
2943
2944                 /* force to re-evaluate folding state of callchains */
2945                 he->init_have_children = false;
2946                 hist_entry__set_folding(he, hb, false);
2947         }
2948 }
2949
2950 static int evsel__hists_browse(struct evsel *evsel, int nr_events, const char *helpline,
2951                                bool left_exits, struct hist_browser_timer *hbt, float min_pcnt,
2952                                struct perf_env *env, bool warn_lost_event,
2953                                struct annotation_options *annotation_opts)
2954 {
2955         struct hists *hists = evsel__hists(evsel);
2956         struct hist_browser *browser = perf_evsel_browser__new(evsel, hbt, env, annotation_opts);
2957         struct branch_info *bi = NULL;
2958 #define MAX_OPTIONS  16
2959         char *options[MAX_OPTIONS];
2960         struct popup_action actions[MAX_OPTIONS];
2961         int nr_options = 0;
2962         int key = -1;
2963         char buf[128];
2964         int delay_secs = hbt ? hbt->refresh : 0;
2965
2966 #define HIST_BROWSER_HELP_COMMON                                        \
2967         "h/?/F1        Show this window\n"                              \
2968         "UP/DOWN/PGUP\n"                                                \
2969         "PGDN/SPACE    Navigate\n"                                      \
2970         "q/ESC/CTRL+C  Exit browser or go back to previous screen\n\n"  \
2971         "For multiple event sessions:\n\n"                              \
2972         "TAB/UNTAB     Switch events\n\n"                               \
2973         "For symbolic views (--sort has sym):\n\n"                      \
2974         "ENTER         Zoom into DSO/Threads & Annotate current symbol\n" \
2975         "ESC           Zoom out\n"                                      \
2976         "+             Expand/Collapse one callchain level\n"           \
2977         "a             Annotate current symbol\n"                       \
2978         "C             Collapse all callchains\n"                       \
2979         "d             Zoom into current DSO\n"                         \
2980         "e             Expand/Collapse main entry callchains\n" \
2981         "E             Expand all callchains\n"                         \
2982         "F             Toggle percentage of filtered entries\n"         \
2983         "H             Display column headers\n"                        \
2984         "k             Zoom into the kernel map\n"                      \
2985         "L             Change percent limit\n"                          \
2986         "m             Display context menu\n"                          \
2987         "S             Zoom into current Processor Socket\n"            \
2988
2989         /* help messages are sorted by lexical order of the hotkey */
2990         static const char report_help[] = HIST_BROWSER_HELP_COMMON
2991         "i             Show header information\n"
2992         "P             Print histograms to perf.hist.N\n"
2993         "r             Run available scripts\n"
2994         "s             Switch to another data file in PWD\n"
2995         "t             Zoom into current Thread\n"
2996         "V             Verbose (DSO names in callchains, etc)\n"
2997         "/             Filter symbol by name\n"
2998         "0-9           Sort by event n in group";
2999         static const char top_help[] = HIST_BROWSER_HELP_COMMON
3000         "P             Print histograms to perf.hist.N\n"
3001         "t             Zoom into current Thread\n"
3002         "V             Verbose (DSO names in callchains, etc)\n"
3003         "z             Toggle zeroing of samples\n"
3004         "f             Enable/Disable events\n"
3005         "/             Filter symbol by name";
3006
3007         if (browser == NULL)
3008                 return -1;
3009
3010         /* reset abort key so that it can get Ctrl-C as a key */
3011         SLang_reset_tty();
3012         SLang_init_tty(0, 0, 0);
3013
3014         if (min_pcnt)
3015                 browser->min_pcnt = min_pcnt;
3016         hist_browser__update_nr_entries(browser);
3017
3018         browser->pstack = pstack__new(3);
3019         if (browser->pstack == NULL)
3020                 goto out;
3021
3022         ui_helpline__push(helpline);
3023
3024         memset(options, 0, sizeof(options));
3025         memset(actions, 0, sizeof(actions));
3026
3027         if (symbol_conf.col_width_list_str)
3028                 perf_hpp__set_user_width(symbol_conf.col_width_list_str);
3029
3030         if (!is_report_browser(hbt))
3031                 browser->b.no_samples_msg = "Collecting samples...";
3032
3033         while (1) {
3034                 struct thread *thread = NULL;
3035                 struct map *map = NULL;
3036                 int choice;
3037                 int socked_id = -1;
3038
3039                 key = 0; // reset key
3040 do_hotkey:               // key came straight from options ui__popup_menu()
3041                 choice = nr_options = 0;
3042                 key = hist_browser__run(browser, helpline, warn_lost_event, key);
3043
3044                 if (browser->he_selection != NULL) {
3045                         thread = hist_browser__selected_thread(browser);
3046                         map = browser->selection->map;
3047                         socked_id = browser->he_selection->socket;
3048                 }
3049                 switch (key) {
3050                 case K_TAB:
3051                 case K_UNTAB:
3052                         if (nr_events == 1)
3053                                 continue;
3054                         /*
3055                          * Exit the browser, let hists__browser_tree
3056                          * go to the next or previous
3057                          */
3058                         goto out_free_stack;
3059                 case '0' ... '9':
3060                         if (!symbol_conf.event_group ||
3061                             evsel->core.nr_members < 2) {
3062                                 snprintf(buf, sizeof(buf),
3063                                          "Sort by index only available with group events!");
3064                                 helpline = buf;
3065                                 continue;
3066                         }
3067
3068                         if (key - '0' == symbol_conf.group_sort_idx)
3069                                 continue;
3070
3071                         symbol_conf.group_sort_idx = key - '0';
3072
3073                         if (symbol_conf.group_sort_idx >= evsel->core.nr_members) {
3074                                 snprintf(buf, sizeof(buf),
3075                                          "Max event group index to sort is %d (index from 0 to %d)",
3076                                          evsel->core.nr_members - 1,
3077                                          evsel->core.nr_members - 1);
3078                                 helpline = buf;
3079                                 continue;
3080                         }
3081
3082                         key = K_RELOAD;
3083                         goto out_free_stack;
3084                 case 'a':
3085                         if (!hists__has(hists, sym)) {
3086                                 ui_browser__warning(&browser->b, delay_secs * 2,
3087                         "Annotation is only available for symbolic views, "
3088                         "include \"sym*\" in --sort to use it.");
3089                                 continue;
3090                         }
3091
3092                         if (!browser->selection ||
3093                             !browser->selection->map ||
3094                             !browser->selection->map->dso ||
3095                             browser->selection->map->dso->annotate_warned) {
3096                                 continue;
3097                         }
3098
3099                         if (!browser->selection->sym) {
3100                                 if (!browser->he_selection)
3101                                         continue;
3102
3103                                 if (sort__mode == SORT_MODE__BRANCH) {
3104                                         bi = browser->he_selection->branch_info;
3105                                         if (!bi || !bi->to.ms.map)
3106                                                 continue;
3107
3108                                         actions->ms.sym = symbol__new_unresolved(bi->to.al_addr, bi->to.ms.map);
3109                                         actions->ms.map = bi->to.ms.map;
3110                                 } else {
3111                                         actions->ms.sym = symbol__new_unresolved(browser->he_selection->ip,
3112                                                                                  browser->selection->map);
3113                                         actions->ms.map = browser->selection->map;
3114                                 }
3115
3116                                 if (!actions->ms.sym)
3117                                         continue;
3118                         } else {
3119                                 if (symbol__annotation(browser->selection->sym)->src == NULL) {
3120                                         ui_browser__warning(&browser->b, delay_secs * 2,
3121                                                 "No samples for the \"%s\" symbol.\n\n"
3122                                                 "Probably appeared just in a callchain",
3123                                                 browser->selection->sym->name);
3124                                         continue;
3125                                 }
3126
3127                                 actions->ms.map = browser->selection->map;
3128                                 actions->ms.sym = browser->selection->sym;
3129                         }
3130
3131                         do_annotate(browser, actions);
3132                         continue;
3133                 case 'P':
3134                         hist_browser__dump(browser);
3135                         continue;
3136                 case 'd':
3137                         actions->ms.map = map;
3138                         do_zoom_dso(browser, actions);
3139                         continue;
3140                 case 'k':
3141                         if (browser->selection != NULL)
3142                                 hists_browser__zoom_map(browser, browser->selection->maps->machine->vmlinux_map);
3143                         continue;
3144                 case 'V':
3145                         verbose = (verbose + 1) % 4;
3146                         browser->show_dso = verbose > 0;
3147                         ui_helpline__fpush("Verbosity level set to %d\n",
3148                                            verbose);
3149                         continue;
3150                 case 't':
3151                         actions->thread = thread;
3152                         do_zoom_thread(browser, actions);
3153                         continue;
3154                 case 'S':
3155                         actions->socket = socked_id;
3156                         do_zoom_socket(browser, actions);
3157                         continue;
3158                 case '/':
3159                         if (ui_browser__input_window("Symbol to show",
3160                                         "Please enter the name of symbol you want to see.\n"
3161                                         "To remove the filter later, press / + ENTER.",
3162                                         buf, "ENTER: OK, ESC: Cancel",
3163                                         delay_secs * 2) == K_ENTER) {
3164                                 hists->symbol_filter_str = *buf ? buf : NULL;
3165                                 hists__filter_by_symbol(hists);
3166                                 hist_browser__reset(browser);
3167                         }
3168                         continue;
3169                 case 'r':
3170                         if (is_report_browser(hbt)) {
3171                                 actions->thread = NULL;
3172                                 actions->ms.sym = NULL;
3173                                 do_run_script(browser, actions);
3174                         }
3175                         continue;
3176                 case 's':
3177                         if (is_report_browser(hbt)) {
3178                                 key = do_switch_data(browser, actions);
3179                                 if (key == K_SWITCH_INPUT_DATA)
3180                                         goto out_free_stack;
3181                         }
3182                         continue;
3183                 case 'i':
3184                         /* env->arch is NULL for live-mode (i.e. perf top) */
3185                         if (env->arch)
3186                                 tui__header_window(env);
3187                         continue;
3188                 case 'F':
3189                         symbol_conf.filter_relative ^= 1;
3190                         continue;
3191                 case 'z':
3192                         if (!is_report_browser(hbt)) {
3193                                 struct perf_top *top = hbt->arg;
3194
3195                                 top->zero = !top->zero;
3196                         }
3197                         continue;
3198                 case 'L':
3199                         if (ui_browser__input_window("Percent Limit",
3200                                         "Please enter the value you want to hide entries under that percent.",
3201                                         buf, "ENTER: OK, ESC: Cancel",
3202                                         delay_secs * 2) == K_ENTER) {
3203                                 char *end;
3204                                 double new_percent = strtod(buf, &end);
3205
3206                                 if (new_percent < 0 || new_percent > 100) {
3207                                         ui_browser__warning(&browser->b, delay_secs * 2,
3208                                                 "Invalid percent: %.2f", new_percent);
3209                                         continue;
3210                                 }
3211
3212                                 hist_browser__update_percent_limit(browser, new_percent);
3213                                 hist_browser__reset(browser);
3214                         }
3215                         continue;
3216                 case K_F1:
3217                 case 'h':
3218                 case '?':
3219                         ui_browser__help_window(&browser->b,
3220                                 is_report_browser(hbt) ? report_help : top_help);
3221                         continue;
3222                 case K_ENTER:
3223                 case K_RIGHT:
3224                 case 'm':
3225                         /* menu */
3226                         break;
3227                 case K_ESC:
3228                 case K_LEFT: {
3229                         const void *top;
3230
3231                         if (pstack__empty(browser->pstack)) {
3232                                 /*
3233                                  * Go back to the perf_evsel_menu__run or other user
3234                                  */
3235                                 if (left_exits)
3236                                         goto out_free_stack;
3237
3238                                 if (key == K_ESC &&
3239                                     ui_browser__dialog_yesno(&browser->b,
3240                                                              "Do you really want to exit?"))
3241                                         goto out_free_stack;
3242
3243                                 continue;
3244                         }
3245                         actions->ms.map = map;
3246                         top = pstack__peek(browser->pstack);
3247                         if (top == &browser->hists->dso_filter) {
3248                                 /*
3249                                  * No need to set actions->dso here since
3250                                  * it's just to remove the current filter.
3251                                  * Ditto for thread below.
3252                                  */
3253                                 do_zoom_dso(browser, actions);
3254                         } else if (top == &browser->hists->thread_filter) {
3255                                 do_zoom_thread(browser, actions);
3256                         } else if (top == &browser->hists->socket_filter) {
3257                                 do_zoom_socket(browser, actions);
3258                         }
3259                         continue;
3260                 }
3261                 case 'q':
3262                 case CTRL('c'):
3263                         goto out_free_stack;
3264                 case 'f':
3265                         if (!is_report_browser(hbt)) {
3266                                 struct perf_top *top = hbt->arg;
3267
3268                                 evlist__toggle_enable(top->evlist);
3269                                 /*
3270                                  * No need to refresh, resort/decay histogram
3271                                  * entries if we are not collecting samples:
3272                                  */
3273                                 if (top->evlist->enabled) {
3274                                         helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
3275                                         hbt->refresh = delay_secs;
3276                                 } else {
3277                                         helpline = "Press 'f' again to re-enable the events";
3278                                         hbt->refresh = 0;
3279                                 }
3280                                 continue;
3281                         }
3282                         /* Fall thru */
3283                 default:
3284                         helpline = "Press '?' for help on key bindings";
3285                         continue;
3286                 }
3287
3288                 if (!hists__has(hists, sym) || browser->selection == NULL)
3289                         goto skip_annotation;
3290
3291                 if (sort__mode == SORT_MODE__BRANCH) {
3292
3293                         if (browser->he_selection)
3294                                 bi = browser->he_selection->branch_info;
3295
3296                         if (bi == NULL)
3297                                 goto skip_annotation;
3298
3299                         nr_options += add_annotate_opt(browser,
3300                                                        &actions[nr_options],
3301                                                        &options[nr_options],
3302                                                        &bi->from.ms,
3303                                                        bi->from.al_addr);
3304                         if (bi->to.ms.sym != bi->from.ms.sym)
3305                                 nr_options += add_annotate_opt(browser,
3306                                                         &actions[nr_options],
3307                                                         &options[nr_options],
3308                                                         &bi->to.ms,
3309                                                         bi->to.al_addr);
3310                 } else {
3311                         nr_options += add_annotate_opt(browser,
3312                                                        &actions[nr_options],
3313                                                        &options[nr_options],
3314                                                        browser->selection,
3315                                                        browser->he_selection->ip);
3316                 }
3317 skip_annotation:
3318                 nr_options += add_thread_opt(browser, &actions[nr_options],
3319                                              &options[nr_options], thread);
3320                 nr_options += add_dso_opt(browser, &actions[nr_options],
3321                                           &options[nr_options], map);
3322                 nr_options += add_callchain_toggle_opt(browser, &actions[nr_options], &options[nr_options]);
3323                 nr_options += add_map_opt(browser, &actions[nr_options],
3324                                           &options[nr_options],
3325                                           browser->selection ?
3326                                                 browser->selection->map : NULL);
3327                 nr_options += add_socket_opt(browser, &actions[nr_options],
3328                                              &options[nr_options],
3329                                              socked_id);
3330                 /* perf script support */
3331                 if (!is_report_browser(hbt))
3332                         goto skip_scripting;
3333
3334                 if (browser->he_selection) {
3335                         if (hists__has(hists, thread) && thread) {
3336                                 nr_options += add_script_opt(browser,
3337                                                              &actions[nr_options],
3338                                                              &options[nr_options],
3339                                                              thread, NULL, evsel);
3340                         }
3341                         /*
3342                          * Note that browser->selection != NULL
3343                          * when browser->he_selection is not NULL,
3344                          * so we don't need to check browser->selection
3345                          * before fetching browser->selection->sym like what
3346                          * we do before fetching browser->selection->map.
3347                          *
3348                          * See hist_browser__show_entry.
3349                          */
3350                         if (hists__has(hists, sym) && browser->selection->sym) {
3351                                 nr_options += add_script_opt(browser,
3352                                                              &actions[nr_options],
3353                                                              &options[nr_options],
3354                                                              NULL, browser->selection->sym,
3355                                                              evsel);
3356                         }
3357                 }
3358                 nr_options += add_script_opt(browser, &actions[nr_options],
3359                                              &options[nr_options], NULL, NULL, evsel);
3360                 nr_options += add_res_sample_opt(browser, &actions[nr_options],
3361                                                  &options[nr_options],
3362                                                  hist_browser__selected_res_sample(browser),
3363                                                  evsel, A_NORMAL);
3364                 nr_options += add_res_sample_opt(browser, &actions[nr_options],
3365                                                  &options[nr_options],
3366                                                  hist_browser__selected_res_sample(browser),
3367                                                  evsel, A_ASM);
3368                 nr_options += add_res_sample_opt(browser, &actions[nr_options],
3369                                                  &options[nr_options],
3370                                                  hist_browser__selected_res_sample(browser),
3371                                                  evsel, A_SOURCE);
3372                 nr_options += add_switch_opt(browser, &actions[nr_options],
3373                                              &options[nr_options]);
3374 skip_scripting:
3375                 nr_options += add_exit_opt(browser, &actions[nr_options],
3376                                            &options[nr_options]);
3377
3378                 do {
3379                         struct popup_action *act;
3380
3381                         choice = ui__popup_menu(nr_options, options, &key);
3382                         if (choice == -1)
3383                                 break;
3384
3385                         if (choice == nr_options)
3386                                 goto do_hotkey;
3387
3388                         act = &actions[choice];
3389                         key = act->fn(browser, act);
3390                 } while (key == 1);
3391
3392                 if (key == K_SWITCH_INPUT_DATA)
3393                         break;
3394         }
3395 out_free_stack:
3396         pstack__delete(browser->pstack);
3397 out:
3398         hist_browser__delete(browser);
3399         free_popup_options(options, MAX_OPTIONS);
3400         return key;
3401 }
3402
3403 struct evsel_menu {
3404         struct ui_browser b;
3405         struct evsel *selection;
3406         struct annotation_options *annotation_opts;
3407         bool lost_events, lost_events_warned;
3408         float min_pcnt;
3409         struct perf_env *env;
3410 };
3411
3412 static void perf_evsel_menu__write(struct ui_browser *browser,
3413                                    void *entry, int row)
3414 {
3415         struct evsel_menu *menu = container_of(browser,
3416                                                     struct evsel_menu, b);
3417         struct evsel *evsel = list_entry(entry, struct evsel, core.node);
3418         struct hists *hists = evsel__hists(evsel);
3419         bool current_entry = ui_browser__is_current_entry(browser, row);
3420         unsigned long nr_events = hists->stats.nr_samples;
3421         const char *ev_name = evsel__name(evsel);
3422         char bf[256], unit;
3423         const char *warn = " ";
3424         size_t printed;
3425
3426         ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
3427                                                        HE_COLORSET_NORMAL);
3428
3429         if (evsel__is_group_event(evsel)) {
3430                 struct evsel *pos;
3431
3432                 ev_name = evsel__group_name(evsel);
3433
3434                 for_each_group_member(pos, evsel) {
3435                         struct hists *pos_hists = evsel__hists(pos);
3436                         nr_events += pos_hists->stats.nr_samples;
3437                 }
3438         }
3439
3440         nr_events = convert_unit(nr_events, &unit);
3441         printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
3442                            unit, unit == ' ' ? "" : " ", ev_name);
3443         ui_browser__printf(browser, "%s", bf);
3444
3445         nr_events = evsel->evlist->stats.nr_events[PERF_RECORD_LOST];
3446         if (nr_events != 0) {
3447                 menu->lost_events = true;
3448                 if (!current_entry)
3449                         ui_browser__set_color(browser, HE_COLORSET_TOP);
3450                 nr_events = convert_unit(nr_events, &unit);
3451                 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
3452                                      nr_events, unit, unit == ' ' ? "" : " ");
3453                 warn = bf;
3454         }
3455
3456         ui_browser__write_nstring(browser, warn, browser->width - printed);
3457
3458         if (current_entry)
3459                 menu->selection = evsel;
3460 }
3461
3462 static int perf_evsel_menu__run(struct evsel_menu *menu,
3463                                 int nr_events, const char *help,
3464                                 struct hist_browser_timer *hbt,
3465                                 bool warn_lost_event)
3466 {
3467         struct evlist *evlist = menu->b.priv;
3468         struct evsel *pos;
3469         const char *title = "Available samples";
3470         int delay_secs = hbt ? hbt->refresh : 0;
3471         int key;
3472
3473         if (ui_browser__show(&menu->b, title,
3474                              "ESC: exit, ENTER|->: Browse histograms") < 0)
3475                 return -1;
3476
3477         while (1) {
3478                 key = ui_browser__run(&menu->b, delay_secs);
3479
3480                 switch (key) {
3481                 case K_TIMER:
3482                         if (hbt)
3483                                 hbt->timer(hbt->arg);
3484
3485                         if (!menu->lost_events_warned &&
3486                             menu->lost_events &&
3487                             warn_lost_event) {
3488                                 ui_browser__warn_lost_events(&menu->b);
3489                                 menu->lost_events_warned = true;
3490                         }
3491                         continue;
3492                 case K_RIGHT:
3493                 case K_ENTER:
3494                         if (!menu->selection)
3495                                 continue;
3496                         pos = menu->selection;
3497 browse_hists:
3498                         evlist__set_selected(evlist, pos);
3499                         /*
3500                          * Give the calling tool a chance to populate the non
3501                          * default evsel resorted hists tree.
3502                          */
3503                         if (hbt)
3504                                 hbt->timer(hbt->arg);
3505                         key = evsel__hists_browse(pos, nr_events, help, true, hbt,
3506                                                   menu->min_pcnt, menu->env,
3507                                                   warn_lost_event,
3508                                                   menu->annotation_opts);
3509                         ui_browser__show_title(&menu->b, title);
3510                         switch (key) {
3511                         case K_TAB:
3512                                 if (pos->core.node.next == &evlist->core.entries)
3513                                         pos = evlist__first(evlist);
3514                                 else
3515                                         pos = evsel__next(pos);
3516                                 goto browse_hists;
3517                         case K_UNTAB:
3518                                 if (pos->core.node.prev == &evlist->core.entries)
3519                                         pos = evlist__last(evlist);
3520                                 else
3521                                         pos = evsel__prev(pos);
3522                                 goto browse_hists;
3523                         case K_SWITCH_INPUT_DATA:
3524                         case K_RELOAD:
3525                         case 'q':
3526                         case CTRL('c'):
3527                                 goto out;
3528                         case K_ESC:
3529                         default:
3530                                 continue;
3531                         }
3532                 case K_LEFT:
3533                         continue;
3534                 case K_ESC:
3535                         if (!ui_browser__dialog_yesno(&menu->b,
3536                                                "Do you really want to exit?"))
3537                                 continue;
3538                         /* Fall thru */
3539                 case 'q':
3540                 case CTRL('c'):
3541                         goto out;
3542                 default:
3543                         continue;
3544                 }
3545         }
3546
3547 out:
3548         ui_browser__hide(&menu->b);
3549         return key;
3550 }
3551
3552 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
3553                                  void *entry)
3554 {
3555         struct evsel *evsel = list_entry(entry, struct evsel, core.node);
3556
3557         if (symbol_conf.event_group && !evsel__is_group_leader(evsel))
3558                 return true;
3559
3560         return false;
3561 }
3562
3563 static int __evlist__tui_browse_hists(struct evlist *evlist, int nr_entries, const char *help,
3564                                       struct hist_browser_timer *hbt, float min_pcnt, struct perf_env *env,
3565                                       bool warn_lost_event, struct annotation_options *annotation_opts)
3566 {
3567         struct evsel *pos;
3568         struct evsel_menu menu = {
3569                 .b = {
3570                         .entries    = &evlist->core.entries,
3571                         .refresh    = ui_browser__list_head_refresh,
3572                         .seek       = ui_browser__list_head_seek,
3573                         .write      = perf_evsel_menu__write,
3574                         .filter     = filter_group_entries,
3575                         .nr_entries = nr_entries,
3576                         .priv       = evlist,
3577                 },
3578                 .min_pcnt = min_pcnt,
3579                 .env = env,
3580                 .annotation_opts = annotation_opts,
3581         };
3582
3583         ui_helpline__push("Press ESC to exit");
3584
3585         evlist__for_each_entry(evlist, pos) {
3586                 const char *ev_name = evsel__name(pos);
3587                 size_t line_len = strlen(ev_name) + 7;
3588
3589                 if (menu.b.width < line_len)
3590                         menu.b.width = line_len;
3591         }
3592
3593         return perf_evsel_menu__run(&menu, nr_entries, help,
3594                                     hbt, warn_lost_event);
3595 }
3596
3597 static bool evlist__single_entry(struct evlist *evlist)
3598 {
3599         int nr_entries = evlist->core.nr_entries;
3600
3601         if (nr_entries == 1)
3602                return true;
3603
3604         if (nr_entries == 2) {
3605                 struct evsel *last = evlist__last(evlist);
3606
3607                 if (evsel__is_dummy_event(last))
3608                         return true;
3609         }
3610
3611         return false;
3612 }
3613
3614 int evlist__tui_browse_hists(struct evlist *evlist, const char *help, struct hist_browser_timer *hbt,
3615                              float min_pcnt, struct perf_env *env, bool warn_lost_event,
3616                              struct annotation_options *annotation_opts)
3617 {
3618         int nr_entries = evlist->core.nr_entries;
3619
3620         if (evlist__single_entry(evlist)) {
3621 single_entry: {
3622                 struct evsel *first = evlist__first(evlist);
3623
3624                 return evsel__hists_browse(first, nr_entries, help, false, hbt, min_pcnt,
3625                                            env, warn_lost_event, annotation_opts);
3626         }
3627         }
3628
3629         if (symbol_conf.event_group) {
3630                 struct evsel *pos;
3631
3632                 nr_entries = 0;
3633                 evlist__for_each_entry(evlist, pos) {
3634                         if (evsel__is_group_leader(pos))
3635                                 nr_entries++;
3636                 }
3637
3638                 if (nr_entries == 1)
3639                         goto single_entry;
3640         }
3641
3642         return __evlist__tui_browse_hists(evlist, nr_entries, help, hbt, min_pcnt, env,
3643                                           warn_lost_event, annotation_opts);
3644 }
3645
3646 static int block_hists_browser__title(struct hist_browser *browser, char *bf,
3647                                       size_t size)
3648 {
3649         struct hists *hists = evsel__hists(browser->block_evsel);
3650         const char *evname = evsel__name(browser->block_evsel);
3651         unsigned long nr_samples = hists->stats.nr_samples;
3652         int ret;
3653
3654         ret = scnprintf(bf, size, "# Samples: %lu", nr_samples);
3655         if (evname)
3656                 scnprintf(bf + ret, size -  ret, " of event '%s'", evname);
3657
3658         return 0;
3659 }
3660
3661 int block_hists_tui_browse(struct block_hist *bh, struct evsel *evsel,
3662                            float min_percent, struct perf_env *env,
3663                            struct annotation_options *annotation_opts)
3664 {
3665         struct hists *hists = &bh->block_hists;
3666         struct hist_browser *browser;
3667         int key = -1;
3668         struct popup_action action;
3669         static const char help[] =
3670         " q             Quit \n";
3671
3672         browser = hist_browser__new(hists);
3673         if (!browser)
3674                 return -1;
3675
3676         browser->block_evsel = evsel;
3677         browser->title = block_hists_browser__title;
3678         browser->min_pcnt = min_percent;
3679         browser->env = env;
3680         browser->annotation_opts = annotation_opts;
3681
3682         /* reset abort key so that it can get Ctrl-C as a key */
3683         SLang_reset_tty();
3684         SLang_init_tty(0, 0, 0);
3685
3686         memset(&action, 0, sizeof(action));
3687
3688         while (1) {
3689                 key = hist_browser__run(browser, "? - help", true, 0);
3690
3691                 switch (key) {
3692                 case 'q':
3693                         goto out;
3694                 case '?':
3695                         ui_browser__help_window(&browser->b, help);
3696                         break;
3697                 case 'a':
3698                 case K_ENTER:
3699                         if (!browser->selection ||
3700                             !browser->selection->sym) {
3701                                 continue;
3702                         }
3703
3704                         action.ms.map = browser->selection->map;
3705                         action.ms.sym = browser->selection->sym;
3706                         do_annotate(browser, &action);
3707                         continue;
3708                 default:
3709                         break;
3710                 }
3711         }
3712
3713 out:
3714         hist_browser__delete(browser);
3715         return 0;
3716 }