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