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