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