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