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