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