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