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