]> Pileus Git - ~andy/linux/blob - tools/perf/ui/browsers/hists.c
Merge branch 'sched-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[~andy/linux] / 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                 zfree(&options[i]);
1272 }
1273
1274 /* Check whether the browser is for 'top' or 'report' */
1275 static inline bool is_report_browser(void *timer)
1276 {
1277         return timer == NULL;
1278 }
1279
1280 /*
1281  * Only runtime switching of perf data file will make "input_name" point
1282  * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1283  * whether we need to call free() for current "input_name" during the switch.
1284  */
1285 static bool is_input_name_malloced = false;
1286
1287 static int switch_data_file(void)
1288 {
1289         char *pwd, *options[32], *abs_path[32], *tmp;
1290         DIR *pwd_dir;
1291         int nr_options = 0, choice = -1, ret = -1;
1292         struct dirent *dent;
1293
1294         pwd = getenv("PWD");
1295         if (!pwd)
1296                 return ret;
1297
1298         pwd_dir = opendir(pwd);
1299         if (!pwd_dir)
1300                 return ret;
1301
1302         memset(options, 0, sizeof(options));
1303         memset(options, 0, sizeof(abs_path));
1304
1305         while ((dent = readdir(pwd_dir))) {
1306                 char path[PATH_MAX];
1307                 u64 magic;
1308                 char *name = dent->d_name;
1309                 FILE *file;
1310
1311                 if (!(dent->d_type == DT_REG))
1312                         continue;
1313
1314                 snprintf(path, sizeof(path), "%s/%s", pwd, name);
1315
1316                 file = fopen(path, "r");
1317                 if (!file)
1318                         continue;
1319
1320                 if (fread(&magic, 1, 8, file) < 8)
1321                         goto close_file_and_continue;
1322
1323                 if (is_perf_magic(magic)) {
1324                         options[nr_options] = strdup(name);
1325                         if (!options[nr_options])
1326                                 goto close_file_and_continue;
1327
1328                         abs_path[nr_options] = strdup(path);
1329                         if (!abs_path[nr_options]) {
1330                                 zfree(&options[nr_options]);
1331                                 ui__warning("Can't search all data files due to memory shortage.\n");
1332                                 fclose(file);
1333                                 break;
1334                         }
1335
1336                         nr_options++;
1337                 }
1338
1339 close_file_and_continue:
1340                 fclose(file);
1341                 if (nr_options >= 32) {
1342                         ui__warning("Too many perf data files in PWD!\n"
1343                                     "Only the first 32 files will be listed.\n");
1344                         break;
1345                 }
1346         }
1347         closedir(pwd_dir);
1348
1349         if (nr_options) {
1350                 choice = ui__popup_menu(nr_options, options);
1351                 if (choice < nr_options && choice >= 0) {
1352                         tmp = strdup(abs_path[choice]);
1353                         if (tmp) {
1354                                 if (is_input_name_malloced)
1355                                         free((void *)input_name);
1356                                 input_name = tmp;
1357                                 is_input_name_malloced = true;
1358                                 ret = 0;
1359                         } else
1360                                 ui__warning("Data switch failed due to memory shortage!\n");
1361                 }
1362         }
1363
1364         free_popup_options(options, nr_options);
1365         free_popup_options(abs_path, nr_options);
1366         return ret;
1367 }
1368
1369 static void hist_browser__update_pcnt_entries(struct hist_browser *hb)
1370 {
1371         u64 nr_entries = 0;
1372         struct rb_node *nd = rb_first(&hb->hists->entries);
1373
1374         while (nd) {
1375                 nr_entries++;
1376                 nd = hists__filter_entries(rb_next(nd), hb->hists,
1377                                            hb->min_pcnt);
1378         }
1379
1380         hb->nr_pcnt_entries = nr_entries;
1381 }
1382
1383 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
1384                                     const char *helpline, const char *ev_name,
1385                                     bool left_exits,
1386                                     struct hist_browser_timer *hbt,
1387                                     float min_pcnt,
1388                                     struct perf_session_env *env)
1389 {
1390         struct hists *hists = &evsel->hists;
1391         struct hist_browser *browser = hist_browser__new(hists);
1392         struct branch_info *bi;
1393         struct pstack *fstack;
1394         char *options[16];
1395         int nr_options = 0;
1396         int key = -1;
1397         char buf[64];
1398         char script_opt[64];
1399         int delay_secs = hbt ? hbt->refresh : 0;
1400
1401 #define HIST_BROWSER_HELP_COMMON                                        \
1402         "h/?/F1        Show this window\n"                              \
1403         "UP/DOWN/PGUP\n"                                                \
1404         "PGDN/SPACE    Navigate\n"                                      \
1405         "q/ESC/CTRL+C  Exit browser\n\n"                                \
1406         "For multiple event sessions:\n\n"                              \
1407         "TAB/UNTAB     Switch events\n\n"                               \
1408         "For symbolic views (--sort has sym):\n\n"                      \
1409         "->            Zoom into DSO/Threads & Annotate current symbol\n" \
1410         "<-            Zoom out\n"                                      \
1411         "a             Annotate current symbol\n"                       \
1412         "C             Collapse all callchains\n"                       \
1413         "d             Zoom into current DSO\n"                         \
1414         "E             Expand all callchains\n"                         \
1415
1416         /* help messages are sorted by lexical order of the hotkey */
1417         const char report_help[] = HIST_BROWSER_HELP_COMMON
1418         "i             Show header information\n"
1419         "P             Print histograms to perf.hist.N\n"
1420         "r             Run available scripts\n"
1421         "s             Switch to another data file in PWD\n"
1422         "t             Zoom into current Thread\n"
1423         "V             Verbose (DSO names in callchains, etc)\n"
1424         "/             Filter symbol by name";
1425         const char top_help[] = HIST_BROWSER_HELP_COMMON
1426         "P             Print histograms to perf.hist.N\n"
1427         "t             Zoom into current Thread\n"
1428         "V             Verbose (DSO names in callchains, etc)\n"
1429         "/             Filter symbol by name";
1430
1431         if (browser == NULL)
1432                 return -1;
1433
1434         if (min_pcnt) {
1435                 browser->min_pcnt = min_pcnt;
1436                 hist_browser__update_pcnt_entries(browser);
1437         }
1438
1439         fstack = pstack__new(2);
1440         if (fstack == NULL)
1441                 goto out;
1442
1443         ui_helpline__push(helpline);
1444
1445         memset(options, 0, sizeof(options));
1446
1447         while (1) {
1448                 const struct thread *thread = NULL;
1449                 const struct dso *dso = NULL;
1450                 int choice = 0,
1451                     annotate = -2, zoom_dso = -2, zoom_thread = -2,
1452                     annotate_f = -2, annotate_t = -2, browse_map = -2;
1453                 int scripts_comm = -2, scripts_symbol = -2,
1454                     scripts_all = -2, switch_data = -2;
1455
1456                 nr_options = 0;
1457
1458                 key = hist_browser__run(browser, ev_name, hbt);
1459
1460                 if (browser->he_selection != NULL) {
1461                         thread = hist_browser__selected_thread(browser);
1462                         dso = browser->selection->map ? browser->selection->map->dso : NULL;
1463                 }
1464                 switch (key) {
1465                 case K_TAB:
1466                 case K_UNTAB:
1467                         if (nr_events == 1)
1468                                 continue;
1469                         /*
1470                          * Exit the browser, let hists__browser_tree
1471                          * go to the next or previous
1472                          */
1473                         goto out_free_stack;
1474                 case 'a':
1475                         if (!sort__has_sym) {
1476                                 ui_browser__warning(&browser->b, delay_secs * 2,
1477                         "Annotation is only available for symbolic views, "
1478                         "include \"sym*\" in --sort to use it.");
1479                                 continue;
1480                         }
1481
1482                         if (browser->selection == NULL ||
1483                             browser->selection->sym == NULL ||
1484                             browser->selection->map->dso->annotate_warned)
1485                                 continue;
1486                         goto do_annotate;
1487                 case 'P':
1488                         hist_browser__dump(browser);
1489                         continue;
1490                 case 'd':
1491                         goto zoom_dso;
1492                 case 'V':
1493                         browser->show_dso = !browser->show_dso;
1494                         continue;
1495                 case 't':
1496                         goto zoom_thread;
1497                 case '/':
1498                         if (ui_browser__input_window("Symbol to show",
1499                                         "Please enter the name of symbol you want to see",
1500                                         buf, "ENTER: OK, ESC: Cancel",
1501                                         delay_secs * 2) == K_ENTER) {
1502                                 hists->symbol_filter_str = *buf ? buf : NULL;
1503                                 hists__filter_by_symbol(hists);
1504                                 hist_browser__reset(browser);
1505                         }
1506                         continue;
1507                 case 'r':
1508                         if (is_report_browser(hbt))
1509                                 goto do_scripts;
1510                         continue;
1511                 case 's':
1512                         if (is_report_browser(hbt))
1513                                 goto do_data_switch;
1514                         continue;
1515                 case 'i':
1516                         /* env->arch is NULL for live-mode (i.e. perf top) */
1517                         if (env->arch)
1518                                 tui__header_window(env);
1519                         continue;
1520                 case K_F1:
1521                 case 'h':
1522                 case '?':
1523                         ui_browser__help_window(&browser->b,
1524                                 is_report_browser(hbt) ? report_help : top_help);
1525                         continue;
1526                 case K_ENTER:
1527                 case K_RIGHT:
1528                         /* menu */
1529                         break;
1530                 case K_LEFT: {
1531                         const void *top;
1532
1533                         if (pstack__empty(fstack)) {
1534                                 /*
1535                                  * Go back to the perf_evsel_menu__run or other user
1536                                  */
1537                                 if (left_exits)
1538                                         goto out_free_stack;
1539                                 continue;
1540                         }
1541                         top = pstack__pop(fstack);
1542                         if (top == &browser->hists->dso_filter)
1543                                 goto zoom_out_dso;
1544                         if (top == &browser->hists->thread_filter)
1545                                 goto zoom_out_thread;
1546                         continue;
1547                 }
1548                 case K_ESC:
1549                         if (!left_exits &&
1550                             !ui_browser__dialog_yesno(&browser->b,
1551                                                "Do you really want to exit?"))
1552                                 continue;
1553                         /* Fall thru */
1554                 case 'q':
1555                 case CTRL('c'):
1556                         goto out_free_stack;
1557                 default:
1558                         continue;
1559                 }
1560
1561                 if (!sort__has_sym)
1562                         goto add_exit_option;
1563
1564                 if (sort__mode == SORT_MODE__BRANCH) {
1565                         bi = browser->he_selection->branch_info;
1566                         if (browser->selection != NULL &&
1567                             bi &&
1568                             bi->from.sym != NULL &&
1569                             !bi->from.map->dso->annotate_warned &&
1570                                 asprintf(&options[nr_options], "Annotate %s",
1571                                          bi->from.sym->name) > 0)
1572                                 annotate_f = nr_options++;
1573
1574                         if (browser->selection != NULL &&
1575                             bi &&
1576                             bi->to.sym != NULL &&
1577                             !bi->to.map->dso->annotate_warned &&
1578                             (bi->to.sym != bi->from.sym ||
1579                              bi->to.map->dso != bi->from.map->dso) &&
1580                                 asprintf(&options[nr_options], "Annotate %s",
1581                                          bi->to.sym->name) > 0)
1582                                 annotate_t = nr_options++;
1583                 } else {
1584
1585                         if (browser->selection != NULL &&
1586                             browser->selection->sym != NULL &&
1587                             !browser->selection->map->dso->annotate_warned &&
1588                                 asprintf(&options[nr_options], "Annotate %s",
1589                                          browser->selection->sym->name) > 0)
1590                                 annotate = nr_options++;
1591                 }
1592
1593                 if (thread != NULL &&
1594                     asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
1595                              (browser->hists->thread_filter ? "out of" : "into"),
1596                              (thread->comm_set ? thread__comm_str(thread) : ""),
1597                              thread->tid) > 0)
1598                         zoom_thread = nr_options++;
1599
1600                 if (dso != NULL &&
1601                     asprintf(&options[nr_options], "Zoom %s %s DSO",
1602                              (browser->hists->dso_filter ? "out of" : "into"),
1603                              (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
1604                         zoom_dso = nr_options++;
1605
1606                 if (browser->selection != NULL &&
1607                     browser->selection->map != NULL &&
1608                     asprintf(&options[nr_options], "Browse map details") > 0)
1609                         browse_map = nr_options++;
1610
1611                 /* perf script support */
1612                 if (browser->he_selection) {
1613                         struct symbol *sym;
1614
1615                         if (asprintf(&options[nr_options], "Run scripts for samples of thread [%s]",
1616                                      thread__comm_str(browser->he_selection->thread)) > 0)
1617                                 scripts_comm = nr_options++;
1618
1619                         sym = browser->he_selection->ms.sym;
1620                         if (sym && sym->namelen &&
1621                                 asprintf(&options[nr_options], "Run scripts for samples of symbol [%s]",
1622                                                 sym->name) > 0)
1623                                 scripts_symbol = nr_options++;
1624                 }
1625
1626                 if (asprintf(&options[nr_options], "Run scripts for all samples") > 0)
1627                         scripts_all = nr_options++;
1628
1629                 if (is_report_browser(hbt) && asprintf(&options[nr_options],
1630                                 "Switch to another data file in PWD") > 0)
1631                         switch_data = nr_options++;
1632 add_exit_option:
1633                 options[nr_options++] = (char *)"Exit";
1634 retry_popup_menu:
1635                 choice = ui__popup_menu(nr_options, options);
1636
1637                 if (choice == nr_options - 1)
1638                         break;
1639
1640                 if (choice == -1) {
1641                         free_popup_options(options, nr_options - 1);
1642                         continue;
1643                 }
1644
1645                 if (choice == annotate || choice == annotate_t || choice == annotate_f) {
1646                         struct hist_entry *he;
1647                         int err;
1648 do_annotate:
1649                         if (!objdump_path && perf_session_env__lookup_objdump(env))
1650                                 continue;
1651
1652                         he = hist_browser__selected_entry(browser);
1653                         if (he == NULL)
1654                                 continue;
1655
1656                         /*
1657                          * we stash the branch_info symbol + map into the
1658                          * the ms so we don't have to rewrite all the annotation
1659                          * code to use branch_info.
1660                          * in branch mode, the ms struct is not used
1661                          */
1662                         if (choice == annotate_f) {
1663                                 he->ms.sym = he->branch_info->from.sym;
1664                                 he->ms.map = he->branch_info->from.map;
1665                         }  else if (choice == annotate_t) {
1666                                 he->ms.sym = he->branch_info->to.sym;
1667                                 he->ms.map = he->branch_info->to.map;
1668                         }
1669
1670                         /*
1671                          * Don't let this be freed, say, by hists__decay_entry.
1672                          */
1673                         he->used = true;
1674                         err = hist_entry__tui_annotate(he, evsel, hbt);
1675                         he->used = false;
1676                         /*
1677                          * offer option to annotate the other branch source or target
1678                          * (if they exists) when returning from annotate
1679                          */
1680                         if ((err == 'q' || err == CTRL('c'))
1681                             && annotate_t != -2 && annotate_f != -2)
1682                                 goto retry_popup_menu;
1683
1684                         ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1685                         if (err)
1686                                 ui_browser__handle_resize(&browser->b);
1687
1688                 } else if (choice == browse_map)
1689                         map__browse(browser->selection->map);
1690                 else if (choice == zoom_dso) {
1691 zoom_dso:
1692                         if (browser->hists->dso_filter) {
1693                                 pstack__remove(fstack, &browser->hists->dso_filter);
1694 zoom_out_dso:
1695                                 ui_helpline__pop();
1696                                 browser->hists->dso_filter = NULL;
1697                                 sort_dso.elide = false;
1698                         } else {
1699                                 if (dso == NULL)
1700                                         continue;
1701                                 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1702                                                    dso->kernel ? "the Kernel" : dso->short_name);
1703                                 browser->hists->dso_filter = dso;
1704                                 sort_dso.elide = true;
1705                                 pstack__push(fstack, &browser->hists->dso_filter);
1706                         }
1707                         hists__filter_by_dso(hists);
1708                         hist_browser__reset(browser);
1709                 } else if (choice == zoom_thread) {
1710 zoom_thread:
1711                         if (browser->hists->thread_filter) {
1712                                 pstack__remove(fstack, &browser->hists->thread_filter);
1713 zoom_out_thread:
1714                                 ui_helpline__pop();
1715                                 browser->hists->thread_filter = NULL;
1716                                 sort_thread.elide = false;
1717                         } else {
1718                                 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1719                                                    thread->comm_set ? thread__comm_str(thread) : "",
1720                                                    thread->tid);
1721                                 browser->hists->thread_filter = thread;
1722                                 sort_thread.elide = true;
1723                                 pstack__push(fstack, &browser->hists->thread_filter);
1724                         }
1725                         hists__filter_by_thread(hists);
1726                         hist_browser__reset(browser);
1727                 }
1728                 /* perf scripts support */
1729                 else if (choice == scripts_all || choice == scripts_comm ||
1730                                 choice == scripts_symbol) {
1731 do_scripts:
1732                         memset(script_opt, 0, 64);
1733
1734                         if (choice == scripts_comm)
1735                                 sprintf(script_opt, " -c %s ", thread__comm_str(browser->he_selection->thread));
1736
1737                         if (choice == scripts_symbol)
1738                                 sprintf(script_opt, " -S %s ", browser->he_selection->ms.sym->name);
1739
1740                         script_browse(script_opt);
1741                 }
1742                 /* Switch to another data file */
1743                 else if (choice == switch_data) {
1744 do_data_switch:
1745                         if (!switch_data_file()) {
1746                                 key = K_SWITCH_INPUT_DATA;
1747                                 break;
1748                         } else
1749                                 ui__warning("Won't switch the data files due to\n"
1750                                         "no valid data file get selected!\n");
1751                 }
1752         }
1753 out_free_stack:
1754         pstack__delete(fstack);
1755 out:
1756         hist_browser__delete(browser);
1757         free_popup_options(options, nr_options - 1);
1758         return key;
1759 }
1760
1761 struct perf_evsel_menu {
1762         struct ui_browser b;
1763         struct perf_evsel *selection;
1764         bool lost_events, lost_events_warned;
1765         float min_pcnt;
1766         struct perf_session_env *env;
1767 };
1768
1769 static void perf_evsel_menu__write(struct ui_browser *browser,
1770                                    void *entry, int row)
1771 {
1772         struct perf_evsel_menu *menu = container_of(browser,
1773                                                     struct perf_evsel_menu, b);
1774         struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1775         bool current_entry = ui_browser__is_current_entry(browser, row);
1776         unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1777         const char *ev_name = perf_evsel__name(evsel);
1778         char bf[256], unit;
1779         const char *warn = " ";
1780         size_t printed;
1781
1782         ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
1783                                                        HE_COLORSET_NORMAL);
1784
1785         if (perf_evsel__is_group_event(evsel)) {
1786                 struct perf_evsel *pos;
1787
1788                 ev_name = perf_evsel__group_name(evsel);
1789
1790                 for_each_group_member(pos, evsel) {
1791                         nr_events += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1792                 }
1793         }
1794
1795         nr_events = convert_unit(nr_events, &unit);
1796         printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
1797                            unit, unit == ' ' ? "" : " ", ev_name);
1798         slsmg_printf("%s", bf);
1799
1800         nr_events = evsel->hists.stats.nr_events[PERF_RECORD_LOST];
1801         if (nr_events != 0) {
1802                 menu->lost_events = true;
1803                 if (!current_entry)
1804                         ui_browser__set_color(browser, HE_COLORSET_TOP);
1805                 nr_events = convert_unit(nr_events, &unit);
1806                 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
1807                                      nr_events, unit, unit == ' ' ? "" : " ");
1808                 warn = bf;
1809         }
1810
1811         slsmg_write_nstring(warn, browser->width - printed);
1812
1813         if (current_entry)
1814                 menu->selection = evsel;
1815 }
1816
1817 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
1818                                 int nr_events, const char *help,
1819                                 struct hist_browser_timer *hbt)
1820 {
1821         struct perf_evlist *evlist = menu->b.priv;
1822         struct perf_evsel *pos;
1823         const char *ev_name, *title = "Available samples";
1824         int delay_secs = hbt ? hbt->refresh : 0;
1825         int key;
1826
1827         if (ui_browser__show(&menu->b, title,
1828                              "ESC: exit, ENTER|->: Browse histograms") < 0)
1829                 return -1;
1830
1831         while (1) {
1832                 key = ui_browser__run(&menu->b, delay_secs);
1833
1834                 switch (key) {
1835                 case K_TIMER:
1836                         hbt->timer(hbt->arg);
1837
1838                         if (!menu->lost_events_warned && menu->lost_events) {
1839                                 ui_browser__warn_lost_events(&menu->b);
1840                                 menu->lost_events_warned = true;
1841                         }
1842                         continue;
1843                 case K_RIGHT:
1844                 case K_ENTER:
1845                         if (!menu->selection)
1846                                 continue;
1847                         pos = menu->selection;
1848 browse_hists:
1849                         perf_evlist__set_selected(evlist, pos);
1850                         /*
1851                          * Give the calling tool a chance to populate the non
1852                          * default evsel resorted hists tree.
1853                          */
1854                         if (hbt)
1855                                 hbt->timer(hbt->arg);
1856                         ev_name = perf_evsel__name(pos);
1857                         key = perf_evsel__hists_browse(pos, nr_events, help,
1858                                                        ev_name, true, hbt,
1859                                                        menu->min_pcnt,
1860                                                        menu->env);
1861                         ui_browser__show_title(&menu->b, title);
1862                         switch (key) {
1863                         case K_TAB:
1864                                 if (pos->node.next == &evlist->entries)
1865                                         pos = perf_evlist__first(evlist);
1866                                 else
1867                                         pos = perf_evsel__next(pos);
1868                                 goto browse_hists;
1869                         case K_UNTAB:
1870                                 if (pos->node.prev == &evlist->entries)
1871                                         pos = perf_evlist__last(evlist);
1872                                 else
1873                                         pos = perf_evsel__prev(pos);
1874                                 goto browse_hists;
1875                         case K_ESC:
1876                                 if (!ui_browser__dialog_yesno(&menu->b,
1877                                                 "Do you really want to exit?"))
1878                                         continue;
1879                                 /* Fall thru */
1880                         case K_SWITCH_INPUT_DATA:
1881                         case 'q':
1882                         case CTRL('c'):
1883                                 goto out;
1884                         default:
1885                                 continue;
1886                         }
1887                 case K_LEFT:
1888                         continue;
1889                 case K_ESC:
1890                         if (!ui_browser__dialog_yesno(&menu->b,
1891                                                "Do you really want to exit?"))
1892                                 continue;
1893                         /* Fall thru */
1894                 case 'q':
1895                 case CTRL('c'):
1896                         goto out;
1897                 default:
1898                         continue;
1899                 }
1900         }
1901
1902 out:
1903         ui_browser__hide(&menu->b);
1904         return key;
1905 }
1906
1907 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
1908                                  void *entry)
1909 {
1910         struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1911
1912         if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
1913                 return true;
1914
1915         return false;
1916 }
1917
1918 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
1919                                            int nr_entries, const char *help,
1920                                            struct hist_browser_timer *hbt,
1921                                            float min_pcnt,
1922                                            struct perf_session_env *env)
1923 {
1924         struct perf_evsel *pos;
1925         struct perf_evsel_menu menu = {
1926                 .b = {
1927                         .entries    = &evlist->entries,
1928                         .refresh    = ui_browser__list_head_refresh,
1929                         .seek       = ui_browser__list_head_seek,
1930                         .write      = perf_evsel_menu__write,
1931                         .filter     = filter_group_entries,
1932                         .nr_entries = nr_entries,
1933                         .priv       = evlist,
1934                 },
1935                 .min_pcnt = min_pcnt,
1936                 .env = env,
1937         };
1938
1939         ui_helpline__push("Press ESC to exit");
1940
1941         evlist__for_each(evlist, pos) {
1942                 const char *ev_name = perf_evsel__name(pos);
1943                 size_t line_len = strlen(ev_name) + 7;
1944
1945                 if (menu.b.width < line_len)
1946                         menu.b.width = line_len;
1947         }
1948
1949         return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
1950 }
1951
1952 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
1953                                   struct hist_browser_timer *hbt,
1954                                   float min_pcnt,
1955                                   struct perf_session_env *env)
1956 {
1957         int nr_entries = evlist->nr_entries;
1958
1959 single_entry:
1960         if (nr_entries == 1) {
1961                 struct perf_evsel *first = perf_evlist__first(evlist);
1962                 const char *ev_name = perf_evsel__name(first);
1963
1964                 return perf_evsel__hists_browse(first, nr_entries, help,
1965                                                 ev_name, false, hbt, min_pcnt,
1966                                                 env);
1967         }
1968
1969         if (symbol_conf.event_group) {
1970                 struct perf_evsel *pos;
1971
1972                 nr_entries = 0;
1973                 evlist__for_each(evlist, pos) {
1974                         if (perf_evsel__is_group_leader(pos))
1975                                 nr_entries++;
1976                 }
1977
1978                 if (nr_entries == 1)
1979                         goto single_entry;
1980         }
1981
1982         return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
1983                                                hbt, min_pcnt, env);
1984 }