6a4681932ba57ed8c519a62cfbf2827d64f26bf7
[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, hists_to_evsel(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_to_evsel(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_to_evsel(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_to_evsel(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 hist_browser__show_headers(struct hist_browser *browser)
1626 {
1627         char headers[1024];
1628
1629         if (symbol_conf.report_hierarchy)
1630                 hists_browser__scnprintf_hierarchy_headers(browser, headers,
1631                                                            sizeof(headers));
1632         else
1633                 hists_browser__scnprintf_headers(browser, headers,
1634                                                  sizeof(headers));
1635         ui_browser__gotorc(&browser->b, 0, 0);
1636         ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1637         ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1638 }
1639
1640 static void ui_browser__hists_init_top(struct ui_browser *browser)
1641 {
1642         if (browser->top == NULL) {
1643                 struct hist_browser *hb;
1644
1645                 hb = container_of(browser, struct hist_browser, b);
1646                 browser->top = rb_first(&hb->hists->entries);
1647         }
1648 }
1649
1650 static unsigned int hist_browser__refresh(struct ui_browser *browser)
1651 {
1652         unsigned row = 0;
1653         u16 header_offset = 0;
1654         struct rb_node *nd;
1655         struct hist_browser *hb = container_of(browser, struct hist_browser, b);
1656
1657         if (hb->show_headers) {
1658                 hist_browser__show_headers(hb);
1659                 header_offset = 1;
1660         }
1661
1662         ui_browser__hists_init_top(browser);
1663         hb->he_selection = NULL;
1664         hb->selection = NULL;
1665
1666         for (nd = browser->top; nd; nd = rb_hierarchy_next(nd)) {
1667                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1668                 float percent;
1669
1670                 if (h->filtered) {
1671                         /* let it move to sibling */
1672                         h->unfolded = false;
1673                         continue;
1674                 }
1675
1676                 percent = hist_entry__get_percent_limit(h);
1677                 if (percent < hb->min_pcnt)
1678                         continue;
1679
1680                 if (symbol_conf.report_hierarchy) {
1681                         row += hist_browser__show_hierarchy_entry(hb, h, row,
1682                                                                   h->depth);
1683                         if (row == browser->rows)
1684                                 break;
1685
1686                         if (h->has_no_entry) {
1687                                 hist_browser__show_no_entry(hb, row, h->depth + 1);
1688                                 row++;
1689                         }
1690                 } else {
1691                         row += hist_browser__show_entry(hb, h, row);
1692                 }
1693
1694                 if (row == browser->rows)
1695                         break;
1696         }
1697
1698         return row + header_offset;
1699 }
1700
1701 static struct rb_node *hists__filter_entries(struct rb_node *nd,
1702                                              float min_pcnt)
1703 {
1704         while (nd != NULL) {
1705                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1706                 float percent = hist_entry__get_percent_limit(h);
1707
1708                 if (!h->filtered && percent >= min_pcnt)
1709                         return nd;
1710
1711                 /*
1712                  * If it's filtered, its all children also were filtered.
1713                  * So move to sibling node.
1714                  */
1715                 if (rb_next(nd))
1716                         nd = rb_next(nd);
1717                 else
1718                         nd = rb_hierarchy_next(nd);
1719         }
1720
1721         return NULL;
1722 }
1723
1724 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
1725                                                   float min_pcnt)
1726 {
1727         while (nd != NULL) {
1728                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1729                 float percent = hist_entry__get_percent_limit(h);
1730
1731                 if (!h->filtered && percent >= min_pcnt)
1732                         return nd;
1733
1734                 nd = rb_hierarchy_prev(nd);
1735         }
1736
1737         return NULL;
1738 }
1739
1740 static void ui_browser__hists_seek(struct ui_browser *browser,
1741                                    off_t offset, int whence)
1742 {
1743         struct hist_entry *h;
1744         struct rb_node *nd;
1745         bool first = true;
1746         struct hist_browser *hb;
1747
1748         hb = container_of(browser, struct hist_browser, b);
1749
1750         if (browser->nr_entries == 0)
1751                 return;
1752
1753         ui_browser__hists_init_top(browser);
1754
1755         switch (whence) {
1756         case SEEK_SET:
1757                 nd = hists__filter_entries(rb_first(browser->entries),
1758                                            hb->min_pcnt);
1759                 break;
1760         case SEEK_CUR:
1761                 nd = browser->top;
1762                 goto do_offset;
1763         case SEEK_END:
1764                 nd = rb_hierarchy_last(rb_last(browser->entries));
1765                 nd = hists__filter_prev_entries(nd, hb->min_pcnt);
1766                 first = false;
1767                 break;
1768         default:
1769                 return;
1770         }
1771
1772         /*
1773          * Moves not relative to the first visible entry invalidates its
1774          * row_offset:
1775          */
1776         h = rb_entry(browser->top, struct hist_entry, rb_node);
1777         h->row_offset = 0;
1778
1779         /*
1780          * Here we have to check if nd is expanded (+), if it is we can't go
1781          * the next top level hist_entry, instead we must compute an offset of
1782          * what _not_ to show and not change the first visible entry.
1783          *
1784          * This offset increments when we are going from top to bottom and
1785          * decreases when we're going from bottom to top.
1786          *
1787          * As we don't have backpointers to the top level in the callchains
1788          * structure, we need to always print the whole hist_entry callchain,
1789          * skipping the first ones that are before the first visible entry
1790          * and stop when we printed enough lines to fill the screen.
1791          */
1792 do_offset:
1793         if (!nd)
1794                 return;
1795
1796         if (offset > 0) {
1797                 do {
1798                         h = rb_entry(nd, struct hist_entry, rb_node);
1799                         if (h->unfolded && h->leaf) {
1800                                 u16 remaining = h->nr_rows - h->row_offset;
1801                                 if (offset > remaining) {
1802                                         offset -= remaining;
1803                                         h->row_offset = 0;
1804                                 } else {
1805                                         h->row_offset += offset;
1806                                         offset = 0;
1807                                         browser->top = nd;
1808                                         break;
1809                                 }
1810                         }
1811                         nd = hists__filter_entries(rb_hierarchy_next(nd),
1812                                                    hb->min_pcnt);
1813                         if (nd == NULL)
1814                                 break;
1815                         --offset;
1816                         browser->top = nd;
1817                 } while (offset != 0);
1818         } else if (offset < 0) {
1819                 while (1) {
1820                         h = rb_entry(nd, struct hist_entry, rb_node);
1821                         if (h->unfolded && h->leaf) {
1822                                 if (first) {
1823                                         if (-offset > h->row_offset) {
1824                                                 offset += h->row_offset;
1825                                                 h->row_offset = 0;
1826                                         } else {
1827                                                 h->row_offset += offset;
1828                                                 offset = 0;
1829                                                 browser->top = nd;
1830                                                 break;
1831                                         }
1832                                 } else {
1833                                         if (-offset > h->nr_rows) {
1834                                                 offset += h->nr_rows;
1835                                                 h->row_offset = 0;
1836                                         } else {
1837                                                 h->row_offset = h->nr_rows + offset;
1838                                                 offset = 0;
1839                                                 browser->top = nd;
1840                                                 break;
1841                                         }
1842                                 }
1843                         }
1844
1845                         nd = hists__filter_prev_entries(rb_hierarchy_prev(nd),
1846                                                         hb->min_pcnt);
1847                         if (nd == NULL)
1848                                 break;
1849                         ++offset;
1850                         browser->top = nd;
1851                         if (offset == 0) {
1852                                 /*
1853                                  * Last unfiltered hist_entry, check if it is
1854                                  * unfolded, if it is then we should have
1855                                  * row_offset at its last entry.
1856                                  */
1857                                 h = rb_entry(nd, struct hist_entry, rb_node);
1858                                 if (h->unfolded && h->leaf)
1859                                         h->row_offset = h->nr_rows;
1860                                 break;
1861                         }
1862                         first = false;
1863                 }
1864         } else {
1865                 browser->top = nd;
1866                 h = rb_entry(nd, struct hist_entry, rb_node);
1867                 h->row_offset = 0;
1868         }
1869 }
1870
1871 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1872                                            struct hist_entry *he, FILE *fp,
1873                                            int level)
1874 {
1875         struct callchain_print_arg arg  = {
1876                 .fp = fp,
1877         };
1878
1879         hist_browser__show_callchain(browser, he, level, 0,
1880                                      hist_browser__fprintf_callchain_entry, &arg,
1881                                      hist_browser__check_dump_full);
1882         return arg.printed;
1883 }
1884
1885 static int hist_browser__fprintf_entry(struct hist_browser *browser,
1886                                        struct hist_entry *he, FILE *fp)
1887 {
1888         char s[8192];
1889         int printed = 0;
1890         char folded_sign = ' ';
1891         struct perf_hpp hpp = {
1892                 .buf = s,
1893                 .size = sizeof(s),
1894         };
1895         struct perf_hpp_fmt *fmt;
1896         bool first = true;
1897         int ret;
1898
1899         if (symbol_conf.use_callchain) {
1900                 folded_sign = hist_entry__folded(he);
1901                 printed += fprintf(fp, "%c ", folded_sign);
1902         }
1903
1904         hists__for_each_format(browser->hists, fmt) {
1905                 if (perf_hpp__should_skip(fmt, he->hists))
1906                         continue;
1907
1908                 if (!first) {
1909                         ret = scnprintf(hpp.buf, hpp.size, "  ");
1910                         advance_hpp(&hpp, ret);
1911                 } else
1912                         first = false;
1913
1914                 ret = fmt->entry(fmt, &hpp, he);
1915                 ret = hist_entry__snprintf_alignment(he, &hpp, fmt, ret);
1916                 advance_hpp(&hpp, ret);
1917         }
1918         printed += fprintf(fp, "%s\n", s);
1919
1920         if (folded_sign == '-')
1921                 printed += hist_browser__fprintf_callchain(browser, he, fp, 1);
1922
1923         return printed;
1924 }
1925
1926
1927 static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,
1928                                                  struct hist_entry *he,
1929                                                  FILE *fp, int level)
1930 {
1931         char s[8192];
1932         int printed = 0;
1933         char folded_sign = ' ';
1934         struct perf_hpp hpp = {
1935                 .buf = s,
1936                 .size = sizeof(s),
1937         };
1938         struct perf_hpp_fmt *fmt;
1939         struct perf_hpp_list_node *fmt_node;
1940         bool first = true;
1941         int ret;
1942         int hierarchy_indent = (he->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
1943
1944         printed = fprintf(fp, "%*s", level * HIERARCHY_INDENT, "");
1945
1946         folded_sign = hist_entry__folded(he);
1947         printed += fprintf(fp, "%c", folded_sign);
1948
1949         /* the first hpp_list_node is for overhead columns */
1950         fmt_node = list_first_entry(&he->hists->hpp_formats,
1951                                     struct perf_hpp_list_node, list);
1952         perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1953                 if (!first) {
1954                         ret = scnprintf(hpp.buf, hpp.size, "  ");
1955                         advance_hpp(&hpp, ret);
1956                 } else
1957                         first = false;
1958
1959                 ret = fmt->entry(fmt, &hpp, he);
1960                 advance_hpp(&hpp, ret);
1961         }
1962
1963         ret = scnprintf(hpp.buf, hpp.size, "%*s", hierarchy_indent, "");
1964         advance_hpp(&hpp, ret);
1965
1966         perf_hpp_list__for_each_format(he->hpp_list, fmt) {
1967                 ret = scnprintf(hpp.buf, hpp.size, "  ");
1968                 advance_hpp(&hpp, ret);
1969
1970                 ret = fmt->entry(fmt, &hpp, he);
1971                 advance_hpp(&hpp, ret);
1972         }
1973
1974         printed += fprintf(fp, "%s\n", rtrim(s));
1975
1976         if (he->leaf && folded_sign == '-') {
1977                 printed += hist_browser__fprintf_callchain(browser, he, fp,
1978                                                            he->depth + 1);
1979         }
1980
1981         return printed;
1982 }
1983
1984 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1985 {
1986         struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
1987                                                    browser->min_pcnt);
1988         int printed = 0;
1989
1990         while (nd) {
1991                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1992
1993                 if (symbol_conf.report_hierarchy) {
1994                         printed += hist_browser__fprintf_hierarchy_entry(browser,
1995                                                                          h, fp,
1996                                                                          h->depth);
1997                 } else {
1998                         printed += hist_browser__fprintf_entry(browser, h, fp);
1999                 }
2000
2001                 nd = hists__filter_entries(rb_hierarchy_next(nd),
2002                                            browser->min_pcnt);
2003         }
2004
2005         return printed;
2006 }
2007
2008 static int hist_browser__dump(struct hist_browser *browser)
2009 {
2010         char filename[64];
2011         FILE *fp;
2012
2013         while (1) {
2014                 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
2015                 if (access(filename, F_OK))
2016                         break;
2017                 /*
2018                  * XXX: Just an arbitrary lazy upper limit
2019                  */
2020                 if (++browser->print_seq == 8192) {
2021                         ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
2022                         return -1;
2023                 }
2024         }
2025
2026         fp = fopen(filename, "w");
2027         if (fp == NULL) {
2028                 char bf[64];
2029                 const char *err = strerror_r(errno, bf, sizeof(bf));
2030                 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
2031                 return -1;
2032         }
2033
2034         ++browser->print_seq;
2035         hist_browser__fprintf(browser, fp);
2036         fclose(fp);
2037         ui_helpline__fpush("%s written!", filename);
2038
2039         return 0;
2040 }
2041
2042 static struct hist_browser *hist_browser__new(struct hists *hists,
2043                                               struct hist_browser_timer *hbt,
2044                                               struct perf_env *env)
2045 {
2046         struct hist_browser *browser = zalloc(sizeof(*browser));
2047
2048         if (browser) {
2049                 browser->hists = hists;
2050                 browser->b.refresh = hist_browser__refresh;
2051                 browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
2052                 browser->b.seek = ui_browser__hists_seek;
2053                 browser->b.use_navkeypressed = true;
2054                 browser->show_headers = symbol_conf.show_hist_headers;
2055                 browser->hbt = hbt;
2056                 browser->env = env;
2057         }
2058
2059         return browser;
2060 }
2061
2062 static void hist_browser__delete(struct hist_browser *browser)
2063 {
2064         free(browser);
2065 }
2066
2067 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
2068 {
2069         return browser->he_selection;
2070 }
2071
2072 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
2073 {
2074         return browser->he_selection->thread;
2075 }
2076
2077 /* Check whether the browser is for 'top' or 'report' */
2078 static inline bool is_report_browser(void *timer)
2079 {
2080         return timer == NULL;
2081 }
2082
2083 static int hists__browser_title(struct hists *hists,
2084                                 struct hist_browser_timer *hbt,
2085                                 char *bf, size_t size)
2086 {
2087         char unit;
2088         int printed;
2089         const struct dso *dso = hists->dso_filter;
2090         const struct thread *thread = hists->thread_filter;
2091         int socket_id = hists->socket_filter;
2092         unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
2093         u64 nr_events = hists->stats.total_period;
2094         struct perf_evsel *evsel = hists_to_evsel(hists);
2095         const char *ev_name = perf_evsel__name(evsel);
2096         char buf[512];
2097         size_t buflen = sizeof(buf);
2098         char ref[30] = " show reference callgraph, ";
2099         bool enable_ref = false;
2100
2101         if (symbol_conf.filter_relative) {
2102                 nr_samples = hists->stats.nr_non_filtered_samples;
2103                 nr_events = hists->stats.total_non_filtered_period;
2104         }
2105
2106         if (perf_evsel__is_group_event(evsel)) {
2107                 struct perf_evsel *pos;
2108
2109                 perf_evsel__group_desc(evsel, buf, buflen);
2110                 ev_name = buf;
2111
2112                 for_each_group_member(pos, evsel) {
2113                         struct hists *pos_hists = evsel__hists(pos);
2114
2115                         if (symbol_conf.filter_relative) {
2116                                 nr_samples += pos_hists->stats.nr_non_filtered_samples;
2117                                 nr_events += pos_hists->stats.total_non_filtered_period;
2118                         } else {
2119                                 nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
2120                                 nr_events += pos_hists->stats.total_period;
2121                         }
2122                 }
2123         }
2124
2125         if (symbol_conf.show_ref_callgraph &&
2126             strstr(ev_name, "call-graph=no"))
2127                 enable_ref = true;
2128         nr_samples = convert_unit(nr_samples, &unit);
2129         printed = scnprintf(bf, size,
2130                            "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64,
2131                            nr_samples, unit, ev_name, enable_ref ? ref : " ", nr_events);
2132
2133
2134         if (hists->uid_filter_str)
2135                 printed += snprintf(bf + printed, size - printed,
2136                                     ", UID: %s", hists->uid_filter_str);
2137         if (thread) {
2138                 if (sort__has_thread) {
2139                         printed += scnprintf(bf + printed, size - printed,
2140                                     ", Thread: %s(%d)",
2141                                      (thread->comm_set ? thread__comm_str(thread) : ""),
2142                                     thread->tid);
2143                 } else {
2144                         printed += scnprintf(bf + printed, size - printed,
2145                                     ", Thread: %s",
2146                                      (thread->comm_set ? thread__comm_str(thread) : ""));
2147                 }
2148         }
2149         if (dso)
2150                 printed += scnprintf(bf + printed, size - printed,
2151                                     ", DSO: %s", dso->short_name);
2152         if (socket_id > -1)
2153                 printed += scnprintf(bf + printed, size - printed,
2154                                     ", Processor Socket: %d", socket_id);
2155         if (!is_report_browser(hbt)) {
2156                 struct perf_top *top = hbt->arg;
2157
2158                 if (top->zero)
2159                         printed += scnprintf(bf + printed, size - printed, " [z]");
2160         }
2161
2162         return printed;
2163 }
2164
2165 static inline void free_popup_options(char **options, int n)
2166 {
2167         int i;
2168
2169         for (i = 0; i < n; ++i)
2170                 zfree(&options[i]);
2171 }
2172
2173 /*
2174  * Only runtime switching of perf data file will make "input_name" point
2175  * to a malloced buffer. So add "is_input_name_malloced" flag to decide
2176  * whether we need to call free() for current "input_name" during the switch.
2177  */
2178 static bool is_input_name_malloced = false;
2179
2180 static int switch_data_file(void)
2181 {
2182         char *pwd, *options[32], *abs_path[32], *tmp;
2183         DIR *pwd_dir;
2184         int nr_options = 0, choice = -1, ret = -1;
2185         struct dirent *dent;
2186
2187         pwd = getenv("PWD");
2188         if (!pwd)
2189                 return ret;
2190
2191         pwd_dir = opendir(pwd);
2192         if (!pwd_dir)
2193                 return ret;
2194
2195         memset(options, 0, sizeof(options));
2196         memset(options, 0, sizeof(abs_path));
2197
2198         while ((dent = readdir(pwd_dir))) {
2199                 char path[PATH_MAX];
2200                 u64 magic;
2201                 char *name = dent->d_name;
2202                 FILE *file;
2203
2204                 if (!(dent->d_type == DT_REG))
2205                         continue;
2206
2207                 snprintf(path, sizeof(path), "%s/%s", pwd, name);
2208
2209                 file = fopen(path, "r");
2210                 if (!file)
2211                         continue;
2212
2213                 if (fread(&magic, 1, 8, file) < 8)
2214                         goto close_file_and_continue;
2215
2216                 if (is_perf_magic(magic)) {
2217                         options[nr_options] = strdup(name);
2218                         if (!options[nr_options])
2219                                 goto close_file_and_continue;
2220
2221                         abs_path[nr_options] = strdup(path);
2222                         if (!abs_path[nr_options]) {
2223                                 zfree(&options[nr_options]);
2224                                 ui__warning("Can't search all data files due to memory shortage.\n");
2225                                 fclose(file);
2226                                 break;
2227                         }
2228
2229                         nr_options++;
2230                 }
2231
2232 close_file_and_continue:
2233                 fclose(file);
2234                 if (nr_options >= 32) {
2235                         ui__warning("Too many perf data files in PWD!\n"
2236                                     "Only the first 32 files will be listed.\n");
2237                         break;
2238                 }
2239         }
2240         closedir(pwd_dir);
2241
2242         if (nr_options) {
2243                 choice = ui__popup_menu(nr_options, options);
2244                 if (choice < nr_options && choice >= 0) {
2245                         tmp = strdup(abs_path[choice]);
2246                         if (tmp) {
2247                                 if (is_input_name_malloced)
2248                                         free((void *)input_name);
2249                                 input_name = tmp;
2250                                 is_input_name_malloced = true;
2251                                 ret = 0;
2252                         } else
2253                                 ui__warning("Data switch failed due to memory shortage!\n");
2254                 }
2255         }
2256
2257         free_popup_options(options, nr_options);
2258         free_popup_options(abs_path, nr_options);
2259         return ret;
2260 }
2261
2262 struct popup_action {
2263         struct thread           *thread;
2264         struct map_symbol       ms;
2265         int                     socket;
2266
2267         int (*fn)(struct hist_browser *browser, struct popup_action *act);
2268 };
2269
2270 static int
2271 do_annotate(struct hist_browser *browser, struct popup_action *act)
2272 {
2273         struct perf_evsel *evsel;
2274         struct annotation *notes;
2275         struct hist_entry *he;
2276         int err;
2277
2278         if (!objdump_path && perf_env__lookup_objdump(browser->env))
2279                 return 0;
2280
2281         notes = symbol__annotation(act->ms.sym);
2282         if (!notes->src)
2283                 return 0;
2284
2285         evsel = hists_to_evsel(browser->hists);
2286         err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
2287         he = hist_browser__selected_entry(browser);
2288         /*
2289          * offer option to annotate the other branch source or target
2290          * (if they exists) when returning from annotate
2291          */
2292         if ((err == 'q' || err == CTRL('c')) && he->branch_info)
2293                 return 1;
2294
2295         ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
2296         if (err)
2297                 ui_browser__handle_resize(&browser->b);
2298         return 0;
2299 }
2300
2301 static int
2302 add_annotate_opt(struct hist_browser *browser __maybe_unused,
2303                  struct popup_action *act, char **optstr,
2304                  struct map *map, struct symbol *sym)
2305 {
2306         if (sym == NULL || map->dso->annotate_warned)
2307                 return 0;
2308
2309         if (asprintf(optstr, "Annotate %s", sym->name) < 0)
2310                 return 0;
2311
2312         act->ms.map = map;
2313         act->ms.sym = sym;
2314         act->fn = do_annotate;
2315         return 1;
2316 }
2317
2318 static int
2319 do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
2320 {
2321         struct thread *thread = act->thread;
2322
2323         if ((!sort__has_thread && !sort__has_comm) || thread == NULL)
2324                 return 0;
2325
2326         if (browser->hists->thread_filter) {
2327                 pstack__remove(browser->pstack, &browser->hists->thread_filter);
2328                 perf_hpp__set_elide(HISTC_THREAD, false);
2329                 thread__zput(browser->hists->thread_filter);
2330                 ui_helpline__pop();
2331         } else {
2332                 if (sort__has_thread) {
2333                         ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
2334                                            thread->comm_set ? thread__comm_str(thread) : "",
2335                                            thread->tid);
2336                 } else {
2337                         ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
2338                                            thread->comm_set ? thread__comm_str(thread) : "");
2339                 }
2340
2341                 browser->hists->thread_filter = thread__get(thread);
2342                 perf_hpp__set_elide(HISTC_THREAD, false);
2343                 pstack__push(browser->pstack, &browser->hists->thread_filter);
2344         }
2345
2346         hists__filter_by_thread(browser->hists);
2347         hist_browser__reset(browser);
2348         return 0;
2349 }
2350
2351 static int
2352 add_thread_opt(struct hist_browser *browser, struct popup_action *act,
2353                char **optstr, struct thread *thread)
2354 {
2355         int ret;
2356
2357         if ((!sort__has_thread && !sort__has_comm) || thread == NULL)
2358                 return 0;
2359
2360         if (sort__has_thread) {
2361                 ret = asprintf(optstr, "Zoom %s %s(%d) thread",
2362                                browser->hists->thread_filter ? "out of" : "into",
2363                                thread->comm_set ? thread__comm_str(thread) : "",
2364                                thread->tid);
2365         } else {
2366                 ret = asprintf(optstr, "Zoom %s %s thread",
2367                                browser->hists->thread_filter ? "out of" : "into",
2368                                thread->comm_set ? thread__comm_str(thread) : "");
2369         }
2370         if (ret < 0)
2371                 return 0;
2372
2373         act->thread = thread;
2374         act->fn = do_zoom_thread;
2375         return 1;
2376 }
2377
2378 static int
2379 do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
2380 {
2381         struct map *map = act->ms.map;
2382
2383         if (!sort__has_dso || map == NULL)
2384                 return 0;
2385
2386         if (browser->hists->dso_filter) {
2387                 pstack__remove(browser->pstack, &browser->hists->dso_filter);
2388                 perf_hpp__set_elide(HISTC_DSO, false);
2389                 browser->hists->dso_filter = NULL;
2390                 ui_helpline__pop();
2391         } else {
2392                 if (map == NULL)
2393                         return 0;
2394                 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
2395                                    __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
2396                 browser->hists->dso_filter = map->dso;
2397                 perf_hpp__set_elide(HISTC_DSO, true);
2398                 pstack__push(browser->pstack, &browser->hists->dso_filter);
2399         }
2400
2401         hists__filter_by_dso(browser->hists);
2402         hist_browser__reset(browser);
2403         return 0;
2404 }
2405
2406 static int
2407 add_dso_opt(struct hist_browser *browser, struct popup_action *act,
2408             char **optstr, struct map *map)
2409 {
2410         if (!sort__has_dso || map == NULL)
2411                 return 0;
2412
2413         if (asprintf(optstr, "Zoom %s %s DSO",
2414                      browser->hists->dso_filter ? "out of" : "into",
2415                      __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
2416                 return 0;
2417
2418         act->ms.map = map;
2419         act->fn = do_zoom_dso;
2420         return 1;
2421 }
2422
2423 static int
2424 do_browse_map(struct hist_browser *browser __maybe_unused,
2425               struct popup_action *act)
2426 {
2427         map__browse(act->ms.map);
2428         return 0;
2429 }
2430
2431 static int
2432 add_map_opt(struct hist_browser *browser __maybe_unused,
2433             struct popup_action *act, char **optstr, struct map *map)
2434 {
2435         if (!sort__has_dso || map == NULL)
2436                 return 0;
2437
2438         if (asprintf(optstr, "Browse map details") < 0)
2439                 return 0;
2440
2441         act->ms.map = map;
2442         act->fn = do_browse_map;
2443         return 1;
2444 }
2445
2446 static int
2447 do_run_script(struct hist_browser *browser __maybe_unused,
2448               struct popup_action *act)
2449 {
2450         char script_opt[64];
2451         memset(script_opt, 0, sizeof(script_opt));
2452
2453         if (act->thread) {
2454                 scnprintf(script_opt, sizeof(script_opt), " -c %s ",
2455                           thread__comm_str(act->thread));
2456         } else if (act->ms.sym) {
2457                 scnprintf(script_opt, sizeof(script_opt), " -S %s ",
2458                           act->ms.sym->name);
2459         }
2460
2461         script_browse(script_opt);
2462         return 0;
2463 }
2464
2465 static int
2466 add_script_opt(struct hist_browser *browser __maybe_unused,
2467                struct popup_action *act, char **optstr,
2468                struct thread *thread, struct symbol *sym)
2469 {
2470         if (thread) {
2471                 if (asprintf(optstr, "Run scripts for samples of thread [%s]",
2472                              thread__comm_str(thread)) < 0)
2473                         return 0;
2474         } else if (sym) {
2475                 if (asprintf(optstr, "Run scripts for samples of symbol [%s]",
2476                              sym->name) < 0)
2477                         return 0;
2478         } else {
2479                 if (asprintf(optstr, "Run scripts for all samples") < 0)
2480                         return 0;
2481         }
2482
2483         act->thread = thread;
2484         act->ms.sym = sym;
2485         act->fn = do_run_script;
2486         return 1;
2487 }
2488
2489 static int
2490 do_switch_data(struct hist_browser *browser __maybe_unused,
2491                struct popup_action *act __maybe_unused)
2492 {
2493         if (switch_data_file()) {
2494                 ui__warning("Won't switch the data files due to\n"
2495                             "no valid data file get selected!\n");
2496                 return 0;
2497         }
2498
2499         return K_SWITCH_INPUT_DATA;
2500 }
2501
2502 static int
2503 add_switch_opt(struct hist_browser *browser,
2504                struct popup_action *act, char **optstr)
2505 {
2506         if (!is_report_browser(browser->hbt))
2507                 return 0;
2508
2509         if (asprintf(optstr, "Switch to another data file in PWD") < 0)
2510                 return 0;
2511
2512         act->fn = do_switch_data;
2513         return 1;
2514 }
2515
2516 static int
2517 do_exit_browser(struct hist_browser *browser __maybe_unused,
2518                 struct popup_action *act __maybe_unused)
2519 {
2520         return 0;
2521 }
2522
2523 static int
2524 add_exit_opt(struct hist_browser *browser __maybe_unused,
2525              struct popup_action *act, char **optstr)
2526 {
2527         if (asprintf(optstr, "Exit") < 0)
2528                 return 0;
2529
2530         act->fn = do_exit_browser;
2531         return 1;
2532 }
2533
2534 static int
2535 do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
2536 {
2537         if (!sort__has_socket || act->socket < 0)
2538                 return 0;
2539
2540         if (browser->hists->socket_filter > -1) {
2541                 pstack__remove(browser->pstack, &browser->hists->socket_filter);
2542                 browser->hists->socket_filter = -1;
2543                 perf_hpp__set_elide(HISTC_SOCKET, false);
2544         } else {
2545                 browser->hists->socket_filter = act->socket;
2546                 perf_hpp__set_elide(HISTC_SOCKET, true);
2547                 pstack__push(browser->pstack, &browser->hists->socket_filter);
2548         }
2549
2550         hists__filter_by_socket(browser->hists);
2551         hist_browser__reset(browser);
2552         return 0;
2553 }
2554
2555 static int
2556 add_socket_opt(struct hist_browser *browser, struct popup_action *act,
2557                char **optstr, int socket_id)
2558 {
2559         if (!sort__has_socket || socket_id < 0)
2560                 return 0;
2561
2562         if (asprintf(optstr, "Zoom %s Processor Socket %d",
2563                      (browser->hists->socket_filter > -1) ? "out of" : "into",
2564                      socket_id) < 0)
2565                 return 0;
2566
2567         act->socket = socket_id;
2568         act->fn = do_zoom_socket;
2569         return 1;
2570 }
2571
2572 static void hist_browser__update_nr_entries(struct hist_browser *hb)
2573 {
2574         u64 nr_entries = 0;
2575         struct rb_node *nd = rb_first(&hb->hists->entries);
2576
2577         if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
2578                 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
2579                 return;
2580         }
2581
2582         while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2583                 nr_entries++;
2584                 nd = rb_hierarchy_next(nd);
2585         }
2586
2587         hb->nr_non_filtered_entries = nr_entries;
2588         hb->nr_hierarchy_entries = nr_entries;
2589 }
2590
2591 static void hist_browser__update_percent_limit(struct hist_browser *hb,
2592                                                double percent)
2593 {
2594         struct hist_entry *he;
2595         struct rb_node *nd = rb_first(&hb->hists->entries);
2596         u64 total = hists__total_period(hb->hists);
2597         u64 min_callchain_hits = total * (percent / 100);
2598
2599         hb->min_pcnt = callchain_param.min_percent = percent;
2600
2601         while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2602                 he = rb_entry(nd, struct hist_entry, rb_node);
2603
2604                 if (he->has_no_entry) {
2605                         he->has_no_entry = false;
2606                         he->nr_rows = 0;
2607                 }
2608
2609                 if (!he->leaf || !symbol_conf.use_callchain)
2610                         goto next;
2611
2612                 if (callchain_param.mode == CHAIN_GRAPH_REL) {
2613                         total = he->stat.period;
2614
2615                         if (symbol_conf.cumulate_callchain)
2616                                 total = he->stat_acc->period;
2617
2618                         min_callchain_hits = total * (percent / 100);
2619                 }
2620
2621                 callchain_param.sort(&he->sorted_chain, he->callchain,
2622                                      min_callchain_hits, &callchain_param);
2623
2624 next:
2625                 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
2626
2627                 /* force to re-evaluate folding state of callchains */
2628                 he->init_have_children = false;
2629                 hist_entry__set_folding(he, hb, false);
2630         }
2631 }
2632
2633 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
2634                                     const char *helpline,
2635                                     bool left_exits,
2636                                     struct hist_browser_timer *hbt,
2637                                     float min_pcnt,
2638                                     struct perf_env *env)
2639 {
2640         struct hists *hists = evsel__hists(evsel);
2641         struct hist_browser *browser = hist_browser__new(hists, hbt, env);
2642         struct branch_info *bi;
2643 #define MAX_OPTIONS  16
2644         char *options[MAX_OPTIONS];
2645         struct popup_action actions[MAX_OPTIONS];
2646         int nr_options = 0;
2647         int key = -1;
2648         char buf[64];
2649         int delay_secs = hbt ? hbt->refresh : 0;
2650         struct perf_hpp_fmt *fmt;
2651
2652 #define HIST_BROWSER_HELP_COMMON                                        \
2653         "h/?/F1        Show this window\n"                              \
2654         "UP/DOWN/PGUP\n"                                                \
2655         "PGDN/SPACE    Navigate\n"                                      \
2656         "q/ESC/CTRL+C  Exit browser\n\n"                                \
2657         "For multiple event sessions:\n\n"                              \
2658         "TAB/UNTAB     Switch events\n\n"                               \
2659         "For symbolic views (--sort has sym):\n\n"                      \
2660         "ENTER         Zoom into DSO/Threads & Annotate current symbol\n" \
2661         "ESC           Zoom out\n"                                      \
2662         "a             Annotate current symbol\n"                       \
2663         "C             Collapse all callchains\n"                       \
2664         "d             Zoom into current DSO\n"                         \
2665         "E             Expand all callchains\n"                         \
2666         "F             Toggle percentage of filtered entries\n"         \
2667         "H             Display column headers\n"                        \
2668         "L             Change percent limit\n"                          \
2669         "m             Display context menu\n"                          \
2670         "S             Zoom into current Processor Socket\n"            \
2671
2672         /* help messages are sorted by lexical order of the hotkey */
2673         const char report_help[] = HIST_BROWSER_HELP_COMMON
2674         "i             Show header information\n"
2675         "P             Print histograms to perf.hist.N\n"
2676         "r             Run available scripts\n"
2677         "s             Switch to another data file in PWD\n"
2678         "t             Zoom into current Thread\n"
2679         "V             Verbose (DSO names in callchains, etc)\n"
2680         "/             Filter symbol by name";
2681         const char top_help[] = HIST_BROWSER_HELP_COMMON
2682         "P             Print histograms to perf.hist.N\n"
2683         "t             Zoom into current Thread\n"
2684         "V             Verbose (DSO names in callchains, etc)\n"
2685         "z             Toggle zeroing of samples\n"
2686         "f             Enable/Disable events\n"
2687         "/             Filter symbol by name";
2688
2689         if (browser == NULL)
2690                 return -1;
2691
2692         /* reset abort key so that it can get Ctrl-C as a key */
2693         SLang_reset_tty();
2694         SLang_init_tty(0, 0, 0);
2695
2696         if (min_pcnt)
2697                 browser->min_pcnt = min_pcnt;
2698         hist_browser__update_nr_entries(browser);
2699
2700         browser->pstack = pstack__new(3);
2701         if (browser->pstack == NULL)
2702                 goto out;
2703
2704         ui_helpline__push(helpline);
2705
2706         memset(options, 0, sizeof(options));
2707         memset(actions, 0, sizeof(actions));
2708
2709         hists__for_each_format(browser->hists, fmt) {
2710                 perf_hpp__reset_width(fmt, hists);
2711                 /*
2712                  * This is done just once, and activates the horizontal scrolling
2713                  * code in the ui_browser code, it would be better to have a the
2714                  * counter in the perf_hpp code, but I couldn't find doing it here
2715                  * works, FIXME by setting this in hist_browser__new, for now, be
2716                  * clever 8-)
2717                  */
2718                 ++browser->b.columns;
2719         }
2720
2721         if (symbol_conf.col_width_list_str)
2722                 perf_hpp__set_user_width(symbol_conf.col_width_list_str);
2723
2724         while (1) {
2725                 struct thread *thread = NULL;
2726                 struct map *map = NULL;
2727                 int choice = 0;
2728                 int socked_id = -1;
2729
2730                 nr_options = 0;
2731
2732                 key = hist_browser__run(browser, helpline);
2733
2734                 if (browser->he_selection != NULL) {
2735                         thread = hist_browser__selected_thread(browser);
2736                         map = browser->selection->map;
2737                         socked_id = browser->he_selection->socket;
2738                 }
2739                 switch (key) {
2740                 case K_TAB:
2741                 case K_UNTAB:
2742                         if (nr_events == 1)
2743                                 continue;
2744                         /*
2745                          * Exit the browser, let hists__browser_tree
2746                          * go to the next or previous
2747                          */
2748                         goto out_free_stack;
2749                 case 'a':
2750                         if (!sort__has_sym) {
2751                                 ui_browser__warning(&browser->b, delay_secs * 2,
2752                         "Annotation is only available for symbolic views, "
2753                         "include \"sym*\" in --sort to use it.");
2754                                 continue;
2755                         }
2756
2757                         if (browser->selection == NULL ||
2758                             browser->selection->sym == NULL ||
2759                             browser->selection->map->dso->annotate_warned)
2760                                 continue;
2761
2762                         actions->ms.map = browser->selection->map;
2763                         actions->ms.sym = browser->selection->sym;
2764                         do_annotate(browser, actions);
2765                         continue;
2766                 case 'P':
2767                         hist_browser__dump(browser);
2768                         continue;
2769                 case 'd':
2770                         actions->ms.map = map;
2771                         do_zoom_dso(browser, actions);
2772                         continue;
2773                 case 'V':
2774                         browser->show_dso = !browser->show_dso;
2775                         continue;
2776                 case 't':
2777                         actions->thread = thread;
2778                         do_zoom_thread(browser, actions);
2779                         continue;
2780                 case 'S':
2781                         actions->socket = socked_id;
2782                         do_zoom_socket(browser, actions);
2783                         continue;
2784                 case '/':
2785                         if (ui_browser__input_window("Symbol to show",
2786                                         "Please enter the name of symbol you want to see.\n"
2787                                         "To remove the filter later, press / + ENTER.",
2788                                         buf, "ENTER: OK, ESC: Cancel",
2789                                         delay_secs * 2) == K_ENTER) {
2790                                 hists->symbol_filter_str = *buf ? buf : NULL;
2791                                 hists__filter_by_symbol(hists);
2792                                 hist_browser__reset(browser);
2793                         }
2794                         continue;
2795                 case 'r':
2796                         if (is_report_browser(hbt)) {
2797                                 actions->thread = NULL;
2798                                 actions->ms.sym = NULL;
2799                                 do_run_script(browser, actions);
2800                         }
2801                         continue;
2802                 case 's':
2803                         if (is_report_browser(hbt)) {
2804                                 key = do_switch_data(browser, actions);
2805                                 if (key == K_SWITCH_INPUT_DATA)
2806                                         goto out_free_stack;
2807                         }
2808                         continue;
2809                 case 'i':
2810                         /* env->arch is NULL for live-mode (i.e. perf top) */
2811                         if (env->arch)
2812                                 tui__header_window(env);
2813                         continue;
2814                 case 'F':
2815                         symbol_conf.filter_relative ^= 1;
2816                         continue;
2817                 case 'z':
2818                         if (!is_report_browser(hbt)) {
2819                                 struct perf_top *top = hbt->arg;
2820
2821                                 top->zero = !top->zero;
2822                         }
2823                         continue;
2824                 case 'L':
2825                         if (ui_browser__input_window("Percent Limit",
2826                                         "Please enter the value you want to hide entries under that percent.",
2827                                         buf, "ENTER: OK, ESC: Cancel",
2828                                         delay_secs * 2) == K_ENTER) {
2829                                 char *end;
2830                                 double new_percent = strtod(buf, &end);
2831
2832                                 if (new_percent < 0 || new_percent > 100) {
2833                                         ui_browser__warning(&browser->b, delay_secs * 2,
2834                                                 "Invalid percent: %.2f", new_percent);
2835                                         continue;
2836                                 }
2837
2838                                 hist_browser__update_percent_limit(browser, new_percent);
2839                                 hist_browser__reset(browser);
2840                         }
2841                         continue;
2842                 case K_F1:
2843                 case 'h':
2844                 case '?':
2845                         ui_browser__help_window(&browser->b,
2846                                 is_report_browser(hbt) ? report_help : top_help);
2847                         continue;
2848                 case K_ENTER:
2849                 case K_RIGHT:
2850                 case 'm':
2851                         /* menu */
2852                         break;
2853                 case K_ESC:
2854                 case K_LEFT: {
2855                         const void *top;
2856
2857                         if (pstack__empty(browser->pstack)) {
2858                                 /*
2859                                  * Go back to the perf_evsel_menu__run or other user
2860                                  */
2861                                 if (left_exits)
2862                                         goto out_free_stack;
2863
2864                                 if (key == K_ESC &&
2865                                     ui_browser__dialog_yesno(&browser->b,
2866                                                              "Do you really want to exit?"))
2867                                         goto out_free_stack;
2868
2869                                 continue;
2870                         }
2871                         top = pstack__peek(browser->pstack);
2872                         if (top == &browser->hists->dso_filter) {
2873                                 /*
2874                                  * No need to set actions->dso here since
2875                                  * it's just to remove the current filter.
2876                                  * Ditto for thread below.
2877                                  */
2878                                 do_zoom_dso(browser, actions);
2879                         } else if (top == &browser->hists->thread_filter) {
2880                                 do_zoom_thread(browser, actions);
2881                         } else if (top == &browser->hists->socket_filter) {
2882                                 do_zoom_socket(browser, actions);
2883                         }
2884                         continue;
2885                 }
2886                 case 'q':
2887                 case CTRL('c'):
2888                         goto out_free_stack;
2889                 case 'f':
2890                         if (!is_report_browser(hbt)) {
2891                                 struct perf_top *top = hbt->arg;
2892
2893                                 perf_evlist__toggle_enable(top->evlist);
2894                                 /*
2895                                  * No need to refresh, resort/decay histogram
2896                                  * entries if we are not collecting samples:
2897                                  */
2898                                 if (top->evlist->enabled) {
2899                                         helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
2900                                         hbt->refresh = delay_secs;
2901                                 } else {
2902                                         helpline = "Press 'f' again to re-enable the events";
2903                                         hbt->refresh = 0;
2904                                 }
2905                                 continue;
2906                         }
2907                         /* Fall thru */
2908                 default:
2909                         helpline = "Press '?' for help on key bindings";
2910                         continue;
2911                 }
2912
2913                 if (!sort__has_sym || browser->selection == NULL)
2914                         goto skip_annotation;
2915
2916                 if (sort__mode == SORT_MODE__BRANCH) {
2917                         bi = browser->he_selection->branch_info;
2918
2919                         if (bi == NULL)
2920                                 goto skip_annotation;
2921
2922                         nr_options += add_annotate_opt(browser,
2923                                                        &actions[nr_options],
2924                                                        &options[nr_options],
2925                                                        bi->from.map,
2926                                                        bi->from.sym);
2927                         if (bi->to.sym != bi->from.sym)
2928                                 nr_options += add_annotate_opt(browser,
2929                                                         &actions[nr_options],
2930                                                         &options[nr_options],
2931                                                         bi->to.map,
2932                                                         bi->to.sym);
2933                 } else {
2934                         nr_options += add_annotate_opt(browser,
2935                                                        &actions[nr_options],
2936                                                        &options[nr_options],
2937                                                        browser->selection->map,
2938                                                        browser->selection->sym);
2939                 }
2940 skip_annotation:
2941                 nr_options += add_thread_opt(browser, &actions[nr_options],
2942                                              &options[nr_options], thread);
2943                 nr_options += add_dso_opt(browser, &actions[nr_options],
2944                                           &options[nr_options], map);
2945                 nr_options += add_map_opt(browser, &actions[nr_options],
2946                                           &options[nr_options],
2947                                           browser->selection ?
2948                                                 browser->selection->map : NULL);
2949                 nr_options += add_socket_opt(browser, &actions[nr_options],
2950                                              &options[nr_options],
2951                                              socked_id);
2952                 /* perf script support */
2953                 if (!is_report_browser(hbt))
2954                         goto skip_scripting;
2955
2956                 if (browser->he_selection) {
2957                         if (sort__has_thread && thread) {
2958                                 nr_options += add_script_opt(browser,
2959                                                              &actions[nr_options],
2960                                                              &options[nr_options],
2961                                                              thread, NULL);
2962                         }
2963                         /*
2964                          * Note that browser->selection != NULL
2965                          * when browser->he_selection is not NULL,
2966                          * so we don't need to check browser->selection
2967                          * before fetching browser->selection->sym like what
2968                          * we do before fetching browser->selection->map.
2969                          *
2970                          * See hist_browser__show_entry.
2971                          */
2972                         if (sort__has_sym && browser->selection->sym) {
2973                                 nr_options += add_script_opt(browser,
2974                                                              &actions[nr_options],
2975                                                              &options[nr_options],
2976                                                              NULL, browser->selection->sym);
2977                         }
2978                 }
2979                 nr_options += add_script_opt(browser, &actions[nr_options],
2980                                              &options[nr_options], NULL, NULL);
2981                 nr_options += add_switch_opt(browser, &actions[nr_options],
2982                                              &options[nr_options]);
2983 skip_scripting:
2984                 nr_options += add_exit_opt(browser, &actions[nr_options],
2985                                            &options[nr_options]);
2986
2987                 do {
2988                         struct popup_action *act;
2989
2990                         choice = ui__popup_menu(nr_options, options);
2991                         if (choice == -1 || choice >= nr_options)
2992                                 break;
2993
2994                         act = &actions[choice];
2995                         key = act->fn(browser, act);
2996                 } while (key == 1);
2997
2998                 if (key == K_SWITCH_INPUT_DATA)
2999                         break;
3000         }
3001 out_free_stack:
3002         pstack__delete(browser->pstack);
3003 out:
3004         hist_browser__delete(browser);
3005         free_popup_options(options, MAX_OPTIONS);
3006         return key;
3007 }
3008
3009 struct perf_evsel_menu {
3010         struct ui_browser b;
3011         struct perf_evsel *selection;
3012         bool lost_events, lost_events_warned;
3013         float min_pcnt;
3014         struct perf_env *env;
3015 };
3016
3017 static void perf_evsel_menu__write(struct ui_browser *browser,
3018                                    void *entry, int row)
3019 {
3020         struct perf_evsel_menu *menu = container_of(browser,
3021                                                     struct perf_evsel_menu, b);
3022         struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
3023         struct hists *hists = evsel__hists(evsel);
3024         bool current_entry = ui_browser__is_current_entry(browser, row);
3025         unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
3026         const char *ev_name = perf_evsel__name(evsel);
3027         char bf[256], unit;
3028         const char *warn = " ";
3029         size_t printed;
3030
3031         ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
3032                                                        HE_COLORSET_NORMAL);
3033
3034         if (perf_evsel__is_group_event(evsel)) {
3035                 struct perf_evsel *pos;
3036
3037                 ev_name = perf_evsel__group_name(evsel);
3038
3039                 for_each_group_member(pos, evsel) {
3040                         struct hists *pos_hists = evsel__hists(pos);
3041                         nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
3042                 }
3043         }
3044
3045         nr_events = convert_unit(nr_events, &unit);
3046         printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
3047                            unit, unit == ' ' ? "" : " ", ev_name);
3048         ui_browser__printf(browser, "%s", bf);
3049
3050         nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
3051         if (nr_events != 0) {
3052                 menu->lost_events = true;
3053                 if (!current_entry)
3054                         ui_browser__set_color(browser, HE_COLORSET_TOP);
3055                 nr_events = convert_unit(nr_events, &unit);
3056                 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
3057                                      nr_events, unit, unit == ' ' ? "" : " ");
3058                 warn = bf;
3059         }
3060
3061         ui_browser__write_nstring(browser, warn, browser->width - printed);
3062
3063         if (current_entry)
3064                 menu->selection = evsel;
3065 }
3066
3067 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
3068                                 int nr_events, const char *help,
3069                                 struct hist_browser_timer *hbt)
3070 {
3071         struct perf_evlist *evlist = menu->b.priv;
3072         struct perf_evsel *pos;
3073         const char *title = "Available samples";
3074         int delay_secs = hbt ? hbt->refresh : 0;
3075         int key;
3076
3077         if (ui_browser__show(&menu->b, title,
3078                              "ESC: exit, ENTER|->: Browse histograms") < 0)
3079                 return -1;
3080
3081         while (1) {
3082                 key = ui_browser__run(&menu->b, delay_secs);
3083
3084                 switch (key) {
3085                 case K_TIMER:
3086                         hbt->timer(hbt->arg);
3087
3088                         if (!menu->lost_events_warned && menu->lost_events) {
3089                                 ui_browser__warn_lost_events(&menu->b);
3090                                 menu->lost_events_warned = true;
3091                         }
3092                         continue;
3093                 case K_RIGHT:
3094                 case K_ENTER:
3095                         if (!menu->selection)
3096                                 continue;
3097                         pos = menu->selection;
3098 browse_hists:
3099                         perf_evlist__set_selected(evlist, pos);
3100                         /*
3101                          * Give the calling tool a chance to populate the non
3102                          * default evsel resorted hists tree.
3103                          */
3104                         if (hbt)
3105                                 hbt->timer(hbt->arg);
3106                         key = perf_evsel__hists_browse(pos, nr_events, help,
3107                                                        true, hbt,
3108                                                        menu->min_pcnt,
3109                                                        menu->env);
3110                         ui_browser__show_title(&menu->b, title);
3111                         switch (key) {
3112                         case K_TAB:
3113                                 if (pos->node.next == &evlist->entries)
3114                                         pos = perf_evlist__first(evlist);
3115                                 else
3116                                         pos = perf_evsel__next(pos);
3117                                 goto browse_hists;
3118                         case K_UNTAB:
3119                                 if (pos->node.prev == &evlist->entries)
3120                                         pos = perf_evlist__last(evlist);
3121                                 else
3122                                         pos = perf_evsel__prev(pos);
3123                                 goto browse_hists;
3124                         case K_SWITCH_INPUT_DATA:
3125                         case 'q':
3126                         case CTRL('c'):
3127                                 goto out;
3128                         case K_ESC:
3129                         default:
3130                                 continue;
3131                         }
3132                 case K_LEFT:
3133                         continue;
3134                 case K_ESC:
3135                         if (!ui_browser__dialog_yesno(&menu->b,
3136                                                "Do you really want to exit?"))
3137                                 continue;
3138                         /* Fall thru */
3139                 case 'q':
3140                 case CTRL('c'):
3141                         goto out;
3142                 default:
3143                         continue;
3144                 }
3145         }
3146
3147 out:
3148         ui_browser__hide(&menu->b);
3149         return key;
3150 }
3151
3152 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
3153                                  void *entry)
3154 {
3155         struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
3156
3157         if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
3158                 return true;
3159
3160         return false;
3161 }
3162
3163 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
3164                                            int nr_entries, const char *help,
3165                                            struct hist_browser_timer *hbt,
3166                                            float min_pcnt,
3167                                            struct perf_env *env)
3168 {
3169         struct perf_evsel *pos;
3170         struct perf_evsel_menu menu = {
3171                 .b = {
3172                         .entries    = &evlist->entries,
3173                         .refresh    = ui_browser__list_head_refresh,
3174                         .seek       = ui_browser__list_head_seek,
3175                         .write      = perf_evsel_menu__write,
3176                         .filter     = filter_group_entries,
3177                         .nr_entries = nr_entries,
3178                         .priv       = evlist,
3179                 },
3180                 .min_pcnt = min_pcnt,
3181                 .env = env,
3182         };
3183
3184         ui_helpline__push("Press ESC to exit");
3185
3186         evlist__for_each(evlist, pos) {
3187                 const char *ev_name = perf_evsel__name(pos);
3188                 size_t line_len = strlen(ev_name) + 7;
3189
3190                 if (menu.b.width < line_len)
3191                         menu.b.width = line_len;
3192         }
3193
3194         return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
3195 }
3196
3197 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
3198                                   struct hist_browser_timer *hbt,
3199                                   float min_pcnt,
3200                                   struct perf_env *env)
3201 {
3202         int nr_entries = evlist->nr_entries;
3203
3204 single_entry:
3205         if (nr_entries == 1) {
3206                 struct perf_evsel *first = perf_evlist__first(evlist);
3207
3208                 return perf_evsel__hists_browse(first, nr_entries, help,
3209                                                 false, hbt, min_pcnt,
3210                                                 env);
3211         }
3212
3213         if (symbol_conf.event_group) {
3214                 struct perf_evsel *pos;
3215
3216                 nr_entries = 0;
3217                 evlist__for_each(evlist, pos) {
3218                         if (perf_evsel__is_group_leader(pos))
3219                                 nr_entries++;
3220                 }
3221
3222                 if (nr_entries == 1)
3223                         goto single_entry;
3224         }
3225
3226         return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
3227                                                hbt, min_pcnt, env);
3228 }