perf hists browser: Move hist_browser into header file
[cascardo/linux.git] / tools / perf / ui / browsers / hists.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <linux/rbtree.h>
5
6 #include "../../util/evsel.h"
7 #include "../../util/evlist.h"
8 #include "../../util/hist.h"
9 #include "../../util/pstack.h"
10 #include "../../util/sort.h"
11 #include "../../util/util.h"
12 #include "../../util/top.h"
13 #include "../../arch/common.h"
14
15 #include "../browsers/hists.h"
16 #include "../helpline.h"
17 #include "../util.h"
18 #include "../ui.h"
19 #include "map.h"
20 #include "annotate.h"
21
22 extern void hist_browser__init_hpp(void);
23
24 static int hists__browser_title(struct hists *hists,
25                                 struct hist_browser_timer *hbt,
26                                 char *bf, size_t size);
27 static void hist_browser__update_nr_entries(struct hist_browser *hb);
28
29 static struct rb_node *hists__filter_entries(struct rb_node *nd,
30                                              float min_pcnt);
31
32 static bool hist_browser__has_filter(struct hist_browser *hb)
33 {
34         return hists__has_filter(hb->hists) || hb->min_pcnt || symbol_conf.has_filter;
35 }
36
37 static int hist_browser__get_folding(struct hist_browser *browser)
38 {
39         struct rb_node *nd;
40         struct hists *hists = browser->hists;
41         int unfolded_rows = 0;
42
43         for (nd = rb_first(&hists->entries);
44              (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
45              nd = rb_hierarchy_next(nd)) {
46                 struct hist_entry *he =
47                         rb_entry(nd, struct hist_entry, rb_node);
48
49                 if (he->leaf && he->unfolded)
50                         unfolded_rows += he->nr_rows;
51         }
52         return unfolded_rows;
53 }
54
55 static u32 hist_browser__nr_entries(struct hist_browser *hb)
56 {
57         u32 nr_entries;
58
59         if (symbol_conf.report_hierarchy)
60                 nr_entries = hb->nr_hierarchy_entries;
61         else if (hist_browser__has_filter(hb))
62                 nr_entries = hb->nr_non_filtered_entries;
63         else
64                 nr_entries = hb->hists->nr_entries;
65
66         hb->nr_callchain_rows = hist_browser__get_folding(hb);
67         return nr_entries + hb->nr_callchain_rows;
68 }
69
70 static void hist_browser__update_rows(struct hist_browser *hb)
71 {
72         struct ui_browser *browser = &hb->b;
73         u16 header_offset = hb->show_headers ? 1 : 0, index_row;
74
75         browser->rows = browser->height - header_offset;
76         /*
77          * Verify if we were at the last line and that line isn't
78          * visibe because we now show the header line(s).
79          */
80         index_row = browser->index - browser->top_idx;
81         if (index_row >= browser->rows)
82                 browser->index -= index_row - browser->rows + 1;
83 }
84
85 static void hist_browser__refresh_dimensions(struct ui_browser *browser)
86 {
87         struct hist_browser *hb = container_of(browser, struct hist_browser, b);
88
89         /* 3 == +/- toggle symbol before actual hist_entry rendering */
90         browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]"));
91         /*
92          * FIXME: Just keeping existing behaviour, but this really should be
93          *        before updating browser->width, as it will invalidate the
94          *        calculation above. Fix this and the fallout in another
95          *        changeset.
96          */
97         ui_browser__refresh_dimensions(browser);
98         hist_browser__update_rows(hb);
99 }
100
101 static void hist_browser__gotorc(struct hist_browser *browser, int row, int column)
102 {
103         u16 header_offset = browser->show_headers ? 1 : 0;
104
105         ui_browser__gotorc(&browser->b, row + header_offset, column);
106 }
107
108 static void hist_browser__reset(struct hist_browser *browser)
109 {
110         /*
111          * The hists__remove_entry_filter() already folds non-filtered
112          * entries so we can assume it has 0 callchain rows.
113          */
114         browser->nr_callchain_rows = 0;
115
116         hist_browser__update_nr_entries(browser);
117         browser->b.nr_entries = hist_browser__nr_entries(browser);
118         hist_browser__refresh_dimensions(&browser->b);
119         ui_browser__reset_index(&browser->b);
120 }
121
122 static char tree__folded_sign(bool unfolded)
123 {
124         return unfolded ? '-' : '+';
125 }
126
127 static char hist_entry__folded(const struct hist_entry *he)
128 {
129         return he->has_children ? tree__folded_sign(he->unfolded) : ' ';
130 }
131
132 static char callchain_list__folded(const struct callchain_list *cl)
133 {
134         return cl->has_children ? tree__folded_sign(cl->unfolded) : ' ';
135 }
136
137 static void callchain_list__set_folding(struct callchain_list *cl, bool unfold)
138 {
139         cl->unfolded = unfold ? cl->has_children : false;
140 }
141
142 static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
143 {
144         int n = 0;
145         struct rb_node *nd;
146
147         for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
148                 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
149                 struct callchain_list *chain;
150                 char folded_sign = ' '; /* No children */
151
152                 list_for_each_entry(chain, &child->val, list) {
153                         ++n;
154                         /* We need this because we may not have children */
155                         folded_sign = callchain_list__folded(chain);
156                         if (folded_sign == '+')
157                                 break;
158                 }
159
160                 if (folded_sign == '-') /* Have children and they're unfolded */
161                         n += callchain_node__count_rows_rb_tree(child);
162         }
163
164         return n;
165 }
166
167 static int callchain_node__count_flat_rows(struct callchain_node *node)
168 {
169         struct callchain_list *chain;
170         char folded_sign = 0;
171         int n = 0;
172
173         list_for_each_entry(chain, &node->parent_val, list) {
174                 if (!folded_sign) {
175                         /* only check first chain list entry */
176                         folded_sign = callchain_list__folded(chain);
177                         if (folded_sign == '+')
178                                 return 1;
179                 }
180                 n++;
181         }
182
183         list_for_each_entry(chain, &node->val, list) {
184                 if (!folded_sign) {
185                         /* node->parent_val list might be empty */
186                         folded_sign = callchain_list__folded(chain);
187                         if (folded_sign == '+')
188                                 return 1;
189                 }
190                 n++;
191         }
192
193         return n;
194 }
195
196 static int callchain_node__count_folded_rows(struct callchain_node *node __maybe_unused)
197 {
198         return 1;
199 }
200
201 static int callchain_node__count_rows(struct callchain_node *node)
202 {
203         struct callchain_list *chain;
204         bool unfolded = false;
205         int n = 0;
206
207         if (callchain_param.mode == CHAIN_FLAT)
208                 return callchain_node__count_flat_rows(node);
209         else if (callchain_param.mode == CHAIN_FOLDED)
210                 return callchain_node__count_folded_rows(node);
211
212         list_for_each_entry(chain, &node->val, list) {
213                 ++n;
214                 unfolded = chain->unfolded;
215         }
216
217         if (unfolded)
218                 n += callchain_node__count_rows_rb_tree(node);
219
220         return n;
221 }
222
223 static int callchain__count_rows(struct rb_root *chain)
224 {
225         struct rb_node *nd;
226         int n = 0;
227
228         for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
229                 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
230                 n += callchain_node__count_rows(node);
231         }
232
233         return n;
234 }
235
236 static int hierarchy_count_rows(struct hist_browser *hb, struct hist_entry *he,
237                                 bool include_children)
238 {
239         int count = 0;
240         struct rb_node *node;
241         struct hist_entry *child;
242
243         if (he->leaf)
244                 return callchain__count_rows(&he->sorted_chain);
245
246         if (he->has_no_entry)
247                 return 1;
248
249         node = rb_first(&he->hroot_out);
250         while (node) {
251                 float percent;
252
253                 child = rb_entry(node, struct hist_entry, rb_node);
254                 percent = hist_entry__get_percent_limit(child);
255
256                 if (!child->filtered && percent >= hb->min_pcnt) {
257                         count++;
258
259                         if (include_children && child->unfolded)
260                                 count += hierarchy_count_rows(hb, child, true);
261                 }
262
263                 node = rb_next(node);
264         }
265         return count;
266 }
267
268 static bool hist_entry__toggle_fold(struct hist_entry *he)
269 {
270         if (!he)
271                 return false;
272
273         if (!he->has_children)
274                 return false;
275
276         he->unfolded = !he->unfolded;
277         return true;
278 }
279
280 static bool callchain_list__toggle_fold(struct callchain_list *cl)
281 {
282         if (!cl)
283                 return false;
284
285         if (!cl->has_children)
286                 return false;
287
288         cl->unfolded = !cl->unfolded;
289         return true;
290 }
291
292 static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
293 {
294         struct rb_node *nd = rb_first(&node->rb_root);
295
296         for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
297                 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
298                 struct callchain_list *chain;
299                 bool first = true;
300
301                 list_for_each_entry(chain, &child->val, list) {
302                         if (first) {
303                                 first = false;
304                                 chain->has_children = chain->list.next != &child->val ||
305                                                          !RB_EMPTY_ROOT(&child->rb_root);
306                         } else
307                                 chain->has_children = chain->list.next == &child->val &&
308                                                          !RB_EMPTY_ROOT(&child->rb_root);
309                 }
310
311                 callchain_node__init_have_children_rb_tree(child);
312         }
313 }
314
315 static void callchain_node__init_have_children(struct callchain_node *node,
316                                                bool has_sibling)
317 {
318         struct callchain_list *chain;
319
320         chain = list_entry(node->val.next, struct callchain_list, list);
321         chain->has_children = has_sibling;
322
323         if (!list_empty(&node->val)) {
324                 chain = list_entry(node->val.prev, struct callchain_list, list);
325                 chain->has_children = !RB_EMPTY_ROOT(&node->rb_root);
326         }
327
328         callchain_node__init_have_children_rb_tree(node);
329 }
330
331 static void callchain__init_have_children(struct rb_root *root)
332 {
333         struct rb_node *nd = rb_first(root);
334         bool has_sibling = nd && rb_next(nd);
335
336         for (nd = rb_first(root); nd; nd = rb_next(nd)) {
337                 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
338                 callchain_node__init_have_children(node, has_sibling);
339                 if (callchain_param.mode == CHAIN_FLAT ||
340                     callchain_param.mode == CHAIN_FOLDED)
341                         callchain_node__make_parent_list(node);
342         }
343 }
344
345 static void hist_entry__init_have_children(struct hist_entry *he)
346 {
347         if (he->init_have_children)
348                 return;
349
350         if (he->leaf) {
351                 he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
352                 callchain__init_have_children(&he->sorted_chain);
353         } else {
354                 he->has_children = !RB_EMPTY_ROOT(&he->hroot_out);
355         }
356
357         he->init_have_children = true;
358 }
359
360 static bool hist_browser__toggle_fold(struct hist_browser *browser)
361 {
362         struct hist_entry *he = browser->he_selection;
363         struct map_symbol *ms = browser->selection;
364         struct callchain_list *cl = container_of(ms, struct callchain_list, ms);
365         bool has_children;
366
367         if (!he || !ms)
368                 return false;
369
370         if (ms == &he->ms)
371                 has_children = hist_entry__toggle_fold(he);
372         else
373                 has_children = callchain_list__toggle_fold(cl);
374
375         if (has_children) {
376                 int child_rows = 0;
377
378                 hist_entry__init_have_children(he);
379                 browser->b.nr_entries -= he->nr_rows;
380
381                 if (he->leaf)
382                         browser->nr_callchain_rows -= he->nr_rows;
383                 else
384                         browser->nr_hierarchy_entries -= he->nr_rows;
385
386                 if (symbol_conf.report_hierarchy)
387                         child_rows = hierarchy_count_rows(browser, he, true);
388
389                 if (he->unfolded) {
390                         if (he->leaf)
391                                 he->nr_rows = callchain__count_rows(&he->sorted_chain);
392                         else
393                                 he->nr_rows = hierarchy_count_rows(browser, he, false);
394
395                         /* account grand children */
396                         if (symbol_conf.report_hierarchy)
397                                 browser->b.nr_entries += child_rows - he->nr_rows;
398
399                         if (!he->leaf && he->nr_rows == 0) {
400                                 he->has_no_entry = true;
401                                 he->nr_rows = 1;
402                         }
403                 } else {
404                         if (symbol_conf.report_hierarchy)
405                                 browser->b.nr_entries -= child_rows - he->nr_rows;
406
407                         if (he->has_no_entry)
408                                 he->has_no_entry = false;
409
410                         he->nr_rows = 0;
411                 }
412
413                 browser->b.nr_entries += he->nr_rows;
414
415                 if (he->leaf)
416                         browser->nr_callchain_rows += he->nr_rows;
417                 else
418                         browser->nr_hierarchy_entries += he->nr_rows;
419
420                 return true;
421         }
422
423         /* If it doesn't have children, no toggling performed */
424         return false;
425 }
426
427 static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
428 {
429         int n = 0;
430         struct rb_node *nd;
431
432         for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
433                 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
434                 struct callchain_list *chain;
435                 bool has_children = false;
436
437                 list_for_each_entry(chain, &child->val, list) {
438                         ++n;
439                         callchain_list__set_folding(chain, unfold);
440                         has_children = chain->has_children;
441                 }
442
443                 if (has_children)
444                         n += callchain_node__set_folding_rb_tree(child, unfold);
445         }
446
447         return n;
448 }
449
450 static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
451 {
452         struct callchain_list *chain;
453         bool has_children = false;
454         int n = 0;
455
456         list_for_each_entry(chain, &node->val, list) {
457                 ++n;
458                 callchain_list__set_folding(chain, unfold);
459                 has_children = chain->has_children;
460         }
461
462         if (has_children)
463                 n += callchain_node__set_folding_rb_tree(node, unfold);
464
465         return n;
466 }
467
468 static int callchain__set_folding(struct rb_root *chain, bool unfold)
469 {
470         struct rb_node *nd;
471         int n = 0;
472
473         for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
474                 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
475                 n += callchain_node__set_folding(node, unfold);
476         }
477
478         return n;
479 }
480
481 static int hierarchy_set_folding(struct hist_browser *hb, struct hist_entry *he,
482                                  bool unfold __maybe_unused)
483 {
484         float percent;
485         struct rb_node *nd;
486         struct hist_entry *child;
487         int n = 0;
488
489         for (nd = rb_first(&he->hroot_out); nd; nd = rb_next(nd)) {
490                 child = rb_entry(nd, struct hist_entry, rb_node);
491                 percent = hist_entry__get_percent_limit(child);
492                 if (!child->filtered && percent >= hb->min_pcnt)
493                         n++;
494         }
495
496         return n;
497 }
498
499 static void hist_entry__set_folding(struct hist_entry *he,
500                                     struct hist_browser *hb, bool unfold)
501 {
502         hist_entry__init_have_children(he);
503         he->unfolded = unfold ? he->has_children : false;
504
505         if (he->has_children) {
506                 int n;
507
508                 if (he->leaf)
509                         n = callchain__set_folding(&he->sorted_chain, unfold);
510                 else
511                         n = hierarchy_set_folding(hb, he, unfold);
512
513                 he->nr_rows = unfold ? n : 0;
514         } else
515                 he->nr_rows = 0;
516 }
517
518 static void
519 __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
520 {
521         struct rb_node *nd;
522         struct hist_entry *he;
523         double percent;
524
525         nd = rb_first(&browser->hists->entries);
526         while (nd) {
527                 he = rb_entry(nd, struct hist_entry, rb_node);
528
529                 /* set folding state even if it's currently folded */
530                 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
531
532                 hist_entry__set_folding(he, browser, unfold);
533
534                 percent = hist_entry__get_percent_limit(he);
535                 if (he->filtered || percent < browser->min_pcnt)
536                         continue;
537
538                 if (!he->depth || unfold)
539                         browser->nr_hierarchy_entries++;
540                 if (he->leaf)
541                         browser->nr_callchain_rows += he->nr_rows;
542                 else if (unfold && !hist_entry__has_hierarchy_children(he, browser->min_pcnt)) {
543                         browser->nr_hierarchy_entries++;
544                         he->has_no_entry = true;
545                         he->nr_rows = 1;
546                 } else
547                         he->has_no_entry = false;
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)
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 perf_hpp_list_node *fmt_node;
1276         struct hpp_arg arg = {
1277                 .b              = &browser->b,
1278                 .current_entry  = current_entry,
1279         };
1280         int column = 0;
1281         int hierarchy_indent = (entry->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
1282
1283         if (current_entry) {
1284                 browser->he_selection = entry;
1285                 browser->selection = &entry->ms;
1286         }
1287
1288         hist_entry__init_have_children(entry);
1289         folded_sign = hist_entry__folded(entry);
1290         arg.folded_sign = folded_sign;
1291
1292         if (entry->leaf && row_offset) {
1293                 row_offset--;
1294                 goto show_callchain;
1295         }
1296
1297         hist_browser__gotorc(browser, row, 0);
1298
1299         if (current_entry && browser->b.navkeypressed)
1300                 ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1301         else
1302                 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1303
1304         ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1305         width -= level * HIERARCHY_INDENT;
1306
1307         /* the first hpp_list_node is for overhead columns */
1308         fmt_node = list_first_entry(&entry->hists->hpp_formats,
1309                                     struct perf_hpp_list_node, list);
1310         perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1311                 char s[2048];
1312                 struct perf_hpp hpp = {
1313                         .buf            = s,
1314                         .size           = sizeof(s),
1315                         .ptr            = &arg,
1316                 };
1317
1318                 if (perf_hpp__should_skip(fmt, entry->hists) ||
1319                     column++ < browser->b.horiz_scroll)
1320                         continue;
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                 perf_hpp_list__for_each_format(entry->hpp_list, fmt) {
1375                         ui_browser__write_nstring(&browser->b, "", 2);
1376                         width -= 2;
1377
1378                         /*
1379                          * No need to call hist_entry__snprintf_alignment()
1380                          * since this fmt is always the last column in the
1381                          * hierarchy mode.
1382                          */
1383                         if (fmt->color) {
1384                                 width -= fmt->color(fmt, &hpp, entry);
1385                         } else {
1386                                 int i = 0;
1387
1388                                 width -= fmt->entry(fmt, &hpp, entry);
1389                                 ui_browser__printf(&browser->b, "%s", ltrim(s));
1390
1391                                 while (isspace(s[i++]))
1392                                         width++;
1393                         }
1394                 }
1395         }
1396
1397         /* The scroll bar isn't being used */
1398         if (!browser->b.navkeypressed)
1399                 width += 1;
1400
1401         ui_browser__write_nstring(&browser->b, "", width);
1402
1403         ++row;
1404         ++printed;
1405
1406 show_callchain:
1407         if (entry->leaf && folded_sign == '-' && row != browser->b.rows) {
1408                 struct callchain_print_arg carg = {
1409                         .row_offset = row_offset,
1410                 };
1411
1412                 printed += hist_browser__show_callchain(browser, entry,
1413                                         level + 1, row,
1414                                         hist_browser__show_callchain_entry, &carg,
1415                                         hist_browser__check_output_full);
1416         }
1417
1418         return printed;
1419 }
1420
1421 static int hist_browser__show_no_entry(struct hist_browser *browser,
1422                                        unsigned short row, int level)
1423 {
1424         int width = browser->b.width;
1425         bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1426         bool first = true;
1427         int column = 0;
1428         int ret;
1429         struct perf_hpp_fmt *fmt;
1430         struct perf_hpp_list_node *fmt_node;
1431         int indent = browser->hists->nr_hpp_node - 2;
1432
1433         if (current_entry) {
1434                 browser->he_selection = NULL;
1435                 browser->selection = NULL;
1436         }
1437
1438         hist_browser__gotorc(browser, row, 0);
1439
1440         if (current_entry && browser->b.navkeypressed)
1441                 ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1442         else
1443                 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1444
1445         ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1446         width -= level * HIERARCHY_INDENT;
1447
1448         /* the first hpp_list_node is for overhead columns */
1449         fmt_node = list_first_entry(&browser->hists->hpp_formats,
1450                                     struct perf_hpp_list_node, list);
1451         perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1452                 if (perf_hpp__should_skip(fmt, browser->hists) ||
1453                     column++ < browser->b.horiz_scroll)
1454                         continue;
1455
1456                 ret = fmt->width(fmt, NULL, browser->hists);
1457
1458                 if (first) {
1459                         /* for folded sign */
1460                         first = false;
1461                         ret++;
1462                 } else {
1463                         /* space between columns */
1464                         ret += 2;
1465                 }
1466
1467                 ui_browser__write_nstring(&browser->b, "", ret);
1468                 width -= ret;
1469         }
1470
1471         ui_browser__write_nstring(&browser->b, "", indent * HIERARCHY_INDENT);
1472         width -= indent * HIERARCHY_INDENT;
1473
1474         if (column >= browser->b.horiz_scroll) {
1475                 char buf[32];
1476
1477                 ret = snprintf(buf, sizeof(buf), "no entry >= %.2f%%", browser->min_pcnt);
1478                 ui_browser__printf(&browser->b, "  %s", buf);
1479                 width -= ret + 2;
1480         }
1481
1482         /* The scroll bar isn't being used */
1483         if (!browser->b.navkeypressed)
1484                 width += 1;
1485
1486         ui_browser__write_nstring(&browser->b, "", width);
1487         return 1;
1488 }
1489
1490 static int advance_hpp_check(struct perf_hpp *hpp, int inc)
1491 {
1492         advance_hpp(hpp, inc);
1493         return hpp->size <= 0;
1494 }
1495
1496 static int hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf, size_t size)
1497 {
1498         struct hists *hists = browser->hists;
1499         struct perf_hpp dummy_hpp = {
1500                 .buf    = buf,
1501                 .size   = size,
1502         };
1503         struct perf_hpp_fmt *fmt;
1504         size_t ret = 0;
1505         int column = 0;
1506
1507         if (symbol_conf.use_callchain) {
1508                 ret = scnprintf(buf, size, "  ");
1509                 if (advance_hpp_check(&dummy_hpp, ret))
1510                         return ret;
1511         }
1512
1513         hists__for_each_format(browser->hists, fmt) {
1514                 if (perf_hpp__should_skip(fmt, hists)  || column++ < browser->b.horiz_scroll)
1515                         continue;
1516
1517                 ret = fmt->header(fmt, &dummy_hpp, hists);
1518                 if (advance_hpp_check(&dummy_hpp, ret))
1519                         break;
1520
1521                 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
1522                 if (advance_hpp_check(&dummy_hpp, ret))
1523                         break;
1524         }
1525
1526         return ret;
1527 }
1528
1529 static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser, char *buf, size_t size)
1530 {
1531         struct hists *hists = browser->hists;
1532         struct perf_hpp dummy_hpp = {
1533                 .buf    = buf,
1534                 .size   = size,
1535         };
1536         struct perf_hpp_fmt *fmt;
1537         struct perf_hpp_list_node *fmt_node;
1538         size_t ret = 0;
1539         int column = 0;
1540         int indent = hists->nr_hpp_node - 2;
1541         bool first_node, first_col;
1542
1543         ret = scnprintf(buf, size, " ");
1544         if (advance_hpp_check(&dummy_hpp, ret))
1545                 return ret;
1546
1547         /* the first hpp_list_node is for overhead columns */
1548         fmt_node = list_first_entry(&hists->hpp_formats,
1549                                     struct perf_hpp_list_node, list);
1550         perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1551                 if (column++ < browser->b.horiz_scroll)
1552                         continue;
1553
1554                 ret = fmt->header(fmt, &dummy_hpp, hists);
1555                 if (advance_hpp_check(&dummy_hpp, ret))
1556                         break;
1557
1558                 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
1559                 if (advance_hpp_check(&dummy_hpp, ret))
1560                         break;
1561         }
1562
1563         ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "%*s",
1564                         indent * HIERARCHY_INDENT, "");
1565         if (advance_hpp_check(&dummy_hpp, ret))
1566                 return ret;
1567
1568         first_node = true;
1569         list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
1570                 if (!first_node) {
1571                         ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " / ");
1572                         if (advance_hpp_check(&dummy_hpp, ret))
1573                                 break;
1574                 }
1575                 first_node = false;
1576
1577                 first_col = true;
1578                 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1579                         char *start;
1580
1581                         if (perf_hpp__should_skip(fmt, hists))
1582                                 continue;
1583
1584                         if (!first_col) {
1585                                 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "+");
1586                                 if (advance_hpp_check(&dummy_hpp, ret))
1587                                         break;
1588                         }
1589                         first_col = false;
1590
1591                         ret = fmt->header(fmt, &dummy_hpp, hists);
1592                         dummy_hpp.buf[ret] = '\0';
1593
1594                         start = trim(dummy_hpp.buf);
1595                         ret = strlen(start);
1596
1597                         if (start != dummy_hpp.buf)
1598                                 memmove(dummy_hpp.buf, start, ret + 1);
1599
1600                         if (advance_hpp_check(&dummy_hpp, ret))
1601                                 break;
1602                 }
1603         }
1604
1605         return ret;
1606 }
1607
1608 static void hists_browser__hierarchy_headers(struct hist_browser *browser)
1609 {
1610         char headers[1024];
1611
1612         hists_browser__scnprintf_hierarchy_headers(browser, headers,
1613                                                    sizeof(headers));
1614
1615         ui_browser__gotorc(&browser->b, 0, 0);
1616         ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1617         ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1618 }
1619
1620 static void hists_browser__headers(struct hist_browser *browser)
1621 {
1622         char headers[1024];
1623
1624         hists_browser__scnprintf_headers(browser, headers,
1625                                          sizeof(headers));
1626
1627         ui_browser__gotorc(&browser->b, 0, 0);
1628         ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1629         ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1630 }
1631
1632 static void hist_browser__show_headers(struct hist_browser *browser)
1633 {
1634         if (symbol_conf.report_hierarchy)
1635                 hists_browser__hierarchy_headers(browser);
1636         else
1637                 hists_browser__headers(browser);
1638 }
1639
1640 static void ui_browser__hists_init_top(struct ui_browser *browser)
1641 {
1642         if (browser->top == NULL) {
1643                 struct hist_browser *hb;
1644
1645                 hb = container_of(browser, struct hist_browser, b);
1646                 browser->top = rb_first(&hb->hists->entries);
1647         }
1648 }
1649
1650 static unsigned int hist_browser__refresh(struct ui_browser *browser)
1651 {
1652         unsigned row = 0;
1653         u16 header_offset = 0;
1654         struct rb_node *nd;
1655         struct hist_browser *hb = container_of(browser, struct hist_browser, b);
1656
1657         if (hb->show_headers) {
1658                 hist_browser__show_headers(hb);
1659                 header_offset = 1;
1660         }
1661
1662         ui_browser__hists_init_top(browser);
1663         hb->he_selection = NULL;
1664         hb->selection = NULL;
1665
1666         for (nd = browser->top; nd; nd = rb_hierarchy_next(nd)) {
1667                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1668                 float percent;
1669
1670                 if (h->filtered) {
1671                         /* let it move to sibling */
1672                         h->unfolded = false;
1673                         continue;
1674                 }
1675
1676                 percent = hist_entry__get_percent_limit(h);
1677                 if (percent < hb->min_pcnt)
1678                         continue;
1679
1680                 if (symbol_conf.report_hierarchy) {
1681                         row += hist_browser__show_hierarchy_entry(hb, h, row,
1682                                                                   h->depth);
1683                         if (row == browser->rows)
1684                                 break;
1685
1686                         if (h->has_no_entry) {
1687                                 hist_browser__show_no_entry(hb, row, h->depth + 1);
1688                                 row++;
1689                         }
1690                 } else {
1691                         row += hist_browser__show_entry(hb, h, row);
1692                 }
1693
1694                 if (row == browser->rows)
1695                         break;
1696         }
1697
1698         return row + header_offset;
1699 }
1700
1701 static struct rb_node *hists__filter_entries(struct rb_node *nd,
1702                                              float min_pcnt)
1703 {
1704         while (nd != NULL) {
1705                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1706                 float percent = hist_entry__get_percent_limit(h);
1707
1708                 if (!h->filtered && percent >= min_pcnt)
1709                         return nd;
1710
1711                 /*
1712                  * If it's filtered, its all children also were filtered.
1713                  * So move to sibling node.
1714                  */
1715                 if (rb_next(nd))
1716                         nd = rb_next(nd);
1717                 else
1718                         nd = rb_hierarchy_next(nd);
1719         }
1720
1721         return NULL;
1722 }
1723
1724 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
1725                                                   float min_pcnt)
1726 {
1727         while (nd != NULL) {
1728                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1729                 float percent = hist_entry__get_percent_limit(h);
1730
1731                 if (!h->filtered && percent >= min_pcnt)
1732                         return nd;
1733
1734                 nd = rb_hierarchy_prev(nd);
1735         }
1736
1737         return NULL;
1738 }
1739
1740 static void ui_browser__hists_seek(struct ui_browser *browser,
1741                                    off_t offset, int whence)
1742 {
1743         struct hist_entry *h;
1744         struct rb_node *nd;
1745         bool first = true;
1746         struct hist_browser *hb;
1747
1748         hb = container_of(browser, struct hist_browser, b);
1749
1750         if (browser->nr_entries == 0)
1751                 return;
1752
1753         ui_browser__hists_init_top(browser);
1754
1755         switch (whence) {
1756         case SEEK_SET:
1757                 nd = hists__filter_entries(rb_first(browser->entries),
1758                                            hb->min_pcnt);
1759                 break;
1760         case SEEK_CUR:
1761                 nd = browser->top;
1762                 goto do_offset;
1763         case SEEK_END:
1764                 nd = rb_hierarchy_last(rb_last(browser->entries));
1765                 nd = hists__filter_prev_entries(nd, hb->min_pcnt);
1766                 first = false;
1767                 break;
1768         default:
1769                 return;
1770         }
1771
1772         /*
1773          * Moves not relative to the first visible entry invalidates its
1774          * row_offset:
1775          */
1776         h = rb_entry(browser->top, struct hist_entry, rb_node);
1777         h->row_offset = 0;
1778
1779         /*
1780          * Here we have to check if nd is expanded (+), if it is we can't go
1781          * the next top level hist_entry, instead we must compute an offset of
1782          * what _not_ to show and not change the first visible entry.
1783          *
1784          * This offset increments when we are going from top to bottom and
1785          * decreases when we're going from bottom to top.
1786          *
1787          * As we don't have backpointers to the top level in the callchains
1788          * structure, we need to always print the whole hist_entry callchain,
1789          * skipping the first ones that are before the first visible entry
1790          * and stop when we printed enough lines to fill the screen.
1791          */
1792 do_offset:
1793         if (!nd)
1794                 return;
1795
1796         if (offset > 0) {
1797                 do {
1798                         h = rb_entry(nd, struct hist_entry, rb_node);
1799                         if (h->unfolded && h->leaf) {
1800                                 u16 remaining = h->nr_rows - h->row_offset;
1801                                 if (offset > remaining) {
1802                                         offset -= remaining;
1803                                         h->row_offset = 0;
1804                                 } else {
1805                                         h->row_offset += offset;
1806                                         offset = 0;
1807                                         browser->top = nd;
1808                                         break;
1809                                 }
1810                         }
1811                         nd = hists__filter_entries(rb_hierarchy_next(nd),
1812                                                    hb->min_pcnt);
1813                         if (nd == NULL)
1814                                 break;
1815                         --offset;
1816                         browser->top = nd;
1817                 } while (offset != 0);
1818         } else if (offset < 0) {
1819                 while (1) {
1820                         h = rb_entry(nd, struct hist_entry, rb_node);
1821                         if (h->unfolded && h->leaf) {
1822                                 if (first) {
1823                                         if (-offset > h->row_offset) {
1824                                                 offset += h->row_offset;
1825                                                 h->row_offset = 0;
1826                                         } else {
1827                                                 h->row_offset += offset;
1828                                                 offset = 0;
1829                                                 browser->top = nd;
1830                                                 break;
1831                                         }
1832                                 } else {
1833                                         if (-offset > h->nr_rows) {
1834                                                 offset += h->nr_rows;
1835                                                 h->row_offset = 0;
1836                                         } else {
1837                                                 h->row_offset = h->nr_rows + offset;
1838                                                 offset = 0;
1839                                                 browser->top = nd;
1840                                                 break;
1841                                         }
1842                                 }
1843                         }
1844
1845                         nd = hists__filter_prev_entries(rb_hierarchy_prev(nd),
1846                                                         hb->min_pcnt);
1847                         if (nd == NULL)
1848                                 break;
1849                         ++offset;
1850                         browser->top = nd;
1851                         if (offset == 0) {
1852                                 /*
1853                                  * Last unfiltered hist_entry, check if it is
1854                                  * unfolded, if it is then we should have
1855                                  * row_offset at its last entry.
1856                                  */
1857                                 h = rb_entry(nd, struct hist_entry, rb_node);
1858                                 if (h->unfolded && h->leaf)
1859                                         h->row_offset = h->nr_rows;
1860                                 break;
1861                         }
1862                         first = false;
1863                 }
1864         } else {
1865                 browser->top = nd;
1866                 h = rb_entry(nd, struct hist_entry, rb_node);
1867                 h->row_offset = 0;
1868         }
1869 }
1870
1871 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1872                                            struct hist_entry *he, FILE *fp,
1873                                            int level)
1874 {
1875         struct callchain_print_arg arg  = {
1876                 .fp = fp,
1877         };
1878
1879         hist_browser__show_callchain(browser, he, level, 0,
1880                                      hist_browser__fprintf_callchain_entry, &arg,
1881                                      hist_browser__check_dump_full);
1882         return arg.printed;
1883 }
1884
1885 static int hist_browser__fprintf_entry(struct hist_browser *browser,
1886                                        struct hist_entry *he, FILE *fp)
1887 {
1888         char s[8192];
1889         int printed = 0;
1890         char folded_sign = ' ';
1891         struct perf_hpp hpp = {
1892                 .buf = s,
1893                 .size = sizeof(s),
1894         };
1895         struct perf_hpp_fmt *fmt;
1896         bool first = true;
1897         int ret;
1898
1899         if (symbol_conf.use_callchain) {
1900                 folded_sign = hist_entry__folded(he);
1901                 printed += fprintf(fp, "%c ", folded_sign);
1902         }
1903
1904         hists__for_each_format(browser->hists, fmt) {
1905                 if (perf_hpp__should_skip(fmt, he->hists))
1906                         continue;
1907
1908                 if (!first) {
1909                         ret = scnprintf(hpp.buf, hpp.size, "  ");
1910                         advance_hpp(&hpp, ret);
1911                 } else
1912                         first = false;
1913
1914                 ret = fmt->entry(fmt, &hpp, he);
1915                 ret = hist_entry__snprintf_alignment(he, &hpp, fmt, ret);
1916                 advance_hpp(&hpp, ret);
1917         }
1918         printed += fprintf(fp, "%s\n", s);
1919
1920         if (folded_sign == '-')
1921                 printed += hist_browser__fprintf_callchain(browser, he, fp, 1);
1922
1923         return printed;
1924 }
1925
1926
1927 static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,
1928                                                  struct hist_entry *he,
1929                                                  FILE *fp, int level)
1930 {
1931         char s[8192];
1932         int printed = 0;
1933         char folded_sign = ' ';
1934         struct perf_hpp hpp = {
1935                 .buf = s,
1936                 .size = sizeof(s),
1937         };
1938         struct perf_hpp_fmt *fmt;
1939         struct perf_hpp_list_node *fmt_node;
1940         bool first = true;
1941         int ret;
1942         int hierarchy_indent = (he->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
1943
1944         printed = fprintf(fp, "%*s", level * HIERARCHY_INDENT, "");
1945
1946         folded_sign = hist_entry__folded(he);
1947         printed += fprintf(fp, "%c", folded_sign);
1948
1949         /* the first hpp_list_node is for overhead columns */
1950         fmt_node = list_first_entry(&he->hists->hpp_formats,
1951                                     struct perf_hpp_list_node, list);
1952         perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1953                 if (!first) {
1954                         ret = scnprintf(hpp.buf, hpp.size, "  ");
1955                         advance_hpp(&hpp, ret);
1956                 } else
1957                         first = false;
1958
1959                 ret = fmt->entry(fmt, &hpp, he);
1960                 advance_hpp(&hpp, ret);
1961         }
1962
1963         ret = scnprintf(hpp.buf, hpp.size, "%*s", hierarchy_indent, "");
1964         advance_hpp(&hpp, ret);
1965
1966         perf_hpp_list__for_each_format(he->hpp_list, fmt) {
1967                 ret = scnprintf(hpp.buf, hpp.size, "  ");
1968                 advance_hpp(&hpp, ret);
1969
1970                 ret = fmt->entry(fmt, &hpp, he);
1971                 advance_hpp(&hpp, ret);
1972         }
1973
1974         printed += fprintf(fp, "%s\n", rtrim(s));
1975
1976         if (he->leaf && folded_sign == '-') {
1977                 printed += hist_browser__fprintf_callchain(browser, he, fp,
1978                                                            he->depth + 1);
1979         }
1980
1981         return printed;
1982 }
1983
1984 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1985 {
1986         struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
1987                                                    browser->min_pcnt);
1988         int printed = 0;
1989
1990         while (nd) {
1991                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1992
1993                 if (symbol_conf.report_hierarchy) {
1994                         printed += hist_browser__fprintf_hierarchy_entry(browser,
1995                                                                          h, fp,
1996                                                                          h->depth);
1997                 } else {
1998                         printed += hist_browser__fprintf_entry(browser, h, fp);
1999                 }
2000
2001                 nd = hists__filter_entries(rb_hierarchy_next(nd),
2002                                            browser->min_pcnt);
2003         }
2004
2005         return printed;
2006 }
2007
2008 static int hist_browser__dump(struct hist_browser *browser)
2009 {
2010         char filename[64];
2011         FILE *fp;
2012
2013         while (1) {
2014                 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
2015                 if (access(filename, F_OK))
2016                         break;
2017                 /*
2018                  * XXX: Just an arbitrary lazy upper limit
2019                  */
2020                 if (++browser->print_seq == 8192) {
2021                         ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
2022                         return -1;
2023                 }
2024         }
2025
2026         fp = fopen(filename, "w");
2027         if (fp == NULL) {
2028                 char bf[64];
2029                 const char *err = strerror_r(errno, bf, sizeof(bf));
2030                 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
2031                 return -1;
2032         }
2033
2034         ++browser->print_seq;
2035         hist_browser__fprintf(browser, fp);
2036         fclose(fp);
2037         ui_helpline__fpush("%s written!", filename);
2038
2039         return 0;
2040 }
2041
2042 static struct hist_browser *hist_browser__new(struct hists *hists,
2043                                               struct hist_browser_timer *hbt,
2044                                               struct perf_env *env)
2045 {
2046         struct hist_browser *browser = zalloc(sizeof(*browser));
2047
2048         if (browser) {
2049                 browser->hists = hists;
2050                 browser->b.refresh = hist_browser__refresh;
2051                 browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
2052                 browser->b.seek = ui_browser__hists_seek;
2053                 browser->b.use_navkeypressed = true;
2054                 browser->show_headers = symbol_conf.show_hist_headers;
2055                 browser->hbt = hbt;
2056                 browser->env = env;
2057         }
2058
2059         return browser;
2060 }
2061
2062 static void hist_browser__delete(struct hist_browser *browser)
2063 {
2064         free(browser);
2065 }
2066
2067 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
2068 {
2069         return browser->he_selection;
2070 }
2071
2072 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
2073 {
2074         return browser->he_selection->thread;
2075 }
2076
2077 /* Check whether the browser is for 'top' or 'report' */
2078 static inline bool is_report_browser(void *timer)
2079 {
2080         return timer == NULL;
2081 }
2082
2083 static int hists__browser_title(struct hists *hists,
2084                                 struct hist_browser_timer *hbt,
2085                                 char *bf, size_t size)
2086 {
2087         char unit;
2088         int printed;
2089         const struct dso *dso = hists->dso_filter;
2090         const struct thread *thread = hists->thread_filter;
2091         int socket_id = hists->socket_filter;
2092         unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
2093         u64 nr_events = hists->stats.total_period;
2094         struct perf_evsel *evsel = hists_to_evsel(hists);
2095         const char *ev_name = perf_evsel__name(evsel);
2096         char buf[512];
2097         size_t buflen = sizeof(buf);
2098         char ref[30] = " show reference callgraph, ";
2099         bool enable_ref = false;
2100
2101         if (symbol_conf.filter_relative) {
2102                 nr_samples = hists->stats.nr_non_filtered_samples;
2103                 nr_events = hists->stats.total_non_filtered_period;
2104         }
2105
2106         if (perf_evsel__is_group_event(evsel)) {
2107                 struct perf_evsel *pos;
2108
2109                 perf_evsel__group_desc(evsel, buf, buflen);
2110                 ev_name = buf;
2111
2112                 for_each_group_member(pos, evsel) {
2113                         struct hists *pos_hists = evsel__hists(pos);
2114
2115                         if (symbol_conf.filter_relative) {
2116                                 nr_samples += pos_hists->stats.nr_non_filtered_samples;
2117                                 nr_events += pos_hists->stats.total_non_filtered_period;
2118                         } else {
2119                                 nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
2120                                 nr_events += pos_hists->stats.total_period;
2121                         }
2122                 }
2123         }
2124
2125         if (symbol_conf.show_ref_callgraph &&
2126             strstr(ev_name, "call-graph=no"))
2127                 enable_ref = true;
2128         nr_samples = convert_unit(nr_samples, &unit);
2129         printed = scnprintf(bf, size,
2130                            "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64,
2131                            nr_samples, unit, ev_name, enable_ref ? ref : " ", nr_events);
2132
2133
2134         if (hists->uid_filter_str)
2135                 printed += snprintf(bf + printed, size - printed,
2136                                     ", UID: %s", hists->uid_filter_str);
2137         if (thread) {
2138                 if (hists__has(hists, thread)) {
2139                         printed += scnprintf(bf + printed, size - printed,
2140                                     ", Thread: %s(%d)",
2141                                      (thread->comm_set ? thread__comm_str(thread) : ""),
2142                                     thread->tid);
2143                 } else {
2144                         printed += scnprintf(bf + printed, size - printed,
2145                                     ", Thread: %s",
2146                                      (thread->comm_set ? thread__comm_str(thread) : ""));
2147                 }
2148         }
2149         if (dso)
2150                 printed += scnprintf(bf + printed, size - printed,
2151                                     ", DSO: %s", dso->short_name);
2152         if (socket_id > -1)
2153                 printed += scnprintf(bf + printed, size - printed,
2154                                     ", Processor Socket: %d", socket_id);
2155         if (!is_report_browser(hbt)) {
2156                 struct perf_top *top = hbt->arg;
2157
2158                 if (top->zero)
2159                         printed += scnprintf(bf + printed, size - printed, " [z]");
2160         }
2161
2162         return printed;
2163 }
2164
2165 static inline void free_popup_options(char **options, int n)
2166 {
2167         int i;
2168
2169         for (i = 0; i < n; ++i)
2170                 zfree(&options[i]);
2171 }
2172
2173 /*
2174  * Only runtime switching of perf data file will make "input_name" point
2175  * to a malloced buffer. So add "is_input_name_malloced" flag to decide
2176  * whether we need to call free() for current "input_name" during the switch.
2177  */
2178 static bool is_input_name_malloced = false;
2179
2180 static int switch_data_file(void)
2181 {
2182         char *pwd, *options[32], *abs_path[32], *tmp;
2183         DIR *pwd_dir;
2184         int nr_options = 0, choice = -1, ret = -1;
2185         struct dirent *dent;
2186
2187         pwd = getenv("PWD");
2188         if (!pwd)
2189                 return ret;
2190
2191         pwd_dir = opendir(pwd);
2192         if (!pwd_dir)
2193                 return ret;
2194
2195         memset(options, 0, sizeof(options));
2196         memset(options, 0, sizeof(abs_path));
2197
2198         while ((dent = readdir(pwd_dir))) {
2199                 char path[PATH_MAX];
2200                 u64 magic;
2201                 char *name = dent->d_name;
2202                 FILE *file;
2203
2204                 if (!(dent->d_type == DT_REG))
2205                         continue;
2206
2207                 snprintf(path, sizeof(path), "%s/%s", pwd, name);
2208
2209                 file = fopen(path, "r");
2210                 if (!file)
2211                         continue;
2212
2213                 if (fread(&magic, 1, 8, file) < 8)
2214                         goto close_file_and_continue;
2215
2216                 if (is_perf_magic(magic)) {
2217                         options[nr_options] = strdup(name);
2218                         if (!options[nr_options])
2219                                 goto close_file_and_continue;
2220
2221                         abs_path[nr_options] = strdup(path);
2222                         if (!abs_path[nr_options]) {
2223                                 zfree(&options[nr_options]);
2224                                 ui__warning("Can't search all data files due to memory shortage.\n");
2225                                 fclose(file);
2226                                 break;
2227                         }
2228
2229                         nr_options++;
2230                 }
2231
2232 close_file_and_continue:
2233                 fclose(file);
2234                 if (nr_options >= 32) {
2235                         ui__warning("Too many perf data files in PWD!\n"
2236                                     "Only the first 32 files will be listed.\n");
2237                         break;
2238                 }
2239         }
2240         closedir(pwd_dir);
2241
2242         if (nr_options) {
2243                 choice = ui__popup_menu(nr_options, options);
2244                 if (choice < nr_options && choice >= 0) {
2245                         tmp = strdup(abs_path[choice]);
2246                         if (tmp) {
2247                                 if (is_input_name_malloced)
2248                                         free((void *)input_name);
2249                                 input_name = tmp;
2250                                 is_input_name_malloced = true;
2251                                 ret = 0;
2252                         } else
2253                                 ui__warning("Data switch failed due to memory shortage!\n");
2254                 }
2255         }
2256
2257         free_popup_options(options, nr_options);
2258         free_popup_options(abs_path, nr_options);
2259         return ret;
2260 }
2261
2262 struct popup_action {
2263         struct thread           *thread;
2264         struct map_symbol       ms;
2265         int                     socket;
2266
2267         int (*fn)(struct hist_browser *browser, struct popup_action *act);
2268 };
2269
2270 static int
2271 do_annotate(struct hist_browser *browser, struct popup_action *act)
2272 {
2273         struct perf_evsel *evsel;
2274         struct annotation *notes;
2275         struct hist_entry *he;
2276         int err;
2277
2278         if (!objdump_path && perf_env__lookup_objdump(browser->env))
2279                 return 0;
2280
2281         notes = symbol__annotation(act->ms.sym);
2282         if (!notes->src)
2283                 return 0;
2284
2285         evsel = hists_to_evsel(browser->hists);
2286         err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
2287         he = hist_browser__selected_entry(browser);
2288         /*
2289          * offer option to annotate the other branch source or target
2290          * (if they exists) when returning from annotate
2291          */
2292         if ((err == 'q' || err == CTRL('c')) && he->branch_info)
2293                 return 1;
2294
2295         ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
2296         if (err)
2297                 ui_browser__handle_resize(&browser->b);
2298         return 0;
2299 }
2300
2301 static int
2302 add_annotate_opt(struct hist_browser *browser __maybe_unused,
2303                  struct popup_action *act, char **optstr,
2304                  struct map *map, struct symbol *sym)
2305 {
2306         if (sym == NULL || map->dso->annotate_warned)
2307                 return 0;
2308
2309         if (asprintf(optstr, "Annotate %s", sym->name) < 0)
2310                 return 0;
2311
2312         act->ms.map = map;
2313         act->ms.sym = sym;
2314         act->fn = do_annotate;
2315         return 1;
2316 }
2317
2318 static int
2319 do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
2320 {
2321         struct thread *thread = act->thread;
2322
2323         if ((!hists__has(browser->hists, thread) &&
2324              !hists__has(browser->hists, comm)) || thread == NULL)
2325                 return 0;
2326
2327         if (browser->hists->thread_filter) {
2328                 pstack__remove(browser->pstack, &browser->hists->thread_filter);
2329                 perf_hpp__set_elide(HISTC_THREAD, false);
2330                 thread__zput(browser->hists->thread_filter);
2331                 ui_helpline__pop();
2332         } else {
2333                 if (hists__has(browser->hists, thread)) {
2334                         ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
2335                                            thread->comm_set ? thread__comm_str(thread) : "",
2336                                            thread->tid);
2337                 } else {
2338                         ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
2339                                            thread->comm_set ? thread__comm_str(thread) : "");
2340                 }
2341
2342                 browser->hists->thread_filter = thread__get(thread);
2343                 perf_hpp__set_elide(HISTC_THREAD, false);
2344                 pstack__push(browser->pstack, &browser->hists->thread_filter);
2345         }
2346
2347         hists__filter_by_thread(browser->hists);
2348         hist_browser__reset(browser);
2349         return 0;
2350 }
2351
2352 static int
2353 add_thread_opt(struct hist_browser *browser, struct popup_action *act,
2354                char **optstr, struct thread *thread)
2355 {
2356         int ret;
2357
2358         if ((!hists__has(browser->hists, thread) &&
2359              !hists__has(browser->hists, comm)) || thread == NULL)
2360                 return 0;
2361
2362         if (hists__has(browser->hists, thread)) {
2363                 ret = asprintf(optstr, "Zoom %s %s(%d) thread",
2364                                browser->hists->thread_filter ? "out of" : "into",
2365                                thread->comm_set ? thread__comm_str(thread) : "",
2366                                thread->tid);
2367         } else {
2368                 ret = asprintf(optstr, "Zoom %s %s thread",
2369                                browser->hists->thread_filter ? "out of" : "into",
2370                                thread->comm_set ? thread__comm_str(thread) : "");
2371         }
2372         if (ret < 0)
2373                 return 0;
2374
2375         act->thread = thread;
2376         act->fn = do_zoom_thread;
2377         return 1;
2378 }
2379
2380 static int
2381 do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
2382 {
2383         struct map *map = act->ms.map;
2384
2385         if (!hists__has(browser->hists, dso) || map == NULL)
2386                 return 0;
2387
2388         if (browser->hists->dso_filter) {
2389                 pstack__remove(browser->pstack, &browser->hists->dso_filter);
2390                 perf_hpp__set_elide(HISTC_DSO, false);
2391                 browser->hists->dso_filter = NULL;
2392                 ui_helpline__pop();
2393         } else {
2394                 if (map == NULL)
2395                         return 0;
2396                 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
2397                                    __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
2398                 browser->hists->dso_filter = map->dso;
2399                 perf_hpp__set_elide(HISTC_DSO, true);
2400                 pstack__push(browser->pstack, &browser->hists->dso_filter);
2401         }
2402
2403         hists__filter_by_dso(browser->hists);
2404         hist_browser__reset(browser);
2405         return 0;
2406 }
2407
2408 static int
2409 add_dso_opt(struct hist_browser *browser, struct popup_action *act,
2410             char **optstr, struct map *map)
2411 {
2412         if (!hists__has(browser->hists, dso) || map == NULL)
2413                 return 0;
2414
2415         if (asprintf(optstr, "Zoom %s %s DSO",
2416                      browser->hists->dso_filter ? "out of" : "into",
2417                      __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
2418                 return 0;
2419
2420         act->ms.map = map;
2421         act->fn = do_zoom_dso;
2422         return 1;
2423 }
2424
2425 static int
2426 do_browse_map(struct hist_browser *browser __maybe_unused,
2427               struct popup_action *act)
2428 {
2429         map__browse(act->ms.map);
2430         return 0;
2431 }
2432
2433 static int
2434 add_map_opt(struct hist_browser *browser,
2435             struct popup_action *act, char **optstr, struct map *map)
2436 {
2437         if (!hists__has(browser->hists, dso) || map == NULL)
2438                 return 0;
2439
2440         if (asprintf(optstr, "Browse map details") < 0)
2441                 return 0;
2442
2443         act->ms.map = map;
2444         act->fn = do_browse_map;
2445         return 1;
2446 }
2447
2448 static int
2449 do_run_script(struct hist_browser *browser __maybe_unused,
2450               struct popup_action *act)
2451 {
2452         char script_opt[64];
2453         memset(script_opt, 0, sizeof(script_opt));
2454
2455         if (act->thread) {
2456                 scnprintf(script_opt, sizeof(script_opt), " -c %s ",
2457                           thread__comm_str(act->thread));
2458         } else if (act->ms.sym) {
2459                 scnprintf(script_opt, sizeof(script_opt), " -S %s ",
2460                           act->ms.sym->name);
2461         }
2462
2463         script_browse(script_opt);
2464         return 0;
2465 }
2466
2467 static int
2468 add_script_opt(struct hist_browser *browser __maybe_unused,
2469                struct popup_action *act, char **optstr,
2470                struct thread *thread, struct symbol *sym)
2471 {
2472         if (thread) {
2473                 if (asprintf(optstr, "Run scripts for samples of thread [%s]",
2474                              thread__comm_str(thread)) < 0)
2475                         return 0;
2476         } else if (sym) {
2477                 if (asprintf(optstr, "Run scripts for samples of symbol [%s]",
2478                              sym->name) < 0)
2479                         return 0;
2480         } else {
2481                 if (asprintf(optstr, "Run scripts for all samples") < 0)
2482                         return 0;
2483         }
2484
2485         act->thread = thread;
2486         act->ms.sym = sym;
2487         act->fn = do_run_script;
2488         return 1;
2489 }
2490
2491 static int
2492 do_switch_data(struct hist_browser *browser __maybe_unused,
2493                struct popup_action *act __maybe_unused)
2494 {
2495         if (switch_data_file()) {
2496                 ui__warning("Won't switch the data files due to\n"
2497                             "no valid data file get selected!\n");
2498                 return 0;
2499         }
2500
2501         return K_SWITCH_INPUT_DATA;
2502 }
2503
2504 static int
2505 add_switch_opt(struct hist_browser *browser,
2506                struct popup_action *act, char **optstr)
2507 {
2508         if (!is_report_browser(browser->hbt))
2509                 return 0;
2510
2511         if (asprintf(optstr, "Switch to another data file in PWD") < 0)
2512                 return 0;
2513
2514         act->fn = do_switch_data;
2515         return 1;
2516 }
2517
2518 static int
2519 do_exit_browser(struct hist_browser *browser __maybe_unused,
2520                 struct popup_action *act __maybe_unused)
2521 {
2522         return 0;
2523 }
2524
2525 static int
2526 add_exit_opt(struct hist_browser *browser __maybe_unused,
2527              struct popup_action *act, char **optstr)
2528 {
2529         if (asprintf(optstr, "Exit") < 0)
2530                 return 0;
2531
2532         act->fn = do_exit_browser;
2533         return 1;
2534 }
2535
2536 static int
2537 do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
2538 {
2539         if (!hists__has(browser->hists, socket) || act->socket < 0)
2540                 return 0;
2541
2542         if (browser->hists->socket_filter > -1) {
2543                 pstack__remove(browser->pstack, &browser->hists->socket_filter);
2544                 browser->hists->socket_filter = -1;
2545                 perf_hpp__set_elide(HISTC_SOCKET, false);
2546         } else {
2547                 browser->hists->socket_filter = act->socket;
2548                 perf_hpp__set_elide(HISTC_SOCKET, true);
2549                 pstack__push(browser->pstack, &browser->hists->socket_filter);
2550         }
2551
2552         hists__filter_by_socket(browser->hists);
2553         hist_browser__reset(browser);
2554         return 0;
2555 }
2556
2557 static int
2558 add_socket_opt(struct hist_browser *browser, struct popup_action *act,
2559                char **optstr, int socket_id)
2560 {
2561         if (!hists__has(browser->hists, socket) || socket_id < 0)
2562                 return 0;
2563
2564         if (asprintf(optstr, "Zoom %s Processor Socket %d",
2565                      (browser->hists->socket_filter > -1) ? "out of" : "into",
2566                      socket_id) < 0)
2567                 return 0;
2568
2569         act->socket = socket_id;
2570         act->fn = do_zoom_socket;
2571         return 1;
2572 }
2573
2574 static void hist_browser__update_nr_entries(struct hist_browser *hb)
2575 {
2576         u64 nr_entries = 0;
2577         struct rb_node *nd = rb_first(&hb->hists->entries);
2578
2579         if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
2580                 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
2581                 return;
2582         }
2583
2584         while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2585                 nr_entries++;
2586                 nd = rb_hierarchy_next(nd);
2587         }
2588
2589         hb->nr_non_filtered_entries = nr_entries;
2590         hb->nr_hierarchy_entries = nr_entries;
2591 }
2592
2593 static void hist_browser__update_percent_limit(struct hist_browser *hb,
2594                                                double percent)
2595 {
2596         struct hist_entry *he;
2597         struct rb_node *nd = rb_first(&hb->hists->entries);
2598         u64 total = hists__total_period(hb->hists);
2599         u64 min_callchain_hits = total * (percent / 100);
2600
2601         hb->min_pcnt = callchain_param.min_percent = percent;
2602
2603         while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2604                 he = rb_entry(nd, struct hist_entry, rb_node);
2605
2606                 if (he->has_no_entry) {
2607                         he->has_no_entry = false;
2608                         he->nr_rows = 0;
2609                 }
2610
2611                 if (!he->leaf || !symbol_conf.use_callchain)
2612                         goto next;
2613
2614                 if (callchain_param.mode == CHAIN_GRAPH_REL) {
2615                         total = he->stat.period;
2616
2617                         if (symbol_conf.cumulate_callchain)
2618                                 total = he->stat_acc->period;
2619
2620                         min_callchain_hits = total * (percent / 100);
2621                 }
2622
2623                 callchain_param.sort(&he->sorted_chain, he->callchain,
2624                                      min_callchain_hits, &callchain_param);
2625
2626 next:
2627                 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
2628
2629                 /* force to re-evaluate folding state of callchains */
2630                 he->init_have_children = false;
2631                 hist_entry__set_folding(he, hb, false);
2632         }
2633 }
2634
2635 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
2636                                     const char *helpline,
2637                                     bool left_exits,
2638                                     struct hist_browser_timer *hbt,
2639                                     float min_pcnt,
2640                                     struct perf_env *env)
2641 {
2642         struct hists *hists = evsel__hists(evsel);
2643         struct hist_browser *browser = hist_browser__new(hists, hbt, env);
2644         struct branch_info *bi;
2645 #define MAX_OPTIONS  16
2646         char *options[MAX_OPTIONS];
2647         struct popup_action actions[MAX_OPTIONS];
2648         int nr_options = 0;
2649         int key = -1;
2650         char buf[64];
2651         int delay_secs = hbt ? hbt->refresh : 0;
2652         struct perf_hpp_fmt *fmt;
2653
2654 #define HIST_BROWSER_HELP_COMMON                                        \
2655         "h/?/F1        Show this window\n"                              \
2656         "UP/DOWN/PGUP\n"                                                \
2657         "PGDN/SPACE    Navigate\n"                                      \
2658         "q/ESC/CTRL+C  Exit browser\n\n"                                \
2659         "For multiple event sessions:\n\n"                              \
2660         "TAB/UNTAB     Switch events\n\n"                               \
2661         "For symbolic views (--sort has sym):\n\n"                      \
2662         "ENTER         Zoom into DSO/Threads & Annotate current symbol\n" \
2663         "ESC           Zoom out\n"                                      \
2664         "a             Annotate current symbol\n"                       \
2665         "C             Collapse all callchains\n"                       \
2666         "d             Zoom into current DSO\n"                         \
2667         "E             Expand all callchains\n"                         \
2668         "F             Toggle percentage of filtered entries\n"         \
2669         "H             Display column headers\n"                        \
2670         "L             Change percent limit\n"                          \
2671         "m             Display context menu\n"                          \
2672         "S             Zoom into current Processor Socket\n"            \
2673
2674         /* help messages are sorted by lexical order of the hotkey */
2675         const char report_help[] = HIST_BROWSER_HELP_COMMON
2676         "i             Show header information\n"
2677         "P             Print histograms to perf.hist.N\n"
2678         "r             Run available scripts\n"
2679         "s             Switch to another data file in PWD\n"
2680         "t             Zoom into current Thread\n"
2681         "V             Verbose (DSO names in callchains, etc)\n"
2682         "/             Filter symbol by name";
2683         const char top_help[] = HIST_BROWSER_HELP_COMMON
2684         "P             Print histograms to perf.hist.N\n"
2685         "t             Zoom into current Thread\n"
2686         "V             Verbose (DSO names in callchains, etc)\n"
2687         "z             Toggle zeroing of samples\n"
2688         "f             Enable/Disable events\n"
2689         "/             Filter symbol by name";
2690
2691         if (browser == NULL)
2692                 return -1;
2693
2694         /* reset abort key so that it can get Ctrl-C as a key */
2695         SLang_reset_tty();
2696         SLang_init_tty(0, 0, 0);
2697
2698         if (min_pcnt)
2699                 browser->min_pcnt = min_pcnt;
2700         hist_browser__update_nr_entries(browser);
2701
2702         browser->pstack = pstack__new(3);
2703         if (browser->pstack == NULL)
2704                 goto out;
2705
2706         ui_helpline__push(helpline);
2707
2708         memset(options, 0, sizeof(options));
2709         memset(actions, 0, sizeof(actions));
2710
2711         hists__for_each_format(browser->hists, fmt) {
2712                 perf_hpp__reset_width(fmt, hists);
2713                 /*
2714                  * This is done just once, and activates the horizontal scrolling
2715                  * code in the ui_browser code, it would be better to have a the
2716                  * counter in the perf_hpp code, but I couldn't find doing it here
2717                  * works, FIXME by setting this in hist_browser__new, for now, be
2718                  * clever 8-)
2719                  */
2720                 ++browser->b.columns;
2721         }
2722
2723         if (symbol_conf.col_width_list_str)
2724                 perf_hpp__set_user_width(symbol_conf.col_width_list_str);
2725
2726         while (1) {
2727                 struct thread *thread = NULL;
2728                 struct map *map = NULL;
2729                 int choice = 0;
2730                 int socked_id = -1;
2731
2732                 nr_options = 0;
2733
2734                 key = hist_browser__run(browser, helpline);
2735
2736                 if (browser->he_selection != NULL) {
2737                         thread = hist_browser__selected_thread(browser);
2738                         map = browser->selection->map;
2739                         socked_id = browser->he_selection->socket;
2740                 }
2741                 switch (key) {
2742                 case K_TAB:
2743                 case K_UNTAB:
2744                         if (nr_events == 1)
2745                                 continue;
2746                         /*
2747                          * Exit the browser, let hists__browser_tree
2748                          * go to the next or previous
2749                          */
2750                         goto out_free_stack;
2751                 case 'a':
2752                         if (!hists__has(hists, sym)) {
2753                                 ui_browser__warning(&browser->b, delay_secs * 2,
2754                         "Annotation is only available for symbolic views, "
2755                         "include \"sym*\" in --sort to use it.");
2756                                 continue;
2757                         }
2758
2759                         if (browser->selection == NULL ||
2760                             browser->selection->sym == NULL ||
2761                             browser->selection->map->dso->annotate_warned)
2762                                 continue;
2763
2764                         actions->ms.map = browser->selection->map;
2765                         actions->ms.sym = browser->selection->sym;
2766                         do_annotate(browser, actions);
2767                         continue;
2768                 case 'P':
2769                         hist_browser__dump(browser);
2770                         continue;
2771                 case 'd':
2772                         actions->ms.map = map;
2773                         do_zoom_dso(browser, actions);
2774                         continue;
2775                 case 'V':
2776                         browser->show_dso = !browser->show_dso;
2777                         continue;
2778                 case 't':
2779                         actions->thread = thread;
2780                         do_zoom_thread(browser, actions);
2781                         continue;
2782                 case 'S':
2783                         actions->socket = socked_id;
2784                         do_zoom_socket(browser, actions);
2785                         continue;
2786                 case '/':
2787                         if (ui_browser__input_window("Symbol to show",
2788                                         "Please enter the name of symbol you want to see.\n"
2789                                         "To remove the filter later, press / + ENTER.",
2790                                         buf, "ENTER: OK, ESC: Cancel",
2791                                         delay_secs * 2) == K_ENTER) {
2792                                 hists->symbol_filter_str = *buf ? buf : NULL;
2793                                 hists__filter_by_symbol(hists);
2794                                 hist_browser__reset(browser);
2795                         }
2796                         continue;
2797                 case 'r':
2798                         if (is_report_browser(hbt)) {
2799                                 actions->thread = NULL;
2800                                 actions->ms.sym = NULL;
2801                                 do_run_script(browser, actions);
2802                         }
2803                         continue;
2804                 case 's':
2805                         if (is_report_browser(hbt)) {
2806                                 key = do_switch_data(browser, actions);
2807                                 if (key == K_SWITCH_INPUT_DATA)
2808                                         goto out_free_stack;
2809                         }
2810                         continue;
2811                 case 'i':
2812                         /* env->arch is NULL for live-mode (i.e. perf top) */
2813                         if (env->arch)
2814                                 tui__header_window(env);
2815                         continue;
2816                 case 'F':
2817                         symbol_conf.filter_relative ^= 1;
2818                         continue;
2819                 case 'z':
2820                         if (!is_report_browser(hbt)) {
2821                                 struct perf_top *top = hbt->arg;
2822
2823                                 top->zero = !top->zero;
2824                         }
2825                         continue;
2826                 case 'L':
2827                         if (ui_browser__input_window("Percent Limit",
2828                                         "Please enter the value you want to hide entries under that percent.",
2829                                         buf, "ENTER: OK, ESC: Cancel",
2830                                         delay_secs * 2) == K_ENTER) {
2831                                 char *end;
2832                                 double new_percent = strtod(buf, &end);
2833
2834                                 if (new_percent < 0 || new_percent > 100) {
2835                                         ui_browser__warning(&browser->b, delay_secs * 2,
2836                                                 "Invalid percent: %.2f", new_percent);
2837                                         continue;
2838                                 }
2839
2840                                 hist_browser__update_percent_limit(browser, new_percent);
2841                                 hist_browser__reset(browser);
2842                         }
2843                         continue;
2844                 case K_F1:
2845                 case 'h':
2846                 case '?':
2847                         ui_browser__help_window(&browser->b,
2848                                 is_report_browser(hbt) ? report_help : top_help);
2849                         continue;
2850                 case K_ENTER:
2851                 case K_RIGHT:
2852                 case 'm':
2853                         /* menu */
2854                         break;
2855                 case K_ESC:
2856                 case K_LEFT: {
2857                         const void *top;
2858
2859                         if (pstack__empty(browser->pstack)) {
2860                                 /*
2861                                  * Go back to the perf_evsel_menu__run or other user
2862                                  */
2863                                 if (left_exits)
2864                                         goto out_free_stack;
2865
2866                                 if (key == K_ESC &&
2867                                     ui_browser__dialog_yesno(&browser->b,
2868                                                              "Do you really want to exit?"))
2869                                         goto out_free_stack;
2870
2871                                 continue;
2872                         }
2873                         top = pstack__peek(browser->pstack);
2874                         if (top == &browser->hists->dso_filter) {
2875                                 /*
2876                                  * No need to set actions->dso here since
2877                                  * it's just to remove the current filter.
2878                                  * Ditto for thread below.
2879                                  */
2880                                 do_zoom_dso(browser, actions);
2881                         } else if (top == &browser->hists->thread_filter) {
2882                                 do_zoom_thread(browser, actions);
2883                         } else if (top == &browser->hists->socket_filter) {
2884                                 do_zoom_socket(browser, actions);
2885                         }
2886                         continue;
2887                 }
2888                 case 'q':
2889                 case CTRL('c'):
2890                         goto out_free_stack;
2891                 case 'f':
2892                         if (!is_report_browser(hbt)) {
2893                                 struct perf_top *top = hbt->arg;
2894
2895                                 perf_evlist__toggle_enable(top->evlist);
2896                                 /*
2897                                  * No need to refresh, resort/decay histogram
2898                                  * entries if we are not collecting samples:
2899                                  */
2900                                 if (top->evlist->enabled) {
2901                                         helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
2902                                         hbt->refresh = delay_secs;
2903                                 } else {
2904                                         helpline = "Press 'f' again to re-enable the events";
2905                                         hbt->refresh = 0;
2906                                 }
2907                                 continue;
2908                         }
2909                         /* Fall thru */
2910                 default:
2911                         helpline = "Press '?' for help on key bindings";
2912                         continue;
2913                 }
2914
2915                 if (!hists__has(hists, sym) || browser->selection == NULL)
2916                         goto skip_annotation;
2917
2918                 if (sort__mode == SORT_MODE__BRANCH) {
2919                         bi = browser->he_selection->branch_info;
2920
2921                         if (bi == NULL)
2922                                 goto skip_annotation;
2923
2924                         nr_options += add_annotate_opt(browser,
2925                                                        &actions[nr_options],
2926                                                        &options[nr_options],
2927                                                        bi->from.map,
2928                                                        bi->from.sym);
2929                         if (bi->to.sym != bi->from.sym)
2930                                 nr_options += add_annotate_opt(browser,
2931                                                         &actions[nr_options],
2932                                                         &options[nr_options],
2933                                                         bi->to.map,
2934                                                         bi->to.sym);
2935                 } else {
2936                         nr_options += add_annotate_opt(browser,
2937                                                        &actions[nr_options],
2938                                                        &options[nr_options],
2939                                                        browser->selection->map,
2940                                                        browser->selection->sym);
2941                 }
2942 skip_annotation:
2943                 nr_options += add_thread_opt(browser, &actions[nr_options],
2944                                              &options[nr_options], thread);
2945                 nr_options += add_dso_opt(browser, &actions[nr_options],
2946                                           &options[nr_options], map);
2947                 nr_options += add_map_opt(browser, &actions[nr_options],
2948                                           &options[nr_options],
2949                                           browser->selection ?
2950                                                 browser->selection->map : NULL);
2951                 nr_options += add_socket_opt(browser, &actions[nr_options],
2952                                              &options[nr_options],
2953                                              socked_id);
2954                 /* perf script support */
2955                 if (!is_report_browser(hbt))
2956                         goto skip_scripting;
2957
2958                 if (browser->he_selection) {
2959                         if (hists__has(hists, thread) && thread) {
2960                                 nr_options += add_script_opt(browser,
2961                                                              &actions[nr_options],
2962                                                              &options[nr_options],
2963                                                              thread, NULL);
2964                         }
2965                         /*
2966                          * Note that browser->selection != NULL
2967                          * when browser->he_selection is not NULL,
2968                          * so we don't need to check browser->selection
2969                          * before fetching browser->selection->sym like what
2970                          * we do before fetching browser->selection->map.
2971                          *
2972                          * See hist_browser__show_entry.
2973                          */
2974                         if (hists__has(hists, sym) && browser->selection->sym) {
2975                                 nr_options += add_script_opt(browser,
2976                                                              &actions[nr_options],
2977                                                              &options[nr_options],
2978                                                              NULL, browser->selection->sym);
2979                         }
2980                 }
2981                 nr_options += add_script_opt(browser, &actions[nr_options],
2982                                              &options[nr_options], NULL, NULL);
2983                 nr_options += add_switch_opt(browser, &actions[nr_options],
2984                                              &options[nr_options]);
2985 skip_scripting:
2986                 nr_options += add_exit_opt(browser, &actions[nr_options],
2987                                            &options[nr_options]);
2988
2989                 do {
2990                         struct popup_action *act;
2991
2992                         choice = ui__popup_menu(nr_options, options);
2993                         if (choice == -1 || choice >= nr_options)
2994                                 break;
2995
2996                         act = &actions[choice];
2997                         key = act->fn(browser, act);
2998                 } while (key == 1);
2999
3000                 if (key == K_SWITCH_INPUT_DATA)
3001                         break;
3002         }
3003 out_free_stack:
3004         pstack__delete(browser->pstack);
3005 out:
3006         hist_browser__delete(browser);
3007         free_popup_options(options, MAX_OPTIONS);
3008         return key;
3009 }
3010
3011 struct perf_evsel_menu {
3012         struct ui_browser b;
3013         struct perf_evsel *selection;
3014         bool lost_events, lost_events_warned;
3015         float min_pcnt;
3016         struct perf_env *env;
3017 };
3018
3019 static void perf_evsel_menu__write(struct ui_browser *browser,
3020                                    void *entry, int row)
3021 {
3022         struct perf_evsel_menu *menu = container_of(browser,
3023                                                     struct perf_evsel_menu, b);
3024         struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
3025         struct hists *hists = evsel__hists(evsel);
3026         bool current_entry = ui_browser__is_current_entry(browser, row);
3027         unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
3028         const char *ev_name = perf_evsel__name(evsel);
3029         char bf[256], unit;
3030         const char *warn = " ";
3031         size_t printed;
3032
3033         ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
3034                                                        HE_COLORSET_NORMAL);
3035
3036         if (perf_evsel__is_group_event(evsel)) {
3037                 struct perf_evsel *pos;
3038
3039                 ev_name = perf_evsel__group_name(evsel);
3040
3041                 for_each_group_member(pos, evsel) {
3042                         struct hists *pos_hists = evsel__hists(pos);
3043                         nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
3044                 }
3045         }
3046
3047         nr_events = convert_unit(nr_events, &unit);
3048         printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
3049                            unit, unit == ' ' ? "" : " ", ev_name);
3050         ui_browser__printf(browser, "%s", bf);
3051
3052         nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
3053         if (nr_events != 0) {
3054                 menu->lost_events = true;
3055                 if (!current_entry)
3056                         ui_browser__set_color(browser, HE_COLORSET_TOP);
3057                 nr_events = convert_unit(nr_events, &unit);
3058                 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
3059                                      nr_events, unit, unit == ' ' ? "" : " ");
3060                 warn = bf;
3061         }
3062
3063         ui_browser__write_nstring(browser, warn, browser->width - printed);
3064
3065         if (current_entry)
3066                 menu->selection = evsel;
3067 }
3068
3069 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
3070                                 int nr_events, const char *help,
3071                                 struct hist_browser_timer *hbt)
3072 {
3073         struct perf_evlist *evlist = menu->b.priv;
3074         struct perf_evsel *pos;
3075         const char *title = "Available samples";
3076         int delay_secs = hbt ? hbt->refresh : 0;
3077         int key;
3078
3079         if (ui_browser__show(&menu->b, title,
3080                              "ESC: exit, ENTER|->: Browse histograms") < 0)
3081                 return -1;
3082
3083         while (1) {
3084                 key = ui_browser__run(&menu->b, delay_secs);
3085
3086                 switch (key) {
3087                 case K_TIMER:
3088                         hbt->timer(hbt->arg);
3089
3090                         if (!menu->lost_events_warned && menu->lost_events) {
3091                                 ui_browser__warn_lost_events(&menu->b);
3092                                 menu->lost_events_warned = true;
3093                         }
3094                         continue;
3095                 case K_RIGHT:
3096                 case K_ENTER:
3097                         if (!menu->selection)
3098                                 continue;
3099                         pos = menu->selection;
3100 browse_hists:
3101                         perf_evlist__set_selected(evlist, pos);
3102                         /*
3103                          * Give the calling tool a chance to populate the non
3104                          * default evsel resorted hists tree.
3105                          */
3106                         if (hbt)
3107                                 hbt->timer(hbt->arg);
3108                         key = perf_evsel__hists_browse(pos, nr_events, help,
3109                                                        true, hbt,
3110                                                        menu->min_pcnt,
3111                                                        menu->env);
3112                         ui_browser__show_title(&menu->b, title);
3113                         switch (key) {
3114                         case K_TAB:
3115                                 if (pos->node.next == &evlist->entries)
3116                                         pos = perf_evlist__first(evlist);
3117                                 else
3118                                         pos = perf_evsel__next(pos);
3119                                 goto browse_hists;
3120                         case K_UNTAB:
3121                                 if (pos->node.prev == &evlist->entries)
3122                                         pos = perf_evlist__last(evlist);
3123                                 else
3124                                         pos = perf_evsel__prev(pos);
3125                                 goto browse_hists;
3126                         case K_SWITCH_INPUT_DATA:
3127                         case 'q':
3128                         case CTRL('c'):
3129                                 goto out;
3130                         case K_ESC:
3131                         default:
3132                                 continue;
3133                         }
3134                 case K_LEFT:
3135                         continue;
3136                 case K_ESC:
3137                         if (!ui_browser__dialog_yesno(&menu->b,
3138                                                "Do you really want to exit?"))
3139                                 continue;
3140                         /* Fall thru */
3141                 case 'q':
3142                 case CTRL('c'):
3143                         goto out;
3144                 default:
3145                         continue;
3146                 }
3147         }
3148
3149 out:
3150         ui_browser__hide(&menu->b);
3151         return key;
3152 }
3153
3154 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
3155                                  void *entry)
3156 {
3157         struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
3158
3159         if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
3160                 return true;
3161
3162         return false;
3163 }
3164
3165 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
3166                                            int nr_entries, const char *help,
3167                                            struct hist_browser_timer *hbt,
3168                                            float min_pcnt,
3169                                            struct perf_env *env)
3170 {
3171         struct perf_evsel *pos;
3172         struct perf_evsel_menu menu = {
3173                 .b = {
3174                         .entries    = &evlist->entries,
3175                         .refresh    = ui_browser__list_head_refresh,
3176                         .seek       = ui_browser__list_head_seek,
3177                         .write      = perf_evsel_menu__write,
3178                         .filter     = filter_group_entries,
3179                         .nr_entries = nr_entries,
3180                         .priv       = evlist,
3181                 },
3182                 .min_pcnt = min_pcnt,
3183                 .env = env,
3184         };
3185
3186         ui_helpline__push("Press ESC to exit");
3187
3188         evlist__for_each(evlist, pos) {
3189                 const char *ev_name = perf_evsel__name(pos);
3190                 size_t line_len = strlen(ev_name) + 7;
3191
3192                 if (menu.b.width < line_len)
3193                         menu.b.width = line_len;
3194         }
3195
3196         return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
3197 }
3198
3199 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
3200                                   struct hist_browser_timer *hbt,
3201                                   float min_pcnt,
3202                                   struct perf_env *env)
3203 {
3204         int nr_entries = evlist->nr_entries;
3205
3206 single_entry:
3207         if (nr_entries == 1) {
3208                 struct perf_evsel *first = perf_evlist__first(evlist);
3209
3210                 return perf_evsel__hists_browse(first, nr_entries, help,
3211                                                 false, hbt, min_pcnt,
3212                                                 env);
3213         }
3214
3215         if (symbol_conf.event_group) {
3216                 struct perf_evsel *pos;
3217
3218                 nr_entries = 0;
3219                 evlist__for_each(evlist, pos) {
3220                         if (perf_evsel__is_group_leader(pos))
3221                                 nr_entries++;
3222                 }
3223
3224                 if (nr_entries == 1)
3225                         goto single_entry;
3226         }
3227
3228         return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
3229                                                hbt, min_pcnt, env);
3230 }