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