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