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