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