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