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