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