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