]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - tools/perf/ui/browsers/hists.c
perf tools: Finish the removal of 'self' arguments
[karo-tx-linux.git] / tools / perf / ui / browsers / hists.c
1 #include <stdio.h>
2 #include "../libslang.h"
3 #include <stdlib.h>
4 #include <string.h>
5 #include <linux/rbtree.h>
6
7 #include "../../util/evsel.h"
8 #include "../../util/evlist.h"
9 #include "../../util/hist.h"
10 #include "../../util/pstack.h"
11 #include "../../util/sort.h"
12 #include "../../util/util.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
21 struct hist_browser {
22         struct ui_browser   b;
23         struct hists        *hists;
24         struct hist_entry   *he_selection;
25         struct map_symbol   *selection;
26         int                  print_seq;
27         bool                 show_dso;
28         float                min_pcnt;
29         u64                  nr_pcnt_entries;
30 };
31
32 extern void hist_browser__init_hpp(void);
33
34 static int hists__browser_title(struct hists *hists, char *bf, size_t size,
35                                 const char *ev_name);
36
37 static void hist_browser__refresh_dimensions(struct hist_browser *browser)
38 {
39         /* 3 == +/- toggle symbol before actual hist_entry rendering */
40         browser->b.width = 3 + (hists__sort_list_width(browser->hists) +
41                              sizeof("[k]"));
42 }
43
44 static void hist_browser__reset(struct hist_browser *browser)
45 {
46         browser->b.nr_entries = browser->hists->nr_entries;
47         hist_browser__refresh_dimensions(browser);
48         ui_browser__reset_index(&browser->b);
49 }
50
51 static char tree__folded_sign(bool unfolded)
52 {
53         return unfolded ? '-' : '+';
54 }
55
56 static char map_symbol__folded(const struct map_symbol *ms)
57 {
58         return ms->has_children ? tree__folded_sign(ms->unfolded) : ' ';
59 }
60
61 static char hist_entry__folded(const struct hist_entry *he)
62 {
63         return map_symbol__folded(&he->ms);
64 }
65
66 static char callchain_list__folded(const struct callchain_list *cl)
67 {
68         return map_symbol__folded(&cl->ms);
69 }
70
71 static void map_symbol__set_folding(struct map_symbol *ms, bool unfold)
72 {
73         ms->unfolded = unfold ? ms->has_children : false;
74 }
75
76 static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
77 {
78         int n = 0;
79         struct rb_node *nd;
80
81         for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
82                 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
83                 struct callchain_list *chain;
84                 char folded_sign = ' '; /* No children */
85
86                 list_for_each_entry(chain, &child->val, list) {
87                         ++n;
88                         /* We need this because we may not have children */
89                         folded_sign = callchain_list__folded(chain);
90                         if (folded_sign == '+')
91                                 break;
92                 }
93
94                 if (folded_sign == '-') /* Have children and they're unfolded */
95                         n += callchain_node__count_rows_rb_tree(child);
96         }
97
98         return n;
99 }
100
101 static int callchain_node__count_rows(struct callchain_node *node)
102 {
103         struct callchain_list *chain;
104         bool unfolded = false;
105         int n = 0;
106
107         list_for_each_entry(chain, &node->val, list) {
108                 ++n;
109                 unfolded = chain->ms.unfolded;
110         }
111
112         if (unfolded)
113                 n += callchain_node__count_rows_rb_tree(node);
114
115         return n;
116 }
117
118 static int callchain__count_rows(struct rb_root *chain)
119 {
120         struct rb_node *nd;
121         int n = 0;
122
123         for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
124                 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
125                 n += callchain_node__count_rows(node);
126         }
127
128         return n;
129 }
130
131 static bool map_symbol__toggle_fold(struct map_symbol *ms)
132 {
133         if (!ms)
134                 return false;
135
136         if (!ms->has_children)
137                 return false;
138
139         ms->unfolded = !ms->unfolded;
140         return true;
141 }
142
143 static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
144 {
145         struct rb_node *nd = rb_first(&node->rb_root);
146
147         for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
148                 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
149                 struct callchain_list *chain;
150                 bool first = true;
151
152                 list_for_each_entry(chain, &child->val, list) {
153                         if (first) {
154                                 first = false;
155                                 chain->ms.has_children = chain->list.next != &child->val ||
156                                                          !RB_EMPTY_ROOT(&child->rb_root);
157                         } else
158                                 chain->ms.has_children = chain->list.next == &child->val &&
159                                                          !RB_EMPTY_ROOT(&child->rb_root);
160                 }
161
162                 callchain_node__init_have_children_rb_tree(child);
163         }
164 }
165
166 static void callchain_node__init_have_children(struct callchain_node *node)
167 {
168         struct callchain_list *chain;
169
170         list_for_each_entry(chain, &node->val, list)
171                 chain->ms.has_children = !RB_EMPTY_ROOT(&node->rb_root);
172
173         callchain_node__init_have_children_rb_tree(node);
174 }
175
176 static void callchain__init_have_children(struct rb_root *root)
177 {
178         struct rb_node *nd;
179
180         for (nd = rb_first(root); nd; nd = rb_next(nd)) {
181                 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
182                 callchain_node__init_have_children(node);
183         }
184 }
185
186 static void hist_entry__init_have_children(struct hist_entry *he)
187 {
188         if (!he->init_have_children) {
189                 he->ms.has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
190                 callchain__init_have_children(&he->sorted_chain);
191                 he->init_have_children = true;
192         }
193 }
194
195 static bool hist_browser__toggle_fold(struct hist_browser *browser)
196 {
197         if (map_symbol__toggle_fold(browser->selection)) {
198                 struct hist_entry *he = browser->he_selection;
199
200                 hist_entry__init_have_children(he);
201                 browser->hists->nr_entries -= he->nr_rows;
202
203                 if (he->ms.unfolded)
204                         he->nr_rows = callchain__count_rows(&he->sorted_chain);
205                 else
206                         he->nr_rows = 0;
207                 browser->hists->nr_entries += he->nr_rows;
208                 browser->b.nr_entries = browser->hists->nr_entries;
209
210                 return true;
211         }
212
213         /* If it doesn't have children, no toggling performed */
214         return false;
215 }
216
217 static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
218 {
219         int n = 0;
220         struct rb_node *nd;
221
222         for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
223                 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
224                 struct callchain_list *chain;
225                 bool has_children = false;
226
227                 list_for_each_entry(chain, &child->val, list) {
228                         ++n;
229                         map_symbol__set_folding(&chain->ms, unfold);
230                         has_children = chain->ms.has_children;
231                 }
232
233                 if (has_children)
234                         n += callchain_node__set_folding_rb_tree(child, unfold);
235         }
236
237         return n;
238 }
239
240 static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
241 {
242         struct callchain_list *chain;
243         bool has_children = false;
244         int n = 0;
245
246         list_for_each_entry(chain, &node->val, list) {
247                 ++n;
248                 map_symbol__set_folding(&chain->ms, unfold);
249                 has_children = chain->ms.has_children;
250         }
251
252         if (has_children)
253                 n += callchain_node__set_folding_rb_tree(node, unfold);
254
255         return n;
256 }
257
258 static int callchain__set_folding(struct rb_root *chain, bool unfold)
259 {
260         struct rb_node *nd;
261         int n = 0;
262
263         for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
264                 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
265                 n += callchain_node__set_folding(node, unfold);
266         }
267
268         return n;
269 }
270
271 static void hist_entry__set_folding(struct hist_entry *he, bool unfold)
272 {
273         hist_entry__init_have_children(he);
274         map_symbol__set_folding(&he->ms, unfold);
275
276         if (he->ms.has_children) {
277                 int n = callchain__set_folding(&he->sorted_chain, unfold);
278                 he->nr_rows = unfold ? n : 0;
279         } else
280                 he->nr_rows = 0;
281 }
282
283 static void hists__set_folding(struct hists *hists, bool unfold)
284 {
285         struct rb_node *nd;
286
287         hists->nr_entries = 0;
288
289         for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
290                 struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
291                 hist_entry__set_folding(he, unfold);
292                 hists->nr_entries += 1 + he->nr_rows;
293         }
294 }
295
296 static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
297 {
298         hists__set_folding(browser->hists, unfold);
299         browser->b.nr_entries = browser->hists->nr_entries;
300         /* Go to the start, we may be way after valid entries after a collapse */
301         ui_browser__reset_index(&browser->b);
302 }
303
304 static void ui_browser__warn_lost_events(struct ui_browser *browser)
305 {
306         ui_browser__warning(browser, 4,
307                 "Events are being lost, check IO/CPU overload!\n\n"
308                 "You may want to run 'perf' using a RT scheduler policy:\n\n"
309                 " perf top -r 80\n\n"
310                 "Or reduce the sampling frequency.");
311 }
312
313 static void hist_browser__update_pcnt_entries(struct hist_browser *hb);
314
315 static int hist_browser__run(struct hist_browser *browser, const char *ev_name,
316                              struct hist_browser_timer *hbt)
317 {
318         int key;
319         char title[160];
320         int delay_secs = hbt ? hbt->refresh : 0;
321
322         browser->b.entries = &browser->hists->entries;
323         browser->b.nr_entries = browser->hists->nr_entries;
324         if (browser->min_pcnt)
325                 browser->b.nr_entries = browser->nr_pcnt_entries;
326
327         hist_browser__refresh_dimensions(browser);
328         hists__browser_title(browser->hists, title, sizeof(title), ev_name);
329
330         if (ui_browser__show(&browser->b, title,
331                              "Press '?' for help on key bindings") < 0)
332                 return -1;
333
334         while (1) {
335                 key = ui_browser__run(&browser->b, delay_secs);
336
337                 switch (key) {
338                 case K_TIMER: {
339                         u64 nr_entries;
340                         hbt->timer(hbt->arg);
341
342                         if (browser->min_pcnt) {
343                                 hist_browser__update_pcnt_entries(browser);
344                                 nr_entries = browser->nr_pcnt_entries;
345                         } else {
346                                 nr_entries = browser->hists->nr_entries;
347                         }
348
349                         ui_browser__update_nr_entries(&browser->b, nr_entries);
350
351                         if (browser->hists->stats.nr_lost_warned !=
352                             browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
353                                 browser->hists->stats.nr_lost_warned =
354                                         browser->hists->stats.nr_events[PERF_RECORD_LOST];
355                                 ui_browser__warn_lost_events(&browser->b);
356                         }
357
358                         hists__browser_title(browser->hists, title, sizeof(title), ev_name);
359                         ui_browser__show_title(&browser->b, title);
360                         continue;
361                 }
362                 case 'D': { /* Debug */
363                         static int seq;
364                         struct hist_entry *h = rb_entry(browser->b.top,
365                                                         struct hist_entry, rb_node);
366                         ui_helpline__pop();
367                         ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
368                                            seq++, browser->b.nr_entries,
369                                            browser->hists->nr_entries,
370                                            browser->b.height,
371                                            browser->b.index,
372                                            browser->b.top_idx,
373                                            h->row_offset, h->nr_rows);
374                 }
375                         break;
376                 case 'C':
377                         /* Collapse the whole world. */
378                         hist_browser__set_folding(browser, false);
379                         break;
380                 case 'E':
381                         /* Expand the whole world. */
382                         hist_browser__set_folding(browser, true);
383                         break;
384                 case K_ENTER:
385                         if (hist_browser__toggle_fold(browser))
386                                 break;
387                         /* fall thru */
388                 default:
389                         goto out;
390                 }
391         }
392 out:
393         ui_browser__hide(&browser->b);
394         return key;
395 }
396
397 static char *callchain_list__sym_name(struct callchain_list *cl,
398                                       char *bf, size_t bfsize, bool show_dso)
399 {
400         int printed;
401
402         if (cl->ms.sym)
403                 printed = scnprintf(bf, bfsize, "%s", cl->ms.sym->name);
404         else
405                 printed = scnprintf(bf, bfsize, "%#" PRIx64, cl->ip);
406
407         if (show_dso)
408                 scnprintf(bf + printed, bfsize - printed, " %s",
409                           cl->ms.map ? cl->ms.map->dso->short_name : "unknown");
410
411         return bf;
412 }
413
414 #define LEVEL_OFFSET_STEP 3
415
416 static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browser,
417                                                      struct callchain_node *chain_node,
418                                                      u64 total, int level,
419                                                      unsigned short row,
420                                                      off_t *row_offset,
421                                                      bool *is_current_entry)
422 {
423         struct rb_node *node;
424         int first_row = row, width, offset = level * LEVEL_OFFSET_STEP;
425         u64 new_total, remaining;
426
427         if (callchain_param.mode == CHAIN_GRAPH_REL)
428                 new_total = chain_node->children_hit;
429         else
430                 new_total = total;
431
432         remaining = new_total;
433         node = rb_first(&chain_node->rb_root);
434         while (node) {
435                 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
436                 struct rb_node *next = rb_next(node);
437                 u64 cumul = callchain_cumul_hits(child);
438                 struct callchain_list *chain;
439                 char folded_sign = ' ';
440                 int first = true;
441                 int extra_offset = 0;
442
443                 remaining -= cumul;
444
445                 list_for_each_entry(chain, &child->val, list) {
446                         char bf[1024], *alloc_str;
447                         const char *str;
448                         int color;
449                         bool was_first = first;
450
451                         if (first)
452                                 first = false;
453                         else
454                                 extra_offset = LEVEL_OFFSET_STEP;
455
456                         folded_sign = callchain_list__folded(chain);
457                         if (*row_offset != 0) {
458                                 --*row_offset;
459                                 goto do_next;
460                         }
461
462                         alloc_str = NULL;
463                         str = callchain_list__sym_name(chain, bf, sizeof(bf),
464                                                        browser->show_dso);
465                         if (was_first) {
466                                 double percent = cumul * 100.0 / new_total;
467
468                                 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
469                                         str = "Not enough memory!";
470                                 else
471                                         str = alloc_str;
472                         }
473
474                         color = HE_COLORSET_NORMAL;
475                         width = browser->b.width - (offset + extra_offset + 2);
476                         if (ui_browser__is_current_entry(&browser->b, row)) {
477                                 browser->selection = &chain->ms;
478                                 color = HE_COLORSET_SELECTED;
479                                 *is_current_entry = true;
480                         }
481
482                         ui_browser__set_color(&browser->b, color);
483                         ui_browser__gotorc(&browser->b, row, 0);
484                         slsmg_write_nstring(" ", offset + extra_offset);
485                         slsmg_printf("%c ", folded_sign);
486                         slsmg_write_nstring(str, width);
487                         free(alloc_str);
488
489                         if (++row == browser->b.height)
490                                 goto out;
491 do_next:
492                         if (folded_sign == '+')
493                                 break;
494                 }
495
496                 if (folded_sign == '-') {
497                         const int new_level = level + (extra_offset ? 2 : 1);
498                         row += hist_browser__show_callchain_node_rb_tree(browser, child, new_total,
499                                                                          new_level, row, row_offset,
500                                                                          is_current_entry);
501                 }
502                 if (row == browser->b.height)
503                         goto out;
504                 node = next;
505         }
506 out:
507         return row - first_row;
508 }
509
510 static int hist_browser__show_callchain_node(struct hist_browser *browser,
511                                              struct callchain_node *node,
512                                              int level, unsigned short row,
513                                              off_t *row_offset,
514                                              bool *is_current_entry)
515 {
516         struct callchain_list *chain;
517         int first_row = row,
518              offset = level * LEVEL_OFFSET_STEP,
519              width = browser->b.width - offset;
520         char folded_sign = ' ';
521
522         list_for_each_entry(chain, &node->val, list) {
523                 char bf[1024], *s;
524                 int color;
525
526                 folded_sign = callchain_list__folded(chain);
527
528                 if (*row_offset != 0) {
529                         --*row_offset;
530                         continue;
531                 }
532
533                 color = HE_COLORSET_NORMAL;
534                 if (ui_browser__is_current_entry(&browser->b, row)) {
535                         browser->selection = &chain->ms;
536                         color = HE_COLORSET_SELECTED;
537                         *is_current_entry = true;
538                 }
539
540                 s = callchain_list__sym_name(chain, bf, sizeof(bf),
541                                              browser->show_dso);
542                 ui_browser__gotorc(&browser->b, row, 0);
543                 ui_browser__set_color(&browser->b, color);
544                 slsmg_write_nstring(" ", offset);
545                 slsmg_printf("%c ", folded_sign);
546                 slsmg_write_nstring(s, width - 2);
547
548                 if (++row == browser->b.height)
549                         goto out;
550         }
551
552         if (folded_sign == '-')
553                 row += hist_browser__show_callchain_node_rb_tree(browser, node,
554                                                                  browser->hists->stats.total_period,
555                                                                  level + 1, row,
556                                                                  row_offset,
557                                                                  is_current_entry);
558 out:
559         return row - first_row;
560 }
561
562 static int hist_browser__show_callchain(struct hist_browser *browser,
563                                         struct rb_root *chain,
564                                         int level, unsigned short row,
565                                         off_t *row_offset,
566                                         bool *is_current_entry)
567 {
568         struct rb_node *nd;
569         int first_row = row;
570
571         for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
572                 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
573
574                 row += hist_browser__show_callchain_node(browser, node, level,
575                                                          row, row_offset,
576                                                          is_current_entry);
577                 if (row == browser->b.height)
578                         break;
579         }
580
581         return row - first_row;
582 }
583
584 struct hpp_arg {
585         struct ui_browser *b;
586         char folded_sign;
587         bool current_entry;
588 };
589
590 static int __hpp__color_callchain(struct hpp_arg *arg)
591 {
592         if (!symbol_conf.use_callchain)
593                 return 0;
594
595         slsmg_printf("%c ", arg->folded_sign);
596         return 2;
597 }
598
599 static int __hpp__color_fmt(struct perf_hpp *hpp, struct hist_entry *he,
600                             u64 (*get_field)(struct hist_entry *),
601                             int (*callchain_cb)(struct hpp_arg *))
602 {
603         int ret = 0;
604         double percent = 0.0;
605         struct hists *hists = he->hists;
606         struct hpp_arg *arg = hpp->ptr;
607
608         if (hists->stats.total_period)
609                 percent = 100.0 * get_field(he) / hists->stats.total_period;
610
611         ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
612
613         if (callchain_cb)
614                 ret += callchain_cb(arg);
615
616         ret += scnprintf(hpp->buf, hpp->size, "%6.2f%%", percent);
617         slsmg_printf("%s", hpp->buf);
618
619         if (symbol_conf.event_group) {
620                 int prev_idx, idx_delta;
621                 struct perf_evsel *evsel = hists_to_evsel(hists);
622                 struct hist_entry *pair;
623                 int nr_members = evsel->nr_members;
624
625                 if (nr_members <= 1)
626                         goto out;
627
628                 prev_idx = perf_evsel__group_idx(evsel);
629
630                 list_for_each_entry(pair, &he->pairs.head, pairs.node) {
631                         u64 period = get_field(pair);
632                         u64 total = pair->hists->stats.total_period;
633
634                         if (!total)
635                                 continue;
636
637                         evsel = hists_to_evsel(pair->hists);
638                         idx_delta = perf_evsel__group_idx(evsel) - prev_idx - 1;
639
640                         while (idx_delta--) {
641                                 /*
642                                  * zero-fill group members in the middle which
643                                  * have no sample
644                                  */
645                                 ui_browser__set_percent_color(arg->b, 0.0,
646                                                         arg->current_entry);
647                                 ret += scnprintf(hpp->buf, hpp->size,
648                                                  " %6.2f%%", 0.0);
649                                 slsmg_printf("%s", hpp->buf);
650                         }
651
652                         percent = 100.0 * period / total;
653                         ui_browser__set_percent_color(arg->b, percent,
654                                                       arg->current_entry);
655                         ret += scnprintf(hpp->buf, hpp->size,
656                                          " %6.2f%%", percent);
657                         slsmg_printf("%s", hpp->buf);
658
659                         prev_idx = perf_evsel__group_idx(evsel);
660                 }
661
662                 idx_delta = nr_members - prev_idx - 1;
663
664                 while (idx_delta--) {
665                         /*
666                          * zero-fill group members at last which have no sample
667                          */
668                         ui_browser__set_percent_color(arg->b, 0.0,
669                                                       arg->current_entry);
670                         ret += scnprintf(hpp->buf, hpp->size,
671                                          " %6.2f%%", 0.0);
672                         slsmg_printf("%s", hpp->buf);
673                 }
674         }
675 out:
676         if (!arg->current_entry || !arg->b->navkeypressed)
677                 ui_browser__set_color(arg->b, HE_COLORSET_NORMAL);
678
679         return ret;
680 }
681
682 #define __HPP_COLOR_PERCENT_FN(_type, _field, _cb)                      \
683 static u64 __hpp_get_##_field(struct hist_entry *he)                    \
684 {                                                                       \
685         return he->stat._field;                                         \
686 }                                                                       \
687                                                                         \
688 static int                                                              \
689 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,\
690                                 struct perf_hpp *hpp,                   \
691                                 struct hist_entry *he)                  \
692 {                                                                       \
693         return __hpp__color_fmt(hpp, he, __hpp_get_##_field, _cb);      \
694 }
695
696 __HPP_COLOR_PERCENT_FN(overhead, period, __hpp__color_callchain)
697 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys, NULL)
698 __HPP_COLOR_PERCENT_FN(overhead_us, period_us, NULL)
699 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys, NULL)
700 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us, NULL)
701
702 #undef __HPP_COLOR_PERCENT_FN
703
704 void hist_browser__init_hpp(void)
705 {
706         perf_hpp__init();
707
708         perf_hpp__format[PERF_HPP__OVERHEAD].color =
709                                 hist_browser__hpp_color_overhead;
710         perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
711                                 hist_browser__hpp_color_overhead_sys;
712         perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
713                                 hist_browser__hpp_color_overhead_us;
714         perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
715                                 hist_browser__hpp_color_overhead_guest_sys;
716         perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
717                                 hist_browser__hpp_color_overhead_guest_us;
718 }
719
720 static int hist_browser__show_entry(struct hist_browser *browser,
721                                     struct hist_entry *entry,
722                                     unsigned short row)
723 {
724         char s[256];
725         int printed = 0;
726         int width = browser->b.width;
727         char folded_sign = ' ';
728         bool current_entry = ui_browser__is_current_entry(&browser->b, row);
729         off_t row_offset = entry->row_offset;
730         bool first = true;
731         struct perf_hpp_fmt *fmt;
732
733         if (current_entry) {
734                 browser->he_selection = entry;
735                 browser->selection = &entry->ms;
736         }
737
738         if (symbol_conf.use_callchain) {
739                 hist_entry__init_have_children(entry);
740                 folded_sign = hist_entry__folded(entry);
741         }
742
743         if (row_offset == 0) {
744                 struct hpp_arg arg = {
745                         .b              = &browser->b,
746                         .folded_sign    = folded_sign,
747                         .current_entry  = current_entry,
748                 };
749                 struct perf_hpp hpp = {
750                         .buf            = s,
751                         .size           = sizeof(s),
752                         .ptr            = &arg,
753                 };
754
755                 ui_browser__gotorc(&browser->b, row, 0);
756
757                 perf_hpp__for_each_format(fmt) {
758                         if (!first) {
759                                 slsmg_printf("  ");
760                                 width -= 2;
761                         }
762                         first = false;
763
764                         if (fmt->color) {
765                                 width -= fmt->color(fmt, &hpp, entry);
766                         } else {
767                                 width -= fmt->entry(fmt, &hpp, entry);
768                                 slsmg_printf("%s", s);
769                         }
770                 }
771
772                 /* The scroll bar isn't being used */
773                 if (!browser->b.navkeypressed)
774                         width += 1;
775
776                 hist_entry__sort_snprintf(entry, s, sizeof(s), browser->hists);
777                 slsmg_write_nstring(s, width);
778                 ++row;
779                 ++printed;
780         } else
781                 --row_offset;
782
783         if (folded_sign == '-' && row != browser->b.height) {
784                 printed += hist_browser__show_callchain(browser, &entry->sorted_chain,
785                                                         1, row, &row_offset,
786                                                         &current_entry);
787                 if (current_entry)
788                         browser->he_selection = entry;
789         }
790
791         return printed;
792 }
793
794 static void ui_browser__hists_init_top(struct ui_browser *browser)
795 {
796         if (browser->top == NULL) {
797                 struct hist_browser *hb;
798
799                 hb = container_of(browser, struct hist_browser, b);
800                 browser->top = rb_first(&hb->hists->entries);
801         }
802 }
803
804 static unsigned int hist_browser__refresh(struct ui_browser *browser)
805 {
806         unsigned row = 0;
807         struct rb_node *nd;
808         struct hist_browser *hb = container_of(browser, struct hist_browser, b);
809
810         ui_browser__hists_init_top(browser);
811
812         for (nd = browser->top; nd; nd = rb_next(nd)) {
813                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
814                 float percent = h->stat.period * 100.0 /
815                                         hb->hists->stats.total_period;
816
817                 if (h->filtered)
818                         continue;
819
820                 if (percent < hb->min_pcnt)
821                         continue;
822
823                 row += hist_browser__show_entry(hb, h, row);
824                 if (row == browser->height)
825                         break;
826         }
827
828         return row;
829 }
830
831 static struct rb_node *hists__filter_entries(struct rb_node *nd,
832                                              struct hists *hists,
833                                              float min_pcnt)
834 {
835         while (nd != NULL) {
836                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
837                 float percent = h->stat.period * 100.0 /
838                                         hists->stats.total_period;
839
840                 if (percent < min_pcnt)
841                         return NULL;
842
843                 if (!h->filtered)
844                         return nd;
845
846                 nd = rb_next(nd);
847         }
848
849         return NULL;
850 }
851
852 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
853                                                   struct hists *hists,
854                                                   float min_pcnt)
855 {
856         while (nd != NULL) {
857                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
858                 float percent = h->stat.period * 100.0 /
859                                         hists->stats.total_period;
860
861                 if (!h->filtered && percent >= min_pcnt)
862                         return nd;
863
864                 nd = rb_prev(nd);
865         }
866
867         return NULL;
868 }
869
870 static void ui_browser__hists_seek(struct ui_browser *browser,
871                                    off_t offset, int whence)
872 {
873         struct hist_entry *h;
874         struct rb_node *nd;
875         bool first = true;
876         struct hist_browser *hb;
877
878         hb = container_of(browser, struct hist_browser, b);
879
880         if (browser->nr_entries == 0)
881                 return;
882
883         ui_browser__hists_init_top(browser);
884
885         switch (whence) {
886         case SEEK_SET:
887                 nd = hists__filter_entries(rb_first(browser->entries),
888                                            hb->hists, hb->min_pcnt);
889                 break;
890         case SEEK_CUR:
891                 nd = browser->top;
892                 goto do_offset;
893         case SEEK_END:
894                 nd = hists__filter_prev_entries(rb_last(browser->entries),
895                                                 hb->hists, hb->min_pcnt);
896                 first = false;
897                 break;
898         default:
899                 return;
900         }
901
902         /*
903          * Moves not relative to the first visible entry invalidates its
904          * row_offset:
905          */
906         h = rb_entry(browser->top, struct hist_entry, rb_node);
907         h->row_offset = 0;
908
909         /*
910          * Here we have to check if nd is expanded (+), if it is we can't go
911          * the next top level hist_entry, instead we must compute an offset of
912          * what _not_ to show and not change the first visible entry.
913          *
914          * This offset increments when we are going from top to bottom and
915          * decreases when we're going from bottom to top.
916          *
917          * As we don't have backpointers to the top level in the callchains
918          * structure, we need to always print the whole hist_entry callchain,
919          * skipping the first ones that are before the first visible entry
920          * and stop when we printed enough lines to fill the screen.
921          */
922 do_offset:
923         if (offset > 0) {
924                 do {
925                         h = rb_entry(nd, struct hist_entry, rb_node);
926                         if (h->ms.unfolded) {
927                                 u16 remaining = h->nr_rows - h->row_offset;
928                                 if (offset > remaining) {
929                                         offset -= remaining;
930                                         h->row_offset = 0;
931                                 } else {
932                                         h->row_offset += offset;
933                                         offset = 0;
934                                         browser->top = nd;
935                                         break;
936                                 }
937                         }
938                         nd = hists__filter_entries(rb_next(nd), hb->hists,
939                                                    hb->min_pcnt);
940                         if (nd == NULL)
941                                 break;
942                         --offset;
943                         browser->top = nd;
944                 } while (offset != 0);
945         } else if (offset < 0) {
946                 while (1) {
947                         h = rb_entry(nd, struct hist_entry, rb_node);
948                         if (h->ms.unfolded) {
949                                 if (first) {
950                                         if (-offset > h->row_offset) {
951                                                 offset += h->row_offset;
952                                                 h->row_offset = 0;
953                                         } else {
954                                                 h->row_offset += offset;
955                                                 offset = 0;
956                                                 browser->top = nd;
957                                                 break;
958                                         }
959                                 } else {
960                                         if (-offset > h->nr_rows) {
961                                                 offset += h->nr_rows;
962                                                 h->row_offset = 0;
963                                         } else {
964                                                 h->row_offset = h->nr_rows + offset;
965                                                 offset = 0;
966                                                 browser->top = nd;
967                                                 break;
968                                         }
969                                 }
970                         }
971
972                         nd = hists__filter_prev_entries(rb_prev(nd), hb->hists,
973                                                         hb->min_pcnt);
974                         if (nd == NULL)
975                                 break;
976                         ++offset;
977                         browser->top = nd;
978                         if (offset == 0) {
979                                 /*
980                                  * Last unfiltered hist_entry, check if it is
981                                  * unfolded, if it is then we should have
982                                  * row_offset at its last entry.
983                                  */
984                                 h = rb_entry(nd, struct hist_entry, rb_node);
985                                 if (h->ms.unfolded)
986                                         h->row_offset = h->nr_rows;
987                                 break;
988                         }
989                         first = false;
990                 }
991         } else {
992                 browser->top = nd;
993                 h = rb_entry(nd, struct hist_entry, rb_node);
994                 h->row_offset = 0;
995         }
996 }
997
998 static int hist_browser__fprintf_callchain_node_rb_tree(struct hist_browser *browser,
999                                                         struct callchain_node *chain_node,
1000                                                         u64 total, int level,
1001                                                         FILE *fp)
1002 {
1003         struct rb_node *node;
1004         int offset = level * LEVEL_OFFSET_STEP;
1005         u64 new_total, remaining;
1006         int printed = 0;
1007
1008         if (callchain_param.mode == CHAIN_GRAPH_REL)
1009                 new_total = chain_node->children_hit;
1010         else
1011                 new_total = total;
1012
1013         remaining = new_total;
1014         node = rb_first(&chain_node->rb_root);
1015         while (node) {
1016                 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1017                 struct rb_node *next = rb_next(node);
1018                 u64 cumul = callchain_cumul_hits(child);
1019                 struct callchain_list *chain;
1020                 char folded_sign = ' ';
1021                 int first = true;
1022                 int extra_offset = 0;
1023
1024                 remaining -= cumul;
1025
1026                 list_for_each_entry(chain, &child->val, list) {
1027                         char bf[1024], *alloc_str;
1028                         const char *str;
1029                         bool was_first = first;
1030
1031                         if (first)
1032                                 first = false;
1033                         else
1034                                 extra_offset = LEVEL_OFFSET_STEP;
1035
1036                         folded_sign = callchain_list__folded(chain);
1037
1038                         alloc_str = NULL;
1039                         str = callchain_list__sym_name(chain, bf, sizeof(bf),
1040                                                        browser->show_dso);
1041                         if (was_first) {
1042                                 double percent = cumul * 100.0 / new_total;
1043
1044                                 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
1045                                         str = "Not enough memory!";
1046                                 else
1047                                         str = alloc_str;
1048                         }
1049
1050                         printed += fprintf(fp, "%*s%c %s\n", offset + extra_offset, " ", folded_sign, str);
1051                         free(alloc_str);
1052                         if (folded_sign == '+')
1053                                 break;
1054                 }
1055
1056                 if (folded_sign == '-') {
1057                         const int new_level = level + (extra_offset ? 2 : 1);
1058                         printed += hist_browser__fprintf_callchain_node_rb_tree(browser, child, new_total,
1059                                                                                 new_level, fp);
1060                 }
1061
1062                 node = next;
1063         }
1064
1065         return printed;
1066 }
1067
1068 static int hist_browser__fprintf_callchain_node(struct hist_browser *browser,
1069                                                 struct callchain_node *node,
1070                                                 int level, FILE *fp)
1071 {
1072         struct callchain_list *chain;
1073         int offset = level * LEVEL_OFFSET_STEP;
1074         char folded_sign = ' ';
1075         int printed = 0;
1076
1077         list_for_each_entry(chain, &node->val, list) {
1078                 char bf[1024], *s;
1079
1080                 folded_sign = callchain_list__folded(chain);
1081                 s = callchain_list__sym_name(chain, bf, sizeof(bf), browser->show_dso);
1082                 printed += fprintf(fp, "%*s%c %s\n", offset, " ", folded_sign, s);
1083         }
1084
1085         if (folded_sign == '-')
1086                 printed += hist_browser__fprintf_callchain_node_rb_tree(browser, node,
1087                                                                         browser->hists->stats.total_period,
1088                                                                         level + 1,  fp);
1089         return printed;
1090 }
1091
1092 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1093                                            struct rb_root *chain, int level, FILE *fp)
1094 {
1095         struct rb_node *nd;
1096         int printed = 0;
1097
1098         for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
1099                 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
1100
1101                 printed += hist_browser__fprintf_callchain_node(browser, node, level, fp);
1102         }
1103
1104         return printed;
1105 }
1106
1107 static int hist_browser__fprintf_entry(struct hist_browser *browser,
1108                                        struct hist_entry *he, FILE *fp)
1109 {
1110         char s[8192];
1111         double percent;
1112         int printed = 0;
1113         char folded_sign = ' ';
1114
1115         if (symbol_conf.use_callchain)
1116                 folded_sign = hist_entry__folded(he);
1117
1118         hist_entry__sort_snprintf(he, s, sizeof(s), browser->hists);
1119         percent = (he->stat.period * 100.0) / browser->hists->stats.total_period;
1120
1121         if (symbol_conf.use_callchain)
1122                 printed += fprintf(fp, "%c ", folded_sign);
1123
1124         printed += fprintf(fp, " %5.2f%%", percent);
1125
1126         if (symbol_conf.show_nr_samples)
1127                 printed += fprintf(fp, " %11u", he->stat.nr_events);
1128
1129         if (symbol_conf.show_total_period)
1130                 printed += fprintf(fp, " %12" PRIu64, he->stat.period);
1131
1132         printed += fprintf(fp, "%s\n", rtrim(s));
1133
1134         if (folded_sign == '-')
1135                 printed += hist_browser__fprintf_callchain(browser, &he->sorted_chain, 1, fp);
1136
1137         return printed;
1138 }
1139
1140 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1141 {
1142         struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
1143                                                    browser->hists,
1144                                                    browser->min_pcnt);
1145         int printed = 0;
1146
1147         while (nd) {
1148                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1149
1150                 printed += hist_browser__fprintf_entry(browser, h, fp);
1151                 nd = hists__filter_entries(rb_next(nd), browser->hists,
1152                                            browser->min_pcnt);
1153         }
1154
1155         return printed;
1156 }
1157
1158 static int hist_browser__dump(struct hist_browser *browser)
1159 {
1160         char filename[64];
1161         FILE *fp;
1162
1163         while (1) {
1164                 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
1165                 if (access(filename, F_OK))
1166                         break;
1167                 /*
1168                  * XXX: Just an arbitrary lazy upper limit
1169                  */
1170                 if (++browser->print_seq == 8192) {
1171                         ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1172                         return -1;
1173                 }
1174         }
1175
1176         fp = fopen(filename, "w");
1177         if (fp == NULL) {
1178                 char bf[64];
1179                 const char *err = strerror_r(errno, bf, sizeof(bf));
1180                 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
1181                 return -1;
1182         }
1183
1184         ++browser->print_seq;
1185         hist_browser__fprintf(browser, fp);
1186         fclose(fp);
1187         ui_helpline__fpush("%s written!", filename);
1188
1189         return 0;
1190 }
1191
1192 static struct hist_browser *hist_browser__new(struct hists *hists)
1193 {
1194         struct hist_browser *browser = zalloc(sizeof(*browser));
1195
1196         if (browser) {
1197                 browser->hists = hists;
1198                 browser->b.refresh = hist_browser__refresh;
1199                 browser->b.seek = ui_browser__hists_seek;
1200                 browser->b.use_navkeypressed = true;
1201         }
1202
1203         return browser;
1204 }
1205
1206 static void hist_browser__delete(struct hist_browser *browser)
1207 {
1208         free(browser);
1209 }
1210
1211 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
1212 {
1213         return browser->he_selection;
1214 }
1215
1216 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
1217 {
1218         return browser->he_selection->thread;
1219 }
1220
1221 static int hists__browser_title(struct hists *hists, char *bf, size_t size,
1222                                 const char *ev_name)
1223 {
1224         char unit;
1225         int printed;
1226         const struct dso *dso = hists->dso_filter;
1227         const struct thread *thread = hists->thread_filter;
1228         unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1229         u64 nr_events = hists->stats.total_period;
1230         struct perf_evsel *evsel = hists_to_evsel(hists);
1231         char buf[512];
1232         size_t buflen = sizeof(buf);
1233
1234         if (perf_evsel__is_group_event(evsel)) {
1235                 struct perf_evsel *pos;
1236
1237                 perf_evsel__group_desc(evsel, buf, buflen);
1238                 ev_name = buf;
1239
1240                 for_each_group_member(pos, evsel) {
1241                         nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1242                         nr_events += pos->hists.stats.total_period;
1243                 }
1244         }
1245
1246         nr_samples = convert_unit(nr_samples, &unit);
1247         printed = scnprintf(bf, size,
1248                            "Samples: %lu%c of event '%s', Event count (approx.): %lu",
1249                            nr_samples, unit, ev_name, nr_events);
1250
1251
1252         if (hists->uid_filter_str)
1253                 printed += snprintf(bf + printed, size - printed,
1254                                     ", UID: %s", hists->uid_filter_str);
1255         if (thread)
1256                 printed += scnprintf(bf + printed, size - printed,
1257                                     ", Thread: %s(%d)",
1258                                      (thread->comm_set ? thread__comm_str(thread) : ""),
1259                                     thread->tid);
1260         if (dso)
1261                 printed += scnprintf(bf + printed, size - printed,
1262                                     ", DSO: %s", dso->short_name);
1263         return printed;
1264 }
1265
1266 static inline void free_popup_options(char **options, int n)
1267 {
1268         int i;
1269
1270         for (i = 0; i < n; ++i) {
1271                 free(options[i]);
1272                 options[i] = NULL;
1273         }
1274 }
1275
1276 /* Check whether the browser is for 'top' or 'report' */
1277 static inline bool is_report_browser(void *timer)
1278 {
1279         return timer == NULL;
1280 }
1281
1282 /*
1283  * Only runtime switching of perf data file will make "input_name" point
1284  * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1285  * whether we need to call free() for current "input_name" during the switch.
1286  */
1287 static bool is_input_name_malloced = false;
1288
1289 static int switch_data_file(void)
1290 {
1291         char *pwd, *options[32], *abs_path[32], *tmp;
1292         DIR *pwd_dir;
1293         int nr_options = 0, choice = -1, ret = -1;
1294         struct dirent *dent;
1295
1296         pwd = getenv("PWD");
1297         if (!pwd)
1298                 return ret;
1299
1300         pwd_dir = opendir(pwd);
1301         if (!pwd_dir)
1302                 return ret;
1303
1304         memset(options, 0, sizeof(options));
1305         memset(options, 0, sizeof(abs_path));
1306
1307         while ((dent = readdir(pwd_dir))) {
1308                 char path[PATH_MAX];
1309                 u64 magic;
1310                 char *name = dent->d_name;
1311                 FILE *file;
1312
1313                 if (!(dent->d_type == DT_REG))
1314                         continue;
1315
1316                 snprintf(path, sizeof(path), "%s/%s", pwd, name);
1317
1318                 file = fopen(path, "r");
1319                 if (!file)
1320                         continue;
1321
1322                 if (fread(&magic, 1, 8, file) < 8)
1323                         goto close_file_and_continue;
1324
1325                 if (is_perf_magic(magic)) {
1326                         options[nr_options] = strdup(name);
1327                         if (!options[nr_options])
1328                                 goto close_file_and_continue;
1329
1330                         abs_path[nr_options] = strdup(path);
1331                         if (!abs_path[nr_options]) {
1332                                 free(options[nr_options]);
1333                                 ui__warning("Can't search all data files due to memory shortage.\n");
1334                                 fclose(file);
1335                                 break;
1336                         }
1337
1338                         nr_options++;
1339                 }
1340
1341 close_file_and_continue:
1342                 fclose(file);
1343                 if (nr_options >= 32) {
1344                         ui__warning("Too many perf data files in PWD!\n"
1345                                     "Only the first 32 files will be listed.\n");
1346                         break;
1347                 }
1348         }
1349         closedir(pwd_dir);
1350
1351         if (nr_options) {
1352                 choice = ui__popup_menu(nr_options, options);
1353                 if (choice < nr_options && choice >= 0) {
1354                         tmp = strdup(abs_path[choice]);
1355                         if (tmp) {
1356                                 if (is_input_name_malloced)
1357                                         free((void *)input_name);
1358                                 input_name = tmp;
1359                                 is_input_name_malloced = true;
1360                                 ret = 0;
1361                         } else
1362                                 ui__warning("Data switch failed due to memory shortage!\n");
1363                 }
1364         }
1365
1366         free_popup_options(options, nr_options);
1367         free_popup_options(abs_path, nr_options);
1368         return ret;
1369 }
1370
1371 static void hist_browser__update_pcnt_entries(struct hist_browser *hb)
1372 {
1373         u64 nr_entries = 0;
1374         struct rb_node *nd = rb_first(&hb->hists->entries);
1375
1376         while (nd) {
1377                 nr_entries++;
1378                 nd = hists__filter_entries(rb_next(nd), hb->hists,
1379                                            hb->min_pcnt);
1380         }
1381
1382         hb->nr_pcnt_entries = nr_entries;
1383 }
1384
1385 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
1386                                     const char *helpline, const char *ev_name,
1387                                     bool left_exits,
1388                                     struct hist_browser_timer *hbt,
1389                                     float min_pcnt,
1390                                     struct perf_session_env *env)
1391 {
1392         struct hists *hists = &evsel->hists;
1393         struct hist_browser *browser = hist_browser__new(hists);
1394         struct branch_info *bi;
1395         struct pstack *fstack;
1396         char *options[16];
1397         int nr_options = 0;
1398         int key = -1;
1399         char buf[64];
1400         char script_opt[64];
1401         int delay_secs = hbt ? hbt->refresh : 0;
1402
1403         if (browser == NULL)
1404                 return -1;
1405
1406         if (min_pcnt) {
1407                 browser->min_pcnt = min_pcnt;
1408                 hist_browser__update_pcnt_entries(browser);
1409         }
1410
1411         fstack = pstack__new(2);
1412         if (fstack == NULL)
1413                 goto out;
1414
1415         ui_helpline__push(helpline);
1416
1417         memset(options, 0, sizeof(options));
1418
1419         while (1) {
1420                 const struct thread *thread = NULL;
1421                 const struct dso *dso = NULL;
1422                 int choice = 0,
1423                     annotate = -2, zoom_dso = -2, zoom_thread = -2,
1424                     annotate_f = -2, annotate_t = -2, browse_map = -2;
1425                 int scripts_comm = -2, scripts_symbol = -2,
1426                     scripts_all = -2, switch_data = -2;
1427
1428                 nr_options = 0;
1429
1430                 key = hist_browser__run(browser, ev_name, hbt);
1431
1432                 if (browser->he_selection != NULL) {
1433                         thread = hist_browser__selected_thread(browser);
1434                         dso = browser->selection->map ? browser->selection->map->dso : NULL;
1435                 }
1436                 switch (key) {
1437                 case K_TAB:
1438                 case K_UNTAB:
1439                         if (nr_events == 1)
1440                                 continue;
1441                         /*
1442                          * Exit the browser, let hists__browser_tree
1443                          * go to the next or previous
1444                          */
1445                         goto out_free_stack;
1446                 case 'a':
1447                         if (!sort__has_sym) {
1448                                 ui_browser__warning(&browser->b, delay_secs * 2,
1449                         "Annotation is only available for symbolic views, "
1450                         "include \"sym*\" in --sort to use it.");
1451                                 continue;
1452                         }
1453
1454                         if (browser->selection == NULL ||
1455                             browser->selection->sym == NULL ||
1456                             browser->selection->map->dso->annotate_warned)
1457                                 continue;
1458                         goto do_annotate;
1459                 case 'P':
1460                         hist_browser__dump(browser);
1461                         continue;
1462                 case 'd':
1463                         goto zoom_dso;
1464                 case 'V':
1465                         browser->show_dso = !browser->show_dso;
1466                         continue;
1467                 case 't':
1468                         goto zoom_thread;
1469                 case '/':
1470                         if (ui_browser__input_window("Symbol to show",
1471                                         "Please enter the name of symbol you want to see",
1472                                         buf, "ENTER: OK, ESC: Cancel",
1473                                         delay_secs * 2) == K_ENTER) {
1474                                 hists->symbol_filter_str = *buf ? buf : NULL;
1475                                 hists__filter_by_symbol(hists);
1476                                 hist_browser__reset(browser);
1477                         }
1478                         continue;
1479                 case 'r':
1480                         if (is_report_browser(hbt))
1481                                 goto do_scripts;
1482                         continue;
1483                 case 's':
1484                         if (is_report_browser(hbt))
1485                                 goto do_data_switch;
1486                         continue;
1487                 case K_F1:
1488                 case 'h':
1489                 case '?':
1490                         ui_browser__help_window(&browser->b,
1491                                         "h/?/F1        Show this window\n"
1492                                         "UP/DOWN/PGUP\n"
1493                                         "PGDN/SPACE    Navigate\n"
1494                                         "q/ESC/CTRL+C  Exit browser\n\n"
1495                                         "For multiple event sessions:\n\n"
1496                                         "TAB/UNTAB Switch events\n\n"
1497                                         "For symbolic views (--sort has sym):\n\n"
1498                                         "->            Zoom into DSO/Threads & Annotate current symbol\n"
1499                                         "<-            Zoom out\n"
1500                                         "a             Annotate current symbol\n"
1501                                         "C             Collapse all callchains\n"
1502                                         "E             Expand all callchains\n"
1503                                         "d             Zoom into current DSO\n"
1504                                         "t             Zoom into current Thread\n"
1505                                         "r             Run available scripts('perf report' only)\n"
1506                                         "s             Switch to another data file in PWD ('perf report' only)\n"
1507                                         "P             Print histograms to perf.hist.N\n"
1508                                         "V             Verbose (DSO names in callchains, etc)\n"
1509                                         "/             Filter symbol by name");
1510                         continue;
1511                 case K_ENTER:
1512                 case K_RIGHT:
1513                         /* menu */
1514                         break;
1515                 case K_LEFT: {
1516                         const void *top;
1517
1518                         if (pstack__empty(fstack)) {
1519                                 /*
1520                                  * Go back to the perf_evsel_menu__run or other user
1521                                  */
1522                                 if (left_exits)
1523                                         goto out_free_stack;
1524                                 continue;
1525                         }
1526                         top = pstack__pop(fstack);
1527                         if (top == &browser->hists->dso_filter)
1528                                 goto zoom_out_dso;
1529                         if (top == &browser->hists->thread_filter)
1530                                 goto zoom_out_thread;
1531                         continue;
1532                 }
1533                 case K_ESC:
1534                         if (!left_exits &&
1535                             !ui_browser__dialog_yesno(&browser->b,
1536                                                "Do you really want to exit?"))
1537                                 continue;
1538                         /* Fall thru */
1539                 case 'q':
1540                 case CTRL('c'):
1541                         goto out_free_stack;
1542                 default:
1543                         continue;
1544                 }
1545
1546                 if (!sort__has_sym)
1547                         goto add_exit_option;
1548
1549                 if (sort__mode == SORT_MODE__BRANCH) {
1550                         bi = browser->he_selection->branch_info;
1551                         if (browser->selection != NULL &&
1552                             bi &&
1553                             bi->from.sym != NULL &&
1554                             !bi->from.map->dso->annotate_warned &&
1555                                 asprintf(&options[nr_options], "Annotate %s",
1556                                          bi->from.sym->name) > 0)
1557                                 annotate_f = nr_options++;
1558
1559                         if (browser->selection != NULL &&
1560                             bi &&
1561                             bi->to.sym != NULL &&
1562                             !bi->to.map->dso->annotate_warned &&
1563                             (bi->to.sym != bi->from.sym ||
1564                              bi->to.map->dso != bi->from.map->dso) &&
1565                                 asprintf(&options[nr_options], "Annotate %s",
1566                                          bi->to.sym->name) > 0)
1567                                 annotate_t = nr_options++;
1568                 } else {
1569
1570                         if (browser->selection != NULL &&
1571                             browser->selection->sym != NULL &&
1572                             !browser->selection->map->dso->annotate_warned &&
1573                                 asprintf(&options[nr_options], "Annotate %s",
1574                                          browser->selection->sym->name) > 0)
1575                                 annotate = nr_options++;
1576                 }
1577
1578                 if (thread != NULL &&
1579                     asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
1580                              (browser->hists->thread_filter ? "out of" : "into"),
1581                              (thread->comm_set ? thread__comm_str(thread) : ""),
1582                              thread->tid) > 0)
1583                         zoom_thread = nr_options++;
1584
1585                 if (dso != NULL &&
1586                     asprintf(&options[nr_options], "Zoom %s %s DSO",
1587                              (browser->hists->dso_filter ? "out of" : "into"),
1588                              (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
1589                         zoom_dso = nr_options++;
1590
1591                 if (browser->selection != NULL &&
1592                     browser->selection->map != NULL &&
1593                     asprintf(&options[nr_options], "Browse map details") > 0)
1594                         browse_map = nr_options++;
1595
1596                 /* perf script support */
1597                 if (browser->he_selection) {
1598                         struct symbol *sym;
1599
1600                         if (asprintf(&options[nr_options], "Run scripts for samples of thread [%s]",
1601                                      thread__comm_str(browser->he_selection->thread)) > 0)
1602                                 scripts_comm = nr_options++;
1603
1604                         sym = browser->he_selection->ms.sym;
1605                         if (sym && sym->namelen &&
1606                                 asprintf(&options[nr_options], "Run scripts for samples of symbol [%s]",
1607                                                 sym->name) > 0)
1608                                 scripts_symbol = nr_options++;
1609                 }
1610
1611                 if (asprintf(&options[nr_options], "Run scripts for all samples") > 0)
1612                         scripts_all = nr_options++;
1613
1614                 if (is_report_browser(hbt) && asprintf(&options[nr_options],
1615                                 "Switch to another data file in PWD") > 0)
1616                         switch_data = nr_options++;
1617 add_exit_option:
1618                 options[nr_options++] = (char *)"Exit";
1619 retry_popup_menu:
1620                 choice = ui__popup_menu(nr_options, options);
1621
1622                 if (choice == nr_options - 1)
1623                         break;
1624
1625                 if (choice == -1) {
1626                         free_popup_options(options, nr_options - 1);
1627                         continue;
1628                 }
1629
1630                 if (choice == annotate || choice == annotate_t || choice == annotate_f) {
1631                         struct hist_entry *he;
1632                         int err;
1633 do_annotate:
1634                         if (!objdump_path && perf_session_env__lookup_objdump(env))
1635                                 continue;
1636
1637                         he = hist_browser__selected_entry(browser);
1638                         if (he == NULL)
1639                                 continue;
1640
1641                         /*
1642                          * we stash the branch_info symbol + map into the
1643                          * the ms so we don't have to rewrite all the annotation
1644                          * code to use branch_info.
1645                          * in branch mode, the ms struct is not used
1646                          */
1647                         if (choice == annotate_f) {
1648                                 he->ms.sym = he->branch_info->from.sym;
1649                                 he->ms.map = he->branch_info->from.map;
1650                         }  else if (choice == annotate_t) {
1651                                 he->ms.sym = he->branch_info->to.sym;
1652                                 he->ms.map = he->branch_info->to.map;
1653                         }
1654
1655                         /*
1656                          * Don't let this be freed, say, by hists__decay_entry.
1657                          */
1658                         he->used = true;
1659                         err = hist_entry__tui_annotate(he, evsel, hbt);
1660                         he->used = false;
1661                         /*
1662                          * offer option to annotate the other branch source or target
1663                          * (if they exists) when returning from annotate
1664                          */
1665                         if ((err == 'q' || err == CTRL('c'))
1666                             && annotate_t != -2 && annotate_f != -2)
1667                                 goto retry_popup_menu;
1668
1669                         ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1670                         if (err)
1671                                 ui_browser__handle_resize(&browser->b);
1672
1673                 } else if (choice == browse_map)
1674                         map__browse(browser->selection->map);
1675                 else if (choice == zoom_dso) {
1676 zoom_dso:
1677                         if (browser->hists->dso_filter) {
1678                                 pstack__remove(fstack, &browser->hists->dso_filter);
1679 zoom_out_dso:
1680                                 ui_helpline__pop();
1681                                 browser->hists->dso_filter = NULL;
1682                                 sort_dso.elide = false;
1683                         } else {
1684                                 if (dso == NULL)
1685                                         continue;
1686                                 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1687                                                    dso->kernel ? "the Kernel" : dso->short_name);
1688                                 browser->hists->dso_filter = dso;
1689                                 sort_dso.elide = true;
1690                                 pstack__push(fstack, &browser->hists->dso_filter);
1691                         }
1692                         hists__filter_by_dso(hists);
1693                         hist_browser__reset(browser);
1694                 } else if (choice == zoom_thread) {
1695 zoom_thread:
1696                         if (browser->hists->thread_filter) {
1697                                 pstack__remove(fstack, &browser->hists->thread_filter);
1698 zoom_out_thread:
1699                                 ui_helpline__pop();
1700                                 browser->hists->thread_filter = NULL;
1701                                 sort_thread.elide = false;
1702                         } else {
1703                                 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1704                                                    thread->comm_set ? thread__comm_str(thread) : "",
1705                                                    thread->tid);
1706                                 browser->hists->thread_filter = thread;
1707                                 sort_thread.elide = true;
1708                                 pstack__push(fstack, &browser->hists->thread_filter);
1709                         }
1710                         hists__filter_by_thread(hists);
1711                         hist_browser__reset(browser);
1712                 }
1713                 /* perf scripts support */
1714                 else if (choice == scripts_all || choice == scripts_comm ||
1715                                 choice == scripts_symbol) {
1716 do_scripts:
1717                         memset(script_opt, 0, 64);
1718
1719                         if (choice == scripts_comm)
1720                                 sprintf(script_opt, " -c %s ", thread__comm_str(browser->he_selection->thread));
1721
1722                         if (choice == scripts_symbol)
1723                                 sprintf(script_opt, " -S %s ", browser->he_selection->ms.sym->name);
1724
1725                         script_browse(script_opt);
1726                 }
1727                 /* Switch to another data file */
1728                 else if (choice == switch_data) {
1729 do_data_switch:
1730                         if (!switch_data_file()) {
1731                                 key = K_SWITCH_INPUT_DATA;
1732                                 break;
1733                         } else
1734                                 ui__warning("Won't switch the data files due to\n"
1735                                         "no valid data file get selected!\n");
1736                 }
1737         }
1738 out_free_stack:
1739         pstack__delete(fstack);
1740 out:
1741         hist_browser__delete(browser);
1742         free_popup_options(options, nr_options - 1);
1743         return key;
1744 }
1745
1746 struct perf_evsel_menu {
1747         struct ui_browser b;
1748         struct perf_evsel *selection;
1749         bool lost_events, lost_events_warned;
1750         float min_pcnt;
1751         struct perf_session_env *env;
1752 };
1753
1754 static void perf_evsel_menu__write(struct ui_browser *browser,
1755                                    void *entry, int row)
1756 {
1757         struct perf_evsel_menu *menu = container_of(browser,
1758                                                     struct perf_evsel_menu, b);
1759         struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1760         bool current_entry = ui_browser__is_current_entry(browser, row);
1761         unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1762         const char *ev_name = perf_evsel__name(evsel);
1763         char bf[256], unit;
1764         const char *warn = " ";
1765         size_t printed;
1766
1767         ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
1768                                                        HE_COLORSET_NORMAL);
1769
1770         if (perf_evsel__is_group_event(evsel)) {
1771                 struct perf_evsel *pos;
1772
1773                 ev_name = perf_evsel__group_name(evsel);
1774
1775                 for_each_group_member(pos, evsel) {
1776                         nr_events += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1777                 }
1778         }
1779
1780         nr_events = convert_unit(nr_events, &unit);
1781         printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
1782                            unit, unit == ' ' ? "" : " ", ev_name);
1783         slsmg_printf("%s", bf);
1784
1785         nr_events = evsel->hists.stats.nr_events[PERF_RECORD_LOST];
1786         if (nr_events != 0) {
1787                 menu->lost_events = true;
1788                 if (!current_entry)
1789                         ui_browser__set_color(browser, HE_COLORSET_TOP);
1790                 nr_events = convert_unit(nr_events, &unit);
1791                 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
1792                                      nr_events, unit, unit == ' ' ? "" : " ");
1793                 warn = bf;
1794         }
1795
1796         slsmg_write_nstring(warn, browser->width - printed);
1797
1798         if (current_entry)
1799                 menu->selection = evsel;
1800 }
1801
1802 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
1803                                 int nr_events, const char *help,
1804                                 struct hist_browser_timer *hbt)
1805 {
1806         struct perf_evlist *evlist = menu->b.priv;
1807         struct perf_evsel *pos;
1808         const char *ev_name, *title = "Available samples";
1809         int delay_secs = hbt ? hbt->refresh : 0;
1810         int key;
1811
1812         if (ui_browser__show(&menu->b, title,
1813                              "ESC: exit, ENTER|->: Browse histograms") < 0)
1814                 return -1;
1815
1816         while (1) {
1817                 key = ui_browser__run(&menu->b, delay_secs);
1818
1819                 switch (key) {
1820                 case K_TIMER:
1821                         hbt->timer(hbt->arg);
1822
1823                         if (!menu->lost_events_warned && menu->lost_events) {
1824                                 ui_browser__warn_lost_events(&menu->b);
1825                                 menu->lost_events_warned = true;
1826                         }
1827                         continue;
1828                 case K_RIGHT:
1829                 case K_ENTER:
1830                         if (!menu->selection)
1831                                 continue;
1832                         pos = menu->selection;
1833 browse_hists:
1834                         perf_evlist__set_selected(evlist, pos);
1835                         /*
1836                          * Give the calling tool a chance to populate the non
1837                          * default evsel resorted hists tree.
1838                          */
1839                         if (hbt)
1840                                 hbt->timer(hbt->arg);
1841                         ev_name = perf_evsel__name(pos);
1842                         key = perf_evsel__hists_browse(pos, nr_events, help,
1843                                                        ev_name, true, hbt,
1844                                                        menu->min_pcnt,
1845                                                        menu->env);
1846                         ui_browser__show_title(&menu->b, title);
1847                         switch (key) {
1848                         case K_TAB:
1849                                 if (pos->node.next == &evlist->entries)
1850                                         pos = list_entry(evlist->entries.next, struct perf_evsel, node);
1851                                 else
1852                                         pos = list_entry(pos->node.next, struct perf_evsel, node);
1853                                 goto browse_hists;
1854                         case K_UNTAB:
1855                                 if (pos->node.prev == &evlist->entries)
1856                                         pos = list_entry(evlist->entries.prev, struct perf_evsel, node);
1857                                 else
1858                                         pos = list_entry(pos->node.prev, struct perf_evsel, node);
1859                                 goto browse_hists;
1860                         case K_ESC:
1861                                 if (!ui_browser__dialog_yesno(&menu->b,
1862                                                 "Do you really want to exit?"))
1863                                         continue;
1864                                 /* Fall thru */
1865                         case K_SWITCH_INPUT_DATA:
1866                         case 'q':
1867                         case CTRL('c'):
1868                                 goto out;
1869                         default:
1870                                 continue;
1871                         }
1872                 case K_LEFT:
1873                         continue;
1874                 case K_ESC:
1875                         if (!ui_browser__dialog_yesno(&menu->b,
1876                                                "Do you really want to exit?"))
1877                                 continue;
1878                         /* Fall thru */
1879                 case 'q':
1880                 case CTRL('c'):
1881                         goto out;
1882                 default:
1883                         continue;
1884                 }
1885         }
1886
1887 out:
1888         ui_browser__hide(&menu->b);
1889         return key;
1890 }
1891
1892 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
1893                                  void *entry)
1894 {
1895         struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1896
1897         if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
1898                 return true;
1899
1900         return false;
1901 }
1902
1903 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
1904                                            int nr_entries, const char *help,
1905                                            struct hist_browser_timer *hbt,
1906                                            float min_pcnt,
1907                                            struct perf_session_env *env)
1908 {
1909         struct perf_evsel *pos;
1910         struct perf_evsel_menu menu = {
1911                 .b = {
1912                         .entries    = &evlist->entries,
1913                         .refresh    = ui_browser__list_head_refresh,
1914                         .seek       = ui_browser__list_head_seek,
1915                         .write      = perf_evsel_menu__write,
1916                         .filter     = filter_group_entries,
1917                         .nr_entries = nr_entries,
1918                         .priv       = evlist,
1919                 },
1920                 .min_pcnt = min_pcnt,
1921                 .env = env,
1922         };
1923
1924         ui_helpline__push("Press ESC to exit");
1925
1926         list_for_each_entry(pos, &evlist->entries, node) {
1927                 const char *ev_name = perf_evsel__name(pos);
1928                 size_t line_len = strlen(ev_name) + 7;
1929
1930                 if (menu.b.width < line_len)
1931                         menu.b.width = line_len;
1932         }
1933
1934         return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
1935 }
1936
1937 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
1938                                   struct hist_browser_timer *hbt,
1939                                   float min_pcnt,
1940                                   struct perf_session_env *env)
1941 {
1942         int nr_entries = evlist->nr_entries;
1943
1944 single_entry:
1945         if (nr_entries == 1) {
1946                 struct perf_evsel *first = list_entry(evlist->entries.next,
1947                                                       struct perf_evsel, node);
1948                 const char *ev_name = perf_evsel__name(first);
1949
1950                 return perf_evsel__hists_browse(first, nr_entries, help,
1951                                                 ev_name, false, hbt, min_pcnt,
1952                                                 env);
1953         }
1954
1955         if (symbol_conf.event_group) {
1956                 struct perf_evsel *pos;
1957
1958                 nr_entries = 0;
1959                 list_for_each_entry(pos, &evlist->entries, node)
1960                         if (perf_evsel__is_group_leader(pos))
1961                                 nr_entries++;
1962
1963                 if (nr_entries == 1)
1964                         goto single_entry;
1965         }
1966
1967         return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
1968                                                hbt, min_pcnt, env);
1969 }