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