Merge tag 'trace-v4.17-rc1-3' of git://git.kernel.org/pub/scm/linux/kernel/git/rosted...
[sfrench/cifs-2.6.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 {
2180         struct hist_browser *browser = hist_browser__new(evsel__hists(evsel));
2181
2182         if (browser) {
2183                 browser->hbt   = hbt;
2184                 browser->env   = env;
2185                 browser->title = hists_browser__scnprintf_title;
2186         }
2187         return browser;
2188 }
2189
2190 void hist_browser__delete(struct hist_browser *browser)
2191 {
2192         free(browser);
2193 }
2194
2195 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
2196 {
2197         return browser->he_selection;
2198 }
2199
2200 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
2201 {
2202         return browser->he_selection->thread;
2203 }
2204
2205 /* Check whether the browser is for 'top' or 'report' */
2206 static inline bool is_report_browser(void *timer)
2207 {
2208         return timer == NULL;
2209 }
2210
2211 static int hists_browser__scnprintf_title(struct hist_browser *browser, char *bf, size_t size)
2212 {
2213         struct hist_browser_timer *hbt = browser->hbt;
2214         int printed = __hists__scnprintf_title(browser->hists, bf, size, !is_report_browser(hbt));
2215
2216         if (!is_report_browser(hbt)) {
2217                 struct perf_top *top = hbt->arg;
2218
2219                 if (top->zero)
2220                         printed += scnprintf(bf + printed, size - printed, " [z]");
2221         }
2222
2223         return printed;
2224 }
2225
2226 static inline void free_popup_options(char **options, int n)
2227 {
2228         int i;
2229
2230         for (i = 0; i < n; ++i)
2231                 zfree(&options[i]);
2232 }
2233
2234 /*
2235  * Only runtime switching of perf data file will make "input_name" point
2236  * to a malloced buffer. So add "is_input_name_malloced" flag to decide
2237  * whether we need to call free() for current "input_name" during the switch.
2238  */
2239 static bool is_input_name_malloced = false;
2240
2241 static int switch_data_file(void)
2242 {
2243         char *pwd, *options[32], *abs_path[32], *tmp;
2244         DIR *pwd_dir;
2245         int nr_options = 0, choice = -1, ret = -1;
2246         struct dirent *dent;
2247
2248         pwd = getenv("PWD");
2249         if (!pwd)
2250                 return ret;
2251
2252         pwd_dir = opendir(pwd);
2253         if (!pwd_dir)
2254                 return ret;
2255
2256         memset(options, 0, sizeof(options));
2257         memset(abs_path, 0, sizeof(abs_path));
2258
2259         while ((dent = readdir(pwd_dir))) {
2260                 char path[PATH_MAX];
2261                 u64 magic;
2262                 char *name = dent->d_name;
2263                 FILE *file;
2264
2265                 if (!(dent->d_type == DT_REG))
2266                         continue;
2267
2268                 snprintf(path, sizeof(path), "%s/%s", pwd, name);
2269
2270                 file = fopen(path, "r");
2271                 if (!file)
2272                         continue;
2273
2274                 if (fread(&magic, 1, 8, file) < 8)
2275                         goto close_file_and_continue;
2276
2277                 if (is_perf_magic(magic)) {
2278                         options[nr_options] = strdup(name);
2279                         if (!options[nr_options])
2280                                 goto close_file_and_continue;
2281
2282                         abs_path[nr_options] = strdup(path);
2283                         if (!abs_path[nr_options]) {
2284                                 zfree(&options[nr_options]);
2285                                 ui__warning("Can't search all data files due to memory shortage.\n");
2286                                 fclose(file);
2287                                 break;
2288                         }
2289
2290                         nr_options++;
2291                 }
2292
2293 close_file_and_continue:
2294                 fclose(file);
2295                 if (nr_options >= 32) {
2296                         ui__warning("Too many perf data files in PWD!\n"
2297                                     "Only the first 32 files will be listed.\n");
2298                         break;
2299                 }
2300         }
2301         closedir(pwd_dir);
2302
2303         if (nr_options) {
2304                 choice = ui__popup_menu(nr_options, options);
2305                 if (choice < nr_options && choice >= 0) {
2306                         tmp = strdup(abs_path[choice]);
2307                         if (tmp) {
2308                                 if (is_input_name_malloced)
2309                                         free((void *)input_name);
2310                                 input_name = tmp;
2311                                 is_input_name_malloced = true;
2312                                 ret = 0;
2313                         } else
2314                                 ui__warning("Data switch failed due to memory shortage!\n");
2315                 }
2316         }
2317
2318         free_popup_options(options, nr_options);
2319         free_popup_options(abs_path, nr_options);
2320         return ret;
2321 }
2322
2323 struct popup_action {
2324         struct thread           *thread;
2325         struct map_symbol       ms;
2326         int                     socket;
2327
2328         int (*fn)(struct hist_browser *browser, struct popup_action *act);
2329 };
2330
2331 static int
2332 do_annotate(struct hist_browser *browser, struct popup_action *act)
2333 {
2334         struct perf_evsel *evsel;
2335         struct annotation *notes;
2336         struct hist_entry *he;
2337         int err;
2338
2339         if (!objdump_path && perf_env__lookup_objdump(browser->env))
2340                 return 0;
2341
2342         notes = symbol__annotation(act->ms.sym);
2343         if (!notes->src)
2344                 return 0;
2345
2346         evsel = hists_to_evsel(browser->hists);
2347         err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
2348         he = hist_browser__selected_entry(browser);
2349         /*
2350          * offer option to annotate the other branch source or target
2351          * (if they exists) when returning from annotate
2352          */
2353         if ((err == 'q' || err == CTRL('c')) && he->branch_info)
2354                 return 1;
2355
2356         ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
2357         if (err)
2358                 ui_browser__handle_resize(&browser->b);
2359         return 0;
2360 }
2361
2362 static int
2363 add_annotate_opt(struct hist_browser *browser __maybe_unused,
2364                  struct popup_action *act, char **optstr,
2365                  struct map *map, struct symbol *sym)
2366 {
2367         if (sym == NULL || map->dso->annotate_warned)
2368                 return 0;
2369
2370         if (asprintf(optstr, "Annotate %s", sym->name) < 0)
2371                 return 0;
2372
2373         act->ms.map = map;
2374         act->ms.sym = sym;
2375         act->fn = do_annotate;
2376         return 1;
2377 }
2378
2379 static int
2380 do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
2381 {
2382         struct thread *thread = act->thread;
2383
2384         if ((!hists__has(browser->hists, thread) &&
2385              !hists__has(browser->hists, comm)) || thread == NULL)
2386                 return 0;
2387
2388         if (browser->hists->thread_filter) {
2389                 pstack__remove(browser->pstack, &browser->hists->thread_filter);
2390                 perf_hpp__set_elide(HISTC_THREAD, false);
2391                 thread__zput(browser->hists->thread_filter);
2392                 ui_helpline__pop();
2393         } else {
2394                 if (hists__has(browser->hists, thread)) {
2395                         ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
2396                                            thread->comm_set ? thread__comm_str(thread) : "",
2397                                            thread->tid);
2398                 } else {
2399                         ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
2400                                            thread->comm_set ? thread__comm_str(thread) : "");
2401                 }
2402
2403                 browser->hists->thread_filter = thread__get(thread);
2404                 perf_hpp__set_elide(HISTC_THREAD, false);
2405                 pstack__push(browser->pstack, &browser->hists->thread_filter);
2406         }
2407
2408         hists__filter_by_thread(browser->hists);
2409         hist_browser__reset(browser);
2410         return 0;
2411 }
2412
2413 static int
2414 add_thread_opt(struct hist_browser *browser, struct popup_action *act,
2415                char **optstr, struct thread *thread)
2416 {
2417         int ret;
2418
2419         if ((!hists__has(browser->hists, thread) &&
2420              !hists__has(browser->hists, comm)) || thread == NULL)
2421                 return 0;
2422
2423         if (hists__has(browser->hists, thread)) {
2424                 ret = asprintf(optstr, "Zoom %s %s(%d) thread",
2425                                browser->hists->thread_filter ? "out of" : "into",
2426                                thread->comm_set ? thread__comm_str(thread) : "",
2427                                thread->tid);
2428         } else {
2429                 ret = asprintf(optstr, "Zoom %s %s thread",
2430                                browser->hists->thread_filter ? "out of" : "into",
2431                                thread->comm_set ? thread__comm_str(thread) : "");
2432         }
2433         if (ret < 0)
2434                 return 0;
2435
2436         act->thread = thread;
2437         act->fn = do_zoom_thread;
2438         return 1;
2439 }
2440
2441 static int
2442 do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
2443 {
2444         struct map *map = act->ms.map;
2445
2446         if (!hists__has(browser->hists, dso) || map == NULL)
2447                 return 0;
2448
2449         if (browser->hists->dso_filter) {
2450                 pstack__remove(browser->pstack, &browser->hists->dso_filter);
2451                 perf_hpp__set_elide(HISTC_DSO, false);
2452                 browser->hists->dso_filter = NULL;
2453                 ui_helpline__pop();
2454         } else {
2455                 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
2456                                    __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
2457                 browser->hists->dso_filter = map->dso;
2458                 perf_hpp__set_elide(HISTC_DSO, true);
2459                 pstack__push(browser->pstack, &browser->hists->dso_filter);
2460         }
2461
2462         hists__filter_by_dso(browser->hists);
2463         hist_browser__reset(browser);
2464         return 0;
2465 }
2466
2467 static int
2468 add_dso_opt(struct hist_browser *browser, struct popup_action *act,
2469             char **optstr, struct map *map)
2470 {
2471         if (!hists__has(browser->hists, dso) || map == NULL)
2472                 return 0;
2473
2474         if (asprintf(optstr, "Zoom %s %s DSO",
2475                      browser->hists->dso_filter ? "out of" : "into",
2476                      __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
2477                 return 0;
2478
2479         act->ms.map = map;
2480         act->fn = do_zoom_dso;
2481         return 1;
2482 }
2483
2484 static int
2485 do_browse_map(struct hist_browser *browser __maybe_unused,
2486               struct popup_action *act)
2487 {
2488         map__browse(act->ms.map);
2489         return 0;
2490 }
2491
2492 static int
2493 add_map_opt(struct hist_browser *browser,
2494             struct popup_action *act, char **optstr, struct map *map)
2495 {
2496         if (!hists__has(browser->hists, dso) || map == NULL)
2497                 return 0;
2498
2499         if (asprintf(optstr, "Browse map details") < 0)
2500                 return 0;
2501
2502         act->ms.map = map;
2503         act->fn = do_browse_map;
2504         return 1;
2505 }
2506
2507 static int
2508 do_run_script(struct hist_browser *browser __maybe_unused,
2509               struct popup_action *act)
2510 {
2511         char script_opt[64];
2512         memset(script_opt, 0, sizeof(script_opt));
2513
2514         if (act->thread) {
2515                 scnprintf(script_opt, sizeof(script_opt), " -c %s ",
2516                           thread__comm_str(act->thread));
2517         } else if (act->ms.sym) {
2518                 scnprintf(script_opt, sizeof(script_opt), " -S %s ",
2519                           act->ms.sym->name);
2520         }
2521
2522         script_browse(script_opt);
2523         return 0;
2524 }
2525
2526 static int
2527 add_script_opt(struct hist_browser *browser __maybe_unused,
2528                struct popup_action *act, char **optstr,
2529                struct thread *thread, struct symbol *sym)
2530 {
2531         if (thread) {
2532                 if (asprintf(optstr, "Run scripts for samples of thread [%s]",
2533                              thread__comm_str(thread)) < 0)
2534                         return 0;
2535         } else if (sym) {
2536                 if (asprintf(optstr, "Run scripts for samples of symbol [%s]",
2537                              sym->name) < 0)
2538                         return 0;
2539         } else {
2540                 if (asprintf(optstr, "Run scripts for all samples") < 0)
2541                         return 0;
2542         }
2543
2544         act->thread = thread;
2545         act->ms.sym = sym;
2546         act->fn = do_run_script;
2547         return 1;
2548 }
2549
2550 static int
2551 do_switch_data(struct hist_browser *browser __maybe_unused,
2552                struct popup_action *act __maybe_unused)
2553 {
2554         if (switch_data_file()) {
2555                 ui__warning("Won't switch the data files due to\n"
2556                             "no valid data file get selected!\n");
2557                 return 0;
2558         }
2559
2560         return K_SWITCH_INPUT_DATA;
2561 }
2562
2563 static int
2564 add_switch_opt(struct hist_browser *browser,
2565                struct popup_action *act, char **optstr)
2566 {
2567         if (!is_report_browser(browser->hbt))
2568                 return 0;
2569
2570         if (asprintf(optstr, "Switch to another data file in PWD") < 0)
2571                 return 0;
2572
2573         act->fn = do_switch_data;
2574         return 1;
2575 }
2576
2577 static int
2578 do_exit_browser(struct hist_browser *browser __maybe_unused,
2579                 struct popup_action *act __maybe_unused)
2580 {
2581         return 0;
2582 }
2583
2584 static int
2585 add_exit_opt(struct hist_browser *browser __maybe_unused,
2586              struct popup_action *act, char **optstr)
2587 {
2588         if (asprintf(optstr, "Exit") < 0)
2589                 return 0;
2590
2591         act->fn = do_exit_browser;
2592         return 1;
2593 }
2594
2595 static int
2596 do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
2597 {
2598         if (!hists__has(browser->hists, socket) || act->socket < 0)
2599                 return 0;
2600
2601         if (browser->hists->socket_filter > -1) {
2602                 pstack__remove(browser->pstack, &browser->hists->socket_filter);
2603                 browser->hists->socket_filter = -1;
2604                 perf_hpp__set_elide(HISTC_SOCKET, false);
2605         } else {
2606                 browser->hists->socket_filter = act->socket;
2607                 perf_hpp__set_elide(HISTC_SOCKET, true);
2608                 pstack__push(browser->pstack, &browser->hists->socket_filter);
2609         }
2610
2611         hists__filter_by_socket(browser->hists);
2612         hist_browser__reset(browser);
2613         return 0;
2614 }
2615
2616 static int
2617 add_socket_opt(struct hist_browser *browser, struct popup_action *act,
2618                char **optstr, int socket_id)
2619 {
2620         if (!hists__has(browser->hists, socket) || socket_id < 0)
2621                 return 0;
2622
2623         if (asprintf(optstr, "Zoom %s Processor Socket %d",
2624                      (browser->hists->socket_filter > -1) ? "out of" : "into",
2625                      socket_id) < 0)
2626                 return 0;
2627
2628         act->socket = socket_id;
2629         act->fn = do_zoom_socket;
2630         return 1;
2631 }
2632
2633 static void hist_browser__update_nr_entries(struct hist_browser *hb)
2634 {
2635         u64 nr_entries = 0;
2636         struct rb_node *nd = rb_first(&hb->hists->entries);
2637
2638         if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
2639                 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
2640                 return;
2641         }
2642
2643         while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2644                 nr_entries++;
2645                 nd = rb_hierarchy_next(nd);
2646         }
2647
2648         hb->nr_non_filtered_entries = nr_entries;
2649         hb->nr_hierarchy_entries = nr_entries;
2650 }
2651
2652 static void hist_browser__update_percent_limit(struct hist_browser *hb,
2653                                                double percent)
2654 {
2655         struct hist_entry *he;
2656         struct rb_node *nd = rb_first(&hb->hists->entries);
2657         u64 total = hists__total_period(hb->hists);
2658         u64 min_callchain_hits = total * (percent / 100);
2659
2660         hb->min_pcnt = callchain_param.min_percent = percent;
2661
2662         while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2663                 he = rb_entry(nd, struct hist_entry, rb_node);
2664
2665                 if (he->has_no_entry) {
2666                         he->has_no_entry = false;
2667                         he->nr_rows = 0;
2668                 }
2669
2670                 if (!he->leaf || !symbol_conf.use_callchain)
2671                         goto next;
2672
2673                 if (callchain_param.mode == CHAIN_GRAPH_REL) {
2674                         total = he->stat.period;
2675
2676                         if (symbol_conf.cumulate_callchain)
2677                                 total = he->stat_acc->period;
2678
2679                         min_callchain_hits = total * (percent / 100);
2680                 }
2681
2682                 callchain_param.sort(&he->sorted_chain, he->callchain,
2683                                      min_callchain_hits, &callchain_param);
2684
2685 next:
2686                 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
2687
2688                 /* force to re-evaluate folding state of callchains */
2689                 he->init_have_children = false;
2690                 hist_entry__set_folding(he, hb, false);
2691         }
2692 }
2693
2694 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
2695                                     const char *helpline,
2696                                     bool left_exits,
2697                                     struct hist_browser_timer *hbt,
2698                                     float min_pcnt,
2699                                     struct perf_env *env,
2700                                     bool warn_lost_event)
2701 {
2702         struct hists *hists = evsel__hists(evsel);
2703         struct hist_browser *browser = perf_evsel_browser__new(evsel, hbt, env);
2704         struct branch_info *bi;
2705 #define MAX_OPTIONS  16
2706         char *options[MAX_OPTIONS];
2707         struct popup_action actions[MAX_OPTIONS];
2708         int nr_options = 0;
2709         int key = -1;
2710         char buf[64];
2711         int delay_secs = hbt ? hbt->refresh : 0;
2712
2713 #define HIST_BROWSER_HELP_COMMON                                        \
2714         "h/?/F1        Show this window\n"                              \
2715         "UP/DOWN/PGUP\n"                                                \
2716         "PGDN/SPACE    Navigate\n"                                      \
2717         "q/ESC/CTRL+C  Exit browser or go back to previous screen\n\n"  \
2718         "For multiple event sessions:\n\n"                              \
2719         "TAB/UNTAB     Switch events\n\n"                               \
2720         "For symbolic views (--sort has sym):\n\n"                      \
2721         "ENTER         Zoom into DSO/Threads & Annotate current symbol\n" \
2722         "ESC           Zoom out\n"                                      \
2723         "a             Annotate current symbol\n"                       \
2724         "C             Collapse all callchains\n"                       \
2725         "d             Zoom into current DSO\n"                         \
2726         "E             Expand all callchains\n"                         \
2727         "F             Toggle percentage of filtered entries\n"         \
2728         "H             Display column headers\n"                        \
2729         "L             Change percent limit\n"                          \
2730         "m             Display context menu\n"                          \
2731         "S             Zoom into current Processor Socket\n"            \
2732
2733         /* help messages are sorted by lexical order of the hotkey */
2734         const char report_help[] = HIST_BROWSER_HELP_COMMON
2735         "i             Show header information\n"
2736         "P             Print histograms to perf.hist.N\n"
2737         "r             Run available scripts\n"
2738         "s             Switch to another data file in PWD\n"
2739         "t             Zoom into current Thread\n"
2740         "V             Verbose (DSO names in callchains, etc)\n"
2741         "/             Filter symbol by name";
2742         const char top_help[] = HIST_BROWSER_HELP_COMMON
2743         "P             Print histograms to perf.hist.N\n"
2744         "t             Zoom into current Thread\n"
2745         "V             Verbose (DSO names in callchains, etc)\n"
2746         "z             Toggle zeroing of samples\n"
2747         "f             Enable/Disable events\n"
2748         "/             Filter symbol by name";
2749
2750         if (browser == NULL)
2751                 return -1;
2752
2753         /* reset abort key so that it can get Ctrl-C as a key */
2754         SLang_reset_tty();
2755         SLang_init_tty(0, 0, 0);
2756
2757         if (min_pcnt)
2758                 browser->min_pcnt = min_pcnt;
2759         hist_browser__update_nr_entries(browser);
2760
2761         browser->pstack = pstack__new(3);
2762         if (browser->pstack == NULL)
2763                 goto out;
2764
2765         ui_helpline__push(helpline);
2766
2767         memset(options, 0, sizeof(options));
2768         memset(actions, 0, sizeof(actions));
2769
2770         if (symbol_conf.col_width_list_str)
2771                 perf_hpp__set_user_width(symbol_conf.col_width_list_str);
2772
2773         while (1) {
2774                 struct thread *thread = NULL;
2775                 struct map *map = NULL;
2776                 int choice = 0;
2777                 int socked_id = -1;
2778
2779                 nr_options = 0;
2780
2781                 key = hist_browser__run(browser, helpline,
2782                                         warn_lost_event);
2783
2784                 if (browser->he_selection != NULL) {
2785                         thread = hist_browser__selected_thread(browser);
2786                         map = browser->selection->map;
2787                         socked_id = browser->he_selection->socket;
2788                 }
2789                 switch (key) {
2790                 case K_TAB:
2791                 case K_UNTAB:
2792                         if (nr_events == 1)
2793                                 continue;
2794                         /*
2795                          * Exit the browser, let hists__browser_tree
2796                          * go to the next or previous
2797                          */
2798                         goto out_free_stack;
2799                 case 'a':
2800                         if (!hists__has(hists, sym)) {
2801                                 ui_browser__warning(&browser->b, delay_secs * 2,
2802                         "Annotation is only available for symbolic views, "
2803                         "include \"sym*\" in --sort to use it.");
2804                                 continue;
2805                         }
2806
2807                         if (browser->selection == NULL ||
2808                             browser->selection->sym == NULL ||
2809                             browser->selection->map->dso->annotate_warned)
2810                                 continue;
2811
2812                         actions->ms.map = browser->selection->map;
2813                         actions->ms.sym = browser->selection->sym;
2814                         do_annotate(browser, actions);
2815                         continue;
2816                 case 'P':
2817                         hist_browser__dump(browser);
2818                         continue;
2819                 case 'd':
2820                         actions->ms.map = map;
2821                         do_zoom_dso(browser, actions);
2822                         continue;
2823                 case 'V':
2824                         verbose = (verbose + 1) % 4;
2825                         browser->show_dso = verbose > 0;
2826                         ui_helpline__fpush("Verbosity level set to %d\n",
2827                                            verbose);
2828                         continue;
2829                 case 't':
2830                         actions->thread = thread;
2831                         do_zoom_thread(browser, actions);
2832                         continue;
2833                 case 'S':
2834                         actions->socket = socked_id;
2835                         do_zoom_socket(browser, actions);
2836                         continue;
2837                 case '/':
2838                         if (ui_browser__input_window("Symbol to show",
2839                                         "Please enter the name of symbol you want to see.\n"
2840                                         "To remove the filter later, press / + ENTER.",
2841                                         buf, "ENTER: OK, ESC: Cancel",
2842                                         delay_secs * 2) == K_ENTER) {
2843                                 hists->symbol_filter_str = *buf ? buf : NULL;
2844                                 hists__filter_by_symbol(hists);
2845                                 hist_browser__reset(browser);
2846                         }
2847                         continue;
2848                 case 'r':
2849                         if (is_report_browser(hbt)) {
2850                                 actions->thread = NULL;
2851                                 actions->ms.sym = NULL;
2852                                 do_run_script(browser, actions);
2853                         }
2854                         continue;
2855                 case 's':
2856                         if (is_report_browser(hbt)) {
2857                                 key = do_switch_data(browser, actions);
2858                                 if (key == K_SWITCH_INPUT_DATA)
2859                                         goto out_free_stack;
2860                         }
2861                         continue;
2862                 case 'i':
2863                         /* env->arch is NULL for live-mode (i.e. perf top) */
2864                         if (env->arch)
2865                                 tui__header_window(env);
2866                         continue;
2867                 case 'F':
2868                         symbol_conf.filter_relative ^= 1;
2869                         continue;
2870                 case 'z':
2871                         if (!is_report_browser(hbt)) {
2872                                 struct perf_top *top = hbt->arg;
2873
2874                                 top->zero = !top->zero;
2875                         }
2876                         continue;
2877                 case 'L':
2878                         if (ui_browser__input_window("Percent Limit",
2879                                         "Please enter the value you want to hide entries under that percent.",
2880                                         buf, "ENTER: OK, ESC: Cancel",
2881                                         delay_secs * 2) == K_ENTER) {
2882                                 char *end;
2883                                 double new_percent = strtod(buf, &end);
2884
2885                                 if (new_percent < 0 || new_percent > 100) {
2886                                         ui_browser__warning(&browser->b, delay_secs * 2,
2887                                                 "Invalid percent: %.2f", new_percent);
2888                                         continue;
2889                                 }
2890
2891                                 hist_browser__update_percent_limit(browser, new_percent);
2892                                 hist_browser__reset(browser);
2893                         }
2894                         continue;
2895                 case K_F1:
2896                 case 'h':
2897                 case '?':
2898                         ui_browser__help_window(&browser->b,
2899                                 is_report_browser(hbt) ? report_help : top_help);
2900                         continue;
2901                 case K_ENTER:
2902                 case K_RIGHT:
2903                 case 'm':
2904                         /* menu */
2905                         break;
2906                 case K_ESC:
2907                 case K_LEFT: {
2908                         const void *top;
2909
2910                         if (pstack__empty(browser->pstack)) {
2911                                 /*
2912                                  * Go back to the perf_evsel_menu__run or other user
2913                                  */
2914                                 if (left_exits)
2915                                         goto out_free_stack;
2916
2917                                 if (key == K_ESC &&
2918                                     ui_browser__dialog_yesno(&browser->b,
2919                                                              "Do you really want to exit?"))
2920                                         goto out_free_stack;
2921
2922                                 continue;
2923                         }
2924                         top = pstack__peek(browser->pstack);
2925                         if (top == &browser->hists->dso_filter) {
2926                                 /*
2927                                  * No need to set actions->dso here since
2928                                  * it's just to remove the current filter.
2929                                  * Ditto for thread below.
2930                                  */
2931                                 do_zoom_dso(browser, actions);
2932                         } else if (top == &browser->hists->thread_filter) {
2933                                 do_zoom_thread(browser, actions);
2934                         } else if (top == &browser->hists->socket_filter) {
2935                                 do_zoom_socket(browser, actions);
2936                         }
2937                         continue;
2938                 }
2939                 case 'q':
2940                 case CTRL('c'):
2941                         goto out_free_stack;
2942                 case 'f':
2943                         if (!is_report_browser(hbt)) {
2944                                 struct perf_top *top = hbt->arg;
2945
2946                                 perf_evlist__toggle_enable(top->evlist);
2947                                 /*
2948                                  * No need to refresh, resort/decay histogram
2949                                  * entries if we are not collecting samples:
2950                                  */
2951                                 if (top->evlist->enabled) {
2952                                         helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
2953                                         hbt->refresh = delay_secs;
2954                                 } else {
2955                                         helpline = "Press 'f' again to re-enable the events";
2956                                         hbt->refresh = 0;
2957                                 }
2958                                 continue;
2959                         }
2960                         /* Fall thru */
2961                 default:
2962                         helpline = "Press '?' for help on key bindings";
2963                         continue;
2964                 }
2965
2966                 if (!hists__has(hists, sym) || browser->selection == NULL)
2967                         goto skip_annotation;
2968
2969                 if (sort__mode == SORT_MODE__BRANCH) {
2970                         bi = browser->he_selection->branch_info;
2971
2972                         if (bi == NULL)
2973                                 goto skip_annotation;
2974
2975                         nr_options += add_annotate_opt(browser,
2976                                                        &actions[nr_options],
2977                                                        &options[nr_options],
2978                                                        bi->from.map,
2979                                                        bi->from.sym);
2980                         if (bi->to.sym != bi->from.sym)
2981                                 nr_options += add_annotate_opt(browser,
2982                                                         &actions[nr_options],
2983                                                         &options[nr_options],
2984                                                         bi->to.map,
2985                                                         bi->to.sym);
2986                 } else {
2987                         nr_options += add_annotate_opt(browser,
2988                                                        &actions[nr_options],
2989                                                        &options[nr_options],
2990                                                        browser->selection->map,
2991                                                        browser->selection->sym);
2992                 }
2993 skip_annotation:
2994                 nr_options += add_thread_opt(browser, &actions[nr_options],
2995                                              &options[nr_options], thread);
2996                 nr_options += add_dso_opt(browser, &actions[nr_options],
2997                                           &options[nr_options], map);
2998                 nr_options += add_map_opt(browser, &actions[nr_options],
2999                                           &options[nr_options],
3000                                           browser->selection ?
3001                                                 browser->selection->map : NULL);
3002                 nr_options += add_socket_opt(browser, &actions[nr_options],
3003                                              &options[nr_options],
3004                                              socked_id);
3005                 /* perf script support */
3006                 if (!is_report_browser(hbt))
3007                         goto skip_scripting;
3008
3009                 if (browser->he_selection) {
3010                         if (hists__has(hists, thread) && thread) {
3011                                 nr_options += add_script_opt(browser,
3012                                                              &actions[nr_options],
3013                                                              &options[nr_options],
3014                                                              thread, NULL);
3015                         }
3016                         /*
3017                          * Note that browser->selection != NULL
3018                          * when browser->he_selection is not NULL,
3019                          * so we don't need to check browser->selection
3020                          * before fetching browser->selection->sym like what
3021                          * we do before fetching browser->selection->map.
3022                          *
3023                          * See hist_browser__show_entry.
3024                          */
3025                         if (hists__has(hists, sym) && browser->selection->sym) {
3026                                 nr_options += add_script_opt(browser,
3027                                                              &actions[nr_options],
3028                                                              &options[nr_options],
3029                                                              NULL, browser->selection->sym);
3030                         }
3031                 }
3032                 nr_options += add_script_opt(browser, &actions[nr_options],
3033                                              &options[nr_options], NULL, NULL);
3034                 nr_options += add_switch_opt(browser, &actions[nr_options],
3035                                              &options[nr_options]);
3036 skip_scripting:
3037                 nr_options += add_exit_opt(browser, &actions[nr_options],
3038                                            &options[nr_options]);
3039
3040                 do {
3041                         struct popup_action *act;
3042
3043                         choice = ui__popup_menu(nr_options, options);
3044                         if (choice == -1 || choice >= nr_options)
3045                                 break;
3046
3047                         act = &actions[choice];
3048                         key = act->fn(browser, act);
3049                 } while (key == 1);
3050
3051                 if (key == K_SWITCH_INPUT_DATA)
3052                         break;
3053         }
3054 out_free_stack:
3055         pstack__delete(browser->pstack);
3056 out:
3057         hist_browser__delete(browser);
3058         free_popup_options(options, MAX_OPTIONS);
3059         return key;
3060 }
3061
3062 struct perf_evsel_menu {
3063         struct ui_browser b;
3064         struct perf_evsel *selection;
3065         bool lost_events, lost_events_warned;
3066         float min_pcnt;
3067         struct perf_env *env;
3068 };
3069
3070 static void perf_evsel_menu__write(struct ui_browser *browser,
3071                                    void *entry, int row)
3072 {
3073         struct perf_evsel_menu *menu = container_of(browser,
3074                                                     struct perf_evsel_menu, b);
3075         struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
3076         struct hists *hists = evsel__hists(evsel);
3077         bool current_entry = ui_browser__is_current_entry(browser, row);
3078         unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
3079         const char *ev_name = perf_evsel__name(evsel);
3080         char bf[256], unit;
3081         const char *warn = " ";
3082         size_t printed;
3083
3084         ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
3085                                                        HE_COLORSET_NORMAL);
3086
3087         if (perf_evsel__is_group_event(evsel)) {
3088                 struct perf_evsel *pos;
3089
3090                 ev_name = perf_evsel__group_name(evsel);
3091
3092                 for_each_group_member(pos, evsel) {
3093                         struct hists *pos_hists = evsel__hists(pos);
3094                         nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
3095                 }
3096         }
3097
3098         nr_events = convert_unit(nr_events, &unit);
3099         printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
3100                            unit, unit == ' ' ? "" : " ", ev_name);
3101         ui_browser__printf(browser, "%s", bf);
3102
3103         nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
3104         if (nr_events != 0) {
3105                 menu->lost_events = true;
3106                 if (!current_entry)
3107                         ui_browser__set_color(browser, HE_COLORSET_TOP);
3108                 nr_events = convert_unit(nr_events, &unit);
3109                 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
3110                                      nr_events, unit, unit == ' ' ? "" : " ");
3111                 warn = bf;
3112         }
3113
3114         ui_browser__write_nstring(browser, warn, browser->width - printed);
3115
3116         if (current_entry)
3117                 menu->selection = evsel;
3118 }
3119
3120 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
3121                                 int nr_events, const char *help,
3122                                 struct hist_browser_timer *hbt,
3123                                 bool warn_lost_event)
3124 {
3125         struct perf_evlist *evlist = menu->b.priv;
3126         struct perf_evsel *pos;
3127         const char *title = "Available samples";
3128         int delay_secs = hbt ? hbt->refresh : 0;
3129         int key;
3130
3131         if (ui_browser__show(&menu->b, title,
3132                              "ESC: exit, ENTER|->: Browse histograms") < 0)
3133                 return -1;
3134
3135         while (1) {
3136                 key = ui_browser__run(&menu->b, delay_secs);
3137
3138                 switch (key) {
3139                 case K_TIMER:
3140                         hbt->timer(hbt->arg);
3141
3142                         if (!menu->lost_events_warned &&
3143                             menu->lost_events &&
3144                             warn_lost_event) {
3145                                 ui_browser__warn_lost_events(&menu->b);
3146                                 menu->lost_events_warned = true;
3147                         }
3148                         continue;
3149                 case K_RIGHT:
3150                 case K_ENTER:
3151                         if (!menu->selection)
3152                                 continue;
3153                         pos = menu->selection;
3154 browse_hists:
3155                         perf_evlist__set_selected(evlist, pos);
3156                         /*
3157                          * Give the calling tool a chance to populate the non
3158                          * default evsel resorted hists tree.
3159                          */
3160                         if (hbt)
3161                                 hbt->timer(hbt->arg);
3162                         key = perf_evsel__hists_browse(pos, nr_events, help,
3163                                                        true, hbt,
3164                                                        menu->min_pcnt,
3165                                                        menu->env,
3166                                                        warn_lost_event);
3167                         ui_browser__show_title(&menu->b, title);
3168                         switch (key) {
3169                         case K_TAB:
3170                                 if (pos->node.next == &evlist->entries)
3171                                         pos = perf_evlist__first(evlist);
3172                                 else
3173                                         pos = perf_evsel__next(pos);
3174                                 goto browse_hists;
3175                         case K_UNTAB:
3176                                 if (pos->node.prev == &evlist->entries)
3177                                         pos = perf_evlist__last(evlist);
3178                                 else
3179                                         pos = perf_evsel__prev(pos);
3180                                 goto browse_hists;
3181                         case K_SWITCH_INPUT_DATA:
3182                         case 'q':
3183                         case CTRL('c'):
3184                                 goto out;
3185                         case K_ESC:
3186                         default:
3187                                 continue;
3188                         }
3189                 case K_LEFT:
3190                         continue;
3191                 case K_ESC:
3192                         if (!ui_browser__dialog_yesno(&menu->b,
3193                                                "Do you really want to exit?"))
3194                                 continue;
3195                         /* Fall thru */
3196                 case 'q':
3197                 case CTRL('c'):
3198                         goto out;
3199                 default:
3200                         continue;
3201                 }
3202         }
3203
3204 out:
3205         ui_browser__hide(&menu->b);
3206         return key;
3207 }
3208
3209 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
3210                                  void *entry)
3211 {
3212         struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
3213
3214         if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
3215                 return true;
3216
3217         return false;
3218 }
3219
3220 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
3221                                            int nr_entries, const char *help,
3222                                            struct hist_browser_timer *hbt,
3223                                            float min_pcnt,
3224                                            struct perf_env *env,
3225                                            bool warn_lost_event)
3226 {
3227         struct perf_evsel *pos;
3228         struct perf_evsel_menu menu = {
3229                 .b = {
3230                         .entries    = &evlist->entries,
3231                         .refresh    = ui_browser__list_head_refresh,
3232                         .seek       = ui_browser__list_head_seek,
3233                         .write      = perf_evsel_menu__write,
3234                         .filter     = filter_group_entries,
3235                         .nr_entries = nr_entries,
3236                         .priv       = evlist,
3237                 },
3238                 .min_pcnt = min_pcnt,
3239                 .env = env,
3240         };
3241
3242         ui_helpline__push("Press ESC to exit");
3243
3244         evlist__for_each_entry(evlist, pos) {
3245                 const char *ev_name = perf_evsel__name(pos);
3246                 size_t line_len = strlen(ev_name) + 7;
3247
3248                 if (menu.b.width < line_len)
3249                         menu.b.width = line_len;
3250         }
3251
3252         return perf_evsel_menu__run(&menu, nr_entries, help,
3253                                     hbt, warn_lost_event);
3254 }
3255
3256 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
3257                                   struct hist_browser_timer *hbt,
3258                                   float min_pcnt,
3259                                   struct perf_env *env,
3260                                   bool warn_lost_event)
3261 {
3262         int nr_entries = evlist->nr_entries;
3263
3264 single_entry:
3265         if (nr_entries == 1) {
3266                 struct perf_evsel *first = perf_evlist__first(evlist);
3267
3268                 return perf_evsel__hists_browse(first, nr_entries, help,
3269                                                 false, hbt, min_pcnt,
3270                                                 env, warn_lost_event);
3271         }
3272
3273         if (symbol_conf.event_group) {
3274                 struct perf_evsel *pos;
3275
3276                 nr_entries = 0;
3277                 evlist__for_each_entry(evlist, pos) {
3278                         if (perf_evsel__is_group_leader(pos))
3279                                 nr_entries++;
3280                 }
3281
3282                 if (nr_entries == 1)
3283                         goto single_entry;
3284         }
3285
3286         return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
3287                                                hbt, min_pcnt, env,
3288                                                warn_lost_event);
3289 }