perf top: Fix refreshing hierarchy entries on TUI
[cascardo/linux.git] / tools / perf / ui / browsers / hists.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <linux/rbtree.h>
5
6 #include "../../util/evsel.h"
7 #include "../../util/evlist.h"
8 #include "../../util/hist.h"
9 #include "../../util/pstack.h"
10 #include "../../util/sort.h"
11 #include "../../util/util.h"
12 #include "../../util/top.h"
13 #include "../../arch/common.h"
14
15 #include "../browsers/hists.h"
16 #include "../helpline.h"
17 #include "../util.h"
18 #include "../ui.h"
19 #include "map.h"
20 #include "annotate.h"
21
22 extern void hist_browser__init_hpp(void);
23
24 static int perf_evsel_browser_title(struct hist_browser *browser,
25                                     char *bf, size_t size);
26 static void hist_browser__update_nr_entries(struct hist_browser *hb);
27
28 static struct rb_node *hists__filter_entries(struct rb_node *nd,
29                                              float min_pcnt);
30
31 static bool hist_browser__has_filter(struct hist_browser *hb)
32 {
33         return hists__has_filter(hb->hists) || hb->min_pcnt || symbol_conf.has_filter;
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         hists__for_each_format(hists, fmt)
2080                 ++browser->b.columns;
2081
2082         hists__reset_column_width(hists);
2083 }
2084
2085 struct hist_browser *hist_browser__new(struct hists *hists)
2086 {
2087         struct hist_browser *browser = zalloc(sizeof(*browser));
2088
2089         if (browser)
2090                 hist_browser__init(browser, hists);
2091
2092         return browser;
2093 }
2094
2095 static struct hist_browser *
2096 perf_evsel_browser__new(struct perf_evsel *evsel,
2097                         struct hist_browser_timer *hbt,
2098                         struct perf_env *env)
2099 {
2100         struct hist_browser *browser = hist_browser__new(evsel__hists(evsel));
2101
2102         if (browser) {
2103                 browser->hbt   = hbt;
2104                 browser->env   = env;
2105                 browser->title = perf_evsel_browser_title;
2106         }
2107         return browser;
2108 }
2109
2110 void hist_browser__delete(struct hist_browser *browser)
2111 {
2112         free(browser);
2113 }
2114
2115 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
2116 {
2117         return browser->he_selection;
2118 }
2119
2120 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
2121 {
2122         return browser->he_selection->thread;
2123 }
2124
2125 /* Check whether the browser is for 'top' or 'report' */
2126 static inline bool is_report_browser(void *timer)
2127 {
2128         return timer == NULL;
2129 }
2130
2131 static int perf_evsel_browser_title(struct hist_browser *browser,
2132                                 char *bf, size_t size)
2133 {
2134         struct hist_browser_timer *hbt = browser->hbt;
2135         struct hists *hists = browser->hists;
2136         char unit;
2137         int printed;
2138         const struct dso *dso = hists->dso_filter;
2139         const struct thread *thread = hists->thread_filter;
2140         int socket_id = hists->socket_filter;
2141         unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
2142         u64 nr_events = hists->stats.total_period;
2143         struct perf_evsel *evsel = hists_to_evsel(hists);
2144         const char *ev_name = perf_evsel__name(evsel);
2145         char buf[512];
2146         size_t buflen = sizeof(buf);
2147         char ref[30] = " show reference callgraph, ";
2148         bool enable_ref = false;
2149
2150         if (symbol_conf.filter_relative) {
2151                 nr_samples = hists->stats.nr_non_filtered_samples;
2152                 nr_events = hists->stats.total_non_filtered_period;
2153         }
2154
2155         if (perf_evsel__is_group_event(evsel)) {
2156                 struct perf_evsel *pos;
2157
2158                 perf_evsel__group_desc(evsel, buf, buflen);
2159                 ev_name = buf;
2160
2161                 for_each_group_member(pos, evsel) {
2162                         struct hists *pos_hists = evsel__hists(pos);
2163
2164                         if (symbol_conf.filter_relative) {
2165                                 nr_samples += pos_hists->stats.nr_non_filtered_samples;
2166                                 nr_events += pos_hists->stats.total_non_filtered_period;
2167                         } else {
2168                                 nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
2169                                 nr_events += pos_hists->stats.total_period;
2170                         }
2171                 }
2172         }
2173
2174         if (symbol_conf.show_ref_callgraph &&
2175             strstr(ev_name, "call-graph=no"))
2176                 enable_ref = true;
2177         nr_samples = convert_unit(nr_samples, &unit);
2178         printed = scnprintf(bf, size,
2179                            "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64,
2180                            nr_samples, unit, ev_name, enable_ref ? ref : " ", nr_events);
2181
2182
2183         if (hists->uid_filter_str)
2184                 printed += snprintf(bf + printed, size - printed,
2185                                     ", UID: %s", hists->uid_filter_str);
2186         if (thread) {
2187                 if (hists__has(hists, thread)) {
2188                         printed += scnprintf(bf + printed, size - printed,
2189                                     ", Thread: %s(%d)",
2190                                      (thread->comm_set ? thread__comm_str(thread) : ""),
2191                                     thread->tid);
2192                 } else {
2193                         printed += scnprintf(bf + printed, size - printed,
2194                                     ", Thread: %s",
2195                                      (thread->comm_set ? thread__comm_str(thread) : ""));
2196                 }
2197         }
2198         if (dso)
2199                 printed += scnprintf(bf + printed, size - printed,
2200                                     ", DSO: %s", dso->short_name);
2201         if (socket_id > -1)
2202                 printed += scnprintf(bf + printed, size - printed,
2203                                     ", Processor Socket: %d", socket_id);
2204         if (!is_report_browser(hbt)) {
2205                 struct perf_top *top = hbt->arg;
2206
2207                 if (top->zero)
2208                         printed += scnprintf(bf + printed, size - printed, " [z]");
2209         }
2210
2211         return printed;
2212 }
2213
2214 static inline void free_popup_options(char **options, int n)
2215 {
2216         int i;
2217
2218         for (i = 0; i < n; ++i)
2219                 zfree(&options[i]);
2220 }
2221
2222 /*
2223  * Only runtime switching of perf data file will make "input_name" point
2224  * to a malloced buffer. So add "is_input_name_malloced" flag to decide
2225  * whether we need to call free() for current "input_name" during the switch.
2226  */
2227 static bool is_input_name_malloced = false;
2228
2229 static int switch_data_file(void)
2230 {
2231         char *pwd, *options[32], *abs_path[32], *tmp;
2232         DIR *pwd_dir;
2233         int nr_options = 0, choice = -1, ret = -1;
2234         struct dirent *dent;
2235
2236         pwd = getenv("PWD");
2237         if (!pwd)
2238                 return ret;
2239
2240         pwd_dir = opendir(pwd);
2241         if (!pwd_dir)
2242                 return ret;
2243
2244         memset(options, 0, sizeof(options));
2245         memset(options, 0, sizeof(abs_path));
2246
2247         while ((dent = readdir(pwd_dir))) {
2248                 char path[PATH_MAX];
2249                 u64 magic;
2250                 char *name = dent->d_name;
2251                 FILE *file;
2252
2253                 if (!(dent->d_type == DT_REG))
2254                         continue;
2255
2256                 snprintf(path, sizeof(path), "%s/%s", pwd, name);
2257
2258                 file = fopen(path, "r");
2259                 if (!file)
2260                         continue;
2261
2262                 if (fread(&magic, 1, 8, file) < 8)
2263                         goto close_file_and_continue;
2264
2265                 if (is_perf_magic(magic)) {
2266                         options[nr_options] = strdup(name);
2267                         if (!options[nr_options])
2268                                 goto close_file_and_continue;
2269
2270                         abs_path[nr_options] = strdup(path);
2271                         if (!abs_path[nr_options]) {
2272                                 zfree(&options[nr_options]);
2273                                 ui__warning("Can't search all data files due to memory shortage.\n");
2274                                 fclose(file);
2275                                 break;
2276                         }
2277
2278                         nr_options++;
2279                 }
2280
2281 close_file_and_continue:
2282                 fclose(file);
2283                 if (nr_options >= 32) {
2284                         ui__warning("Too many perf data files in PWD!\n"
2285                                     "Only the first 32 files will be listed.\n");
2286                         break;
2287                 }
2288         }
2289         closedir(pwd_dir);
2290
2291         if (nr_options) {
2292                 choice = ui__popup_menu(nr_options, options);
2293                 if (choice < nr_options && choice >= 0) {
2294                         tmp = strdup(abs_path[choice]);
2295                         if (tmp) {
2296                                 if (is_input_name_malloced)
2297                                         free((void *)input_name);
2298                                 input_name = tmp;
2299                                 is_input_name_malloced = true;
2300                                 ret = 0;
2301                         } else
2302                                 ui__warning("Data switch failed due to memory shortage!\n");
2303                 }
2304         }
2305
2306         free_popup_options(options, nr_options);
2307         free_popup_options(abs_path, nr_options);
2308         return ret;
2309 }
2310
2311 struct popup_action {
2312         struct thread           *thread;
2313         struct map_symbol       ms;
2314         int                     socket;
2315
2316         int (*fn)(struct hist_browser *browser, struct popup_action *act);
2317 };
2318
2319 static int
2320 do_annotate(struct hist_browser *browser, struct popup_action *act)
2321 {
2322         struct perf_evsel *evsel;
2323         struct annotation *notes;
2324         struct hist_entry *he;
2325         int err;
2326
2327         if (!objdump_path && perf_env__lookup_objdump(browser->env))
2328                 return 0;
2329
2330         notes = symbol__annotation(act->ms.sym);
2331         if (!notes->src)
2332                 return 0;
2333
2334         evsel = hists_to_evsel(browser->hists);
2335         err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
2336         he = hist_browser__selected_entry(browser);
2337         /*
2338          * offer option to annotate the other branch source or target
2339          * (if they exists) when returning from annotate
2340          */
2341         if ((err == 'q' || err == CTRL('c')) && he->branch_info)
2342                 return 1;
2343
2344         ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
2345         if (err)
2346                 ui_browser__handle_resize(&browser->b);
2347         return 0;
2348 }
2349
2350 static int
2351 add_annotate_opt(struct hist_browser *browser __maybe_unused,
2352                  struct popup_action *act, char **optstr,
2353                  struct map *map, struct symbol *sym)
2354 {
2355         if (sym == NULL || map->dso->annotate_warned)
2356                 return 0;
2357
2358         if (asprintf(optstr, "Annotate %s", sym->name) < 0)
2359                 return 0;
2360
2361         act->ms.map = map;
2362         act->ms.sym = sym;
2363         act->fn = do_annotate;
2364         return 1;
2365 }
2366
2367 static int
2368 do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
2369 {
2370         struct thread *thread = act->thread;
2371
2372         if ((!hists__has(browser->hists, thread) &&
2373              !hists__has(browser->hists, comm)) || thread == NULL)
2374                 return 0;
2375
2376         if (browser->hists->thread_filter) {
2377                 pstack__remove(browser->pstack, &browser->hists->thread_filter);
2378                 perf_hpp__set_elide(HISTC_THREAD, false);
2379                 thread__zput(browser->hists->thread_filter);
2380                 ui_helpline__pop();
2381         } else {
2382                 if (hists__has(browser->hists, thread)) {
2383                         ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
2384                                            thread->comm_set ? thread__comm_str(thread) : "",
2385                                            thread->tid);
2386                 } else {
2387                         ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
2388                                            thread->comm_set ? thread__comm_str(thread) : "");
2389                 }
2390
2391                 browser->hists->thread_filter = thread__get(thread);
2392                 perf_hpp__set_elide(HISTC_THREAD, false);
2393                 pstack__push(browser->pstack, &browser->hists->thread_filter);
2394         }
2395
2396         hists__filter_by_thread(browser->hists);
2397         hist_browser__reset(browser);
2398         return 0;
2399 }
2400
2401 static int
2402 add_thread_opt(struct hist_browser *browser, struct popup_action *act,
2403                char **optstr, struct thread *thread)
2404 {
2405         int ret;
2406
2407         if ((!hists__has(browser->hists, thread) &&
2408              !hists__has(browser->hists, comm)) || thread == NULL)
2409                 return 0;
2410
2411         if (hists__has(browser->hists, thread)) {
2412                 ret = asprintf(optstr, "Zoom %s %s(%d) thread",
2413                                browser->hists->thread_filter ? "out of" : "into",
2414                                thread->comm_set ? thread__comm_str(thread) : "",
2415                                thread->tid);
2416         } else {
2417                 ret = asprintf(optstr, "Zoom %s %s thread",
2418                                browser->hists->thread_filter ? "out of" : "into",
2419                                thread->comm_set ? thread__comm_str(thread) : "");
2420         }
2421         if (ret < 0)
2422                 return 0;
2423
2424         act->thread = thread;
2425         act->fn = do_zoom_thread;
2426         return 1;
2427 }
2428
2429 static int
2430 do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
2431 {
2432         struct map *map = act->ms.map;
2433
2434         if (!hists__has(browser->hists, dso) || map == NULL)
2435                 return 0;
2436
2437         if (browser->hists->dso_filter) {
2438                 pstack__remove(browser->pstack, &browser->hists->dso_filter);
2439                 perf_hpp__set_elide(HISTC_DSO, false);
2440                 browser->hists->dso_filter = NULL;
2441                 ui_helpline__pop();
2442         } else {
2443                 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
2444                                    __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
2445                 browser->hists->dso_filter = map->dso;
2446                 perf_hpp__set_elide(HISTC_DSO, true);
2447                 pstack__push(browser->pstack, &browser->hists->dso_filter);
2448         }
2449
2450         hists__filter_by_dso(browser->hists);
2451         hist_browser__reset(browser);
2452         return 0;
2453 }
2454
2455 static int
2456 add_dso_opt(struct hist_browser *browser, struct popup_action *act,
2457             char **optstr, struct map *map)
2458 {
2459         if (!hists__has(browser->hists, dso) || map == NULL)
2460                 return 0;
2461
2462         if (asprintf(optstr, "Zoom %s %s DSO",
2463                      browser->hists->dso_filter ? "out of" : "into",
2464                      __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
2465                 return 0;
2466
2467         act->ms.map = map;
2468         act->fn = do_zoom_dso;
2469         return 1;
2470 }
2471
2472 static int
2473 do_browse_map(struct hist_browser *browser __maybe_unused,
2474               struct popup_action *act)
2475 {
2476         map__browse(act->ms.map);
2477         return 0;
2478 }
2479
2480 static int
2481 add_map_opt(struct hist_browser *browser,
2482             struct popup_action *act, char **optstr, struct map *map)
2483 {
2484         if (!hists__has(browser->hists, dso) || map == NULL)
2485                 return 0;
2486
2487         if (asprintf(optstr, "Browse map details") < 0)
2488                 return 0;
2489
2490         act->ms.map = map;
2491         act->fn = do_browse_map;
2492         return 1;
2493 }
2494
2495 static int
2496 do_run_script(struct hist_browser *browser __maybe_unused,
2497               struct popup_action *act)
2498 {
2499         char script_opt[64];
2500         memset(script_opt, 0, sizeof(script_opt));
2501
2502         if (act->thread) {
2503                 scnprintf(script_opt, sizeof(script_opt), " -c %s ",
2504                           thread__comm_str(act->thread));
2505         } else if (act->ms.sym) {
2506                 scnprintf(script_opt, sizeof(script_opt), " -S %s ",
2507                           act->ms.sym->name);
2508         }
2509
2510         script_browse(script_opt);
2511         return 0;
2512 }
2513
2514 static int
2515 add_script_opt(struct hist_browser *browser __maybe_unused,
2516                struct popup_action *act, char **optstr,
2517                struct thread *thread, struct symbol *sym)
2518 {
2519         if (thread) {
2520                 if (asprintf(optstr, "Run scripts for samples of thread [%s]",
2521                              thread__comm_str(thread)) < 0)
2522                         return 0;
2523         } else if (sym) {
2524                 if (asprintf(optstr, "Run scripts for samples of symbol [%s]",
2525                              sym->name) < 0)
2526                         return 0;
2527         } else {
2528                 if (asprintf(optstr, "Run scripts for all samples") < 0)
2529                         return 0;
2530         }
2531
2532         act->thread = thread;
2533         act->ms.sym = sym;
2534         act->fn = do_run_script;
2535         return 1;
2536 }
2537
2538 static int
2539 do_switch_data(struct hist_browser *browser __maybe_unused,
2540                struct popup_action *act __maybe_unused)
2541 {
2542         if (switch_data_file()) {
2543                 ui__warning("Won't switch the data files due to\n"
2544                             "no valid data file get selected!\n");
2545                 return 0;
2546         }
2547
2548         return K_SWITCH_INPUT_DATA;
2549 }
2550
2551 static int
2552 add_switch_opt(struct hist_browser *browser,
2553                struct popup_action *act, char **optstr)
2554 {
2555         if (!is_report_browser(browser->hbt))
2556                 return 0;
2557
2558         if (asprintf(optstr, "Switch to another data file in PWD") < 0)
2559                 return 0;
2560
2561         act->fn = do_switch_data;
2562         return 1;
2563 }
2564
2565 static int
2566 do_exit_browser(struct hist_browser *browser __maybe_unused,
2567                 struct popup_action *act __maybe_unused)
2568 {
2569         return 0;
2570 }
2571
2572 static int
2573 add_exit_opt(struct hist_browser *browser __maybe_unused,
2574              struct popup_action *act, char **optstr)
2575 {
2576         if (asprintf(optstr, "Exit") < 0)
2577                 return 0;
2578
2579         act->fn = do_exit_browser;
2580         return 1;
2581 }
2582
2583 static int
2584 do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
2585 {
2586         if (!hists__has(browser->hists, socket) || act->socket < 0)
2587                 return 0;
2588
2589         if (browser->hists->socket_filter > -1) {
2590                 pstack__remove(browser->pstack, &browser->hists->socket_filter);
2591                 browser->hists->socket_filter = -1;
2592                 perf_hpp__set_elide(HISTC_SOCKET, false);
2593         } else {
2594                 browser->hists->socket_filter = act->socket;
2595                 perf_hpp__set_elide(HISTC_SOCKET, true);
2596                 pstack__push(browser->pstack, &browser->hists->socket_filter);
2597         }
2598
2599         hists__filter_by_socket(browser->hists);
2600         hist_browser__reset(browser);
2601         return 0;
2602 }
2603
2604 static int
2605 add_socket_opt(struct hist_browser *browser, struct popup_action *act,
2606                char **optstr, int socket_id)
2607 {
2608         if (!hists__has(browser->hists, socket) || socket_id < 0)
2609                 return 0;
2610
2611         if (asprintf(optstr, "Zoom %s Processor Socket %d",
2612                      (browser->hists->socket_filter > -1) ? "out of" : "into",
2613                      socket_id) < 0)
2614                 return 0;
2615
2616         act->socket = socket_id;
2617         act->fn = do_zoom_socket;
2618         return 1;
2619 }
2620
2621 static void hist_browser__update_nr_entries(struct hist_browser *hb)
2622 {
2623         u64 nr_entries = 0;
2624         struct rb_node *nd = rb_first(&hb->hists->entries);
2625
2626         if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
2627                 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
2628                 return;
2629         }
2630
2631         while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2632                 nr_entries++;
2633                 nd = rb_hierarchy_next(nd);
2634         }
2635
2636         hb->nr_non_filtered_entries = nr_entries;
2637         hb->nr_hierarchy_entries = nr_entries;
2638 }
2639
2640 static void hist_browser__update_percent_limit(struct hist_browser *hb,
2641                                                double percent)
2642 {
2643         struct hist_entry *he;
2644         struct rb_node *nd = rb_first(&hb->hists->entries);
2645         u64 total = hists__total_period(hb->hists);
2646         u64 min_callchain_hits = total * (percent / 100);
2647
2648         hb->min_pcnt = callchain_param.min_percent = percent;
2649
2650         while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2651                 he = rb_entry(nd, struct hist_entry, rb_node);
2652
2653                 if (he->has_no_entry) {
2654                         he->has_no_entry = false;
2655                         he->nr_rows = 0;
2656                 }
2657
2658                 if (!he->leaf || !symbol_conf.use_callchain)
2659                         goto next;
2660
2661                 if (callchain_param.mode == CHAIN_GRAPH_REL) {
2662                         total = he->stat.period;
2663
2664                         if (symbol_conf.cumulate_callchain)
2665                                 total = he->stat_acc->period;
2666
2667                         min_callchain_hits = total * (percent / 100);
2668                 }
2669
2670                 callchain_param.sort(&he->sorted_chain, he->callchain,
2671                                      min_callchain_hits, &callchain_param);
2672
2673 next:
2674                 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
2675
2676                 /* force to re-evaluate folding state of callchains */
2677                 he->init_have_children = false;
2678                 hist_entry__set_folding(he, hb, false);
2679         }
2680 }
2681
2682 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
2683                                     const char *helpline,
2684                                     bool left_exits,
2685                                     struct hist_browser_timer *hbt,
2686                                     float min_pcnt,
2687                                     struct perf_env *env)
2688 {
2689         struct hists *hists = evsel__hists(evsel);
2690         struct hist_browser *browser = perf_evsel_browser__new(evsel, hbt, env);
2691         struct branch_info *bi;
2692 #define MAX_OPTIONS  16
2693         char *options[MAX_OPTIONS];
2694         struct popup_action actions[MAX_OPTIONS];
2695         int nr_options = 0;
2696         int key = -1;
2697         char buf[64];
2698         int delay_secs = hbt ? hbt->refresh : 0;
2699
2700 #define HIST_BROWSER_HELP_COMMON                                        \
2701         "h/?/F1        Show this window\n"                              \
2702         "UP/DOWN/PGUP\n"                                                \
2703         "PGDN/SPACE    Navigate\n"                                      \
2704         "q/ESC/CTRL+C  Exit browser\n\n"                                \
2705         "For multiple event sessions:\n\n"                              \
2706         "TAB/UNTAB     Switch events\n\n"                               \
2707         "For symbolic views (--sort has sym):\n\n"                      \
2708         "ENTER         Zoom into DSO/Threads & Annotate current symbol\n" \
2709         "ESC           Zoom out\n"                                      \
2710         "a             Annotate current symbol\n"                       \
2711         "C             Collapse all callchains\n"                       \
2712         "d             Zoom into current DSO\n"                         \
2713         "E             Expand all callchains\n"                         \
2714         "F             Toggle percentage of filtered entries\n"         \
2715         "H             Display column headers\n"                        \
2716         "L             Change percent limit\n"                          \
2717         "m             Display context menu\n"                          \
2718         "S             Zoom into current Processor Socket\n"            \
2719
2720         /* help messages are sorted by lexical order of the hotkey */
2721         const char report_help[] = HIST_BROWSER_HELP_COMMON
2722         "i             Show header information\n"
2723         "P             Print histograms to perf.hist.N\n"
2724         "r             Run available scripts\n"
2725         "s             Switch to another data file in PWD\n"
2726         "t             Zoom into current Thread\n"
2727         "V             Verbose (DSO names in callchains, etc)\n"
2728         "/             Filter symbol by name";
2729         const char top_help[] = HIST_BROWSER_HELP_COMMON
2730         "P             Print histograms to perf.hist.N\n"
2731         "t             Zoom into current Thread\n"
2732         "V             Verbose (DSO names in callchains, etc)\n"
2733         "z             Toggle zeroing of samples\n"
2734         "f             Enable/Disable events\n"
2735         "/             Filter symbol by name";
2736
2737         if (browser == NULL)
2738                 return -1;
2739
2740         /* reset abort key so that it can get Ctrl-C as a key */
2741         SLang_reset_tty();
2742         SLang_init_tty(0, 0, 0);
2743
2744         if (min_pcnt)
2745                 browser->min_pcnt = min_pcnt;
2746         hist_browser__update_nr_entries(browser);
2747
2748         browser->pstack = pstack__new(3);
2749         if (browser->pstack == NULL)
2750                 goto out;
2751
2752         ui_helpline__push(helpline);
2753
2754         memset(options, 0, sizeof(options));
2755         memset(actions, 0, sizeof(actions));
2756
2757         if (symbol_conf.col_width_list_str)
2758                 perf_hpp__set_user_width(symbol_conf.col_width_list_str);
2759
2760         while (1) {
2761                 struct thread *thread = NULL;
2762                 struct map *map = NULL;
2763                 int choice = 0;
2764                 int socked_id = -1;
2765
2766                 nr_options = 0;
2767
2768                 key = hist_browser__run(browser, helpline);
2769
2770                 if (browser->he_selection != NULL) {
2771                         thread = hist_browser__selected_thread(browser);
2772                         map = browser->selection->map;
2773                         socked_id = browser->he_selection->socket;
2774                 }
2775                 switch (key) {
2776                 case K_TAB:
2777                 case K_UNTAB:
2778                         if (nr_events == 1)
2779                                 continue;
2780                         /*
2781                          * Exit the browser, let hists__browser_tree
2782                          * go to the next or previous
2783                          */
2784                         goto out_free_stack;
2785                 case 'a':
2786                         if (!hists__has(hists, sym)) {
2787                                 ui_browser__warning(&browser->b, delay_secs * 2,
2788                         "Annotation is only available for symbolic views, "
2789                         "include \"sym*\" in --sort to use it.");
2790                                 continue;
2791                         }
2792
2793                         if (browser->selection == NULL ||
2794                             browser->selection->sym == NULL ||
2795                             browser->selection->map->dso->annotate_warned)
2796                                 continue;
2797
2798                         actions->ms.map = browser->selection->map;
2799                         actions->ms.sym = browser->selection->sym;
2800                         do_annotate(browser, actions);
2801                         continue;
2802                 case 'P':
2803                         hist_browser__dump(browser);
2804                         continue;
2805                 case 'd':
2806                         actions->ms.map = map;
2807                         do_zoom_dso(browser, actions);
2808                         continue;
2809                 case 'V':
2810                         browser->show_dso = !browser->show_dso;
2811                         continue;
2812                 case 't':
2813                         actions->thread = thread;
2814                         do_zoom_thread(browser, actions);
2815                         continue;
2816                 case 'S':
2817                         actions->socket = socked_id;
2818                         do_zoom_socket(browser, actions);
2819                         continue;
2820                 case '/':
2821                         if (ui_browser__input_window("Symbol to show",
2822                                         "Please enter the name of symbol you want to see.\n"
2823                                         "To remove the filter later, press / + ENTER.",
2824                                         buf, "ENTER: OK, ESC: Cancel",
2825                                         delay_secs * 2) == K_ENTER) {
2826                                 hists->symbol_filter_str = *buf ? buf : NULL;
2827                                 hists__filter_by_symbol(hists);
2828                                 hist_browser__reset(browser);
2829                         }
2830                         continue;
2831                 case 'r':
2832                         if (is_report_browser(hbt)) {
2833                                 actions->thread = NULL;
2834                                 actions->ms.sym = NULL;
2835                                 do_run_script(browser, actions);
2836                         }
2837                         continue;
2838                 case 's':
2839                         if (is_report_browser(hbt)) {
2840                                 key = do_switch_data(browser, actions);
2841                                 if (key == K_SWITCH_INPUT_DATA)
2842                                         goto out_free_stack;
2843                         }
2844                         continue;
2845                 case 'i':
2846                         /* env->arch is NULL for live-mode (i.e. perf top) */
2847                         if (env->arch)
2848                                 tui__header_window(env);
2849                         continue;
2850                 case 'F':
2851                         symbol_conf.filter_relative ^= 1;
2852                         continue;
2853                 case 'z':
2854                         if (!is_report_browser(hbt)) {
2855                                 struct perf_top *top = hbt->arg;
2856
2857                                 top->zero = !top->zero;
2858                         }
2859                         continue;
2860                 case 'L':
2861                         if (ui_browser__input_window("Percent Limit",
2862                                         "Please enter the value you want to hide entries under that percent.",
2863                                         buf, "ENTER: OK, ESC: Cancel",
2864                                         delay_secs * 2) == K_ENTER) {
2865                                 char *end;
2866                                 double new_percent = strtod(buf, &end);
2867
2868                                 if (new_percent < 0 || new_percent > 100) {
2869                                         ui_browser__warning(&browser->b, delay_secs * 2,
2870                                                 "Invalid percent: %.2f", new_percent);
2871                                         continue;
2872                                 }
2873
2874                                 hist_browser__update_percent_limit(browser, new_percent);
2875                                 hist_browser__reset(browser);
2876                         }
2877                         continue;
2878                 case K_F1:
2879                 case 'h':
2880                 case '?':
2881                         ui_browser__help_window(&browser->b,
2882                                 is_report_browser(hbt) ? report_help : top_help);
2883                         continue;
2884                 case K_ENTER:
2885                 case K_RIGHT:
2886                 case 'm':
2887                         /* menu */
2888                         break;
2889                 case K_ESC:
2890                 case K_LEFT: {
2891                         const void *top;
2892
2893                         if (pstack__empty(browser->pstack)) {
2894                                 /*
2895                                  * Go back to the perf_evsel_menu__run or other user
2896                                  */
2897                                 if (left_exits)
2898                                         goto out_free_stack;
2899
2900                                 if (key == K_ESC &&
2901                                     ui_browser__dialog_yesno(&browser->b,
2902                                                              "Do you really want to exit?"))
2903                                         goto out_free_stack;
2904
2905                                 continue;
2906                         }
2907                         top = pstack__peek(browser->pstack);
2908                         if (top == &browser->hists->dso_filter) {
2909                                 /*
2910                                  * No need to set actions->dso here since
2911                                  * it's just to remove the current filter.
2912                                  * Ditto for thread below.
2913                                  */
2914                                 do_zoom_dso(browser, actions);
2915                         } else if (top == &browser->hists->thread_filter) {
2916                                 do_zoom_thread(browser, actions);
2917                         } else if (top == &browser->hists->socket_filter) {
2918                                 do_zoom_socket(browser, actions);
2919                         }
2920                         continue;
2921                 }
2922                 case 'q':
2923                 case CTRL('c'):
2924                         goto out_free_stack;
2925                 case 'f':
2926                         if (!is_report_browser(hbt)) {
2927                                 struct perf_top *top = hbt->arg;
2928
2929                                 perf_evlist__toggle_enable(top->evlist);
2930                                 /*
2931                                  * No need to refresh, resort/decay histogram
2932                                  * entries if we are not collecting samples:
2933                                  */
2934                                 if (top->evlist->enabled) {
2935                                         helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
2936                                         hbt->refresh = delay_secs;
2937                                 } else {
2938                                         helpline = "Press 'f' again to re-enable the events";
2939                                         hbt->refresh = 0;
2940                                 }
2941                                 continue;
2942                         }
2943                         /* Fall thru */
2944                 default:
2945                         helpline = "Press '?' for help on key bindings";
2946                         continue;
2947                 }
2948
2949                 if (!hists__has(hists, sym) || browser->selection == NULL)
2950                         goto skip_annotation;
2951
2952                 if (sort__mode == SORT_MODE__BRANCH) {
2953                         bi = browser->he_selection->branch_info;
2954
2955                         if (bi == NULL)
2956                                 goto skip_annotation;
2957
2958                         nr_options += add_annotate_opt(browser,
2959                                                        &actions[nr_options],
2960                                                        &options[nr_options],
2961                                                        bi->from.map,
2962                                                        bi->from.sym);
2963                         if (bi->to.sym != bi->from.sym)
2964                                 nr_options += add_annotate_opt(browser,
2965                                                         &actions[nr_options],
2966                                                         &options[nr_options],
2967                                                         bi->to.map,
2968                                                         bi->to.sym);
2969                 } else {
2970                         nr_options += add_annotate_opt(browser,
2971                                                        &actions[nr_options],
2972                                                        &options[nr_options],
2973                                                        browser->selection->map,
2974                                                        browser->selection->sym);
2975                 }
2976 skip_annotation:
2977                 nr_options += add_thread_opt(browser, &actions[nr_options],
2978                                              &options[nr_options], thread);
2979                 nr_options += add_dso_opt(browser, &actions[nr_options],
2980                                           &options[nr_options], map);
2981                 nr_options += add_map_opt(browser, &actions[nr_options],
2982                                           &options[nr_options],
2983                                           browser->selection ?
2984                                                 browser->selection->map : NULL);
2985                 nr_options += add_socket_opt(browser, &actions[nr_options],
2986                                              &options[nr_options],
2987                                              socked_id);
2988                 /* perf script support */
2989                 if (!is_report_browser(hbt))
2990                         goto skip_scripting;
2991
2992                 if (browser->he_selection) {
2993                         if (hists__has(hists, thread) && thread) {
2994                                 nr_options += add_script_opt(browser,
2995                                                              &actions[nr_options],
2996                                                              &options[nr_options],
2997                                                              thread, NULL);
2998                         }
2999                         /*
3000                          * Note that browser->selection != NULL
3001                          * when browser->he_selection is not NULL,
3002                          * so we don't need to check browser->selection
3003                          * before fetching browser->selection->sym like what
3004                          * we do before fetching browser->selection->map.
3005                          *
3006                          * See hist_browser__show_entry.
3007                          */
3008                         if (hists__has(hists, sym) && browser->selection->sym) {
3009                                 nr_options += add_script_opt(browser,
3010                                                              &actions[nr_options],
3011                                                              &options[nr_options],
3012                                                              NULL, browser->selection->sym);
3013                         }
3014                 }
3015                 nr_options += add_script_opt(browser, &actions[nr_options],
3016                                              &options[nr_options], NULL, NULL);
3017                 nr_options += add_switch_opt(browser, &actions[nr_options],
3018                                              &options[nr_options]);
3019 skip_scripting:
3020                 nr_options += add_exit_opt(browser, &actions[nr_options],
3021                                            &options[nr_options]);
3022
3023                 do {
3024                         struct popup_action *act;
3025
3026                         choice = ui__popup_menu(nr_options, options);
3027                         if (choice == -1 || choice >= nr_options)
3028                                 break;
3029
3030                         act = &actions[choice];
3031                         key = act->fn(browser, act);
3032                 } while (key == 1);
3033
3034                 if (key == K_SWITCH_INPUT_DATA)
3035                         break;
3036         }
3037 out_free_stack:
3038         pstack__delete(browser->pstack);
3039 out:
3040         hist_browser__delete(browser);
3041         free_popup_options(options, MAX_OPTIONS);
3042         return key;
3043 }
3044
3045 struct perf_evsel_menu {
3046         struct ui_browser b;
3047         struct perf_evsel *selection;
3048         bool lost_events, lost_events_warned;
3049         float min_pcnt;
3050         struct perf_env *env;
3051 };
3052
3053 static void perf_evsel_menu__write(struct ui_browser *browser,
3054                                    void *entry, int row)
3055 {
3056         struct perf_evsel_menu *menu = container_of(browser,
3057                                                     struct perf_evsel_menu, b);
3058         struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
3059         struct hists *hists = evsel__hists(evsel);
3060         bool current_entry = ui_browser__is_current_entry(browser, row);
3061         unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
3062         const char *ev_name = perf_evsel__name(evsel);
3063         char bf[256], unit;
3064         const char *warn = " ";
3065         size_t printed;
3066
3067         ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
3068                                                        HE_COLORSET_NORMAL);
3069
3070         if (perf_evsel__is_group_event(evsel)) {
3071                 struct perf_evsel *pos;
3072
3073                 ev_name = perf_evsel__group_name(evsel);
3074
3075                 for_each_group_member(pos, evsel) {
3076                         struct hists *pos_hists = evsel__hists(pos);
3077                         nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
3078                 }
3079         }
3080
3081         nr_events = convert_unit(nr_events, &unit);
3082         printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
3083                            unit, unit == ' ' ? "" : " ", ev_name);
3084         ui_browser__printf(browser, "%s", bf);
3085
3086         nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
3087         if (nr_events != 0) {
3088                 menu->lost_events = true;
3089                 if (!current_entry)
3090                         ui_browser__set_color(browser, HE_COLORSET_TOP);
3091                 nr_events = convert_unit(nr_events, &unit);
3092                 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
3093                                      nr_events, unit, unit == ' ' ? "" : " ");
3094                 warn = bf;
3095         }
3096
3097         ui_browser__write_nstring(browser, warn, browser->width - printed);
3098
3099         if (current_entry)
3100                 menu->selection = evsel;
3101 }
3102
3103 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
3104                                 int nr_events, const char *help,
3105                                 struct hist_browser_timer *hbt)
3106 {
3107         struct perf_evlist *evlist = menu->b.priv;
3108         struct perf_evsel *pos;
3109         const char *title = "Available samples";
3110         int delay_secs = hbt ? hbt->refresh : 0;
3111         int key;
3112
3113         if (ui_browser__show(&menu->b, title,
3114                              "ESC: exit, ENTER|->: Browse histograms") < 0)
3115                 return -1;
3116
3117         while (1) {
3118                 key = ui_browser__run(&menu->b, delay_secs);
3119
3120                 switch (key) {
3121                 case K_TIMER:
3122                         hbt->timer(hbt->arg);
3123
3124                         if (!menu->lost_events_warned && menu->lost_events) {
3125                                 ui_browser__warn_lost_events(&menu->b);
3126                                 menu->lost_events_warned = true;
3127                         }
3128                         continue;
3129                 case K_RIGHT:
3130                 case K_ENTER:
3131                         if (!menu->selection)
3132                                 continue;
3133                         pos = menu->selection;
3134 browse_hists:
3135                         perf_evlist__set_selected(evlist, pos);
3136                         /*
3137                          * Give the calling tool a chance to populate the non
3138                          * default evsel resorted hists tree.
3139                          */
3140                         if (hbt)
3141                                 hbt->timer(hbt->arg);
3142                         key = perf_evsel__hists_browse(pos, nr_events, help,
3143                                                        true, hbt,
3144                                                        menu->min_pcnt,
3145                                                        menu->env);
3146                         ui_browser__show_title(&menu->b, title);
3147                         switch (key) {
3148                         case K_TAB:
3149                                 if (pos->node.next == &evlist->entries)
3150                                         pos = perf_evlist__first(evlist);
3151                                 else
3152                                         pos = perf_evsel__next(pos);
3153                                 goto browse_hists;
3154                         case K_UNTAB:
3155                                 if (pos->node.prev == &evlist->entries)
3156                                         pos = perf_evlist__last(evlist);
3157                                 else
3158                                         pos = perf_evsel__prev(pos);
3159                                 goto browse_hists;
3160                         case K_SWITCH_INPUT_DATA:
3161                         case 'q':
3162                         case CTRL('c'):
3163                                 goto out;
3164                         case K_ESC:
3165                         default:
3166                                 continue;
3167                         }
3168                 case K_LEFT:
3169                         continue;
3170                 case K_ESC:
3171                         if (!ui_browser__dialog_yesno(&menu->b,
3172                                                "Do you really want to exit?"))
3173                                 continue;
3174                         /* Fall thru */
3175                 case 'q':
3176                 case CTRL('c'):
3177                         goto out;
3178                 default:
3179                         continue;
3180                 }
3181         }
3182
3183 out:
3184         ui_browser__hide(&menu->b);
3185         return key;
3186 }
3187
3188 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
3189                                  void *entry)
3190 {
3191         struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
3192
3193         if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
3194                 return true;
3195
3196         return false;
3197 }
3198
3199 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
3200                                            int nr_entries, const char *help,
3201                                            struct hist_browser_timer *hbt,
3202                                            float min_pcnt,
3203                                            struct perf_env *env)
3204 {
3205         struct perf_evsel *pos;
3206         struct perf_evsel_menu menu = {
3207                 .b = {
3208                         .entries    = &evlist->entries,
3209                         .refresh    = ui_browser__list_head_refresh,
3210                         .seek       = ui_browser__list_head_seek,
3211                         .write      = perf_evsel_menu__write,
3212                         .filter     = filter_group_entries,
3213                         .nr_entries = nr_entries,
3214                         .priv       = evlist,
3215                 },
3216                 .min_pcnt = min_pcnt,
3217                 .env = env,
3218         };
3219
3220         ui_helpline__push("Press ESC to exit");
3221
3222         evlist__for_each_entry(evlist, pos) {
3223                 const char *ev_name = perf_evsel__name(pos);
3224                 size_t line_len = strlen(ev_name) + 7;
3225
3226                 if (menu.b.width < line_len)
3227                         menu.b.width = line_len;
3228         }
3229
3230         return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
3231 }
3232
3233 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
3234                                   struct hist_browser_timer *hbt,
3235                                   float min_pcnt,
3236                                   struct perf_env *env)
3237 {
3238         int nr_entries = evlist->nr_entries;
3239
3240 single_entry:
3241         if (nr_entries == 1) {
3242                 struct perf_evsel *first = perf_evlist__first(evlist);
3243
3244                 return perf_evsel__hists_browse(first, nr_entries, help,
3245                                                 false, hbt, min_pcnt,
3246                                                 env);
3247         }
3248
3249         if (symbol_conf.event_group) {
3250                 struct perf_evsel *pos;
3251
3252                 nr_entries = 0;
3253                 evlist__for_each_entry(evlist, pos) {
3254                         if (perf_evsel__is_group_leader(pos))
3255                                 nr_entries++;
3256                 }
3257
3258                 if (nr_entries == 1)
3259                         goto single_entry;
3260         }
3261
3262         return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
3263                                                hbt, min_pcnt, env);
3264 }