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