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