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