netfilter: remove unnecessary goto statement for error recovery
[cascardo/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 <newt.h>
6 #include <linux/rbtree.h>
7
8 #include "../../util/evsel.h"
9 #include "../../util/evlist.h"
10 #include "../../util/hist.h"
11 #include "../../util/pstack.h"
12 #include "../../util/sort.h"
13 #include "../../util/util.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                 has_symbols;
28 };
29
30 static int hists__browser_title(struct hists *hists, char *bf, size_t size,
31                                 const char *ev_name);
32
33 static void hist_browser__refresh_dimensions(struct hist_browser *browser)
34 {
35         /* 3 == +/- toggle symbol before actual hist_entry rendering */
36         browser->b.width = 3 + (hists__sort_list_width(browser->hists) +
37                              sizeof("[k]"));
38 }
39
40 static void hist_browser__reset(struct hist_browser *browser)
41 {
42         browser->b.nr_entries = browser->hists->nr_entries;
43         hist_browser__refresh_dimensions(browser);
44         ui_browser__reset_index(&browser->b);
45 }
46
47 static char tree__folded_sign(bool unfolded)
48 {
49         return unfolded ? '-' : '+';
50 }
51
52 static char map_symbol__folded(const struct map_symbol *ms)
53 {
54         return ms->has_children ? tree__folded_sign(ms->unfolded) : ' ';
55 }
56
57 static char hist_entry__folded(const struct hist_entry *he)
58 {
59         return map_symbol__folded(&he->ms);
60 }
61
62 static char callchain_list__folded(const struct callchain_list *cl)
63 {
64         return map_symbol__folded(&cl->ms);
65 }
66
67 static void map_symbol__set_folding(struct map_symbol *ms, bool unfold)
68 {
69         ms->unfolded = unfold ? ms->has_children : false;
70 }
71
72 static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
73 {
74         int n = 0;
75         struct rb_node *nd;
76
77         for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
78                 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
79                 struct callchain_list *chain;
80                 char folded_sign = ' '; /* No children */
81
82                 list_for_each_entry(chain, &child->val, list) {
83                         ++n;
84                         /* We need this because we may not have children */
85                         folded_sign = callchain_list__folded(chain);
86                         if (folded_sign == '+')
87                                 break;
88                 }
89
90                 if (folded_sign == '-') /* Have children and they're unfolded */
91                         n += callchain_node__count_rows_rb_tree(child);
92         }
93
94         return n;
95 }
96
97 static int callchain_node__count_rows(struct callchain_node *node)
98 {
99         struct callchain_list *chain;
100         bool unfolded = false;
101         int n = 0;
102
103         list_for_each_entry(chain, &node->val, list) {
104                 ++n;
105                 unfolded = chain->ms.unfolded;
106         }
107
108         if (unfolded)
109                 n += callchain_node__count_rows_rb_tree(node);
110
111         return n;
112 }
113
114 static int callchain__count_rows(struct rb_root *chain)
115 {
116         struct rb_node *nd;
117         int n = 0;
118
119         for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
120                 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
121                 n += callchain_node__count_rows(node);
122         }
123
124         return n;
125 }
126
127 static bool map_symbol__toggle_fold(struct map_symbol *ms)
128 {
129         if (!ms)
130                 return false;
131
132         if (!ms->has_children)
133                 return false;
134
135         ms->unfolded = !ms->unfolded;
136         return true;
137 }
138
139 static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
140 {
141         struct rb_node *nd = rb_first(&node->rb_root);
142
143         for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
144                 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
145                 struct callchain_list *chain;
146                 bool first = true;
147
148                 list_for_each_entry(chain, &child->val, list) {
149                         if (first) {
150                                 first = false;
151                                 chain->ms.has_children = chain->list.next != &child->val ||
152                                                          !RB_EMPTY_ROOT(&child->rb_root);
153                         } else
154                                 chain->ms.has_children = chain->list.next == &child->val &&
155                                                          !RB_EMPTY_ROOT(&child->rb_root);
156                 }
157
158                 callchain_node__init_have_children_rb_tree(child);
159         }
160 }
161
162 static void callchain_node__init_have_children(struct callchain_node *node)
163 {
164         struct callchain_list *chain;
165
166         list_for_each_entry(chain, &node->val, list)
167                 chain->ms.has_children = !RB_EMPTY_ROOT(&node->rb_root);
168
169         callchain_node__init_have_children_rb_tree(node);
170 }
171
172 static void callchain__init_have_children(struct rb_root *root)
173 {
174         struct rb_node *nd;
175
176         for (nd = rb_first(root); nd; nd = rb_next(nd)) {
177                 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
178                 callchain_node__init_have_children(node);
179         }
180 }
181
182 static void hist_entry__init_have_children(struct hist_entry *he)
183 {
184         if (!he->init_have_children) {
185                 he->ms.has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
186                 callchain__init_have_children(&he->sorted_chain);
187                 he->init_have_children = true;
188         }
189 }
190
191 static bool hist_browser__toggle_fold(struct hist_browser *browser)
192 {
193         if (map_symbol__toggle_fold(browser->selection)) {
194                 struct hist_entry *he = browser->he_selection;
195
196                 hist_entry__init_have_children(he);
197                 browser->hists->nr_entries -= he->nr_rows;
198
199                 if (he->ms.unfolded)
200                         he->nr_rows = callchain__count_rows(&he->sorted_chain);
201                 else
202                         he->nr_rows = 0;
203                 browser->hists->nr_entries += he->nr_rows;
204                 browser->b.nr_entries = browser->hists->nr_entries;
205
206                 return true;
207         }
208
209         /* If it doesn't have children, no toggling performed */
210         return false;
211 }
212
213 static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
214 {
215         int n = 0;
216         struct rb_node *nd;
217
218         for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
219                 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
220                 struct callchain_list *chain;
221                 bool has_children = false;
222
223                 list_for_each_entry(chain, &child->val, list) {
224                         ++n;
225                         map_symbol__set_folding(&chain->ms, unfold);
226                         has_children = chain->ms.has_children;
227                 }
228
229                 if (has_children)
230                         n += callchain_node__set_folding_rb_tree(child, unfold);
231         }
232
233         return n;
234 }
235
236 static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
237 {
238         struct callchain_list *chain;
239         bool has_children = false;
240         int n = 0;
241
242         list_for_each_entry(chain, &node->val, list) {
243                 ++n;
244                 map_symbol__set_folding(&chain->ms, unfold);
245                 has_children = chain->ms.has_children;
246         }
247
248         if (has_children)
249                 n += callchain_node__set_folding_rb_tree(node, unfold);
250
251         return n;
252 }
253
254 static int callchain__set_folding(struct rb_root *chain, bool unfold)
255 {
256         struct rb_node *nd;
257         int n = 0;
258
259         for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
260                 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
261                 n += callchain_node__set_folding(node, unfold);
262         }
263
264         return n;
265 }
266
267 static void hist_entry__set_folding(struct hist_entry *he, bool unfold)
268 {
269         hist_entry__init_have_children(he);
270         map_symbol__set_folding(&he->ms, unfold);
271
272         if (he->ms.has_children) {
273                 int n = callchain__set_folding(&he->sorted_chain, unfold);
274                 he->nr_rows = unfold ? n : 0;
275         } else
276                 he->nr_rows = 0;
277 }
278
279 static void hists__set_folding(struct hists *hists, bool unfold)
280 {
281         struct rb_node *nd;
282
283         hists->nr_entries = 0;
284
285         for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
286                 struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
287                 hist_entry__set_folding(he, unfold);
288                 hists->nr_entries += 1 + he->nr_rows;
289         }
290 }
291
292 static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
293 {
294         hists__set_folding(browser->hists, unfold);
295         browser->b.nr_entries = browser->hists->nr_entries;
296         /* Go to the start, we may be way after valid entries after a collapse */
297         ui_browser__reset_index(&browser->b);
298 }
299
300 static void ui_browser__warn_lost_events(struct ui_browser *browser)
301 {
302         ui_browser__warning(browser, 4,
303                 "Events are being lost, check IO/CPU overload!\n\n"
304                 "You may want to run 'perf' using a RT scheduler policy:\n\n"
305                 " perf top -r 80\n\n"
306                 "Or reduce the sampling frequency.");
307 }
308
309 static int hist_browser__run(struct hist_browser *browser, const char *ev_name,
310                              void(*timer)(void *arg), void *arg, int delay_secs)
311 {
312         int key;
313         char title[160];
314
315         browser->b.entries = &browser->hists->entries;
316         browser->b.nr_entries = browser->hists->nr_entries;
317
318         hist_browser__refresh_dimensions(browser);
319         hists__browser_title(browser->hists, title, sizeof(title), ev_name);
320
321         if (ui_browser__show(&browser->b, title,
322                              "Press '?' for help on key bindings") < 0)
323                 return -1;
324
325         while (1) {
326                 key = ui_browser__run(&browser->b, delay_secs);
327
328                 switch (key) {
329                 case K_TIMER:
330                         timer(arg);
331                         ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
332
333                         if (browser->hists->stats.nr_lost_warned !=
334                             browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
335                                 browser->hists->stats.nr_lost_warned =
336                                         browser->hists->stats.nr_events[PERF_RECORD_LOST];
337                                 ui_browser__warn_lost_events(&browser->b);
338                         }
339
340                         hists__browser_title(browser->hists, title, sizeof(title), ev_name);
341                         ui_browser__show_title(&browser->b, title);
342                         continue;
343                 case 'D': { /* Debug */
344                         static int seq;
345                         struct hist_entry *h = rb_entry(browser->b.top,
346                                                         struct hist_entry, rb_node);
347                         ui_helpline__pop();
348                         ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
349                                            seq++, browser->b.nr_entries,
350                                            browser->hists->nr_entries,
351                                            browser->b.height,
352                                            browser->b.index,
353                                            browser->b.top_idx,
354                                            h->row_offset, h->nr_rows);
355                 }
356                         break;
357                 case 'C':
358                         /* Collapse the whole world. */
359                         hist_browser__set_folding(browser, false);
360                         break;
361                 case 'E':
362                         /* Expand the whole world. */
363                         hist_browser__set_folding(browser, true);
364                         break;
365                 case K_ENTER:
366                         if (hist_browser__toggle_fold(browser))
367                                 break;
368                         /* fall thru */
369                 default:
370                         goto out;
371                 }
372         }
373 out:
374         ui_browser__hide(&browser->b);
375         return key;
376 }
377
378 static char *callchain_list__sym_name(struct callchain_list *cl,
379                                       char *bf, size_t bfsize)
380 {
381         if (cl->ms.sym)
382                 return cl->ms.sym->name;
383
384         snprintf(bf, bfsize, "%#" PRIx64, cl->ip);
385         return bf;
386 }
387
388 #define LEVEL_OFFSET_STEP 3
389
390 static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browser,
391                                                      struct callchain_node *chain_node,
392                                                      u64 total, int level,
393                                                      unsigned short row,
394                                                      off_t *row_offset,
395                                                      bool *is_current_entry)
396 {
397         struct rb_node *node;
398         int first_row = row, width, offset = level * LEVEL_OFFSET_STEP;
399         u64 new_total, remaining;
400
401         if (callchain_param.mode == CHAIN_GRAPH_REL)
402                 new_total = chain_node->children_hit;
403         else
404                 new_total = total;
405
406         remaining = new_total;
407         node = rb_first(&chain_node->rb_root);
408         while (node) {
409                 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
410                 struct rb_node *next = rb_next(node);
411                 u64 cumul = callchain_cumul_hits(child);
412                 struct callchain_list *chain;
413                 char folded_sign = ' ';
414                 int first = true;
415                 int extra_offset = 0;
416
417                 remaining -= cumul;
418
419                 list_for_each_entry(chain, &child->val, list) {
420                         char ipstr[BITS_PER_LONG / 4 + 1], *alloc_str;
421                         const char *str;
422                         int color;
423                         bool was_first = first;
424
425                         if (first)
426                                 first = false;
427                         else
428                                 extra_offset = LEVEL_OFFSET_STEP;
429
430                         folded_sign = callchain_list__folded(chain);
431                         if (*row_offset != 0) {
432                                 --*row_offset;
433                                 goto do_next;
434                         }
435
436                         alloc_str = NULL;
437                         str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
438                         if (was_first) {
439                                 double percent = cumul * 100.0 / new_total;
440
441                                 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
442                                         str = "Not enough memory!";
443                                 else
444                                         str = alloc_str;
445                         }
446
447                         color = HE_COLORSET_NORMAL;
448                         width = browser->b.width - (offset + extra_offset + 2);
449                         if (ui_browser__is_current_entry(&browser->b, row)) {
450                                 browser->selection = &chain->ms;
451                                 color = HE_COLORSET_SELECTED;
452                                 *is_current_entry = true;
453                         }
454
455                         ui_browser__set_color(&browser->b, color);
456                         ui_browser__gotorc(&browser->b, row, 0);
457                         slsmg_write_nstring(" ", offset + extra_offset);
458                         slsmg_printf("%c ", folded_sign);
459                         slsmg_write_nstring(str, width);
460                         free(alloc_str);
461
462                         if (++row == browser->b.height)
463                                 goto out;
464 do_next:
465                         if (folded_sign == '+')
466                                 break;
467                 }
468
469                 if (folded_sign == '-') {
470                         const int new_level = level + (extra_offset ? 2 : 1);
471                         row += hist_browser__show_callchain_node_rb_tree(browser, child, new_total,
472                                                                          new_level, row, row_offset,
473                                                                          is_current_entry);
474                 }
475                 if (row == browser->b.height)
476                         goto out;
477                 node = next;
478         }
479 out:
480         return row - first_row;
481 }
482
483 static int hist_browser__show_callchain_node(struct hist_browser *browser,
484                                              struct callchain_node *node,
485                                              int level, unsigned short row,
486                                              off_t *row_offset,
487                                              bool *is_current_entry)
488 {
489         struct callchain_list *chain;
490         int first_row = row,
491              offset = level * LEVEL_OFFSET_STEP,
492              width = browser->b.width - offset;
493         char folded_sign = ' ';
494
495         list_for_each_entry(chain, &node->val, list) {
496                 char ipstr[BITS_PER_LONG / 4 + 1], *s;
497                 int color;
498
499                 folded_sign = callchain_list__folded(chain);
500
501                 if (*row_offset != 0) {
502                         --*row_offset;
503                         continue;
504                 }
505
506                 color = HE_COLORSET_NORMAL;
507                 if (ui_browser__is_current_entry(&browser->b, row)) {
508                         browser->selection = &chain->ms;
509                         color = HE_COLORSET_SELECTED;
510                         *is_current_entry = true;
511                 }
512
513                 s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
514                 ui_browser__gotorc(&browser->b, row, 0);
515                 ui_browser__set_color(&browser->b, color);
516                 slsmg_write_nstring(" ", offset);
517                 slsmg_printf("%c ", folded_sign);
518                 slsmg_write_nstring(s, width - 2);
519
520                 if (++row == browser->b.height)
521                         goto out;
522         }
523
524         if (folded_sign == '-')
525                 row += hist_browser__show_callchain_node_rb_tree(browser, node,
526                                                                  browser->hists->stats.total_period,
527                                                                  level + 1, row,
528                                                                  row_offset,
529                                                                  is_current_entry);
530 out:
531         return row - first_row;
532 }
533
534 static int hist_browser__show_callchain(struct hist_browser *browser,
535                                         struct rb_root *chain,
536                                         int level, unsigned short row,
537                                         off_t *row_offset,
538                                         bool *is_current_entry)
539 {
540         struct rb_node *nd;
541         int first_row = row;
542
543         for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
544                 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
545
546                 row += hist_browser__show_callchain_node(browser, node, level,
547                                                          row, row_offset,
548                                                          is_current_entry);
549                 if (row == browser->b.height)
550                         break;
551         }
552
553         return row - first_row;
554 }
555
556 static int hist_browser__show_entry(struct hist_browser *browser,
557                                     struct hist_entry *entry,
558                                     unsigned short row)
559 {
560         char s[256];
561         double percent;
562         int printed = 0;
563         int width = browser->b.width - 6; /* The percentage */
564         char folded_sign = ' ';
565         bool current_entry = ui_browser__is_current_entry(&browser->b, row);
566         off_t row_offset = entry->row_offset;
567
568         if (current_entry) {
569                 browser->he_selection = entry;
570                 browser->selection = &entry->ms;
571         }
572
573         if (symbol_conf.use_callchain) {
574                 hist_entry__init_have_children(entry);
575                 folded_sign = hist_entry__folded(entry);
576         }
577
578         if (row_offset == 0) {
579                 hist_entry__snprintf(entry, s, sizeof(s), browser->hists);
580                 percent = (entry->period * 100.0) / browser->hists->stats.total_period;
581
582                 ui_browser__set_percent_color(&browser->b, percent, current_entry);
583                 ui_browser__gotorc(&browser->b, row, 0);
584                 if (symbol_conf.use_callchain) {
585                         slsmg_printf("%c ", folded_sign);
586                         width -= 2;
587                 }
588
589                 slsmg_printf(" %5.2f%%", percent);
590
591                 /* The scroll bar isn't being used */
592                 if (!browser->b.navkeypressed)
593                         width += 1;
594
595                 if (!current_entry || !browser->b.navkeypressed)
596                         ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
597
598                 if (symbol_conf.show_nr_samples) {
599                         slsmg_printf(" %11u", entry->nr_events);
600                         width -= 12;
601                 }
602
603                 if (symbol_conf.show_total_period) {
604                         slsmg_printf(" %12" PRIu64, entry->period);
605                         width -= 13;
606                 }
607
608                 slsmg_write_nstring(s, width);
609                 ++row;
610                 ++printed;
611         } else
612                 --row_offset;
613
614         if (folded_sign == '-' && row != browser->b.height) {
615                 printed += hist_browser__show_callchain(browser, &entry->sorted_chain,
616                                                         1, row, &row_offset,
617                                                         &current_entry);
618                 if (current_entry)
619                         browser->he_selection = entry;
620         }
621
622         return printed;
623 }
624
625 static void ui_browser__hists_init_top(struct ui_browser *browser)
626 {
627         if (browser->top == NULL) {
628                 struct hist_browser *hb;
629
630                 hb = container_of(browser, struct hist_browser, b);
631                 browser->top = rb_first(&hb->hists->entries);
632         }
633 }
634
635 static unsigned int hist_browser__refresh(struct ui_browser *browser)
636 {
637         unsigned row = 0;
638         struct rb_node *nd;
639         struct hist_browser *hb = container_of(browser, struct hist_browser, b);
640
641         ui_browser__hists_init_top(browser);
642
643         for (nd = browser->top; nd; nd = rb_next(nd)) {
644                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
645
646                 if (h->filtered)
647                         continue;
648
649                 row += hist_browser__show_entry(hb, h, row);
650                 if (row == browser->height)
651                         break;
652         }
653
654         return row;
655 }
656
657 static struct rb_node *hists__filter_entries(struct rb_node *nd)
658 {
659         while (nd != NULL) {
660                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
661                 if (!h->filtered)
662                         return nd;
663
664                 nd = rb_next(nd);
665         }
666
667         return NULL;
668 }
669
670 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd)
671 {
672         while (nd != NULL) {
673                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
674                 if (!h->filtered)
675                         return nd;
676
677                 nd = rb_prev(nd);
678         }
679
680         return NULL;
681 }
682
683 static void ui_browser__hists_seek(struct ui_browser *browser,
684                                    off_t offset, int whence)
685 {
686         struct hist_entry *h;
687         struct rb_node *nd;
688         bool first = true;
689
690         if (browser->nr_entries == 0)
691                 return;
692
693         ui_browser__hists_init_top(browser);
694
695         switch (whence) {
696         case SEEK_SET:
697                 nd = hists__filter_entries(rb_first(browser->entries));
698                 break;
699         case SEEK_CUR:
700                 nd = browser->top;
701                 goto do_offset;
702         case SEEK_END:
703                 nd = hists__filter_prev_entries(rb_last(browser->entries));
704                 first = false;
705                 break;
706         default:
707                 return;
708         }
709
710         /*
711          * Moves not relative to the first visible entry invalidates its
712          * row_offset:
713          */
714         h = rb_entry(browser->top, struct hist_entry, rb_node);
715         h->row_offset = 0;
716
717         /*
718          * Here we have to check if nd is expanded (+), if it is we can't go
719          * the next top level hist_entry, instead we must compute an offset of
720          * what _not_ to show and not change the first visible entry.
721          *
722          * This offset increments when we are going from top to bottom and
723          * decreases when we're going from bottom to top.
724          *
725          * As we don't have backpointers to the top level in the callchains
726          * structure, we need to always print the whole hist_entry callchain,
727          * skipping the first ones that are before the first visible entry
728          * and stop when we printed enough lines to fill the screen.
729          */
730 do_offset:
731         if (offset > 0) {
732                 do {
733                         h = rb_entry(nd, struct hist_entry, rb_node);
734                         if (h->ms.unfolded) {
735                                 u16 remaining = h->nr_rows - h->row_offset;
736                                 if (offset > remaining) {
737                                         offset -= remaining;
738                                         h->row_offset = 0;
739                                 } else {
740                                         h->row_offset += offset;
741                                         offset = 0;
742                                         browser->top = nd;
743                                         break;
744                                 }
745                         }
746                         nd = hists__filter_entries(rb_next(nd));
747                         if (nd == NULL)
748                                 break;
749                         --offset;
750                         browser->top = nd;
751                 } while (offset != 0);
752         } else if (offset < 0) {
753                 while (1) {
754                         h = rb_entry(nd, struct hist_entry, rb_node);
755                         if (h->ms.unfolded) {
756                                 if (first) {
757                                         if (-offset > h->row_offset) {
758                                                 offset += h->row_offset;
759                                                 h->row_offset = 0;
760                                         } else {
761                                                 h->row_offset += offset;
762                                                 offset = 0;
763                                                 browser->top = nd;
764                                                 break;
765                                         }
766                                 } else {
767                                         if (-offset > h->nr_rows) {
768                                                 offset += h->nr_rows;
769                                                 h->row_offset = 0;
770                                         } else {
771                                                 h->row_offset = h->nr_rows + offset;
772                                                 offset = 0;
773                                                 browser->top = nd;
774                                                 break;
775                                         }
776                                 }
777                         }
778
779                         nd = hists__filter_prev_entries(rb_prev(nd));
780                         if (nd == NULL)
781                                 break;
782                         ++offset;
783                         browser->top = nd;
784                         if (offset == 0) {
785                                 /*
786                                  * Last unfiltered hist_entry, check if it is
787                                  * unfolded, if it is then we should have
788                                  * row_offset at its last entry.
789                                  */
790                                 h = rb_entry(nd, struct hist_entry, rb_node);
791                                 if (h->ms.unfolded)
792                                         h->row_offset = h->nr_rows;
793                                 break;
794                         }
795                         first = false;
796                 }
797         } else {
798                 browser->top = nd;
799                 h = rb_entry(nd, struct hist_entry, rb_node);
800                 h->row_offset = 0;
801         }
802 }
803
804 static int hist_browser__fprintf_callchain_node_rb_tree(struct hist_browser *browser,
805                                                         struct callchain_node *chain_node,
806                                                         u64 total, int level,
807                                                         FILE *fp)
808 {
809         struct rb_node *node;
810         int offset = level * LEVEL_OFFSET_STEP;
811         u64 new_total, remaining;
812         int printed = 0;
813
814         if (callchain_param.mode == CHAIN_GRAPH_REL)
815                 new_total = chain_node->children_hit;
816         else
817                 new_total = total;
818
819         remaining = new_total;
820         node = rb_first(&chain_node->rb_root);
821         while (node) {
822                 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
823                 struct rb_node *next = rb_next(node);
824                 u64 cumul = callchain_cumul_hits(child);
825                 struct callchain_list *chain;
826                 char folded_sign = ' ';
827                 int first = true;
828                 int extra_offset = 0;
829
830                 remaining -= cumul;
831
832                 list_for_each_entry(chain, &child->val, list) {
833                         char ipstr[BITS_PER_LONG / 4 + 1], *alloc_str;
834                         const char *str;
835                         bool was_first = first;
836
837                         if (first)
838                                 first = false;
839                         else
840                                 extra_offset = LEVEL_OFFSET_STEP;
841
842                         folded_sign = callchain_list__folded(chain);
843
844                         alloc_str = NULL;
845                         str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
846                         if (was_first) {
847                                 double percent = cumul * 100.0 / new_total;
848
849                                 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
850                                         str = "Not enough memory!";
851                                 else
852                                         str = alloc_str;
853                         }
854
855                         printed += fprintf(fp, "%*s%c %s\n", offset + extra_offset, " ", folded_sign, str);
856                         free(alloc_str);
857                         if (folded_sign == '+')
858                                 break;
859                 }
860
861                 if (folded_sign == '-') {
862                         const int new_level = level + (extra_offset ? 2 : 1);
863                         printed += hist_browser__fprintf_callchain_node_rb_tree(browser, child, new_total,
864                                                                                 new_level, fp);
865                 }
866
867                 node = next;
868         }
869
870         return printed;
871 }
872
873 static int hist_browser__fprintf_callchain_node(struct hist_browser *browser,
874                                                 struct callchain_node *node,
875                                                 int level, FILE *fp)
876 {
877         struct callchain_list *chain;
878         int offset = level * LEVEL_OFFSET_STEP;
879         char folded_sign = ' ';
880         int printed = 0;
881
882         list_for_each_entry(chain, &node->val, list) {
883                 char ipstr[BITS_PER_LONG / 4 + 1], *s;
884
885                 folded_sign = callchain_list__folded(chain);
886                 s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
887                 printed += fprintf(fp, "%*s%c %s\n", offset, " ", folded_sign, s);
888         }
889
890         if (folded_sign == '-')
891                 printed += hist_browser__fprintf_callchain_node_rb_tree(browser, node,
892                                                                         browser->hists->stats.total_period,
893                                                                         level + 1,  fp);
894         return printed;
895 }
896
897 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
898                                            struct rb_root *chain, int level, FILE *fp)
899 {
900         struct rb_node *nd;
901         int printed = 0;
902
903         for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
904                 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
905
906                 printed += hist_browser__fprintf_callchain_node(browser, node, level, fp);
907         }
908
909         return printed;
910 }
911
912 static int hist_browser__fprintf_entry(struct hist_browser *browser,
913                                        struct hist_entry *he, FILE *fp)
914 {
915         char s[8192];
916         double percent;
917         int printed = 0;
918         char folded_sign = ' ';
919
920         if (symbol_conf.use_callchain)
921                 folded_sign = hist_entry__folded(he);
922
923         hist_entry__snprintf(he, s, sizeof(s), browser->hists);
924         percent = (he->period * 100.0) / browser->hists->stats.total_period;
925
926         if (symbol_conf.use_callchain)
927                 printed += fprintf(fp, "%c ", folded_sign);
928
929         printed += fprintf(fp, " %5.2f%%", percent);
930
931         if (symbol_conf.show_nr_samples)
932                 printed += fprintf(fp, " %11u", he->nr_events);
933
934         if (symbol_conf.show_total_period)
935                 printed += fprintf(fp, " %12" PRIu64, he->period);
936
937         printed += fprintf(fp, "%s\n", rtrim(s));
938
939         if (folded_sign == '-')
940                 printed += hist_browser__fprintf_callchain(browser, &he->sorted_chain, 1, fp);
941
942         return printed;
943 }
944
945 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
946 {
947         struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries));
948         int printed = 0;
949
950         while (nd) {
951                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
952
953                 printed += hist_browser__fprintf_entry(browser, h, fp);
954                 nd = hists__filter_entries(rb_next(nd));
955         }
956
957         return printed;
958 }
959
960 static int hist_browser__dump(struct hist_browser *browser)
961 {
962         char filename[64];
963         FILE *fp;
964
965         while (1) {
966                 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
967                 if (access(filename, F_OK))
968                         break;
969                 /*
970                  * XXX: Just an arbitrary lazy upper limit
971                  */
972                 if (++browser->print_seq == 8192) {
973                         ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
974                         return -1;
975                 }
976         }
977
978         fp = fopen(filename, "w");
979         if (fp == NULL) {
980                 char bf[64];
981                 strerror_r(errno, bf, sizeof(bf));
982                 ui_helpline__fpush("Couldn't write to %s: %s", filename, bf);
983                 return -1;
984         }
985
986         ++browser->print_seq;
987         hist_browser__fprintf(browser, fp);
988         fclose(fp);
989         ui_helpline__fpush("%s written!", filename);
990
991         return 0;
992 }
993
994 static struct hist_browser *hist_browser__new(struct hists *hists)
995 {
996         struct hist_browser *browser = zalloc(sizeof(*browser));
997
998         if (browser) {
999                 browser->hists = hists;
1000                 browser->b.refresh = hist_browser__refresh;
1001                 browser->b.seek = ui_browser__hists_seek;
1002                 browser->b.use_navkeypressed = true;
1003                 if (sort__branch_mode == 1)
1004                         browser->has_symbols = sort_sym_from.list.next != NULL;
1005                 else
1006                         browser->has_symbols = sort_sym.list.next != NULL;
1007         }
1008
1009         return browser;
1010 }
1011
1012 static void hist_browser__delete(struct hist_browser *browser)
1013 {
1014         free(browser);
1015 }
1016
1017 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
1018 {
1019         return browser->he_selection;
1020 }
1021
1022 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
1023 {
1024         return browser->he_selection->thread;
1025 }
1026
1027 static int hists__browser_title(struct hists *hists, char *bf, size_t size,
1028                                 const char *ev_name)
1029 {
1030         char unit;
1031         int printed;
1032         const struct dso *dso = hists->dso_filter;
1033         const struct thread *thread = hists->thread_filter;
1034         unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1035         u64 nr_events = hists->stats.total_period;
1036
1037         nr_samples = convert_unit(nr_samples, &unit);
1038         printed = scnprintf(bf, size,
1039                            "Samples: %lu%c of event '%s', Event count (approx.): %lu",
1040                            nr_samples, unit, ev_name, nr_events);
1041
1042
1043         if (hists->uid_filter_str)
1044                 printed += snprintf(bf + printed, size - printed,
1045                                     ", UID: %s", hists->uid_filter_str);
1046         if (thread)
1047                 printed += scnprintf(bf + printed, size - printed,
1048                                     ", Thread: %s(%d)",
1049                                     (thread->comm_set ? thread->comm : ""),
1050                                     thread->pid);
1051         if (dso)
1052                 printed += scnprintf(bf + printed, size - printed,
1053                                     ", DSO: %s", dso->short_name);
1054         return printed;
1055 }
1056
1057 static inline void free_popup_options(char **options, int n)
1058 {
1059         int i;
1060
1061         for (i = 0; i < n; ++i) {
1062                 free(options[i]);
1063                 options[i] = NULL;
1064         }
1065 }
1066
1067 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
1068                                     const char *helpline, const char *ev_name,
1069                                     bool left_exits,
1070                                     void(*timer)(void *arg), void *arg,
1071                                     int delay_secs)
1072 {
1073         struct hists *hists = &evsel->hists;
1074         struct hist_browser *browser = hist_browser__new(hists);
1075         struct branch_info *bi;
1076         struct pstack *fstack;
1077         char *options[16];
1078         int nr_options = 0;
1079         int key = -1;
1080         char buf[64];
1081
1082         if (browser == NULL)
1083                 return -1;
1084
1085         fstack = pstack__new(2);
1086         if (fstack == NULL)
1087                 goto out;
1088
1089         ui_helpline__push(helpline);
1090
1091         memset(options, 0, sizeof(options));
1092
1093         while (1) {
1094                 const struct thread *thread = NULL;
1095                 const struct dso *dso = NULL;
1096                 int choice = 0,
1097                     annotate = -2, zoom_dso = -2, zoom_thread = -2,
1098                     annotate_f = -2, annotate_t = -2, browse_map = -2;
1099
1100                 nr_options = 0;
1101
1102                 key = hist_browser__run(browser, ev_name, timer, arg, delay_secs);
1103
1104                 if (browser->he_selection != NULL) {
1105                         thread = hist_browser__selected_thread(browser);
1106                         dso = browser->selection->map ? browser->selection->map->dso : NULL;
1107                 }
1108                 switch (key) {
1109                 case K_TAB:
1110                 case K_UNTAB:
1111                         if (nr_events == 1)
1112                                 continue;
1113                         /*
1114                          * Exit the browser, let hists__browser_tree
1115                          * go to the next or previous
1116                          */
1117                         goto out_free_stack;
1118                 case 'a':
1119                         if (!browser->has_symbols) {
1120                                 ui_browser__warning(&browser->b, delay_secs * 2,
1121                         "Annotation is only available for symbolic views, "
1122                         "include \"sym*\" in --sort to use it.");
1123                                 continue;
1124                         }
1125
1126                         if (browser->selection == NULL ||
1127                             browser->selection->sym == NULL ||
1128                             browser->selection->map->dso->annotate_warned)
1129                                 continue;
1130                         goto do_annotate;
1131                 case 'P':
1132                         hist_browser__dump(browser);
1133                         continue;
1134                 case 'd':
1135                         goto zoom_dso;
1136                 case 't':
1137                         goto zoom_thread;
1138                 case '/':
1139                         if (ui_browser__input_window("Symbol to show",
1140                                         "Please enter the name of symbol you want to see",
1141                                         buf, "ENTER: OK, ESC: Cancel",
1142                                         delay_secs * 2) == K_ENTER) {
1143                                 hists->symbol_filter_str = *buf ? buf : NULL;
1144                                 hists__filter_by_symbol(hists);
1145                                 hist_browser__reset(browser);
1146                         }
1147                         continue;
1148                 case K_F1:
1149                 case 'h':
1150                 case '?':
1151                         ui_browser__help_window(&browser->b,
1152                                         "h/?/F1        Show this window\n"
1153                                         "UP/DOWN/PGUP\n"
1154                                         "PGDN/SPACE    Navigate\n"
1155                                         "q/ESC/CTRL+C  Exit browser\n\n"
1156                                         "For multiple event sessions:\n\n"
1157                                         "TAB/UNTAB Switch events\n\n"
1158                                         "For symbolic views (--sort has sym):\n\n"
1159                                         "->            Zoom into DSO/Threads & Annotate current symbol\n"
1160                                         "<-            Zoom out\n"
1161                                         "a             Annotate current symbol\n"
1162                                         "C             Collapse all callchains\n"
1163                                         "E             Expand all callchains\n"
1164                                         "d             Zoom into current DSO\n"
1165                                         "t             Zoom into current Thread\n"
1166                                         "P             Print histograms to perf.hist.N\n"
1167                                         "/             Filter symbol by name");
1168                         continue;
1169                 case K_ENTER:
1170                 case K_RIGHT:
1171                         /* menu */
1172                         break;
1173                 case K_LEFT: {
1174                         const void *top;
1175
1176                         if (pstack__empty(fstack)) {
1177                                 /*
1178                                  * Go back to the perf_evsel_menu__run or other user
1179                                  */
1180                                 if (left_exits)
1181                                         goto out_free_stack;
1182                                 continue;
1183                         }
1184                         top = pstack__pop(fstack);
1185                         if (top == &browser->hists->dso_filter)
1186                                 goto zoom_out_dso;
1187                         if (top == &browser->hists->thread_filter)
1188                                 goto zoom_out_thread;
1189                         continue;
1190                 }
1191                 case K_ESC:
1192                         if (!left_exits &&
1193                             !ui_browser__dialog_yesno(&browser->b,
1194                                                "Do you really want to exit?"))
1195                                 continue;
1196                         /* Fall thru */
1197                 case 'q':
1198                 case CTRL('c'):
1199                         goto out_free_stack;
1200                 default:
1201                         continue;
1202                 }
1203
1204                 if (!browser->has_symbols)
1205                         goto add_exit_option;
1206
1207                 if (sort__branch_mode == 1) {
1208                         bi = browser->he_selection->branch_info;
1209                         if (browser->selection != NULL &&
1210                             bi &&
1211                             bi->from.sym != NULL &&
1212                             !bi->from.map->dso->annotate_warned &&
1213                                 asprintf(&options[nr_options], "Annotate %s",
1214                                          bi->from.sym->name) > 0)
1215                                 annotate_f = nr_options++;
1216
1217                         if (browser->selection != NULL &&
1218                             bi &&
1219                             bi->to.sym != NULL &&
1220                             !bi->to.map->dso->annotate_warned &&
1221                             (bi->to.sym != bi->from.sym ||
1222                              bi->to.map->dso != bi->from.map->dso) &&
1223                                 asprintf(&options[nr_options], "Annotate %s",
1224                                          bi->to.sym->name) > 0)
1225                                 annotate_t = nr_options++;
1226                 } else {
1227
1228                         if (browser->selection != NULL &&
1229                             browser->selection->sym != NULL &&
1230                             !browser->selection->map->dso->annotate_warned &&
1231                                 asprintf(&options[nr_options], "Annotate %s",
1232                                          browser->selection->sym->name) > 0)
1233                                 annotate = nr_options++;
1234                 }
1235
1236                 if (thread != NULL &&
1237                     asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
1238                              (browser->hists->thread_filter ? "out of" : "into"),
1239                              (thread->comm_set ? thread->comm : ""),
1240                              thread->pid) > 0)
1241                         zoom_thread = nr_options++;
1242
1243                 if (dso != NULL &&
1244                     asprintf(&options[nr_options], "Zoom %s %s DSO",
1245                              (browser->hists->dso_filter ? "out of" : "into"),
1246                              (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
1247                         zoom_dso = nr_options++;
1248
1249                 if (browser->selection != NULL &&
1250                     browser->selection->map != NULL &&
1251                     asprintf(&options[nr_options], "Browse map details") > 0)
1252                         browse_map = nr_options++;
1253 add_exit_option:
1254                 options[nr_options++] = (char *)"Exit";
1255 retry_popup_menu:
1256                 choice = ui__popup_menu(nr_options, options);
1257
1258                 if (choice == nr_options - 1)
1259                         break;
1260
1261                 if (choice == -1) {
1262                         free_popup_options(options, nr_options - 1);
1263                         continue;
1264                 }
1265
1266                 if (choice == annotate || choice == annotate_t || choice == annotate_f) {
1267                         struct hist_entry *he;
1268                         int err;
1269 do_annotate:
1270                         he = hist_browser__selected_entry(browser);
1271                         if (he == NULL)
1272                                 continue;
1273
1274                         /*
1275                          * we stash the branch_info symbol + map into the
1276                          * the ms so we don't have to rewrite all the annotation
1277                          * code to use branch_info.
1278                          * in branch mode, the ms struct is not used
1279                          */
1280                         if (choice == annotate_f) {
1281                                 he->ms.sym = he->branch_info->from.sym;
1282                                 he->ms.map = he->branch_info->from.map;
1283                         }  else if (choice == annotate_t) {
1284                                 he->ms.sym = he->branch_info->to.sym;
1285                                 he->ms.map = he->branch_info->to.map;
1286                         }
1287
1288                         /*
1289                          * Don't let this be freed, say, by hists__decay_entry.
1290                          */
1291                         he->used = true;
1292                         err = hist_entry__tui_annotate(he, evsel->idx,
1293                                                        timer, arg, delay_secs);
1294                         he->used = false;
1295                         /*
1296                          * offer option to annotate the other branch source or target
1297                          * (if they exists) when returning from annotate
1298                          */
1299                         if ((err == 'q' || err == CTRL('c'))
1300                             && annotate_t != -2 && annotate_f != -2)
1301                                 goto retry_popup_menu;
1302
1303                         ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1304                         if (err)
1305                                 ui_browser__handle_resize(&browser->b);
1306
1307                 } else if (choice == browse_map)
1308                         map__browse(browser->selection->map);
1309                 else if (choice == zoom_dso) {
1310 zoom_dso:
1311                         if (browser->hists->dso_filter) {
1312                                 pstack__remove(fstack, &browser->hists->dso_filter);
1313 zoom_out_dso:
1314                                 ui_helpline__pop();
1315                                 browser->hists->dso_filter = NULL;
1316                                 sort_dso.elide = false;
1317                         } else {
1318                                 if (dso == NULL)
1319                                         continue;
1320                                 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1321                                                    dso->kernel ? "the Kernel" : dso->short_name);
1322                                 browser->hists->dso_filter = dso;
1323                                 sort_dso.elide = true;
1324                                 pstack__push(fstack, &browser->hists->dso_filter);
1325                         }
1326                         hists__filter_by_dso(hists);
1327                         hist_browser__reset(browser);
1328                 } else if (choice == zoom_thread) {
1329 zoom_thread:
1330                         if (browser->hists->thread_filter) {
1331                                 pstack__remove(fstack, &browser->hists->thread_filter);
1332 zoom_out_thread:
1333                                 ui_helpline__pop();
1334                                 browser->hists->thread_filter = NULL;
1335                                 sort_thread.elide = false;
1336                         } else {
1337                                 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1338                                                    thread->comm_set ? thread->comm : "",
1339                                                    thread->pid);
1340                                 browser->hists->thread_filter = thread;
1341                                 sort_thread.elide = true;
1342                                 pstack__push(fstack, &browser->hists->thread_filter);
1343                         }
1344                         hists__filter_by_thread(hists);
1345                         hist_browser__reset(browser);
1346                 }
1347         }
1348 out_free_stack:
1349         pstack__delete(fstack);
1350 out:
1351         hist_browser__delete(browser);
1352         free_popup_options(options, nr_options - 1);
1353         return key;
1354 }
1355
1356 struct perf_evsel_menu {
1357         struct ui_browser b;
1358         struct perf_evsel *selection;
1359         bool lost_events, lost_events_warned;
1360 };
1361
1362 static void perf_evsel_menu__write(struct ui_browser *browser,
1363                                    void *entry, int row)
1364 {
1365         struct perf_evsel_menu *menu = container_of(browser,
1366                                                     struct perf_evsel_menu, b);
1367         struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1368         bool current_entry = ui_browser__is_current_entry(browser, row);
1369         unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1370         const char *ev_name = perf_evsel__name(evsel);
1371         char bf[256], unit;
1372         const char *warn = " ";
1373         size_t printed;
1374
1375         ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
1376                                                        HE_COLORSET_NORMAL);
1377
1378         nr_events = convert_unit(nr_events, &unit);
1379         printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
1380                            unit, unit == ' ' ? "" : " ", ev_name);
1381         slsmg_printf("%s", bf);
1382
1383         nr_events = evsel->hists.stats.nr_events[PERF_RECORD_LOST];
1384         if (nr_events != 0) {
1385                 menu->lost_events = true;
1386                 if (!current_entry)
1387                         ui_browser__set_color(browser, HE_COLORSET_TOP);
1388                 nr_events = convert_unit(nr_events, &unit);
1389                 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
1390                                      nr_events, unit, unit == ' ' ? "" : " ");
1391                 warn = bf;
1392         }
1393
1394         slsmg_write_nstring(warn, browser->width - printed);
1395
1396         if (current_entry)
1397                 menu->selection = evsel;
1398 }
1399
1400 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
1401                                 int nr_events, const char *help,
1402                                 void(*timer)(void *arg), void *arg, int delay_secs)
1403 {
1404         struct perf_evlist *evlist = menu->b.priv;
1405         struct perf_evsel *pos;
1406         const char *ev_name, *title = "Available samples";
1407         int key;
1408
1409         if (ui_browser__show(&menu->b, title,
1410                              "ESC: exit, ENTER|->: Browse histograms") < 0)
1411                 return -1;
1412
1413         while (1) {
1414                 key = ui_browser__run(&menu->b, delay_secs);
1415
1416                 switch (key) {
1417                 case K_TIMER:
1418                         timer(arg);
1419
1420                         if (!menu->lost_events_warned && menu->lost_events) {
1421                                 ui_browser__warn_lost_events(&menu->b);
1422                                 menu->lost_events_warned = true;
1423                         }
1424                         continue;
1425                 case K_RIGHT:
1426                 case K_ENTER:
1427                         if (!menu->selection)
1428                                 continue;
1429                         pos = menu->selection;
1430 browse_hists:
1431                         perf_evlist__set_selected(evlist, pos);
1432                         /*
1433                          * Give the calling tool a chance to populate the non
1434                          * default evsel resorted hists tree.
1435                          */
1436                         if (timer)
1437                                 timer(arg);
1438                         ev_name = perf_evsel__name(pos);
1439                         key = perf_evsel__hists_browse(pos, nr_events, help,
1440                                                        ev_name, true, timer,
1441                                                        arg, delay_secs);
1442                         ui_browser__show_title(&menu->b, title);
1443                         switch (key) {
1444                         case K_TAB:
1445                                 if (pos->node.next == &evlist->entries)
1446                                         pos = list_entry(evlist->entries.next, struct perf_evsel, node);
1447                                 else
1448                                         pos = list_entry(pos->node.next, struct perf_evsel, node);
1449                                 goto browse_hists;
1450                         case K_UNTAB:
1451                                 if (pos->node.prev == &evlist->entries)
1452                                         pos = list_entry(evlist->entries.prev, struct perf_evsel, node);
1453                                 else
1454                                         pos = list_entry(pos->node.prev, struct perf_evsel, node);
1455                                 goto browse_hists;
1456                         case K_ESC:
1457                                 if (!ui_browser__dialog_yesno(&menu->b,
1458                                                 "Do you really want to exit?"))
1459                                         continue;
1460                                 /* Fall thru */
1461                         case 'q':
1462                         case CTRL('c'):
1463                                 goto out;
1464                         default:
1465                                 continue;
1466                         }
1467                 case K_LEFT:
1468                         continue;
1469                 case K_ESC:
1470                         if (!ui_browser__dialog_yesno(&menu->b,
1471                                                "Do you really want to exit?"))
1472                                 continue;
1473                         /* Fall thru */
1474                 case 'q':
1475                 case CTRL('c'):
1476                         goto out;
1477                 default:
1478                         continue;
1479                 }
1480         }
1481
1482 out:
1483         ui_browser__hide(&menu->b);
1484         return key;
1485 }
1486
1487 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
1488                                            const char *help,
1489                                            void(*timer)(void *arg), void *arg,
1490                                            int delay_secs)
1491 {
1492         struct perf_evsel *pos;
1493         struct perf_evsel_menu menu = {
1494                 .b = {
1495                         .entries    = &evlist->entries,
1496                         .refresh    = ui_browser__list_head_refresh,
1497                         .seek       = ui_browser__list_head_seek,
1498                         .write      = perf_evsel_menu__write,
1499                         .nr_entries = evlist->nr_entries,
1500                         .priv       = evlist,
1501                 },
1502         };
1503
1504         ui_helpline__push("Press ESC to exit");
1505
1506         list_for_each_entry(pos, &evlist->entries, node) {
1507                 const char *ev_name = perf_evsel__name(pos);
1508                 size_t line_len = strlen(ev_name) + 7;
1509
1510                 if (menu.b.width < line_len)
1511                         menu.b.width = line_len;
1512         }
1513
1514         return perf_evsel_menu__run(&menu, evlist->nr_entries, help, timer,
1515                                     arg, delay_secs);
1516 }
1517
1518 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
1519                                   void(*timer)(void *arg), void *arg,
1520                                   int delay_secs)
1521 {
1522         if (evlist->nr_entries == 1) {
1523                 struct perf_evsel *first = list_entry(evlist->entries.next,
1524                                                       struct perf_evsel, node);
1525                 const char *ev_name = perf_evsel__name(first);
1526                 return perf_evsel__hists_browse(first, evlist->nr_entries, help,
1527                                                 ev_name, false, timer, arg,
1528                                                 delay_secs);
1529         }
1530
1531         return __perf_evlist__tui_browse_hists(evlist, help,
1532                                                timer, arg, delay_secs);
1533 }