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