OSDN Git Service

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