]> asedeno.scripts.mit.edu Git - linux.git/blob - tools/perf/ui/browsers/hists.c
perf report: Sort by sampled cycles percent per block for tui
[linux.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 *map, struct symbol *sym)
2409 {
2410         if (sym == NULL || map->dso->annotate_warned)
2411                 return 0;
2412
2413         if (asprintf(optstr, "Annotate %s", sym->name) < 0)
2414                 return 0;
2415
2416         act->ms.map = map;
2417         act->ms.sym = sym;
2418         act->fn = do_annotate;
2419         return 1;
2420 }
2421
2422 static int
2423 do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
2424 {
2425         struct thread *thread = act->thread;
2426
2427         if ((!hists__has(browser->hists, thread) &&
2428              !hists__has(browser->hists, comm)) || thread == NULL)
2429                 return 0;
2430
2431         if (browser->hists->thread_filter) {
2432                 pstack__remove(browser->pstack, &browser->hists->thread_filter);
2433                 perf_hpp__set_elide(HISTC_THREAD, false);
2434                 thread__zput(browser->hists->thread_filter);
2435                 ui_helpline__pop();
2436         } else {
2437                 if (hists__has(browser->hists, thread)) {
2438                         ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
2439                                            thread->comm_set ? thread__comm_str(thread) : "",
2440                                            thread->tid);
2441                 } else {
2442                         ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
2443                                            thread->comm_set ? thread__comm_str(thread) : "");
2444                 }
2445
2446                 browser->hists->thread_filter = thread__get(thread);
2447                 perf_hpp__set_elide(HISTC_THREAD, false);
2448                 pstack__push(browser->pstack, &browser->hists->thread_filter);
2449         }
2450
2451         hists__filter_by_thread(browser->hists);
2452         hist_browser__reset(browser);
2453         return 0;
2454 }
2455
2456 static int
2457 add_thread_opt(struct hist_browser *browser, struct popup_action *act,
2458                char **optstr, struct thread *thread)
2459 {
2460         int ret;
2461
2462         if ((!hists__has(browser->hists, thread) &&
2463              !hists__has(browser->hists, comm)) || thread == NULL)
2464                 return 0;
2465
2466         if (hists__has(browser->hists, thread)) {
2467                 ret = asprintf(optstr, "Zoom %s %s(%d) thread",
2468                                browser->hists->thread_filter ? "out of" : "into",
2469                                thread->comm_set ? thread__comm_str(thread) : "",
2470                                thread->tid);
2471         } else {
2472                 ret = asprintf(optstr, "Zoom %s %s thread",
2473                                browser->hists->thread_filter ? "out of" : "into",
2474                                thread->comm_set ? thread__comm_str(thread) : "");
2475         }
2476         if (ret < 0)
2477                 return 0;
2478
2479         act->thread = thread;
2480         act->fn = do_zoom_thread;
2481         return 1;
2482 }
2483
2484 static int
2485 do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
2486 {
2487         struct map *map = act->ms.map;
2488
2489         if (!hists__has(browser->hists, dso) || map == NULL)
2490                 return 0;
2491
2492         if (browser->hists->dso_filter) {
2493                 pstack__remove(browser->pstack, &browser->hists->dso_filter);
2494                 perf_hpp__set_elide(HISTC_DSO, false);
2495                 browser->hists->dso_filter = NULL;
2496                 ui_helpline__pop();
2497         } else {
2498                 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
2499                                    __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
2500                 browser->hists->dso_filter = map->dso;
2501                 perf_hpp__set_elide(HISTC_DSO, true);
2502                 pstack__push(browser->pstack, &browser->hists->dso_filter);
2503         }
2504
2505         hists__filter_by_dso(browser->hists);
2506         hist_browser__reset(browser);
2507         return 0;
2508 }
2509
2510 static int
2511 add_dso_opt(struct hist_browser *browser, struct popup_action *act,
2512             char **optstr, struct map *map)
2513 {
2514         if (!hists__has(browser->hists, dso) || map == NULL)
2515                 return 0;
2516
2517         if (asprintf(optstr, "Zoom %s %s DSO",
2518                      browser->hists->dso_filter ? "out of" : "into",
2519                      __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
2520                 return 0;
2521
2522         act->ms.map = map;
2523         act->fn = do_zoom_dso;
2524         return 1;
2525 }
2526
2527 static int
2528 do_browse_map(struct hist_browser *browser __maybe_unused,
2529               struct popup_action *act)
2530 {
2531         map__browse(act->ms.map);
2532         return 0;
2533 }
2534
2535 static int
2536 add_map_opt(struct hist_browser *browser,
2537             struct popup_action *act, char **optstr, struct map *map)
2538 {
2539         if (!hists__has(browser->hists, dso) || map == NULL)
2540                 return 0;
2541
2542         if (asprintf(optstr, "Browse map details") < 0)
2543                 return 0;
2544
2545         act->ms.map = map;
2546         act->fn = do_browse_map;
2547         return 1;
2548 }
2549
2550 static int
2551 do_run_script(struct hist_browser *browser __maybe_unused,
2552               struct popup_action *act)
2553 {
2554         char *script_opt;
2555         int len;
2556         int n = 0;
2557
2558         len = 100;
2559         if (act->thread)
2560                 len += strlen(thread__comm_str(act->thread));
2561         else if (act->ms.sym)
2562                 len += strlen(act->ms.sym->name);
2563         script_opt = malloc(len);
2564         if (!script_opt)
2565                 return -1;
2566
2567         script_opt[0] = 0;
2568         if (act->thread) {
2569                 n = scnprintf(script_opt, len, " -c %s ",
2570                           thread__comm_str(act->thread));
2571         } else if (act->ms.sym) {
2572                 n = scnprintf(script_opt, len, " -S %s ",
2573                           act->ms.sym->name);
2574         }
2575
2576         if (act->time) {
2577                 char start[32], end[32];
2578                 unsigned long starttime = act->time;
2579                 unsigned long endtime = act->time + symbol_conf.time_quantum;
2580
2581                 if (starttime == endtime) { /* Display 1ms as fallback */
2582                         starttime -= 1*NSEC_PER_MSEC;
2583                         endtime += 1*NSEC_PER_MSEC;
2584                 }
2585                 timestamp__scnprintf_usec(starttime, start, sizeof start);
2586                 timestamp__scnprintf_usec(endtime, end, sizeof end);
2587                 n += snprintf(script_opt + n, len - n, " --time %s,%s", start, end);
2588         }
2589
2590         script_browse(script_opt, act->evsel);
2591         free(script_opt);
2592         return 0;
2593 }
2594
2595 static int
2596 do_res_sample_script(struct hist_browser *browser __maybe_unused,
2597                      struct popup_action *act)
2598 {
2599         struct hist_entry *he;
2600
2601         he = hist_browser__selected_entry(browser);
2602         res_sample_browse(he->res_samples, he->num_res, act->evsel, act->rstype);
2603         return 0;
2604 }
2605
2606 static int
2607 add_script_opt_2(struct hist_browser *browser __maybe_unused,
2608                struct popup_action *act, char **optstr,
2609                struct thread *thread, struct symbol *sym,
2610                struct evsel *evsel, const char *tstr)
2611 {
2612
2613         if (thread) {
2614                 if (asprintf(optstr, "Run scripts for samples of thread [%s]%s",
2615                              thread__comm_str(thread), tstr) < 0)
2616                         return 0;
2617         } else if (sym) {
2618                 if (asprintf(optstr, "Run scripts for samples of symbol [%s]%s",
2619                              sym->name, tstr) < 0)
2620                         return 0;
2621         } else {
2622                 if (asprintf(optstr, "Run scripts for all samples%s", tstr) < 0)
2623                         return 0;
2624         }
2625
2626         act->thread = thread;
2627         act->ms.sym = sym;
2628         act->evsel = evsel;
2629         act->fn = do_run_script;
2630         return 1;
2631 }
2632
2633 static int
2634 add_script_opt(struct hist_browser *browser,
2635                struct popup_action *act, char **optstr,
2636                struct thread *thread, struct symbol *sym,
2637                struct evsel *evsel)
2638 {
2639         int n, j;
2640         struct hist_entry *he;
2641
2642         n = add_script_opt_2(browser, act, optstr, thread, sym, evsel, "");
2643
2644         he = hist_browser__selected_entry(browser);
2645         if (sort_order && strstr(sort_order, "time")) {
2646                 char tstr[128];
2647
2648                 optstr++;
2649                 act++;
2650                 j = sprintf(tstr, " in ");
2651                 j += timestamp__scnprintf_usec(he->time, tstr + j,
2652                                                sizeof tstr - j);
2653                 j += sprintf(tstr + j, "-");
2654                 timestamp__scnprintf_usec(he->time + symbol_conf.time_quantum,
2655                                           tstr + j, sizeof tstr - j);
2656                 n += add_script_opt_2(browser, act, optstr, thread, sym,
2657                                           evsel, tstr);
2658                 act->time = he->time;
2659         }
2660         return n;
2661 }
2662
2663 static int
2664 add_res_sample_opt(struct hist_browser *browser __maybe_unused,
2665                    struct popup_action *act, char **optstr,
2666                    struct res_sample *res_sample,
2667                    struct evsel *evsel,
2668                    enum rstype type)
2669 {
2670         if (!res_sample)
2671                 return 0;
2672
2673         if (asprintf(optstr, "Show context for individual samples %s",
2674                 type == A_ASM ? "with assembler" :
2675                 type == A_SOURCE ? "with source" : "") < 0)
2676                 return 0;
2677
2678         act->fn = do_res_sample_script;
2679         act->evsel = evsel;
2680         act->rstype = type;
2681         return 1;
2682 }
2683
2684 static int
2685 do_switch_data(struct hist_browser *browser __maybe_unused,
2686                struct popup_action *act __maybe_unused)
2687 {
2688         if (switch_data_file()) {
2689                 ui__warning("Won't switch the data files due to\n"
2690                             "no valid data file get selected!\n");
2691                 return 0;
2692         }
2693
2694         return K_SWITCH_INPUT_DATA;
2695 }
2696
2697 static int
2698 add_switch_opt(struct hist_browser *browser,
2699                struct popup_action *act, char **optstr)
2700 {
2701         if (!is_report_browser(browser->hbt))
2702                 return 0;
2703
2704         if (asprintf(optstr, "Switch to another data file in PWD") < 0)
2705                 return 0;
2706
2707         act->fn = do_switch_data;
2708         return 1;
2709 }
2710
2711 static int
2712 do_exit_browser(struct hist_browser *browser __maybe_unused,
2713                 struct popup_action *act __maybe_unused)
2714 {
2715         return 0;
2716 }
2717
2718 static int
2719 add_exit_opt(struct hist_browser *browser __maybe_unused,
2720              struct popup_action *act, char **optstr)
2721 {
2722         if (asprintf(optstr, "Exit") < 0)
2723                 return 0;
2724
2725         act->fn = do_exit_browser;
2726         return 1;
2727 }
2728
2729 static int
2730 do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
2731 {
2732         if (!hists__has(browser->hists, socket) || act->socket < 0)
2733                 return 0;
2734
2735         if (browser->hists->socket_filter > -1) {
2736                 pstack__remove(browser->pstack, &browser->hists->socket_filter);
2737                 browser->hists->socket_filter = -1;
2738                 perf_hpp__set_elide(HISTC_SOCKET, false);
2739         } else {
2740                 browser->hists->socket_filter = act->socket;
2741                 perf_hpp__set_elide(HISTC_SOCKET, true);
2742                 pstack__push(browser->pstack, &browser->hists->socket_filter);
2743         }
2744
2745         hists__filter_by_socket(browser->hists);
2746         hist_browser__reset(browser);
2747         return 0;
2748 }
2749
2750 static int
2751 add_socket_opt(struct hist_browser *browser, struct popup_action *act,
2752                char **optstr, int socket_id)
2753 {
2754         if (!hists__has(browser->hists, socket) || socket_id < 0)
2755                 return 0;
2756
2757         if (asprintf(optstr, "Zoom %s Processor Socket %d",
2758                      (browser->hists->socket_filter > -1) ? "out of" : "into",
2759                      socket_id) < 0)
2760                 return 0;
2761
2762         act->socket = socket_id;
2763         act->fn = do_zoom_socket;
2764         return 1;
2765 }
2766
2767 static void hist_browser__update_nr_entries(struct hist_browser *hb)
2768 {
2769         u64 nr_entries = 0;
2770         struct rb_node *nd = rb_first_cached(&hb->hists->entries);
2771
2772         if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
2773                 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
2774                 return;
2775         }
2776
2777         while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2778                 nr_entries++;
2779                 nd = rb_hierarchy_next(nd);
2780         }
2781
2782         hb->nr_non_filtered_entries = nr_entries;
2783         hb->nr_hierarchy_entries = nr_entries;
2784 }
2785
2786 static void hist_browser__update_percent_limit(struct hist_browser *hb,
2787                                                double percent)
2788 {
2789         struct hist_entry *he;
2790         struct rb_node *nd = rb_first_cached(&hb->hists->entries);
2791         u64 total = hists__total_period(hb->hists);
2792         u64 min_callchain_hits = total * (percent / 100);
2793
2794         hb->min_pcnt = callchain_param.min_percent = percent;
2795
2796         while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2797                 he = rb_entry(nd, struct hist_entry, rb_node);
2798
2799                 if (he->has_no_entry) {
2800                         he->has_no_entry = false;
2801                         he->nr_rows = 0;
2802                 }
2803
2804                 if (!he->leaf || !hist_entry__has_callchains(he) || !symbol_conf.use_callchain)
2805                         goto next;
2806
2807                 if (callchain_param.mode == CHAIN_GRAPH_REL) {
2808                         total = he->stat.period;
2809
2810                         if (symbol_conf.cumulate_callchain)
2811                                 total = he->stat_acc->period;
2812
2813                         min_callchain_hits = total * (percent / 100);
2814                 }
2815
2816                 callchain_param.sort(&he->sorted_chain, he->callchain,
2817                                      min_callchain_hits, &callchain_param);
2818
2819 next:
2820                 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
2821
2822                 /* force to re-evaluate folding state of callchains */
2823                 he->init_have_children = false;
2824                 hist_entry__set_folding(he, hb, false);
2825         }
2826 }
2827
2828 static int perf_evsel__hists_browse(struct evsel *evsel, int nr_events,
2829                                     const char *helpline,
2830                                     bool left_exits,
2831                                     struct hist_browser_timer *hbt,
2832                                     float min_pcnt,
2833                                     struct perf_env *env,
2834                                     bool warn_lost_event,
2835                                     struct annotation_options *annotation_opts)
2836 {
2837         struct hists *hists = evsel__hists(evsel);
2838         struct hist_browser *browser = perf_evsel_browser__new(evsel, hbt, env, annotation_opts);
2839         struct branch_info *bi = NULL;
2840 #define MAX_OPTIONS  16
2841         char *options[MAX_OPTIONS];
2842         struct popup_action actions[MAX_OPTIONS];
2843         int nr_options = 0;
2844         int key = -1;
2845         char buf[64];
2846         int delay_secs = hbt ? hbt->refresh : 0;
2847
2848 #define HIST_BROWSER_HELP_COMMON                                        \
2849         "h/?/F1        Show this window\n"                              \
2850         "UP/DOWN/PGUP\n"                                                \
2851         "PGDN/SPACE    Navigate\n"                                      \
2852         "q/ESC/CTRL+C  Exit browser or go back to previous screen\n\n"  \
2853         "For multiple event sessions:\n\n"                              \
2854         "TAB/UNTAB     Switch events\n\n"                               \
2855         "For symbolic views (--sort has sym):\n\n"                      \
2856         "ENTER         Zoom into DSO/Threads & Annotate current symbol\n" \
2857         "ESC           Zoom out\n"                                      \
2858         "a             Annotate current symbol\n"                       \
2859         "C             Collapse all callchains\n"                       \
2860         "d             Zoom into current DSO\n"                         \
2861         "E             Expand all callchains\n"                         \
2862         "F             Toggle percentage of filtered entries\n"         \
2863         "H             Display column headers\n"                        \
2864         "L             Change percent limit\n"                          \
2865         "m             Display context menu\n"                          \
2866         "S             Zoom into current Processor Socket\n"            \
2867
2868         /* help messages are sorted by lexical order of the hotkey */
2869         static const char report_help[] = HIST_BROWSER_HELP_COMMON
2870         "i             Show header information\n"
2871         "P             Print histograms to perf.hist.N\n"
2872         "r             Run available scripts\n"
2873         "s             Switch to another data file in PWD\n"
2874         "t             Zoom into current Thread\n"
2875         "V             Verbose (DSO names in callchains, etc)\n"
2876         "/             Filter symbol by name";
2877         static const char top_help[] = HIST_BROWSER_HELP_COMMON
2878         "P             Print histograms to perf.hist.N\n"
2879         "t             Zoom into current Thread\n"
2880         "V             Verbose (DSO names in callchains, etc)\n"
2881         "z             Toggle zeroing of samples\n"
2882         "f             Enable/Disable events\n"
2883         "/             Filter symbol by name";
2884
2885         if (browser == NULL)
2886                 return -1;
2887
2888         /* reset abort key so that it can get Ctrl-C as a key */
2889         SLang_reset_tty();
2890         SLang_init_tty(0, 0, 0);
2891
2892         if (min_pcnt)
2893                 browser->min_pcnt = min_pcnt;
2894         hist_browser__update_nr_entries(browser);
2895
2896         browser->pstack = pstack__new(3);
2897         if (browser->pstack == NULL)
2898                 goto out;
2899
2900         ui_helpline__push(helpline);
2901
2902         memset(options, 0, sizeof(options));
2903         memset(actions, 0, sizeof(actions));
2904
2905         if (symbol_conf.col_width_list_str)
2906                 perf_hpp__set_user_width(symbol_conf.col_width_list_str);
2907
2908         if (!is_report_browser(hbt))
2909                 browser->b.no_samples_msg = "Collecting samples...";
2910
2911         while (1) {
2912                 struct thread *thread = NULL;
2913                 struct map *map = NULL;
2914                 int choice = 0;
2915                 int socked_id = -1;
2916
2917                 nr_options = 0;
2918
2919                 key = hist_browser__run(browser, helpline,
2920                                         warn_lost_event);
2921
2922                 if (browser->he_selection != NULL) {
2923                         thread = hist_browser__selected_thread(browser);
2924                         map = browser->selection->map;
2925                         socked_id = browser->he_selection->socket;
2926                 }
2927                 switch (key) {
2928                 case K_TAB:
2929                 case K_UNTAB:
2930                         if (nr_events == 1)
2931                                 continue;
2932                         /*
2933                          * Exit the browser, let hists__browser_tree
2934                          * go to the next or previous
2935                          */
2936                         goto out_free_stack;
2937                 case 'a':
2938                         if (!hists__has(hists, sym)) {
2939                                 ui_browser__warning(&browser->b, delay_secs * 2,
2940                         "Annotation is only available for symbolic views, "
2941                         "include \"sym*\" in --sort to use it.");
2942                                 continue;
2943                         }
2944
2945                         if (browser->selection == NULL ||
2946                             browser->selection->sym == NULL ||
2947                             browser->selection->map->dso->annotate_warned)
2948                                 continue;
2949
2950                         actions->ms.map = browser->selection->map;
2951                         actions->ms.sym = browser->selection->sym;
2952                         do_annotate(browser, actions);
2953                         continue;
2954                 case 'P':
2955                         hist_browser__dump(browser);
2956                         continue;
2957                 case 'd':
2958                         actions->ms.map = map;
2959                         do_zoom_dso(browser, actions);
2960                         continue;
2961                 case 'V':
2962                         verbose = (verbose + 1) % 4;
2963                         browser->show_dso = verbose > 0;
2964                         ui_helpline__fpush("Verbosity level set to %d\n",
2965                                            verbose);
2966                         continue;
2967                 case 't':
2968                         actions->thread = thread;
2969                         do_zoom_thread(browser, actions);
2970                         continue;
2971                 case 'S':
2972                         actions->socket = socked_id;
2973                         do_zoom_socket(browser, actions);
2974                         continue;
2975                 case '/':
2976                         if (ui_browser__input_window("Symbol to show",
2977                                         "Please enter the name of symbol you want to see.\n"
2978                                         "To remove the filter later, press / + ENTER.",
2979                                         buf, "ENTER: OK, ESC: Cancel",
2980                                         delay_secs * 2) == K_ENTER) {
2981                                 hists->symbol_filter_str = *buf ? buf : NULL;
2982                                 hists__filter_by_symbol(hists);
2983                                 hist_browser__reset(browser);
2984                         }
2985                         continue;
2986                 case 'r':
2987                         if (is_report_browser(hbt)) {
2988                                 actions->thread = NULL;
2989                                 actions->ms.sym = NULL;
2990                                 do_run_script(browser, actions);
2991                         }
2992                         continue;
2993                 case 's':
2994                         if (is_report_browser(hbt)) {
2995                                 key = do_switch_data(browser, actions);
2996                                 if (key == K_SWITCH_INPUT_DATA)
2997                                         goto out_free_stack;
2998                         }
2999                         continue;
3000                 case 'i':
3001                         /* env->arch is NULL for live-mode (i.e. perf top) */
3002                         if (env->arch)
3003                                 tui__header_window(env);
3004                         continue;
3005                 case 'F':
3006                         symbol_conf.filter_relative ^= 1;
3007                         continue;
3008                 case 'z':
3009                         if (!is_report_browser(hbt)) {
3010                                 struct perf_top *top = hbt->arg;
3011
3012                                 top->zero = !top->zero;
3013                         }
3014                         continue;
3015                 case 'L':
3016                         if (ui_browser__input_window("Percent Limit",
3017                                         "Please enter the value you want to hide entries under that percent.",
3018                                         buf, "ENTER: OK, ESC: Cancel",
3019                                         delay_secs * 2) == K_ENTER) {
3020                                 char *end;
3021                                 double new_percent = strtod(buf, &end);
3022
3023                                 if (new_percent < 0 || new_percent > 100) {
3024                                         ui_browser__warning(&browser->b, delay_secs * 2,
3025                                                 "Invalid percent: %.2f", new_percent);
3026                                         continue;
3027                                 }
3028
3029                                 hist_browser__update_percent_limit(browser, new_percent);
3030                                 hist_browser__reset(browser);
3031                         }
3032                         continue;
3033                 case K_F1:
3034                 case 'h':
3035                 case '?':
3036                         ui_browser__help_window(&browser->b,
3037                                 is_report_browser(hbt) ? report_help : top_help);
3038                         continue;
3039                 case K_ENTER:
3040                 case K_RIGHT:
3041                 case 'm':
3042                         /* menu */
3043                         break;
3044                 case K_ESC:
3045                 case K_LEFT: {
3046                         const void *top;
3047
3048                         if (pstack__empty(browser->pstack)) {
3049                                 /*
3050                                  * Go back to the perf_evsel_menu__run or other user
3051                                  */
3052                                 if (left_exits)
3053                                         goto out_free_stack;
3054
3055                                 if (key == K_ESC &&
3056                                     ui_browser__dialog_yesno(&browser->b,
3057                                                              "Do you really want to exit?"))
3058                                         goto out_free_stack;
3059
3060                                 continue;
3061                         }
3062                         top = pstack__peek(browser->pstack);
3063                         if (top == &browser->hists->dso_filter) {
3064                                 /*
3065                                  * No need to set actions->dso here since
3066                                  * it's just to remove the current filter.
3067                                  * Ditto for thread below.
3068                                  */
3069                                 do_zoom_dso(browser, actions);
3070                         } else if (top == &browser->hists->thread_filter) {
3071                                 do_zoom_thread(browser, actions);
3072                         } else if (top == &browser->hists->socket_filter) {
3073                                 do_zoom_socket(browser, actions);
3074                         }
3075                         continue;
3076                 }
3077                 case 'q':
3078                 case CTRL('c'):
3079                         goto out_free_stack;
3080                 case 'f':
3081                         if (!is_report_browser(hbt)) {
3082                                 struct perf_top *top = hbt->arg;
3083
3084                                 perf_evlist__toggle_enable(top->evlist);
3085                                 /*
3086                                  * No need to refresh, resort/decay histogram
3087                                  * entries if we are not collecting samples:
3088                                  */
3089                                 if (top->evlist->enabled) {
3090                                         helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
3091                                         hbt->refresh = delay_secs;
3092                                 } else {
3093                                         helpline = "Press 'f' again to re-enable the events";
3094                                         hbt->refresh = 0;
3095                                 }
3096                                 continue;
3097                         }
3098                         /* Fall thru */
3099                 default:
3100                         helpline = "Press '?' for help on key bindings";
3101                         continue;
3102                 }
3103
3104                 if (!hists__has(hists, sym) || browser->selection == NULL)
3105                         goto skip_annotation;
3106
3107                 if (sort__mode == SORT_MODE__BRANCH) {
3108
3109                         if (browser->he_selection)
3110                                 bi = browser->he_selection->branch_info;
3111
3112                         if (bi == NULL)
3113                                 goto skip_annotation;
3114
3115                         nr_options += add_annotate_opt(browser,
3116                                                        &actions[nr_options],
3117                                                        &options[nr_options],
3118                                                        bi->from.map,
3119                                                        bi->from.sym);
3120                         if (bi->to.sym != bi->from.sym)
3121                                 nr_options += add_annotate_opt(browser,
3122                                                         &actions[nr_options],
3123                                                         &options[nr_options],
3124                                                         bi->to.map,
3125                                                         bi->to.sym);
3126                 } else {
3127                         nr_options += add_annotate_opt(browser,
3128                                                        &actions[nr_options],
3129                                                        &options[nr_options],
3130                                                        browser->selection->map,
3131                                                        browser->selection->sym);
3132                 }
3133 skip_annotation:
3134                 nr_options += add_thread_opt(browser, &actions[nr_options],
3135                                              &options[nr_options], thread);
3136                 nr_options += add_dso_opt(browser, &actions[nr_options],
3137                                           &options[nr_options], map);
3138                 nr_options += add_map_opt(browser, &actions[nr_options],
3139                                           &options[nr_options],
3140                                           browser->selection ?
3141                                                 browser->selection->map : NULL);
3142                 nr_options += add_socket_opt(browser, &actions[nr_options],
3143                                              &options[nr_options],
3144                                              socked_id);
3145                 /* perf script support */
3146                 if (!is_report_browser(hbt))
3147                         goto skip_scripting;
3148
3149                 if (browser->he_selection) {
3150                         if (hists__has(hists, thread) && thread) {
3151                                 nr_options += add_script_opt(browser,
3152                                                              &actions[nr_options],
3153                                                              &options[nr_options],
3154                                                              thread, NULL, evsel);
3155                         }
3156                         /*
3157                          * Note that browser->selection != NULL
3158                          * when browser->he_selection is not NULL,
3159                          * so we don't need to check browser->selection
3160                          * before fetching browser->selection->sym like what
3161                          * we do before fetching browser->selection->map.
3162                          *
3163                          * See hist_browser__show_entry.
3164                          */
3165                         if (hists__has(hists, sym) && browser->selection->sym) {
3166                                 nr_options += add_script_opt(browser,
3167                                                              &actions[nr_options],
3168                                                              &options[nr_options],
3169                                                              NULL, browser->selection->sym,
3170                                                              evsel);
3171                         }
3172                 }
3173                 nr_options += add_script_opt(browser, &actions[nr_options],
3174                                              &options[nr_options], NULL, NULL, evsel);
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_NORMAL);
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_ASM);
3183                 nr_options += add_res_sample_opt(browser, &actions[nr_options],
3184                                                  &options[nr_options],
3185                                  hist_browser__selected_entry(browser)->res_samples,
3186                                  evsel, A_SOURCE);
3187                 nr_options += add_switch_opt(browser, &actions[nr_options],
3188                                              &options[nr_options]);
3189 skip_scripting:
3190                 nr_options += add_exit_opt(browser, &actions[nr_options],
3191                                            &options[nr_options]);
3192
3193                 do {
3194                         struct popup_action *act;
3195
3196                         choice = ui__popup_menu(nr_options, options);
3197                         if (choice == -1 || choice >= nr_options)
3198                                 break;
3199
3200                         act = &actions[choice];
3201                         key = act->fn(browser, act);
3202                 } while (key == 1);
3203
3204                 if (key == K_SWITCH_INPUT_DATA)
3205                         break;
3206         }
3207 out_free_stack:
3208         pstack__delete(browser->pstack);
3209 out:
3210         hist_browser__delete(browser);
3211         free_popup_options(options, MAX_OPTIONS);
3212         return key;
3213 }
3214
3215 struct evsel_menu {
3216         struct ui_browser b;
3217         struct evsel *selection;
3218         struct annotation_options *annotation_opts;
3219         bool lost_events, lost_events_warned;
3220         float min_pcnt;
3221         struct perf_env *env;
3222 };
3223
3224 static void perf_evsel_menu__write(struct ui_browser *browser,
3225                                    void *entry, int row)
3226 {
3227         struct evsel_menu *menu = container_of(browser,
3228                                                     struct evsel_menu, b);
3229         struct evsel *evsel = list_entry(entry, struct evsel, core.node);
3230         struct hists *hists = evsel__hists(evsel);
3231         bool current_entry = ui_browser__is_current_entry(browser, row);
3232         unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
3233         const char *ev_name = perf_evsel__name(evsel);
3234         char bf[256], unit;
3235         const char *warn = " ";
3236         size_t printed;
3237
3238         ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
3239                                                        HE_COLORSET_NORMAL);
3240
3241         if (perf_evsel__is_group_event(evsel)) {
3242                 struct evsel *pos;
3243
3244                 ev_name = perf_evsel__group_name(evsel);
3245
3246                 for_each_group_member(pos, evsel) {
3247                         struct hists *pos_hists = evsel__hists(pos);
3248                         nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
3249                 }
3250         }
3251
3252         nr_events = convert_unit(nr_events, &unit);
3253         printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
3254                            unit, unit == ' ' ? "" : " ", ev_name);
3255         ui_browser__printf(browser, "%s", bf);
3256
3257         nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
3258         if (nr_events != 0) {
3259                 menu->lost_events = true;
3260                 if (!current_entry)
3261                         ui_browser__set_color(browser, HE_COLORSET_TOP);
3262                 nr_events = convert_unit(nr_events, &unit);
3263                 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
3264                                      nr_events, unit, unit == ' ' ? "" : " ");
3265                 warn = bf;
3266         }
3267
3268         ui_browser__write_nstring(browser, warn, browser->width - printed);
3269
3270         if (current_entry)
3271                 menu->selection = evsel;
3272 }
3273
3274 static int perf_evsel_menu__run(struct evsel_menu *menu,
3275                                 int nr_events, const char *help,
3276                                 struct hist_browser_timer *hbt,
3277                                 bool warn_lost_event)
3278 {
3279         struct evlist *evlist = menu->b.priv;
3280         struct evsel *pos;
3281         const char *title = "Available samples";
3282         int delay_secs = hbt ? hbt->refresh : 0;
3283         int key;
3284
3285         if (ui_browser__show(&menu->b, title,
3286                              "ESC: exit, ENTER|->: Browse histograms") < 0)
3287                 return -1;
3288
3289         while (1) {
3290                 key = ui_browser__run(&menu->b, delay_secs);
3291
3292                 switch (key) {
3293                 case K_TIMER:
3294                         if (hbt)
3295                                 hbt->timer(hbt->arg);
3296
3297                         if (!menu->lost_events_warned &&
3298                             menu->lost_events &&
3299                             warn_lost_event) {
3300                                 ui_browser__warn_lost_events(&menu->b);
3301                                 menu->lost_events_warned = true;
3302                         }
3303                         continue;
3304                 case K_RIGHT:
3305                 case K_ENTER:
3306                         if (!menu->selection)
3307                                 continue;
3308                         pos = menu->selection;
3309 browse_hists:
3310                         perf_evlist__set_selected(evlist, pos);
3311                         /*
3312                          * Give the calling tool a chance to populate the non
3313                          * default evsel resorted hists tree.
3314                          */
3315                         if (hbt)
3316                                 hbt->timer(hbt->arg);
3317                         key = perf_evsel__hists_browse(pos, nr_events, help,
3318                                                        true, hbt,
3319                                                        menu->min_pcnt,
3320                                                        menu->env,
3321                                                        warn_lost_event,
3322                                                        menu->annotation_opts);
3323                         ui_browser__show_title(&menu->b, title);
3324                         switch (key) {
3325                         case K_TAB:
3326                                 if (pos->core.node.next == &evlist->core.entries)
3327                                         pos = evlist__first(evlist);
3328                                 else
3329                                         pos = perf_evsel__next(pos);
3330                                 goto browse_hists;
3331                         case K_UNTAB:
3332                                 if (pos->core.node.prev == &evlist->core.entries)
3333                                         pos = evlist__last(evlist);
3334                                 else
3335                                         pos = perf_evsel__prev(pos);
3336                                 goto browse_hists;
3337                         case K_SWITCH_INPUT_DATA:
3338                         case 'q':
3339                         case CTRL('c'):
3340                                 goto out;
3341                         case K_ESC:
3342                         default:
3343                                 continue;
3344                         }
3345                 case K_LEFT:
3346                         continue;
3347                 case K_ESC:
3348                         if (!ui_browser__dialog_yesno(&menu->b,
3349                                                "Do you really want to exit?"))
3350                                 continue;
3351                         /* Fall thru */
3352                 case 'q':
3353                 case CTRL('c'):
3354                         goto out;
3355                 default:
3356                         continue;
3357                 }
3358         }
3359
3360 out:
3361         ui_browser__hide(&menu->b);
3362         return key;
3363 }
3364
3365 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
3366                                  void *entry)
3367 {
3368         struct evsel *evsel = list_entry(entry, struct evsel, core.node);
3369
3370         if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
3371                 return true;
3372
3373         return false;
3374 }
3375
3376 static int __perf_evlist__tui_browse_hists(struct evlist *evlist,
3377                                            int nr_entries, const char *help,
3378                                            struct hist_browser_timer *hbt,
3379                                            float min_pcnt,
3380                                            struct perf_env *env,
3381                                            bool warn_lost_event,
3382                                            struct annotation_options *annotation_opts)
3383 {
3384         struct evsel *pos;
3385         struct evsel_menu menu = {
3386                 .b = {
3387                         .entries    = &evlist->core.entries,
3388                         .refresh    = ui_browser__list_head_refresh,
3389                         .seek       = ui_browser__list_head_seek,
3390                         .write      = perf_evsel_menu__write,
3391                         .filter     = filter_group_entries,
3392                         .nr_entries = nr_entries,
3393                         .priv       = evlist,
3394                 },
3395                 .min_pcnt = min_pcnt,
3396                 .env = env,
3397                 .annotation_opts = annotation_opts,
3398         };
3399
3400         ui_helpline__push("Press ESC to exit");
3401
3402         evlist__for_each_entry(evlist, pos) {
3403                 const char *ev_name = perf_evsel__name(pos);
3404                 size_t line_len = strlen(ev_name) + 7;
3405
3406                 if (menu.b.width < line_len)
3407                         menu.b.width = line_len;
3408         }
3409
3410         return perf_evsel_menu__run(&menu, nr_entries, help,
3411                                     hbt, warn_lost_event);
3412 }
3413
3414 int perf_evlist__tui_browse_hists(struct evlist *evlist, const char *help,
3415                                   struct hist_browser_timer *hbt,
3416                                   float min_pcnt,
3417                                   struct perf_env *env,
3418                                   bool warn_lost_event,
3419                                   struct annotation_options *annotation_opts)
3420 {
3421         int nr_entries = evlist->core.nr_entries;
3422
3423 single_entry:
3424         if (nr_entries == 1) {
3425                 struct evsel *first = evlist__first(evlist);
3426
3427                 return perf_evsel__hists_browse(first, nr_entries, help,
3428                                                 false, hbt, min_pcnt,
3429                                                 env, warn_lost_event,
3430                                                 annotation_opts);
3431         }
3432
3433         if (symbol_conf.event_group) {
3434                 struct evsel *pos;
3435
3436                 nr_entries = 0;
3437                 evlist__for_each_entry(evlist, pos) {
3438                         if (perf_evsel__is_group_leader(pos))
3439                                 nr_entries++;
3440                 }
3441
3442                 if (nr_entries == 1)
3443                         goto single_entry;
3444         }
3445
3446         return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
3447                                                hbt, min_pcnt, env,
3448                                                warn_lost_event,
3449                                                annotation_opts);
3450 }