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