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