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